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}
16 \def\psqrt#1{\sqrt{\mathstrut#1}}
18 \def\pct!{{\char`\%}} % percent sign in ordinary text
19 \font\tenlogo=logo10 % font used for the METAFONT logo
21 \def\MF{{\tenlogo META}\-{\tenlogo FONT}}
22 \def\MP{{\tenlogo META}\-{\tenlogo POST}}
23 \def\[#1]{\ignorespaces} % left over from pascal web
24 \def\<#1>{$\langle#1\rangle$}
25 \def\section{\mathhexbox278}
26 \let\swap=\leftrightarrow
27 \def\round{\mathop{\rm round}\nolimits}
28 \mathchardef\vb="026A % synonym for `\|'
30 \def\(#1){} % this is used to make section names sort themselves better
31 \def\9#1{} % this is used for sort keys in the index via @@:sort key}{entry@@>
38 This is \MP, a graphics-language processor based on D. E. Knuth's \MF.
40 The main purpose of the following program is to explain the algorithms of \MP\
41 as clearly as possible. However, the program has been written so that it
42 can be tuned to run efficiently in a wide variety of operating environments
43 by making comparatively few changes. Such flexibility is possible because
44 the documentation that follows is written in the \.{WEB} language, which is
45 at a higher level than C.
47 A large piece of software like \MP\ has inherent complexity that cannot
48 be reduced below a certain level of difficulty, although each individual
49 part is fairly simple by itself. The \.{WEB} language is intended to make
50 the algorithms as readable as possible, by reflecting the way the
51 individual program pieces fit together and by providing the
52 cross-references that connect different parts. Detailed comments about
53 what is going on, and about why things were done in certain ways, have
54 been liberally sprinkled throughout the program. These comments explain
55 features of the implementation, but they rarely attempt to explain the
56 \MP\ language itself, since the reader is supposed to be familiar with
57 {\sl The {\logos METAFONT\/}book} as well as the manual
59 @:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
60 {\sl A User's Manual for MetaPost}, Computing Science Technical Report 162,
61 AT\AM T Bell Laboratories.
63 @ The present implementation is a preliminary version, but the possibilities
64 for new features are limited by the desire to remain as nearly compatible
65 with \MF\ as possible.
67 On the other hand, the \.{WEB} description can be extended without changing
68 the core of the program, and it has been designed so that such
69 extensions are not extremely difficult to make.
70 The |banner| string defined here should be changed whenever \MP\
71 undergoes any modifications, so that it will be clear which version of
72 \MP\ might be the guilty party when a problem arises.
74 @^system dependencies@>
76 @d banner "This is MetaPost, Version 1.002" /* printed when \MP\ starts */
77 @d metapost_version "1.002"
78 @d mplib_version "0.20"
79 @d version_string " (Cweb version 0.20)"
84 @ The external library header for \MP\ is |mplib.h|. It contains a
85 few typedefs and the header defintions for the externally used
88 The most important of the typedefs is the definition of the structure
89 |MP_options|, that acts as a small, configurable front-end to the fairly
90 large |MP_instance| structure.
93 typedef struct MP_instance * MP;
95 typedef struct MP_options {
98 @<Exported function headers@>
100 @ The internal header file is much longer: it not only lists the complete
101 |MP_instance|, but also a lot of functions that have to be available to
102 the \ps\ backend, that is defined in a separate \.{WEB} file.
104 The variables from |MP_options| are included inside the |MP_instance|
109 typedef struct psout_data_struct * psout_data;
111 typedef signed int integer;
113 @<Types in the outer block@>;
114 @<Constants in the outer block@>
115 # ifndef LIBAVL_ALLOCATOR
116 # define LIBAVL_ALLOCATOR
117 struct libavl_allocator {
118 void *(*libavl_malloc) (struct libavl_allocator *, size_t libavl_size);
119 void (*libavl_free) (struct libavl_allocator *, void *libavl_block);
122 typedef struct MP_instance {
126 @<Internal library declarations@>
134 #include <unistd.h> /* for access() */
135 #include <time.h> /* for struct tm \& co */
137 #include "mpmp.h" /* internal header */
138 #include "mppsout.h" /* internal header */
141 @<Basic printing procedures@>
142 @<Error handling procedures@>
144 @ Here are the functions that set up the \MP\ instance.
147 @<Declare |mp_reallocate| functions@>;
148 struct MP_options *mp_options (void);
149 MP mp_new (struct MP_options *opt);
152 struct MP_options *mp_options (void) {
153 struct MP_options *opt;
154 opt = malloc(sizeof(MP_options));
156 memset (opt,0,sizeof(MP_options));
161 @ The |__attribute__| pragma is gcc-only.
163 @<Internal library ... @>=
164 #if !defined(__GNUC__) || (__GNUC__ < 2)
165 # define __attribute__(x)
166 #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
169 MP __attribute__ ((noinline))
170 mp_new (struct MP_options *opt) {
172 mp = xmalloc(1,sizeof(MP_instance));
173 @<Set |ini_version|@>;
174 @<Setup the non-local jump buffer in |mp_new|@>;
175 @<Allocate or initialize variables@>
176 if (opt->main_memory>mp->mem_max)
177 mp_reallocate_memory(mp,opt->main_memory);
178 mp_reallocate_paths(mp,1000);
179 mp_reallocate_fonts(mp,8);
184 void mp_free (MP mp) {
185 int k; /* loop variable */
186 @<Dealloc variables@>
191 void __attribute__((noinline))
192 mp_do_initialize ( MP mp) {
193 @<Local variables for initialization@>
194 @<Set initial values of key variables@>
196 int mp_initialize (MP mp) { /* this procedure gets things started properly */
197 mp->history=mp_fatal_error_stop; /* in case we quit during initialization */
198 @<Install and test the non-local jump buffer@>;
199 t_open_out; /* open the terminal for output */
200 @<Check the ``constant'' values...@>;
203 snprintf(ss,256,"Ouch---my internal constants have been clobbered!\n"
204 "---case %i",(int)mp->bad);
205 do_fprintf(mp->err_out,(char *)ss);
209 mp_do_initialize(mp); /* erase preloaded mem */
210 if (mp->ini_version) {
211 @<Run inimpost commands@>;
213 @<Initialize the output routines@>;
214 @<Get the first line of input and prepare to start@>;
216 mp_init_map_file(mp, mp->troff_mode);
217 mp->history=mp_spotless; /* ready to go! */
218 if (mp->troff_mode) {
219 mp->internal[mp_gtroffmode]=unity;
220 mp->internal[mp_prologues]=unity;
222 if ( mp->start_sym>0 ) { /* insert the `\&{everyjob}' symbol */
223 mp->cur_sym=mp->start_sym; mp_back_input(mp);
229 @<Exported function headers@>=
230 extern struct MP_options *mp_options (void);
231 extern MP mp_new (struct MP_options *opt) ;
232 extern void mp_free (MP mp);
233 extern int mp_initialize (MP mp);
235 @ The overall \MP\ program begins with the heading just shown, after which
236 comes a bunch of procedure declarations and function declarations.
237 Finally we will get to the main program, which begins with the
238 comment `|start_here|'. If you want to skip down to the
239 main program now, you can look up `|start_here|' in the index.
240 But the author suggests that the best way to understand this program
241 is to follow pretty much the order of \MP's components as they appear in the
242 \.{WEB} description you are now reading, since the present ordering is
243 intended to combine the advantages of the ``bottom up'' and ``top down''
244 approaches to the problem of understanding a somewhat complicated system.
246 @ Some of the code below is intended to be used only when diagnosing the
247 strange behavior that sometimes occurs when \MP\ is being installed or
248 when system wizards are fooling around with \MP\ without quite knowing
249 what they are doing. Such code will not normally be compiled; it is
250 delimited by the preprocessor test `|#ifdef DEBUG .. #endif|'.
252 @ This program has two important variations: (1) There is a long and slow
253 version called \.{INIMP}, which does the extra calculations needed to
255 initialize \MP's internal tables; and (2)~there is a shorter and faster
256 production version, which cuts the initialization to a bare minimum.
258 Which is which is decided at runtime.
260 @ The following parameters can be changed at compile time to extend or
261 reduce \MP's capacity. They may have different values in \.{INIMP} and
262 in production versions of \MP.
264 @^system dependencies@>
267 #define file_name_size 255 /* file names shouldn't be longer than this */
268 #define bistack_size 1500 /* size of stack for bisection algorithms;
269 should probably be left at this value */
271 @ Like the preceding parameters, the following quantities can be changed
272 at compile time to extend or reduce \MP's capacity. But if they are changed,
273 it is necessary to rerun the initialization program \.{INIMP}
275 to generate new tables for the production \MP\ program.
276 One can't simply make helter-skelter changes to the following constants,
277 since certain rather complex initialization
278 numbers are computed from them.
281 int max_strings; /* maximum number of strings; must not exceed |max_halfword| */
282 int pool_size; /* maximum number of characters in strings, including all
283 error messages and help texts, and the names of all identifiers */
284 int mem_max; /* greatest index in \MP's internal |mem| array;
285 must be strictly less than |max_halfword|;
286 must be equal to |mem_top| in \.{INIMP}, otherwise |>=mem_top| */
287 int mem_top; /* largest index in the |mem| array dumped by \.{INIMP};
288 must not be greater than |mem_max| */
290 @ @<Option variables@>=
291 int error_line; /* width of context lines on terminal error messages */
292 int half_error_line; /* width of first lines of contexts in terminal
293 error messages; should be between 30 and |error_line-15| */
294 int max_print_line; /* width of longest text lines output; should be at least 60 */
295 int hash_size; /* maximum number of symbolic tokens,
296 must be less than |max_halfword-3*param_size| */
297 int hash_prime; /* a prime number equal to about 85\pct! of |hash_size| */
298 int param_size; /* maximum number of simultaneous macro parameters */
299 int max_in_open; /* maximum number of input files and error insertions that
300 can be going on simultaneously */
301 int main_memory; /* only for options, to set up |mem_max| and |mem_top| */
302 void *userdata; /* this allows the calling application to setup local */
305 @d set_value(a,b,c) do { a=c; if (b>c) a=b; } while (0)
310 set_value(mp->error_line,opt->error_line,79);
311 set_value(mp->half_error_line,opt->half_error_line,50);
312 set_value(mp->max_print_line,opt->max_print_line,100);
313 mp->main_memory=5000;
316 set_value(mp->hash_size,opt->hash_size,9500);
317 set_value(mp->hash_prime,opt->hash_prime,7919);
318 set_value(mp->param_size,opt->param_size,150);
319 set_value(mp->max_in_open,opt->max_in_open,10);
320 mp->userdata=opt->userdata;
322 @ In case somebody has inadvertently made bad settings of the ``constants,''
323 \MP\ checks them using a global variable called |bad|.
325 This is the first of many sections of \MP\ where global variables are
329 integer bad; /* is some ``constant'' wrong? */
331 @ Later on we will say `\ignorespaces|if (mem_max>=max_halfword) bad=10;|',
332 or something similar. (We can't do that until |max_halfword| has been defined.)
334 @<Check the ``constant'' values for consistency@>=
336 if ( (mp->half_error_line<30)||(mp->half_error_line>mp->error_line-15) ) mp->bad=1;
337 if ( mp->max_print_line<60 ) mp->bad=2;
338 if ( mp->mem_top<=1100 ) mp->bad=4;
339 if (mp->hash_prime>mp->hash_size ) mp->bad=5;
341 @ Some |goto| labels are used by the following definitions. The label
342 `|restart|' is occasionally used at the very beginning of a procedure; and
343 the label `|reswitch|' is occasionally used just prior to a |case|
344 statement in which some cases change the conditions and we wish to branch
345 to the newly applicable case. Loops that are set up with the |loop|
346 construction defined below are commonly exited by going to `|done|' or to
347 `|found|' or to `|not_found|', and they are sometimes repeated by going to
348 `|continue|'. If two or more parts of a subroutine start differently but
349 end up the same, the shared code may be gathered together at
352 @ Here are some macros for common programming idioms.
354 @d incr(A) (A)=(A)+1 /* increase a variable by unity */
355 @d decr(A) (A)=(A)-1 /* decrease a variable by unity */
356 @d negate(A) (A)=-(A) /* change the sign of a variable */
357 @d double(A) (A)=(A)+(A)
360 @d do_nothing /* empty statement */
361 @d Return goto exit /* terminate a procedure call */
362 @f return nil /* \.{WEB} will henceforth say |return| instead of \\{return} */
364 @* \[2] The character set.
365 In order to make \MP\ readily portable to a wide variety of
366 computers, all of its input text is converted to an internal eight-bit
367 code that includes standard ASCII, the ``American Standard Code for
368 Information Interchange.'' This conversion is done immediately when each
369 character is read in. Conversely, characters are converted from ASCII to
370 the user's external representation just before they are output to a
374 Such an internal code is relevant to users of \MP\ only with respect to
375 the \&{char} and \&{ASCII} operations, and the comparison of strings.
377 @ Characters of text that have been converted to \MP's internal form
378 are said to be of type |ASCII_code|, which is a subrange of the integers.
381 typedef unsigned char ASCII_code; /* eight-bit numbers */
383 @ The present specification of \MP\ has been written under the assumption
384 that the character set contains at least the letters and symbols associated
385 with ASCII codes 040 through 0176; all of these characters are now
386 available on most computer terminals.
388 We shall use the name |text_char| to stand for the data type of the characters
389 that are converted to and from |ASCII_code| when they are input and output.
390 We shall also assume that |text_char| consists of the elements
391 |chr(first_text_char)| through |chr(last_text_char)|, inclusive.
392 The following definitions should be adjusted if necessary.
393 @^system dependencies@>
395 @d first_text_char 0 /* ordinal number of the smallest element of |text_char| */
396 @d last_text_char 255 /* ordinal number of the largest element of |text_char| */
399 typedef unsigned char text_char; /* the data type of characters in text files */
401 @ @<Local variables for init...@>=
404 @ The \MP\ processor converts between ASCII code and
405 the user's external character set by means of arrays |xord| and |xchr|
406 that are analogous to Pascal's |ord| and |chr| functions.
408 @d xchr(A) mp->xchr[(A)]
409 @d xord(A) mp->xord[(A)]
412 ASCII_code xord[256]; /* specifies conversion of input characters */
413 text_char xchr[256]; /* specifies conversion of output characters */
415 @ The core system assumes all 8-bit is acceptable. If it is not,
416 a change file has to alter the below section.
417 @^system dependencies@>
419 Additionally, people with extended character sets can
420 assign codes arbitrarily, giving an |xchr| equivalent to whatever
421 characters the users of \MP\ are allowed to have in their input files.
422 Appropriate changes to \MP's |char_class| table should then be made.
423 (Unlike \TeX, each installation of \MP\ has a fixed assignment of category
424 codes, called the |char_class|.) Such changes make portability of programs
425 more difficult, so they should be introduced cautiously if at all.
426 @^character set dependencies@>
427 @^system dependencies@>
430 for (i=0;i<=0377;i++) { xchr(i)=i; }
432 @ The following system-independent code makes the |xord| array contain a
433 suitable inverse to the information in |xchr|. Note that if |xchr[i]=xchr[j]|
434 where |i<j<0177|, the value of |xord[xchr[i]]| will turn out to be
435 |j| or more; hence, standard ASCII code numbers will be used instead of
436 codes below 040 in case there is a coincidence.
439 for (i=first_text_char;i<=last_text_char;i++) {
442 for (i=0200;i<=0377;i++) { xord(xchr(i))=i;}
443 for (i=0;i<=0176;i++) { xord(xchr(i))=i;}
445 @* \[3] Input and output.
446 The bane of portability is the fact that different operating systems treat
447 input and output quite differently, perhaps because computer scientists
448 have not given sufficient attention to this problem. People have felt somehow
449 that input and output are not part of ``real'' programming. Well, it is true
450 that some kinds of programming are more fun than others. With existing
451 input/output conventions being so diverse and so messy, the only sources of
452 joy in such parts of the code are the rare occasions when one can find a
453 way to make the program a little less bad than it might have been. We have
454 two choices, either to attack I/O now and get it over with, or to postpone
455 I/O until near the end. Neither prospect is very attractive, so let's
458 The basic operations we need to do are (1)~inputting and outputting of
459 text, to or from a file or the user's terminal; (2)~inputting and
460 outputting of eight-bit bytes, to or from a file; (3)~instructing the
461 operating system to initiate (``open'') or to terminate (``close'') input or
462 output from a specified file; (4)~testing whether the end of an input
463 file has been reached; (5)~display of bits on the user's screen.
464 The bit-display operation will be discussed in a later section; we shall
465 deal here only with more traditional kinds of I/O.
467 @ Finding files happens in a slightly roundabout fashion: the \MP\
468 instance object contains a field that holds a function pointer that finds a
469 file, and returns its name, or NULL. For this, it receives three
470 parameters: the non-qualified name |fname|, the intended |fopen|
471 operation type |fmode|, and the type of the file |ftype|.
473 The file types that are passed on in |ftype| can be used to
474 differentiate file searches if a library like kpathsea is used,
475 the fopen mode is passed along for the same reason.
478 typedef unsigned char eight_bits ; /* unsigned one-byte quantity */
480 @ @<Exported types@>=
482 mp_filetype_terminal = 0, /* the terminal */
483 mp_filetype_error, /* the terminal */
484 mp_filetype_program , /* \MP\ language input */
485 mp_filetype_log, /* the log file */
486 mp_filetype_postscript, /* the postscript output */
487 mp_filetype_memfile, /* memory dumps */
488 mp_filetype_metrics, /* TeX font metric files */
489 mp_filetype_fontmap, /* PostScript font mapping files */
490 mp_filetype_font, /* PostScript type1 font programs */
491 mp_filetype_encoding, /* PostScript font encoding files */
492 mp_filetype_text, /* first text file for readfrom and writeto primitives */
494 typedef char *(*mp_file_finder)(MP, char *, char *, int);
495 typedef void *(*mp_file_opener)(MP, char *, char *, int);
496 typedef char *(*mp_file_reader)(MP, void *, size_t *);
497 typedef void (*mp_binfile_reader)(MP, void *, void **, size_t *);
498 typedef void (*mp_file_closer)(MP, void *);
499 typedef int (*mp_file_eoftest)(MP, void *);
500 typedef void (*mp_file_flush)(MP, void *);
501 typedef void (*mp_file_writer)(MP, void *, char *);
502 typedef void (*mp_binfile_writer)(MP, void *, void *, size_t);
505 @ @<Option variables@>=
506 mp_file_finder find_file;
507 mp_file_opener open_file;
508 mp_file_reader read_ascii_file;
509 mp_binfile_reader read_binary_file;
510 mp_file_closer close_file;
511 mp_file_eoftest eof_file;
512 mp_file_flush flush_file;
513 mp_file_writer write_ascii_file;
514 mp_binfile_writer write_binary_file;
516 @ The default function for finding files is |mp_find_file|. It is
517 pretty stupid: it will only find files in the current directory.
519 This function may disappear altogether, it is currently only
520 used for the default font map file.
523 char *mp_find_file (MP mp, char *fname, char *fmode, int ftype) {
524 if (fmode[0] != 'r' || (! access (fname,R_OK)) || ftype) {
525 return strdup(fname);
530 @ This has to be done very early on, so it is best to put it in with
531 the |mp_new| allocations
533 @d set_callback_option(A) do { mp->A = mp_##A;
534 if (opt->A!=NULL) mp->A = opt->A;
537 @<Allocate or initialize ...@>=
538 set_callback_option(find_file);
539 set_callback_option(open_file);
540 set_callback_option(read_ascii_file);
541 set_callback_option(read_binary_file);
542 set_callback_option(close_file);
543 set_callback_option(eof_file);
544 set_callback_option(flush_file);
545 set_callback_option(write_ascii_file);
546 set_callback_option(write_binary_file);
548 @ Because |mp_find_file| is used so early, it has to be in the helpers
552 char *mp_find_file (MP mp, char *fname, char *fmode, int ftype) ;
553 void *mp_open_file (MP mp ,char *fname, char *fmode, int ftype) ;
554 char *mp_read_ascii_file (MP mp, void *f, size_t *size) ;
555 void mp_read_binary_file (MP mp, void *f, void **d, size_t *size) ;
556 void mp_close_file (MP mp, void *f) ;
557 int mp_eof_file (MP mp, void *f) ;
558 void mp_flush_file (MP mp, void *f) ;
559 void mp_write_ascii_file (MP mp, void *f, char *s) ;
560 void mp_write_binary_file (MP mp, void *f, void *s, size_t t) ;
562 @ The function to open files can now be very short.
565 void *mp_open_file(MP mp, char *fname, char *fmode, int ftype) {
567 if (ftype==mp_filetype_terminal) {
568 return (fmode[0] == 'r' ? stdin : stdout);
569 } else if (ftype==mp_filetype_error) {
571 } else if (fname != NULL && (fmode[0] != 'r' || (! access (fname,R_OK)))) {
572 return (void *)fopen(fname, fmode);
578 @ This is a legacy interface: (almost) all file names pass through |name_of_file|.
581 char name_of_file[file_name_size+1]; /* the name of a system file */
582 int name_length;/* this many characters are actually
583 relevant in |name_of_file| (the rest are blank) */
585 @ @<Option variables@>=
586 int print_found_names; /* configuration parameter */
588 @ If this parameter is true, the terminal and log will report the found
589 file names for input files instead of the requested ones.
590 It is off by default because it creates an extra filename lookup.
592 @<Allocate or initialize ...@>=
593 mp->print_found_names = (opt->print_found_names>0 ? true : false);
595 @ \MP's file-opening procedures return |false| if no file identified by
596 |name_of_file| could be opened.
598 The |OPEN_FILE| macro takes care of the |print_found_names| parameter.
599 It is not used for opening a mem file for read, because that file name
603 if (mp->print_found_names) {
604 char *s = (mp->find_file)(mp,mp->name_of_file,A,ftype);
606 *f = (mp->open_file)(mp,mp->name_of_file,A, ftype);
607 strncpy(mp->name_of_file,s,file_name_size);
613 *f = (mp->open_file)(mp,mp->name_of_file,A, ftype);
616 return (*f ? true : false)
619 boolean mp_a_open_in (MP mp, void **f, int ftype) {
620 /* open a text file for input */
624 boolean mp_w_open_in (MP mp, void **f) {
625 /* open a word file for input */
626 *f = (mp->open_file)(mp,mp->name_of_file,"rb",mp_filetype_memfile);
627 return (*f ? true : false);
630 boolean mp_a_open_out (MP mp, void **f, int ftype) {
631 /* open a text file for output */
635 boolean mp_b_open_out (MP mp, void **f, int ftype) {
636 /* open a binary file for output */
640 boolean mp_w_open_out (MP mp, void **f) {
641 /* open a word file for output */
642 int ftype = mp_filetype_memfile;
647 char *mp_read_ascii_file (MP mp, void *ff, size_t *size) {
649 size_t len = 0, lim = 128;
651 FILE *f = (FILE *)ff;
658 if (s==NULL) return NULL;
659 while (c!=EOF && c!='\n' && c!='\r') {
661 s =realloc(s, (lim+(lim>>2)));
662 if (s==NULL) return NULL;
670 if (c!=EOF && c!='\n')
680 void mp_write_ascii_file (MP mp, void *f, char *s) {
689 void mp_read_binary_file (MP mp, void *f, void **data, size_t *size) {
692 len = fread(*data,1,*size,(FILE *)f);
698 void mp_write_binary_file (MP mp, void *f, void *s, size_t size) {
701 fwrite(s,size,1,(FILE *)f);
707 void mp_close_file (MP mp, void *f) {
714 int mp_eof_file (MP mp, void *f) {
716 return feof((FILE *)f);
723 void mp_flush_file (MP mp, void *f) {
729 @ Input from text files is read one line at a time, using a routine called
730 |input_ln|. This function is defined in terms of global variables called
731 |buffer|, |first|, and |last| that will be described in detail later; for
732 now, it suffices for us to know that |buffer| is an array of |ASCII_code|
733 values, and that |first| and |last| are indices into this array
734 representing the beginning and ending of a line of text.
737 size_t buf_size; /* maximum number of characters simultaneously present in
738 current lines of open files */
739 ASCII_code *buffer; /* lines of characters being read */
740 size_t first; /* the first unused position in |buffer| */
741 size_t last; /* end of the line just input to |buffer| */
742 size_t max_buf_stack; /* largest index used in |buffer| */
744 @ @<Allocate or initialize ...@>=
746 mp->buffer = xmalloc((mp->buf_size+1),sizeof(ASCII_code));
748 @ @<Dealloc variables@>=
752 void mp_reallocate_buffer(MP mp, size_t l) {
754 if (l>max_halfword) {
755 mp_confusion(mp,"buffer size"); /* can't happen (I hope) */
757 buffer = xmalloc((l+1),sizeof(ASCII_code));
758 memcpy(buffer,mp->buffer,(mp->buf_size+1));
760 mp->buffer = buffer ;
764 @ The |input_ln| function brings the next line of input from the specified
765 field into available positions of the buffer array and returns the value
766 |true|, unless the file has already been entirely read, in which case it
767 returns |false| and sets |last:=first|. In general, the |ASCII_code|
768 numbers that represent the next line of the file are input into
769 |buffer[first]|, |buffer[first+1]|, \dots, |buffer[last-1]|; and the
770 global variable |last| is set equal to |first| plus the length of the
771 line. Trailing blanks are removed from the line; thus, either |last=first|
772 (in which case the line was entirely blank) or |buffer[last-1]<>" "|.
775 The variable |max_buf_stack|, which is used to keep track of how large
776 the |buf_size| parameter must be to accommodate the present job, is
777 also kept up to date by |input_ln|.
780 boolean mp_input_ln (MP mp, void *f ) {
781 /* inputs the next line or returns |false| */
784 mp->last=mp->first; /* cf.\ Matthew 19\thinspace:\thinspace30 */
785 s = (mp->read_ascii_file)(mp,f, &size);
789 mp->last = mp->first+size;
790 if ( mp->last>=mp->max_buf_stack ) {
791 mp->max_buf_stack=mp->last+1;
792 while ( mp->max_buf_stack>=mp->buf_size ) {
793 mp_reallocate_buffer(mp,(mp->buf_size+(mp->buf_size>>2)));
796 memcpy((mp->buffer+mp->first),s,size);
797 /* while ( mp->buffer[mp->last]==' ' ) mp->last--; */
803 @ The user's terminal acts essentially like other files of text, except
804 that it is used both for input and for output. When the terminal is
805 considered an input file, the file variable is called |term_in|, and when it
806 is considered an output file the file variable is |term_out|.
807 @^system dependencies@>
810 void * term_in; /* the terminal as an input file */
811 void * term_out; /* the terminal as an output file */
812 void * err_out; /* the terminal as an output file */
814 @ Here is how to open the terminal files. In the default configuration,
815 nothing happens except that the command line (if there is one) is copied
816 to the input buffer. The variable |command_line| will be filled by the
817 |main| procedure. The copying can not be done earlier in the program
818 logic because in the |INI| version, the |buffer| is also used for primitive
821 @^system dependencies@>
823 @d t_open_out do {/* open the terminal for text output */
824 mp->term_out = (mp->open_file)(mp,"terminal", "w", mp_filetype_terminal);
825 mp->err_out = (mp->open_file)(mp,"error", "w", mp_filetype_error);
827 @d t_open_in do { /* open the terminal for text input */
828 mp->term_in = (mp->open_file)(mp,"terminal", "r", mp_filetype_terminal);
829 if (mp->command_line!=NULL) {
830 mp->last = strlen(mp->command_line);
831 strncpy((char *)mp->buffer,mp->command_line,mp->last);
832 xfree(mp->command_line);
838 @d t_close_out do { /* close the terminal */
839 (mp->close_file)(mp,mp->term_out);
840 (mp->close_file)(mp,mp->err_out);
843 @d t_close_in do { /* close the terminal */
844 (mp->close_file)(mp,mp->term_in);
847 @<Option variables@>=
850 @ @<Allocate or initialize ...@>=
851 mp->command_line = xstrdup(opt->command_line);
853 @ Sometimes it is necessary to synchronize the input/output mixture that
854 happens on the user's terminal, and three system-dependent
855 procedures are used for this
856 purpose. The first of these, |update_terminal|, is called when we want
857 to make sure that everything we have output to the terminal so far has
858 actually left the computer's internal buffers and been sent.
859 The second, |clear_terminal|, is called when we wish to cancel any
860 input that the user may have typed ahead (since we are about to
861 issue an unexpected error message). The third, |wake_up_terminal|,
862 is supposed to revive the terminal if the user has disabled it by
863 some instruction to the operating system. The following macros show how
864 these operations can be specified:
865 @^system dependencies@>
867 @d update_terminal (mp->flush_file)(mp,mp->term_out) /* empty the terminal output buffer */
868 @d clear_terminal do_nothing /* clear the terminal input buffer */
869 @d wake_up_terminal (mp->flush_file)(mp,mp->term_out)
870 /* cancel the user's cancellation of output */
872 @ We need a special routine to read the first line of \MP\ input from
873 the user's terminal. This line is different because it is read before we
874 have opened the transcript file; there is sort of a ``chicken and
875 egg'' problem here. If the user types `\.{input cmr10}' on the first
876 line, or if some macro invoked by that line does such an \.{input},
877 the transcript file will be named `\.{cmr10.log}'; but if no \.{input}
878 commands are performed during the first line of terminal input, the transcript
879 file will acquire its default name `\.{mpout.log}'. (The transcript file
880 will not contain error messages generated by the first line before the
881 first \.{input} command.)
883 The first line is even more special. It's nice to let the user start
884 running a \MP\ job by typing a command line like `\.{MP cmr10}'; in
885 such a case, \MP\ will operate as if the first line of input were
886 `\.{cmr10}', i.e., the first line will consist of the remainder of the
887 command line, after the part that invoked \MP.
889 @ Different systems have different ways to get started. But regardless of
890 what conventions are adopted, the routine that initializes the terminal
891 should satisfy the following specifications:
893 \yskip\textindent{1)}It should open file |term_in| for input from the
894 terminal. (The file |term_out| will already be open for output to the
897 \textindent{2)}If the user has given a command line, this line should be
898 considered the first line of terminal input. Otherwise the
899 user should be prompted with `\.{**}', and the first line of input
900 should be whatever is typed in response.
902 \textindent{3)}The first line of input, which might or might not be a
903 command line, should appear in locations |first| to |last-1| of the
906 \textindent{4)}The global variable |loc| should be set so that the
907 character to be read next by \MP\ is in |buffer[loc]|. This
908 character should not be blank, and we should have |loc<last|.
910 \yskip\noindent(It may be necessary to prompt the user several times
911 before a non-blank line comes in. The prompt is `\.{**}' instead of the
912 later `\.*' because the meaning is slightly different: `\.{input}' need
913 not be typed immediately after~`\.{**}'.)
915 @d loc mp->cur_input.loc_field /* location of first unread character in |buffer| */
917 @ The following program does the required initialization
918 without retrieving a possible command line.
919 It should be clear how to modify this routine to deal with command lines,
920 if the system permits them.
921 @^system dependencies@>
924 boolean mp_init_terminal (MP mp) { /* gets the terminal input started */
931 if (!mp->noninteractive) {
932 wake_up_terminal; do_fprintf(mp->term_out,"**"); update_terminal;
935 if ( ! mp_input_ln(mp, mp->term_in ) ) { /* this shouldn't happen */
936 do_fprintf(mp->term_out,"\n! End of file on the terminal... why?");
937 @.End of file on the terminal@>
941 while ( (loc<(int)mp->last)&&(mp->buffer[loc]==' ') )
943 if ( loc<(int)mp->last ) {
944 return true; /* return unless the line was all blank */
946 if (!mp->noninteractive) {
947 do_fprintf(mp->term_out,"Please type the name of your input file.\n");
953 boolean mp_init_terminal (MP mp) ;
956 @* \[4] String handling.
957 Symbolic token names and diagnostic messages are variable-length strings
958 of eight-bit characters. Many strings \MP\ uses are simply literals
959 in the compiled source, like the error messages and the names of the
960 internal parameters. Other strings are used or defined from the \MP\ input
961 language, and these have to be interned.
963 \MP\ uses strings more extensively than \MF\ does, but the necessary
964 operations can still be handled with a fairly simple data structure.
965 The array |str_pool| contains all of the (eight-bit) ASCII codes in all
966 of the strings, and the array |str_start| contains indices of the starting
967 points of each string. Strings are referred to by integer numbers, so that
968 string number |s| comprises the characters |str_pool[j]| for
969 |str_start[s]<=j<str_start[ss]| where |ss=next_str[s]|. The string pool
970 is allocated sequentially and |str_pool[pool_ptr]| is the next unused
971 location. The first string number not currently in use is |str_ptr|
972 and |next_str[str_ptr]| begins a list of free string numbers. String
973 pool entries |str_start[str_ptr]| up to |pool_ptr| are reserved for a
974 string currently being constructed.
976 String numbers 0 to 255 are reserved for strings that correspond to single
977 ASCII characters. This is in accordance with the conventions of \.{WEB},
979 which converts single-character strings into the ASCII code number of the
980 single character involved, while it converts other strings into integers
981 and builds a string pool file. Thus, when the string constant \.{"."} appears
982 in the program below, \.{WEB} converts it into the integer 46, which is the
983 ASCII code for a period, while \.{WEB} will convert a string like \.{"hello"}
984 into some integer greater than~255. String number 46 will presumably be the
985 single character `\..'\thinspace; but some ASCII codes have no standard visible
986 representation, and \MP\ may need to be able to print an arbitrary
987 ASCII character, so the first 256 strings are used to specify exactly what
988 should be printed for each of the 256 possibilities.
991 typedef int pool_pointer; /* for variables that point into |str_pool| */
992 typedef int str_number; /* for variables that point into |str_start| */
995 ASCII_code *str_pool; /* the characters */
996 pool_pointer *str_start; /* the starting pointers */
997 str_number *next_str; /* for linking strings in order */
998 pool_pointer pool_ptr; /* first unused position in |str_pool| */
999 str_number str_ptr; /* number of the current string being created */
1000 pool_pointer init_pool_ptr; /* the starting value of |pool_ptr| */
1001 str_number init_str_use; /* the initial number of strings in use */
1002 pool_pointer max_pool_ptr; /* the maximum so far of |pool_ptr| */
1003 str_number max_str_ptr; /* the maximum so far of |str_ptr| */
1005 @ @<Allocate or initialize ...@>=
1006 mp->str_pool = xmalloc ((mp->pool_size +1),sizeof(ASCII_code));
1007 mp->str_start = xmalloc ((mp->max_strings+1),sizeof(pool_pointer));
1008 mp->next_str = xmalloc ((mp->max_strings+1),sizeof(str_number));
1010 @ @<Dealloc variables@>=
1011 xfree(mp->str_pool);
1012 xfree(mp->str_start);
1013 xfree(mp->next_str);
1015 @ Most printing is done from |char *|s, but sometimes not. Here are
1016 functions that convert an internal string into a |char *| for use
1017 by the printing routines, and vice versa.
1019 @d str(A) mp_str(mp,A)
1020 @d rts(A) mp_rts(mp,A)
1023 int mp_xstrcmp (const char *a, const char *b);
1024 char * mp_str (MP mp, str_number s);
1027 str_number mp_rts (MP mp, char *s);
1028 str_number mp_make_string (MP mp);
1030 @ The attempt to catch interrupted strings that is in |mp_rts|, is not
1031 very good: it does not handle nesting over more than one level.
1034 int mp_xstrcmp (const char *a, const char *b) {
1035 if (a==NULL && b==NULL)
1045 char * mp_str (MP mp, str_number ss) {
1048 if (ss==mp->str_ptr) {
1052 s = xmalloc(len+1,sizeof(char));
1053 strncpy(s,(char *)(mp->str_pool+(mp->str_start[ss])),len);
1058 str_number mp_rts (MP mp, char *s) {
1059 int r; /* the new string */
1060 int old; /* a possible string in progress */
1064 } else if (strlen(s)==1) {
1068 str_room((integer)strlen(s));
1069 if (mp->str_start[mp->str_ptr]<mp->pool_ptr)
1070 old = mp_make_string(mp);
1075 r = mp_make_string(mp);
1077 str_room(length(old));
1078 while (i<length(old)) {
1079 append_char((mp->str_start[old]+i));
1081 mp_flush_string(mp,old);
1087 @ Except for |strs_used_up|, the following string statistics are only
1088 maintained when code between |stat| $\ldots$ |tats| delimiters is not
1092 integer strs_used_up; /* strings in use or unused but not reclaimed */
1093 integer pool_in_use; /* total number of cells of |str_pool| actually in use */
1094 integer strs_in_use; /* total number of strings actually in use */
1095 integer max_pl_used; /* maximum |pool_in_use| so far */
1096 integer max_strs_used; /* maximum |strs_in_use| so far */
1098 @ Several of the elementary string operations are performed using \.{WEB}
1099 macros instead of functions, because many of the
1100 operations are done quite frequently and we want to avoid the
1101 overhead of procedure calls. For example, here is
1102 a simple macro that computes the length of a string.
1105 @d str_stop(A) mp->str_start[mp->next_str[(A)]] /* one cell past the end of string
1107 @d length(A) (str_stop((A))-mp->str_start[(A)]) /* the number of characters in string \# */
1109 @ The length of the current string is called |cur_length|. If we decide that
1110 the current string is not needed, |flush_cur_string| resets |pool_ptr| so that
1111 |cur_length| becomes zero.
1113 @d cur_length (mp->pool_ptr - mp->str_start[mp->str_ptr])
1114 @d flush_cur_string mp->pool_ptr=mp->str_start[mp->str_ptr]
1116 @ Strings are created by appending character codes to |str_pool|.
1117 The |append_char| macro, defined here, does not check to see if the
1118 value of |pool_ptr| has gotten too high; this test is supposed to be
1119 made before |append_char| is used.
1121 To test if there is room to append |l| more characters to |str_pool|,
1122 we shall write |str_room(l)|, which tries to make sure there is enough room
1123 by compacting the string pool if necessary. If this does not work,
1124 |do_compaction| aborts \MP\ and gives an apologetic error message.
1126 @d append_char(A) /* put |ASCII_code| \# at the end of |str_pool| */
1127 { mp->str_pool[mp->pool_ptr]=(A); incr(mp->pool_ptr);
1129 @d str_room(A) /* make sure that the pool hasn't overflowed */
1130 { if ( mp->pool_ptr+(A) > mp->max_pool_ptr ) {
1131 if ( mp->pool_ptr+(A) > mp->pool_size ) mp_do_compaction(mp, (A));
1132 else mp->max_pool_ptr=mp->pool_ptr+(A); }
1135 @ The following routine is similar to |str_room(1)| but it uses the
1136 argument |mp->pool_size| to prevent |do_compaction| from aborting when
1137 string space is exhausted.
1139 @<Declare the procedure called |unit_str_room|@>=
1140 void mp_unit_str_room (MP mp);
1143 void mp_unit_str_room (MP mp) {
1144 if ( mp->pool_ptr>=mp->pool_size ) mp_do_compaction(mp, mp->pool_size);
1145 if ( mp->pool_ptr>=mp->max_pool_ptr ) mp->max_pool_ptr=mp->pool_ptr+1;
1148 @ \MP's string expressions are implemented in a brute-force way: Every
1149 new string or substring that is needed is simply copied into the string pool.
1150 Space is eventually reclaimed by a procedure called |do_compaction| with
1151 the aid of a simple system system of reference counts.
1152 @^reference counts@>
1154 The number of references to string number |s| will be |str_ref[s]|. The
1155 special value |str_ref[s]=max_str_ref=127| is used to denote an unknown
1156 positive number of references; such strings will never be recycled. If
1157 a string is ever referred to more than 126 times, simultaneously, we
1158 put it in this category. Hence a single byte suffices to store each |str_ref|.
1160 @d max_str_ref 127 /* ``infinite'' number of references */
1161 @d add_str_ref(A) { if ( mp->str_ref[(A)]<max_str_ref ) incr(mp->str_ref[(A)]);
1167 @ @<Allocate or initialize ...@>=
1168 mp->str_ref = xmalloc ((mp->max_strings+1),sizeof(int));
1170 @ @<Dealloc variables@>=
1173 @ Here's what we do when a string reference disappears:
1175 @d delete_str_ref(A) {
1176 if ( mp->str_ref[(A)]<max_str_ref ) {
1177 if ( mp->str_ref[(A)]>1 ) decr(mp->str_ref[(A)]);
1178 else mp_flush_string(mp, (A));
1182 @<Declare the procedure called |flush_string|@>=
1183 void mp_flush_string (MP mp,str_number s) ;
1186 @ We can't flush the first set of static strings at all, so there
1187 is no point in trying
1190 void mp_flush_string (MP mp,str_number s) {
1192 mp->pool_in_use=mp->pool_in_use-length(s);
1193 decr(mp->strs_in_use);
1194 if ( mp->next_str[s]!=mp->str_ptr ) {
1198 decr(mp->strs_used_up);
1200 mp->pool_ptr=mp->str_start[mp->str_ptr];
1204 @ C literals cannot be simply added, they need to be set so they can't
1207 @d intern(A) mp_intern(mp,(A))
1210 str_number mp_intern (MP mp, char *s) {
1213 mp->str_ref[r] = max_str_ref;
1218 str_number mp_intern (MP mp, char *s);
1221 @ Once a sequence of characters has been appended to |str_pool|, it
1222 officially becomes a string when the function |make_string| is called.
1223 This function returns the identification number of the new string as its
1226 When getting the next unused string number from the linked list, we pretend
1228 $$ \hbox{|max_str_ptr+1|, |max_str_ptr+2|, $\ldots$, |mp->max_strings|} $$
1229 are linked sequentially even though the |next_str| entries have not been
1230 initialized yet. We never allow |str_ptr| to reach |mp->max_strings|;
1231 |do_compaction| is responsible for making sure of this.
1234 @<Declare the procedure called |do_compaction|@>;
1235 @<Declare the procedure called |unit_str_room|@>;
1236 str_number mp_make_string (MP mp);
1239 str_number mp_make_string (MP mp) { /* current string enters the pool */
1240 str_number s; /* the new string */
1243 mp->str_ptr=mp->next_str[s];
1244 if ( mp->str_ptr>mp->max_str_ptr ) {
1245 if ( mp->str_ptr==mp->max_strings ) {
1247 mp_do_compaction(mp, 0);
1251 if ( mp->strs_used_up!=mp->max_str_ptr ) mp_confusion(mp, "s");
1252 @:this can't happen s}{\quad \.s@>
1254 mp->max_str_ptr=mp->str_ptr;
1255 mp->next_str[mp->str_ptr]=mp->max_str_ptr+1;
1259 mp->str_start[mp->str_ptr]=mp->pool_ptr;
1260 incr(mp->strs_used_up);
1261 incr(mp->strs_in_use);
1262 mp->pool_in_use=mp->pool_in_use+length(s);
1263 if ( mp->pool_in_use>mp->max_pl_used )
1264 mp->max_pl_used=mp->pool_in_use;
1265 if ( mp->strs_in_use>mp->max_strs_used )
1266 mp->max_strs_used=mp->strs_in_use;
1270 @ The most interesting string operation is string pool compaction. The idea
1271 is to recover unused space in the |str_pool| array by recopying the strings
1272 to close the gaps created when some strings become unused. All string
1273 numbers~$k$ where |str_ref[k]=0| are to be linked into the list of free string
1274 numbers after |str_ptr|. If this fails to free enough pool space we issue an
1275 |overflow| error unless |needed=mp->pool_size|. Calling |do_compaction|
1276 with |needed=mp->pool_size| supresses all overflow tests.
1278 The compaction process starts with |last_fixed_str| because all lower numbered
1279 strings are permanently allocated with |max_str_ref| in their |str_ref| entries.
1282 str_number last_fixed_str; /* last permanently allocated string */
1283 str_number fixed_str_use; /* number of permanently allocated strings */
1285 @ @<Declare the procedure called |do_compaction|@>=
1286 void mp_do_compaction (MP mp, pool_pointer needed) ;
1289 void mp_do_compaction (MP mp, pool_pointer needed) {
1290 str_number str_use; /* a count of strings in use */
1291 str_number r,s,t; /* strings being manipulated */
1292 pool_pointer p,q; /* destination and source for copying string characters */
1293 @<Advance |last_fixed_str| as far as possible and set |str_use|@>;
1294 r=mp->last_fixed_str;
1297 while ( s!=mp->str_ptr ) {
1298 while ( mp->str_ref[s]==0 ) {
1299 @<Advance |s| and add the old |s| to the list of free string numbers;
1300 then |break| if |s=str_ptr|@>;
1302 r=s; s=mp->next_str[s];
1304 @<Move string |r| back so that |str_start[r]=p|; make |p| the location
1305 after the end of the string@>;
1307 @<Move the current string back so that it starts at |p|@>;
1308 if ( needed<mp->pool_size ) {
1309 @<Make sure that there is room for another string with |needed| characters@>;
1311 @<Account for the compaction and make sure the statistics agree with the
1313 mp->strs_used_up=str_use;
1316 @ @<Advance |last_fixed_str| as far as possible and set |str_use|@>=
1317 t=mp->next_str[mp->last_fixed_str];
1318 while (t!=mp->str_ptr && mp->str_ref[t]==max_str_ref) {
1319 incr(mp->fixed_str_use);
1320 mp->last_fixed_str=t;
1323 str_use=mp->fixed_str_use
1325 @ Because of the way |flush_string| has been written, it should never be
1326 necessary to |break| here. The extra line of code seems worthwhile to
1327 preserve the generality of |do_compaction|.
1329 @<Advance |s| and add the old |s| to the list of free string numbers;...@>=
1334 mp->next_str[t]=mp->next_str[mp->str_ptr];
1335 mp->next_str[mp->str_ptr]=t;
1336 if ( s==mp->str_ptr ) break;
1339 @ The string currently starts at |str_start[r]| and ends just before
1340 |str_start[s]|. We don't change |str_start[s]| because it might be needed
1341 to locate the next string.
1343 @<Move string |r| back so that |str_start[r]=p|; make |p| the location...@>=
1346 while ( q<mp->str_start[s] ) {
1347 mp->str_pool[p]=mp->str_pool[q];
1351 @ Pointers |str_start[str_ptr]| and |pool_ptr| have not been updated. When
1352 we do this, anything between them should be moved.
1354 @ @<Move the current string back so that it starts at |p|@>=
1355 q=mp->str_start[mp->str_ptr];
1356 mp->str_start[mp->str_ptr]=p;
1357 while ( q<mp->pool_ptr ) {
1358 mp->str_pool[p]=mp->str_pool[q];
1363 @ We must remember that |str_ptr| is not allowed to reach |mp->max_strings|.
1365 @<Make sure that there is room for another string with |needed| char...@>=
1366 if ( str_use>=mp->max_strings-1 )
1367 mp_reallocate_strings (mp,str_use);
1368 if ( mp->pool_ptr+needed>mp->max_pool_ptr ) {
1369 mp_reallocate_pool(mp, mp->pool_ptr+needed);
1370 mp->max_pool_ptr=mp->pool_ptr+needed;
1374 void mp_reallocate_strings (MP mp, str_number str_use) ;
1375 void mp_reallocate_pool(MP mp, pool_pointer needed) ;
1378 void mp_reallocate_strings (MP mp, str_number str_use) {
1379 while ( str_use>=mp->max_strings-1 ) {
1380 int l = mp->max_strings + (mp->max_strings>>2);
1381 XREALLOC (mp->str_ref, l, int);
1382 XREALLOC (mp->str_start, l, pool_pointer);
1383 XREALLOC (mp->next_str, l, str_number);
1384 mp->max_strings = l;
1387 void mp_reallocate_pool(MP mp, pool_pointer needed) {
1388 while ( needed>mp->pool_size ) {
1389 int l = mp->pool_size + (mp->pool_size>>2);
1390 XREALLOC (mp->str_pool, l, ASCII_code);
1395 @ @<Account for the compaction and make sure the statistics agree with...@>=
1396 if ( (mp->str_start[mp->str_ptr]!=mp->pool_in_use)||(str_use!=mp->strs_in_use) )
1397 mp_confusion(mp, "string");
1398 @:this can't happen string}{\quad string@>
1399 incr(mp->pact_count);
1400 mp->pact_chars=mp->pact_chars+mp->pool_ptr-str_stop(mp->last_fixed_str);
1401 mp->pact_strs=mp->pact_strs+str_use-mp->fixed_str_use;
1403 s=mp->str_ptr; t=str_use;
1404 while ( s<=mp->max_str_ptr ){
1405 if ( t>mp->max_str_ptr ) mp_confusion(mp, "\"");
1406 incr(t); s=mp->next_str[s];
1408 if ( t<=mp->max_str_ptr ) mp_confusion(mp, "\"");
1411 @ A few more global variables are needed to keep track of statistics when
1412 |stat| $\ldots$ |tats| blocks are not commented out.
1415 integer pact_count; /* number of string pool compactions so far */
1416 integer pact_chars; /* total number of characters moved during compactions */
1417 integer pact_strs; /* total number of strings moved during compactions */
1419 @ @<Initialize compaction statistics@>=
1424 @ The following subroutine compares string |s| with another string of the
1425 same length that appears in |buffer| starting at position |k|;
1426 the result is |true| if and only if the strings are equal.
1429 boolean mp_str_eq_buf (MP mp,str_number s, integer k) {
1430 /* test equality of strings */
1431 pool_pointer j; /* running index */
1433 while ( j<str_stop(s) ) {
1434 if ( mp->str_pool[j++]!=mp->buffer[k++] )
1440 @ Here is a similar routine, but it compares two strings in the string pool,
1441 and it does not assume that they have the same length. If the first string
1442 is lexicographically greater than, less than, or equal to the second,
1443 the result is respectively positive, negative, or zero.
1446 integer mp_str_vs_str (MP mp, str_number s, str_number t) {
1447 /* test equality of strings */
1448 pool_pointer j,k; /* running indices */
1449 integer ls,lt; /* lengths */
1450 integer l; /* length remaining to test */
1451 ls=length(s); lt=length(t);
1452 if ( ls<=lt ) l=ls; else l=lt;
1453 j=mp->str_start[s]; k=mp->str_start[t];
1455 if ( mp->str_pool[j]!=mp->str_pool[k] ) {
1456 return (mp->str_pool[j]-mp->str_pool[k]);
1463 @ The initial values of |str_pool|, |str_start|, |pool_ptr|,
1464 and |str_ptr| are computed by the \.{INIMP} program, based in part
1465 on the information that \.{WEB} has output while processing \MP.
1470 void mp_get_strings_started (MP mp) {
1471 /* initializes the string pool,
1472 but returns |false| if something goes wrong */
1473 int k; /* small indices or counters */
1474 str_number g; /* a new string */
1475 mp->pool_ptr=0; mp->str_ptr=0; mp->max_pool_ptr=0; mp->max_str_ptr=0;
1478 mp->pool_in_use=0; mp->strs_in_use=0;
1479 mp->max_pl_used=0; mp->max_strs_used=0;
1480 @<Initialize compaction statistics@>;
1482 @<Make the first 256 strings@>;
1483 g=mp_make_string(mp); /* string 256 == "" */
1484 mp->str_ref[g]=max_str_ref;
1485 mp->last_fixed_str=mp->str_ptr-1;
1486 mp->fixed_str_use=mp->str_ptr;
1491 void mp_get_strings_started (MP mp);
1493 @ The first 256 strings will consist of a single character only.
1495 @<Make the first 256...@>=
1496 for (k=0;k<=255;k++) {
1498 g=mp_make_string(mp);
1499 mp->str_ref[g]=max_str_ref;
1502 @ The first 128 strings will contain 95 standard ASCII characters, and the
1503 other 33 characters will be printed in three-symbol form like `\.{\^\^A}'
1504 unless a system-dependent change is made here. Installations that have
1505 an extended character set, where for example |xchr[032]=@t\.{'^^Z'}@>|,
1506 would like string 032 to be printed as the single character 032 instead
1507 of the three characters 0136, 0136, 0132 (\.{\^\^Z}). On the other hand,
1508 even people with an extended character set will want to represent string
1509 015 by \.{\^\^M}, since 015 is ASCII's ``carriage return'' code; the idea is
1510 to produce visible strings instead of tabs or line-feeds or carriage-returns
1511 or bell-rings or characters that are treated anomalously in text files.
1513 Unprintable characters of codes 128--255 are, similarly, rendered
1514 \.{\^\^80}--\.{\^\^ff}.
1516 The boolean expression defined here should be |true| unless \MP\ internal
1517 code number~|k| corresponds to a non-troublesome visible symbol in the
1518 local character set.
1519 If character |k| cannot be printed, and |k<0200|, then character |k+0100| or
1520 |k-0100| must be printable; moreover, ASCII codes |[060..071, 0141..0146]|
1522 @^character set dependencies@>
1523 @^system dependencies@>
1525 @<Character |k| cannot be printed@>=
1528 @* \[5] On-line and off-line printing.
1529 Messages that are sent to a user's terminal and to the transcript-log file
1530 are produced by several `|print|' procedures. These procedures will
1531 direct their output to a variety of places, based on the setting of
1532 the global variable |selector|, which has the following possible
1536 \hang |term_and_log|, the normal setting, prints on the terminal and on the
1539 \hang |log_only|, prints only on the transcript file.
1541 \hang |term_only|, prints only on the terminal.
1543 \hang |no_print|, doesn't print at all. This is used only in rare cases
1544 before the transcript file is open.
1546 \hang |pseudo|, puts output into a cyclic buffer that is used
1547 by the |show_context| routine; when we get to that routine we shall discuss
1548 the reasoning behind this curious mode.
1550 \hang |new_string|, appends the output to the current string in the
1553 \hang |>=write_file| prints on one of the files used for the \&{write}
1554 @:write_}{\&{write} primitive@>
1558 \noindent The symbolic names `|term_and_log|', etc., have been assigned
1559 numeric codes that satisfy the convenient relations |no_print+1=term_only|,
1560 |no_print+2=log_only|, |term_only+2=log_only+1=term_and_log|. These
1561 relations are not used when |selector| could be |pseudo|, or |new_string|.
1562 We need not check for unprintable characters when |selector<pseudo|.
1564 Three additional global variables, |tally|, |term_offset| and |file_offset|
1565 record the number of characters that have been printed
1566 since they were most recently cleared to zero. We use |tally| to record
1567 the length of (possibly very long) stretches of printing; |term_offset|,
1568 and |file_offset|, on the other hand, keep track of how many
1569 characters have appeared so far on the current line that has been output
1570 to the terminal, the transcript file, or the \ps\ output file, respectively.
1572 @d new_string 0 /* printing is deflected to the string pool */
1573 @d pseudo 2 /* special |selector| setting for |show_context| */
1574 @d no_print 3 /* |selector| setting that makes data disappear */
1575 @d term_only 4 /* printing is destined for the terminal only */
1576 @d log_only 5 /* printing is destined for the transcript file only */
1577 @d term_and_log 6 /* normal |selector| setting */
1578 @d write_file 7 /* first write file selector */
1581 void * log_file; /* transcript of \MP\ session */
1582 void * ps_file; /* the generic font output goes here */
1583 unsigned int selector; /* where to print a message */
1584 unsigned char dig[23]; /* digits in a number being output */
1585 integer tally; /* the number of characters recently printed */
1586 unsigned int term_offset;
1587 /* the number of characters on the current terminal line */
1588 unsigned int file_offset;
1589 /* the number of characters on the current file line */
1590 ASCII_code *trick_buf; /* circular buffer for pseudoprinting */
1591 integer trick_count; /* threshold for pseudoprinting, explained later */
1592 integer first_count; /* another variable for pseudoprinting */
1594 @ @<Allocate or initialize ...@>=
1595 memset(mp->dig,0,23);
1596 mp->trick_buf = xmalloc((mp->error_line+1),sizeof(ASCII_code));
1598 @ @<Dealloc variables@>=
1599 xfree(mp->trick_buf);
1601 @ @<Initialize the output routines@>=
1602 mp->selector=term_only; mp->tally=0; mp->term_offset=0; mp->file_offset=0;
1604 @ Macro abbreviations for output to the terminal and to the log file are
1605 defined here for convenience. Some systems need special conventions
1606 for terminal output, and it is possible to adhere to those conventions
1607 by changing |wterm|, |wterm_ln|, and |wterm_cr| here.
1608 @^system dependencies@>
1610 @d do_fprintf(f,b) (mp->write_ascii_file)(mp,f,b)
1611 @d wterm(A) do_fprintf(mp->term_out,(A))
1612 @d wterm_chr(A) { unsigned char ss[2]; ss[0]=(A); ss[1]=0; do_fprintf(mp->term_out,(char *)ss); }
1613 @d wterm_cr do_fprintf(mp->term_out,"\n")
1614 @d wterm_ln(A) { wterm_cr; do_fprintf(mp->term_out,(A)); }
1615 @d wlog(A) do_fprintf(mp->log_file,(A))
1616 @d wlog_chr(A) { unsigned char ss[2]; ss[0]=(A); ss[1]=0; do_fprintf(mp->log_file,(char *)ss); }
1617 @d wlog_cr do_fprintf(mp->log_file, "\n")
1618 @d wlog_ln(A) {wlog_cr; do_fprintf(mp->log_file,(A)); }
1621 @ To end a line of text output, we call |print_ln|. Cases |0..max_write_files|
1622 use an array |wr_file| that will be declared later.
1624 @d mp_print_text(A) mp_print_str(mp,text((A)))
1627 void mp_print_ln (MP mp);
1628 void mp_print_visible_char (MP mp, ASCII_code s);
1629 void mp_print_char (MP mp, ASCII_code k);
1630 void mp_print (MP mp, char *s);
1631 void mp_print_str (MP mp, str_number s);
1632 void mp_print_nl (MP mp, char *s);
1633 void mp_print_two (MP mp,scaled x, scaled y) ;
1634 void mp_print_scaled (MP mp,scaled s);
1636 @ @<Basic print...@>=
1637 void mp_print_ln (MP mp) { /* prints an end-of-line */
1638 switch (mp->selector) {
1641 mp->term_offset=0; mp->file_offset=0;
1644 wlog_cr; mp->file_offset=0;
1647 wterm_cr; mp->term_offset=0;
1654 do_fprintf(mp->wr_file[(mp->selector-write_file)],"\n");
1656 } /* note that |tally| is not affected */
1658 @ The |print_visible_char| procedure sends one character to the desired
1659 destination, using the |xchr| array to map it into an external character
1660 compatible with |input_ln|. (It assumes that it is always called with
1661 a visible ASCII character.) All printing comes through |print_ln| or
1662 |print_char|, which ultimately calls |print_visible_char|, hence these
1663 routines are the ones that limit lines to at most |max_print_line| characters.
1664 But we must make an exception for the \ps\ output file since it is not safe
1665 to cut up lines arbitrarily in \ps.
1667 Procedure |unit_str_room| needs to be declared |forward| here because it calls
1668 |do_compaction| and |do_compaction| can call the error routines. Actually,
1669 |unit_str_room| avoids |overflow| errors but it can call |confusion|.
1671 @<Basic printing...@>=
1672 void mp_print_visible_char (MP mp, ASCII_code s) { /* prints a single character */
1673 switch (mp->selector) {
1675 wterm_chr(xchr(s)); wlog_chr(xchr(s));
1676 incr(mp->term_offset); incr(mp->file_offset);
1677 if ( mp->term_offset==(unsigned)mp->max_print_line ) {
1678 wterm_cr; mp->term_offset=0;
1680 if ( mp->file_offset==(unsigned)mp->max_print_line ) {
1681 wlog_cr; mp->file_offset=0;
1685 wlog_chr(xchr(s)); incr(mp->file_offset);
1686 if ( mp->file_offset==(unsigned)mp->max_print_line ) mp_print_ln(mp);
1689 wterm_chr(xchr(s)); incr(mp->term_offset);
1690 if ( mp->term_offset==(unsigned)mp->max_print_line ) mp_print_ln(mp);
1695 if ( mp->tally<mp->trick_count )
1696 mp->trick_buf[mp->tally % mp->error_line]=s;
1699 if ( mp->pool_ptr>=mp->max_pool_ptr ) {
1700 mp_unit_str_room(mp);
1701 if ( mp->pool_ptr>=mp->pool_size )
1702 goto DONE; /* drop characters if string space is full */
1707 { char ss[2]; ss[0] = xchr(s); ss[1]=0;
1708 do_fprintf(mp->wr_file[(mp->selector-write_file)],(char *)ss);
1715 @ The |print_char| procedure sends one character to the desired destination.
1716 File names and string expressions might contain |ASCII_code| values that
1717 can't be printed using |print_visible_char|. These characters will be
1718 printed in three- or four-symbol form like `\.{\^\^A}' or `\.{\^\^e4}'.
1719 (This procedure assumes that it is safe to bypass all checks for unprintable
1720 characters when |selector| is in the range |0..max_write_files-1|.
1721 The user might want to write unprintable characters.
1723 @d print_lc_hex(A) do { l=(A);
1724 mp_print_visible_char(mp, (l<10 ? l+'0' : l-10+'a'));
1727 @<Basic printing...@>=
1728 void mp_print_char (MP mp, ASCII_code k) { /* prints a single character */
1729 int l; /* small index or counter */
1730 if ( mp->selector<pseudo || mp->selector>=write_file) {
1731 mp_print_visible_char(mp, k);
1732 } else if ( @<Character |k| cannot be printed@> ) {
1735 mp_print_visible_char(mp, k+0100);
1736 } else if ( k<0200 ) {
1737 mp_print_visible_char(mp, k-0100);
1739 print_lc_hex(k / 16);
1740 print_lc_hex(k % 16);
1743 mp_print_visible_char(mp, k);
1747 @ An entire string is output by calling |print|. Note that if we are outputting
1748 the single standard ASCII character \.c, we could call |print("c")|, since
1749 |"c"=99| is the number of a single-character string, as explained above. But
1750 |print_char("c")| is quicker, so \MP\ goes directly to the |print_char|
1751 routine when it knows that this is safe. (The present implementation
1752 assumes that it is always safe to print a visible ASCII character.)
1753 @^system dependencies@>
1756 void mp_do_print (MP mp, char *ss, unsigned int len) { /* prints string |s| */
1759 mp_print_char(mp, ss[j]); incr(j);
1765 void mp_print (MP mp, char *ss) {
1766 mp_do_print(mp, ss, strlen(ss));
1768 void mp_print_str (MP mp, str_number s) {
1769 pool_pointer j; /* current character code position */
1770 if ( (s<0)||(s>mp->max_str_ptr) ) {
1771 mp_do_print(mp,"???",3); /* this can't happen */
1775 mp_do_print(mp, (char *)(mp->str_pool+j), (str_stop(s)-j));
1779 @ Here is the very first thing that \MP\ prints: a headline that identifies
1780 the version number and base name. The |term_offset| variable is temporarily
1781 incorrect, but the discrepancy is not serious since we assume that the banner
1782 and mem identifier together will occupy at most |max_print_line|
1783 character positions.
1785 @<Initialize the output...@>=
1787 wterm (version_string);
1788 if (mp->mem_ident!=NULL)
1789 mp_print(mp,mp->mem_ident);
1793 @ The procedure |print_nl| is like |print|, but it makes sure that the
1794 string appears at the beginning of a new line.
1797 void mp_print_nl (MP mp, char *s) { /* prints string |s| at beginning of line */
1798 switch(mp->selector) {
1800 if ( (mp->term_offset>0)||(mp->file_offset>0) ) mp_print_ln(mp);
1803 if ( mp->file_offset>0 ) mp_print_ln(mp);
1806 if ( mp->term_offset>0 ) mp_print_ln(mp);
1812 } /* there are no other cases */
1816 @ An array of digits in the range |0..9| is printed by |print_the_digs|.
1819 void mp_print_the_digs (MP mp, eight_bits k) {
1820 /* prints |dig[k-1]|$\,\ldots\,$|dig[0]| */
1822 decr(k); mp_print_char(mp, '0'+mp->dig[k]);
1826 @ The following procedure, which prints out the decimal representation of a
1827 given integer |n|, has been written carefully so that it works properly
1828 if |n=0| or if |(-n)| would cause overflow. It does not apply |%| or |/|
1829 to negative arguments, since such operations are not implemented consistently
1833 void mp_print_int (MP mp,integer n) { /* prints an integer in decimal form */
1834 integer m; /* used to negate |n| in possibly dangerous cases */
1835 int k = 0; /* index to current digit; we assume that $|n|<10^{23}$ */
1837 mp_print_char(mp, '-');
1838 if ( n>-100000000 ) {
1841 m=-1-n; n=m / 10; m=(m % 10)+1; k=1;
1845 mp->dig[0]=0; incr(n);
1850 mp->dig[k]=n % 10; n=n / 10; incr(k);
1852 mp_print_the_digs(mp, k);
1856 void mp_print_int (MP mp,integer n);
1858 @ \MP\ also makes use of a trivial procedure to print two digits. The
1859 following subroutine is usually called with a parameter in the range |0<=n<=99|.
1862 void mp_print_dd (MP mp,integer n) { /* prints two least significant digits */
1864 mp_print_char(mp, '0'+(n / 10));
1865 mp_print_char(mp, '0'+(n % 10));
1870 void mp_print_dd (MP mp,integer n);
1872 @ Here is a procedure that asks the user to type a line of input,
1873 assuming that the |selector| setting is either |term_only| or |term_and_log|.
1874 The input is placed into locations |first| through |last-1| of the
1875 |buffer| array, and echoed on the transcript file if appropriate.
1877 This procedure is never called when |interaction<mp_scroll_mode|.
1879 @d prompt_input(A) do {
1880 if (!mp->noninteractive) {
1881 wake_up_terminal; mp_print(mp, (A));
1884 } while (0) /* prints a string and gets a line of input */
1887 void mp_term_input (MP mp) { /* gets a line from the terminal */
1888 size_t k; /* index into |buffer| */
1889 update_terminal; /* Now the user sees the prompt for sure */
1890 if (!mp_input_ln(mp, mp->term_in )) {
1891 if (!mp->noninteractive) {
1892 mp_fatal_error(mp, "End of file on the terminal!");
1893 @.End of file on the terminal@>
1894 } else { /* we are done with this input chunk */
1895 longjmp(mp->jump_buf,1);
1898 if (!mp->noninteractive) {
1899 mp->term_offset=0; /* the user's line ended with \<\rm return> */
1900 decr(mp->selector); /* prepare to echo the input */
1901 if ( mp->last!=mp->first ) {
1902 for (k=mp->first;k<=mp->last-1;k++) {
1903 mp_print_char(mp, mp->buffer[k]);
1907 mp->buffer[mp->last]='%';
1908 incr(mp->selector); /* restore previous status */
1912 @* \[6] Reporting errors.
1913 When something anomalous is detected, \MP\ typically does something like this:
1914 $$\vbox{\halign{#\hfil\cr
1915 |print_err("Something anomalous has been detected");|\cr
1916 |help3("This is the first line of my offer to help.")|\cr
1917 |("This is the second line. I'm trying to")|\cr
1918 |("explain the best way for you to proceed.");|\cr
1920 A two-line help message would be given using |help2|, etc.; these informal
1921 helps should use simple vocabulary that complements the words used in the
1922 official error message that was printed. (Outside the U.S.A., the help
1923 messages should preferably be translated into the local vernacular. Each
1924 line of help is at most 60 characters long, in the present implementation,
1925 so that |max_print_line| will not be exceeded.)
1927 The |print_err| procedure supplies a `\.!' before the official message,
1928 and makes sure that the terminal is awake if a stop is going to occur.
1929 The |error| procedure supplies a `\..' after the official message, then it
1930 shows the location of the error; and if |interaction=error_stop_mode|,
1931 it also enters into a dialog with the user, during which time the help
1932 message may be printed.
1933 @^system dependencies@>
1935 @ The global variable |interaction| has four settings, representing increasing
1936 amounts of user interaction:
1939 enum mp_interaction_mode {
1940 mp_unspecified_mode=0, /* extra value for command-line switch */
1941 mp_batch_mode, /* omits all stops and omits terminal output */
1942 mp_nonstop_mode, /* omits all stops */
1943 mp_scroll_mode, /* omits error stops */
1944 mp_error_stop_mode, /* stops at every opportunity to interact */
1947 @ @<Option variables@>=
1948 int interaction; /* current level of interaction */
1949 int noninteractive; /* do we have a terminal? */
1951 @ Set it here so it can be overwritten by the commandline
1953 @<Allocate or initialize ...@>=
1954 mp->interaction=opt->interaction;
1955 if (mp->interaction==mp_unspecified_mode || mp->interaction>mp_error_stop_mode)
1956 mp->interaction=mp_error_stop_mode;
1957 if (mp->interaction<mp_unspecified_mode)
1958 mp->interaction=mp_batch_mode;
1959 mp->noninteractive=opt->noninteractive;
1963 @d print_err(A) mp_print_err(mp,(A))
1966 void mp_print_err(MP mp, char * A);
1969 void mp_print_err(MP mp, char * A) {
1970 if ( mp->interaction==mp_error_stop_mode )
1972 mp_print_nl(mp, "! ");
1978 @ \MP\ is careful not to call |error| when the print |selector| setting
1979 might be unusual. The only possible values of |selector| at the time of
1982 \yskip\hang|no_print| (when |interaction=mp_batch_mode|
1983 and |log_file| not yet open);
1985 \hang|term_only| (when |interaction>mp_batch_mode| and |log_file| not yet open);
1987 \hang|log_only| (when |interaction=mp_batch_mode| and |log_file| is open);
1989 \hang|term_and_log| (when |interaction>mp_batch_mode| and |log_file| is open).
1991 @<Initialize the print |selector| based on |interaction|@>=
1992 if ( mp->interaction==mp_batch_mode ) mp->selector=no_print; else mp->selector=term_only
1994 @ A global variable |deletions_allowed| is set |false| if the |get_next|
1995 routine is active when |error| is called; this ensures that |get_next|
1996 will never be called recursively.
1999 The global variable |history| records the worst level of error that
2000 has been detected. It has four possible values: |spotless|, |warning_issued|,
2001 |error_message_issued|, and |fatal_error_stop|.
2003 Another global variable, |error_count|, is increased by one when an
2004 |error| occurs without an interactive dialog, and it is reset to zero at
2005 the end of every statement. If |error_count| reaches 100, \MP\ decides
2006 that there is no point in continuing further.
2009 enum mp_history_states {
2010 mp_spotless=0, /* |history| value when nothing has been amiss yet */
2011 mp_warning_issued, /* |history| value when |begin_diagnostic| has been called */
2012 mp_error_message_issued, /* |history| value when |error| has been called */
2013 mp_fatal_error_stop, /* |history| value when termination was premature */
2017 boolean deletions_allowed; /* is it safe for |error| to call |get_next|? */
2018 int history; /* has the source input been clean so far? */
2019 int error_count; /* the number of scrolled errors since the last statement ended */
2021 @ The value of |history| is initially |fatal_error_stop|, but it will
2022 be changed to |spotless| if \MP\ survives the initialization process.
2024 @<Allocate or ...@>=
2025 mp->deletions_allowed=true; mp->error_count=0; /* |history| is initialized elsewhere */
2027 @ Since errors can be detected almost anywhere in \MP, we want to declare the
2028 error procedures near the beginning of the program. But the error procedures
2029 in turn use some other procedures, which need to be declared |forward|
2030 before we get to |error| itself.
2032 It is possible for |error| to be called recursively if some error arises
2033 when |get_next| is being used to delete a token, and/or if some fatal error
2034 occurs while \MP\ is trying to fix a non-fatal one. But such recursion
2036 is never more than two levels deep.
2039 void mp_get_next (MP mp);
2040 void mp_term_input (MP mp);
2041 void mp_show_context (MP mp);
2042 void mp_begin_file_reading (MP mp);
2043 void mp_open_log_file (MP mp);
2044 void mp_clear_for_error_prompt (MP mp);
2045 void mp_debug_help (MP mp);
2046 @<Declare the procedure called |flush_string|@>
2049 void mp_normalize_selector (MP mp);
2051 @ Individual lines of help are recorded in the array |help_line|, which
2052 contains entries in positions |0..(help_ptr-1)|. They should be printed
2053 in reverse order, i.e., with |help_line[0]| appearing last.
2055 @d hlp1(A) mp->help_line[0]=(A); }
2056 @d hlp2(A) mp->help_line[1]=(A); hlp1
2057 @d hlp3(A) mp->help_line[2]=(A); hlp2
2058 @d hlp4(A) mp->help_line[3]=(A); hlp3
2059 @d hlp5(A) mp->help_line[4]=(A); hlp4
2060 @d hlp6(A) mp->help_line[5]=(A); hlp5
2061 @d help0 mp->help_ptr=0 /* sometimes there might be no help */
2062 @d help1 { mp->help_ptr=1; hlp1 /* use this with one help line */
2063 @d help2 { mp->help_ptr=2; hlp2 /* use this with two help lines */
2064 @d help3 { mp->help_ptr=3; hlp3 /* use this with three help lines */
2065 @d help4 { mp->help_ptr=4; hlp4 /* use this with four help lines */
2066 @d help5 { mp->help_ptr=5; hlp5 /* use this with five help lines */
2067 @d help6 { mp->help_ptr=6; hlp6 /* use this with six help lines */
2070 char * help_line[6]; /* helps for the next |error| */
2071 unsigned int help_ptr; /* the number of help lines present */
2072 boolean use_err_help; /* should the |err_help| string be shown? */
2073 str_number err_help; /* a string set up by \&{errhelp} */
2074 str_number filename_template; /* a string set up by \&{filenametemplate} */
2076 @ @<Allocate or ...@>=
2077 mp->help_ptr=0; mp->use_err_help=false; mp->err_help=0; mp->filename_template=0;
2079 @ The |jump_out| procedure just cuts across all active procedure levels and
2080 goes to |end_of_MP|. This is the only nonlocal |goto| statement in the
2081 whole program. It is used when there is no recovery from a particular error.
2083 The program uses a |jump_buf| to handle this, this is initialized at three
2084 spots: the start of |mp_new|, the start of |mp_initialize|, and the start
2085 of |mp_run|. Those are the only library enty points.
2087 @^system dependencies@>
2092 @ @<Install and test the non-local jump buffer@>=
2093 if (setjmp(mp->jump_buf) != 0) { return mp->history; }
2096 @ @<Setup the non-local jump buffer in |mp_new|@>=
2097 if (setjmp(mp->jump_buf) != 0) return NULL;
2099 @ If the array of internals is still |NULL| when |jump_out| is called, a
2100 crash occured during initialization, and it is not safe to run the normal
2104 void mp_jump_out (MP mp) {
2105 if(mp->internal!=NULL)
2106 mp_close_files_and_terminate(mp);
2107 longjmp(mp->jump_buf,1);
2110 @ Here now is the general |error| routine.
2113 void mp_error (MP mp) { /* completes the job of error reporting */
2114 ASCII_code c; /* what the user types */
2115 integer s1,s2,s3; /* used to save global variables when deleting tokens */
2116 pool_pointer j; /* character position being printed */
2117 if ( mp->history<mp_error_message_issued )
2118 mp->history=mp_error_message_issued;
2119 mp_print_char(mp, '.'); mp_show_context(mp);
2120 if ((!mp->noninteractive) && (mp->interaction==mp_error_stop_mode )) {
2121 @<Get user's advice and |return|@>;
2123 incr(mp->error_count);
2124 if ( mp->error_count==100 ) {
2125 mp_print_nl(mp,"(That makes 100 errors; please try again.)");
2126 @.That makes 100 errors...@>
2127 mp->history=mp_fatal_error_stop; mp_jump_out(mp);
2129 @<Put help message on the transcript file@>;
2131 void mp_warn (MP mp, char *msg) {
2132 int saved_selector = mp->selector;
2133 mp_normalize_selector(mp);
2134 mp_print_nl(mp,"Warning: ");
2136 mp->selector = saved_selector;
2139 @ @<Exported function ...@>=
2140 void mp_error (MP mp);
2141 void mp_warn (MP mp, char *msg);
2144 @ @<Get user's advice...@>=
2147 mp_clear_for_error_prompt(mp); prompt_input("? ");
2149 if ( mp->last==mp->first ) return;
2150 c=mp->buffer[mp->first];
2151 if ( c>='a' ) c=c+'A'-'a'; /* convert to uppercase */
2152 @<Interpret code |c| and |return| if done@>;
2155 @ It is desirable to provide an `\.E' option here that gives the user
2156 an easy way to return from \MP\ to the system editor, with the offending
2157 line ready to be edited. But such an extension requires some system
2158 wizardry, so the present implementation simply types out the name of the
2160 edited and the relevant line number.
2161 @^system dependencies@>
2164 typedef void (*mp_run_editor_command)(MP, char *, int);
2166 @ @<Option variables@>=
2167 mp_run_editor_command run_editor;
2169 @ @<Allocate or initialize ...@>=
2170 set_callback_option(run_editor);
2173 void mp_run_editor (MP mp, char *fname, int fline);
2175 @ @c void mp_run_editor (MP mp, char *fname, int fline) {
2176 mp_print_nl(mp, "You want to edit file ");
2177 @.You want to edit file x@>
2178 mp_print(mp, fname);
2179 mp_print(mp, " at line ");
2180 mp_print_int(mp, fline);
2181 mp->interaction=mp_scroll_mode;
2186 There is a secret `\.D' option available when the debugging routines haven't
2190 @<Interpret code |c| and |return| if done@>=
2192 case '0': case '1': case '2': case '3': case '4':
2193 case '5': case '6': case '7': case '8': case '9':
2194 if ( mp->deletions_allowed ) {
2195 @<Delete |c-"0"| tokens and |continue|@>;
2200 mp_debug_help(mp); continue;
2204 if ( mp->file_ptr>0 ){
2205 (mp->run_editor)(mp,
2206 str(mp->input_stack[mp->file_ptr].name_field),
2211 @<Print the help information and |continue|@>;
2214 @<Introduce new material from the terminal and |return|@>;
2216 case 'Q': case 'R': case 'S':
2217 @<Change the interaction level and |return|@>;
2220 mp->interaction=mp_scroll_mode; mp_jump_out(mp);
2225 @<Print the menu of available options@>
2227 @ @<Print the menu...@>=
2229 mp_print(mp, "Type <return> to proceed, S to scroll future error messages,");
2230 @.Type <return> to proceed...@>
2231 mp_print_nl(mp, "R to run without stopping, Q to run quietly,");
2232 mp_print_nl(mp, "I to insert something, ");
2233 if ( mp->file_ptr>0 )
2234 mp_print(mp, "E to edit your file,");
2235 if ( mp->deletions_allowed )
2236 mp_print_nl(mp, "1 or ... or 9 to ignore the next 1 to 9 tokens of input,");
2237 mp_print_nl(mp, "H for help, X to quit.");
2240 @ Here the author of \MP\ apologizes for making use of the numerical
2241 relation between |"Q"|, |"R"|, |"S"|, and the desired interaction settings
2242 |mp_batch_mode|, |mp_nonstop_mode|, |mp_scroll_mode|.
2243 @^Knuth, Donald Ervin@>
2245 @<Change the interaction...@>=
2247 mp->error_count=0; mp->interaction=mp_batch_mode+c-'Q';
2248 mp_print(mp, "OK, entering ");
2250 case 'Q': mp_print(mp, "batchmode"); decr(mp->selector); break;
2251 case 'R': mp_print(mp, "nonstopmode"); break;
2252 case 'S': mp_print(mp, "scrollmode"); break;
2253 } /* there are no other cases */
2254 mp_print(mp, "..."); mp_print_ln(mp); update_terminal; return;
2257 @ When the following code is executed, |buffer[(first+1)..(last-1)]| may
2258 contain the material inserted by the user; otherwise another prompt will
2259 be given. In order to understand this part of the program fully, you need
2260 to be familiar with \MP's input stacks.
2262 @<Introduce new material...@>=
2264 mp_begin_file_reading(mp); /* enter a new syntactic level for terminal input */
2265 if ( mp->last>mp->first+1 ) {
2266 loc=mp->first+1; mp->buffer[mp->first]=' ';
2268 prompt_input("insert>"); loc=mp->first;
2271 mp->first=mp->last+1; mp->cur_input.limit_field=mp->last; return;
2274 @ We allow deletion of up to 99 tokens at a time.
2276 @<Delete |c-"0"| tokens...@>=
2278 s1=mp->cur_cmd; s2=mp->cur_mod; s3=mp->cur_sym; mp->OK_to_interrupt=false;
2279 if ( (mp->last>mp->first+1) && (mp->buffer[mp->first+1]>='0')&&(mp->buffer[mp->first+1]<='9') )
2280 c=c*10+mp->buffer[mp->first+1]-'0'*11;
2284 mp_get_next(mp); /* one-level recursive call of |error| is possible */
2285 @<Decrease the string reference count, if the current token is a string@>;
2288 mp->cur_cmd=s1; mp->cur_mod=s2; mp->cur_sym=s3; mp->OK_to_interrupt=true;
2289 help2("I have just deleted some text, as you asked.")
2290 ("You can now delete more, or insert, or whatever.");
2291 mp_show_context(mp);
2295 @ @<Print the help info...@>=
2297 if ( mp->use_err_help ) {
2298 @<Print the string |err_help|, possibly on several lines@>;
2299 mp->use_err_help=false;
2301 if ( mp->help_ptr==0 ) {
2302 help2("Sorry, I don't know how to help in this situation.")
2303 ("Maybe you should try asking a human?");
2306 decr(mp->help_ptr); mp_print(mp, mp->help_line[mp->help_ptr]); mp_print_ln(mp);
2307 } while (mp->help_ptr!=0);
2309 help4("Sorry, I already gave what help I could...")
2310 ("Maybe you should try asking a human?")
2311 ("An error might have occurred before I noticed any problems.")
2312 ("``If all else fails, read the instructions.''");
2316 @ @<Print the string |err_help|, possibly on several lines@>=
2317 j=mp->str_start[mp->err_help];
2318 while ( j<str_stop(mp->err_help) ) {
2319 if ( mp->str_pool[j]!='%' ) mp_print_str(mp, mp->str_pool[j]);
2320 else if ( j+1==str_stop(mp->err_help) ) mp_print_ln(mp);
2321 else if ( mp->str_pool[j+1]!='%' ) mp_print_ln(mp);
2322 else { incr(j); mp_print_char(mp, '%'); };
2326 @ @<Put help message on the transcript file@>=
2327 if ( mp->interaction>mp_batch_mode ) decr(mp->selector); /* avoid terminal output */
2328 if ( mp->use_err_help ) {
2329 mp_print_nl(mp, "");
2330 @<Print the string |err_help|, possibly on several lines@>;
2332 while ( mp->help_ptr>0 ){
2333 decr(mp->help_ptr); mp_print_nl(mp, mp->help_line[mp->help_ptr]);
2337 if ( mp->interaction>mp_batch_mode ) incr(mp->selector); /* re-enable terminal output */
2340 @ In anomalous cases, the print selector might be in an unknown state;
2341 the following subroutine is called to fix things just enough to keep
2342 running a bit longer.
2345 void mp_normalize_selector (MP mp) {
2346 if ( mp->log_opened ) mp->selector=term_and_log;
2347 else mp->selector=term_only;
2348 if ( mp->job_name==NULL ) mp_open_log_file(mp);
2349 if ( mp->interaction==mp_batch_mode ) decr(mp->selector);
2352 @ The following procedure prints \MP's last words before dying.
2354 @d succumb { if ( mp->interaction==mp_error_stop_mode )
2355 mp->interaction=mp_scroll_mode; /* no more interaction */
2356 if ( mp->log_opened ) mp_error(mp);
2357 /*| if ( mp->interaction>mp_batch_mode ) mp_debug_help(mp); |*/
2358 mp->history=mp_fatal_error_stop; mp_jump_out(mp); /* irrecoverable error */
2362 void mp_fatal_error (MP mp, char *s) { /* prints |s|, and that's it */
2363 mp_normalize_selector(mp);
2364 print_err("Emergency stop"); help1(s); succumb;
2368 @ @<Exported function ...@>=
2369 void mp_fatal_error (MP mp, char *s);
2372 @ Here is the most dreaded error message.
2375 void mp_overflow (MP mp, char *s, integer n) { /* stop due to finiteness */
2376 mp_normalize_selector(mp);
2377 print_err("MetaPost capacity exceeded, sorry [");
2378 @.MetaPost capacity exceeded ...@>
2379 mp_print(mp, s); mp_print_char(mp, '='); mp_print_int(mp, n); mp_print_char(mp, ']');
2380 help2("If you really absolutely need more capacity,")
2381 ("you can ask a wizard to enlarge me.");
2385 @ @<Internal library declarations@>=
2386 void mp_overflow (MP mp, char *s, integer n);
2388 @ The program might sometime run completely amok, at which point there is
2389 no choice but to stop. If no previous error has been detected, that's bad
2390 news; a message is printed that is really intended for the \MP\
2391 maintenance person instead of the user (unless the user has been
2392 particularly diabolical). The index entries for `this can't happen' may
2393 help to pinpoint the problem.
2396 @<Internal library ...@>=
2397 void mp_confusion (MP mp,char *s);
2399 @ @<Error hand...@>=
2400 void mp_confusion (MP mp,char *s) {
2401 /* consistency check violated; |s| tells where */
2402 mp_normalize_selector(mp);
2403 if ( mp->history<mp_error_message_issued ) {
2404 print_err("This can't happen ("); mp_print(mp, s); mp_print_char(mp, ')');
2405 @.This can't happen@>
2406 help1("I'm broken. Please show this to someone who can fix can fix");
2408 print_err("I can\'t go on meeting you like this");
2409 @.I can't go on...@>
2410 help2("One of your faux pas seems to have wounded me deeply...")
2411 ("in fact, I'm barely conscious. Please fix it and try again.");
2416 @ Users occasionally want to interrupt \MP\ while it's running.
2417 If the runtime system allows this, one can implement
2418 a routine that sets the global variable |interrupt| to some nonzero value
2419 when such an interrupt is signaled. Otherwise there is probably at least
2420 a way to make |interrupt| nonzero using the C debugger.
2421 @^system dependencies@>
2424 @d check_interrupt { if ( mp->interrupt!=0 )
2425 mp_pause_for_instructions(mp); }
2428 integer interrupt; /* should \MP\ pause for instructions? */
2429 boolean OK_to_interrupt; /* should interrupts be observed? */
2430 integer run_state; /* are we processing input ?*/
2432 @ @<Allocate or ...@>=
2433 mp->interrupt=0; mp->OK_to_interrupt=true; mp->run_state=0;
2435 @ When an interrupt has been detected, the program goes into its
2436 highest interaction level and lets the user have the full flexibility of
2437 the |error| routine. \MP\ checks for interrupts only at times when it is
2441 void mp_pause_for_instructions (MP mp) {
2442 if ( mp->OK_to_interrupt ) {
2443 mp->interaction=mp_error_stop_mode;
2444 if ( (mp->selector==log_only)||(mp->selector==no_print) )
2446 print_err("Interruption");
2449 ("Try to insert some instructions for me (e.g.,`I show x'),")
2450 ("unless you just want to quit by typing `X'.");
2451 mp->deletions_allowed=false; mp_error(mp); mp->deletions_allowed=true;
2456 @ Many of \MP's error messages state that a missing token has been
2457 inserted behind the scenes. We can save string space and program space
2458 by putting this common code into a subroutine.
2461 void mp_missing_err (MP mp, char *s) {
2462 print_err("Missing `"); mp_print(mp, s); mp_print(mp, "' has been inserted");
2463 @.Missing...inserted@>
2466 @* \[7] Arithmetic with scaled numbers.
2467 The principal computations performed by \MP\ are done entirely in terms of
2468 integers less than $2^{31}$ in magnitude; thus, the arithmetic specified in this
2469 program can be carried out in exactly the same way on a wide variety of
2470 computers, including some small ones.
2473 But C does not rigidly define the |/| operation in the case of negative
2474 dividends; for example, the result of |(-2*n-1) / 2| is |-(n+1)| on some
2475 computers and |-n| on others (is this true ?). There are two principal
2476 types of arithmetic: ``translation-preserving,'' in which the identity
2477 |(a+q*b)/b=(a/b)+q| is valid; and ``negation-preserving,'' in which
2478 |(-a)/b=-(a/b)|. This leads to two \MP s, which can produce
2479 different results, although the differences should be negligible when the
2480 language is being used properly. The \TeX\ processor has been defined
2481 carefully so that both varieties of arithmetic will produce identical
2482 output, but it would be too inefficient to constrain \MP\ in a similar way.
2484 @d el_gordo 017777777777 /* $2^{31}-1$, the largest value that \MP\ likes */
2486 @ One of \MP's most common operations is the calculation of
2487 $\lfloor{a+b\over2}\rfloor$,
2488 the midpoint of two given integers |a| and~|b|. The most decent way to do
2489 this is to write `|(a+b)/2|'; but on many machines it is more efficient
2490 to calculate `|(a+b)>>1|'.
2492 Therefore the midpoint operation will always be denoted by `|half(a+b)|'
2493 in this program. If \MP\ is being implemented with languages that permit
2494 binary shifting, the |half| macro should be changed to make this operation
2495 as efficient as possible. Since some systems have shift operators that can
2496 only be trusted to work on positive numbers, there is also a macro |halfp|
2497 that is used only when the quantity being halved is known to be positive
2500 @d half(A) ((A) / 2)
2501 @d halfp(A) ((A) >> 1)
2503 @ A single computation might use several subroutine calls, and it is
2504 desirable to avoid producing multiple error messages in case of arithmetic
2505 overflow. So the routines below set the global variable |arith_error| to |true|
2506 instead of reporting errors directly to the user.
2509 boolean arith_error; /* has arithmetic overflow occurred recently? */
2511 @ @<Allocate or ...@>=
2512 mp->arith_error=false;
2514 @ At crucial points the program will say |check_arith|, to test if
2515 an arithmetic error has been detected.
2517 @d check_arith { if ( mp->arith_error ) mp_clear_arith(mp); }
2520 void mp_clear_arith (MP mp) {
2521 print_err("Arithmetic overflow");
2522 @.Arithmetic overflow@>
2523 help4("Uh, oh. A little while ago one of the quantities that I was")
2524 ("computing got too large, so I'm afraid your answers will be")
2525 ("somewhat askew. You'll probably have to adopt different")
2526 ("tactics next time. But I shall try to carry on anyway.");
2528 mp->arith_error=false;
2531 @ Addition is not always checked to make sure that it doesn't overflow,
2532 but in places where overflow isn't too unlikely the |slow_add| routine
2535 @c integer mp_slow_add (MP mp,integer x, integer y) {
2537 if ( y<=el_gordo-x ) {
2540 mp->arith_error=true;
2543 } else if ( -y<=el_gordo+x ) {
2546 mp->arith_error=true;
2551 @ Fixed-point arithmetic is done on {\sl scaled integers\/} that are multiples
2552 of $2^{-16}$. In other words, a binary point is assumed to be sixteen bit
2553 positions from the right end of a binary computer word.
2555 @d quarter_unit 040000 /* $2^{14}$, represents 0.250000 */
2556 @d half_unit 0100000 /* $2^{15}$, represents 0.50000 */
2557 @d three_quarter_unit 0140000 /* $3\cdot2^{14}$, represents 0.75000 */
2558 @d unity 0200000 /* $2^{16}$, represents 1.00000 */
2559 @d two 0400000 /* $2^{17}$, represents 2.00000 */
2560 @d three 0600000 /* $2^{17}+2^{16}$, represents 3.00000 */
2563 typedef integer scaled; /* this type is used for scaled integers */
2564 typedef unsigned char small_number; /* this type is self-explanatory */
2566 @ The following function is used to create a scaled integer from a given decimal
2567 fraction $(.d_0d_1\ldots d_{k-1})$, where |0<=k<=17|. The digit $d_i$ is
2568 given in |dig[i]|, and the calculation produces a correctly rounded result.
2571 scaled mp_round_decimals (MP mp,small_number k) {
2572 /* converts a decimal fraction */
2573 integer a = 0; /* the accumulator */
2575 a=(a+mp->dig[k]*two) / 10;
2580 @ Conversely, here is a procedure analogous to |print_int|. If the output
2581 of this procedure is subsequently read by \MP\ and converted by the
2582 |round_decimals| routine above, it turns out that the original value will
2583 be reproduced exactly. A decimal point is printed only if the value is
2584 not an integer. If there is more than one way to print the result with
2585 the optimum number of digits following the decimal point, the closest
2586 possible value is given.
2588 The invariant relation in the \&{repeat} loop is that a sequence of
2589 decimal digits yet to be printed will yield the original number if and only if
2590 they form a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$.
2591 We can stop if and only if $f=0$ satisfies this condition; the loop will
2592 terminate before $s$ can possibly become zero.
2594 @<Basic printing...@>=
2595 void mp_print_scaled (MP mp,scaled s) { /* prints scaled real, rounded to five digits */
2596 scaled delta; /* amount of allowable inaccuracy */
2598 mp_print_char(mp, '-');
2599 negate(s); /* print the sign, if negative */
2601 mp_print_int(mp, s / unity); /* print the integer part */
2605 mp_print_char(mp, '.');
2608 s=s+0100000-(delta / 2); /* round the final digit */
2609 mp_print_char(mp, '0'+(s / unity));
2616 @ We often want to print two scaled quantities in parentheses,
2617 separated by a comma.
2619 @<Basic printing...@>=
2620 void mp_print_two (MP mp,scaled x, scaled y) { /* prints `|(x,y)|' */
2621 mp_print_char(mp, '(');
2622 mp_print_scaled(mp, x);
2623 mp_print_char(mp, ',');
2624 mp_print_scaled(mp, y);
2625 mp_print_char(mp, ')');
2628 @ The |scaled| quantities in \MP\ programs are generally supposed to be
2629 less than $2^{12}$ in absolute value, so \MP\ does much of its internal
2630 arithmetic with 28~significant bits of precision. A |fraction| denotes
2631 a scaled integer whose binary point is assumed to be 28 bit positions
2634 @d fraction_half 01000000000 /* $2^{27}$, represents 0.50000000 */
2635 @d fraction_one 02000000000 /* $2^{28}$, represents 1.00000000 */
2636 @d fraction_two 04000000000 /* $2^{29}$, represents 2.00000000 */
2637 @d fraction_three 06000000000 /* $3\cdot2^{28}$, represents 3.00000000 */
2638 @d fraction_four 010000000000 /* $2^{30}$, represents 4.00000000 */
2641 typedef integer fraction; /* this type is used for scaled fractions */
2643 @ In fact, the two sorts of scaling discussed above aren't quite
2644 sufficient; \MP\ has yet another, used internally to keep track of angles
2645 in units of $2^{-20}$ degrees.
2647 @d forty_five_deg 0264000000 /* $45\cdot2^{20}$, represents $45^\circ$ */
2648 @d ninety_deg 0550000000 /* $90\cdot2^{20}$, represents $90^\circ$ */
2649 @d one_eighty_deg 01320000000 /* $180\cdot2^{20}$, represents $180^\circ$ */
2650 @d three_sixty_deg 02640000000 /* $360\cdot2^{20}$, represents $360^\circ$ */
2653 typedef integer angle; /* this type is used for scaled angles */
2655 @ The |make_fraction| routine produces the |fraction| equivalent of
2656 |p/q|, given integers |p| and~|q|; it computes the integer
2657 $f=\lfloor2^{28}p/q+{1\over2}\rfloor$, when $p$ and $q$ are
2658 positive. If |p| and |q| are both of the same scaled type |t|,
2659 the ``type relation'' |make_fraction(t,t)=fraction| is valid;
2660 and it's also possible to use the subroutine ``backwards,'' using
2661 the relation |make_fraction(t,fraction)=t| between scaled types.
2663 If the result would have magnitude $2^{31}$ or more, |make_fraction|
2664 sets |arith_error:=true|. Most of \MP's internal computations have
2665 been designed to avoid this sort of error.
2667 If this subroutine were programmed in assembly language on a typical
2668 machine, we could simply compute |(@t$2^{28}$@>*p)div q|, since a
2669 double-precision product can often be input to a fixed-point division
2670 instruction. But when we are restricted to int-eger arithmetic it
2671 is necessary either to resort to multiple-precision maneuvering
2672 or to use a simple but slow iteration. The multiple-precision technique
2673 would be about three times faster than the code adopted here, but it
2674 would be comparatively long and tricky, involving about sixteen
2675 additional multiplications and divisions.
2677 This operation is part of \MP's ``inner loop''; indeed, it will
2678 consume nearly 10\pct! of the running time (exclusive of input and output)
2679 if the code below is left unchanged. A machine-dependent recoding
2680 will therefore make \MP\ run faster. The present implementation
2681 is highly portable, but slow; it avoids multiplication and division
2682 except in the initial stage. System wizards should be careful to
2683 replace it with a routine that is guaranteed to produce identical
2684 results in all cases.
2685 @^system dependencies@>
2687 As noted below, a few more routines should also be replaced by machine-dependent
2688 code, for efficiency. But when a procedure is not part of the ``inner loop,''
2689 such changes aren't advisable; simplicity and robustness are
2690 preferable to trickery, unless the cost is too high.
2694 fraction mp_make_fraction (MP mp,integer p, integer q);
2695 integer mp_take_scaled (MP mp,integer q, scaled f) ;
2697 @ If FIXPT is not defined, we need these preprocessor values
2699 @d ELGORDO 0x7fffffff
2700 @d TWEXP31 2147483648.0
2701 @d TWEXP28 268435456.0
2703 @d TWEXP_16 (1.0/65536.0)
2704 @d TWEXP_28 (1.0/268435456.0)
2708 fraction mp_make_fraction (MP mp,integer p, integer q) {
2710 integer f; /* the fraction bits, with a leading 1 bit */
2711 integer n; /* the integer part of $\vert p/q\vert$ */
2712 integer be_careful; /* disables certain compiler optimizations */
2713 boolean negative = false; /* should the result be negated? */
2715 negate(p); negative=true;
2719 if ( q==0 ) mp_confusion(mp, '/');
2721 @:this can't happen /}{\quad \./@>
2722 negate(q); negative = ! negative;
2726 mp->arith_error=true;
2727 return ( negative ? -el_gordo : el_gordo);
2729 n=(n-1)*fraction_one;
2730 @<Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$@>;
2731 return (negative ? (-(f+n)) : (f+n));
2737 if (q==0) mp_confusion(mp,'/');
2739 d = TWEXP28 * (double)p /(double)q;
2742 if (d>=TWEXP31) {mp->arith_error=true; return ELGORDO;}
2744 if (d==i && ( ((q>0 ? -q : q)&077777)
2745 * (((i&037777)<<1)-1) & 04000)!=0) --i;
2748 if (d<= -TWEXP31) {mp->arith_error=true; return -ELGORDO;}
2750 if (d==i && ( ((q>0 ? q : -q)&077777)
2751 * (((i&037777)<<1)+1) & 04000)!=0) ++i;
2757 @ The |repeat| loop here preserves the following invariant relations
2758 between |f|, |p|, and~|q|:
2759 (i)~|0<=p<q|; (ii)~$fq+p=2^k(q+p_0)$, where $k$ is an integer and
2760 $p_0$ is the original value of~$p$.
2762 Notice that the computation specifies
2763 |(p-q)+p| instead of |(p+p)-q|, because the latter could overflow.
2764 Let us hope that optimizing compilers do not miss this point; a
2765 special variable |be_careful| is used to emphasize the necessary
2766 order of computation. Optimizing compilers should keep |be_careful|
2767 in a register, not store it in memory.
2770 @<Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$@>=
2774 be_careful=p-q; p=be_careful+p;
2780 } while (f<fraction_one);
2782 if ( be_careful+p>=0 ) incr(f);
2785 @ The dual of |make_fraction| is |take_fraction|, which multiplies a
2786 given integer~|q| by a fraction~|f|. When the operands are positive, it
2787 computes $p=\lfloor qf/2^{28}+{1\over2}\rfloor$, a symmetric function
2790 This routine is even more ``inner loopy'' than |make_fraction|;
2791 the present implementation consumes almost 20\pct! of \MP's computation
2792 time during typical jobs, so a machine-language substitute is advisable.
2793 @^inner loop@> @^system dependencies@>
2796 integer mp_take_fraction (MP mp,integer q, fraction f) ;
2800 integer mp_take_fraction (MP mp,integer q, fraction f) {
2801 integer p; /* the fraction so far */
2802 boolean negative; /* should the result be negated? */
2803 integer n; /* additional multiple of $q$ */
2804 integer be_careful; /* disables certain compiler optimizations */
2805 @<Reduce to the case that |f>=0| and |q>0|@>;
2806 if ( f<fraction_one ) {
2809 n=f / fraction_one; f=f % fraction_one;
2810 if ( q<=el_gordo / n ) {
2813 mp->arith_error=true; n=el_gordo;
2817 @<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>;
2818 be_careful=n-el_gordo;
2819 if ( be_careful+p>0 ){
2820 mp->arith_error=true; n=el_gordo-p;
2827 integer mp_take_fraction (MP mp,integer p, fraction q) {
2830 d = (double)p * (double)q * TWEXP_28;
2834 if (d!=TWEXP31 || (((p&077777)*(q&077777))&040000)==0)
2835 mp->arith_error = true;
2839 if (d==i && (((p&077777)*(q&077777))&040000)!=0) --i;
2843 if (d!= -TWEXP31 || ((-(p&077777)*(q&077777))&040000)==0)
2844 mp->arith_error = true;
2848 if (d==i && ((-(p&077777)*(q&077777))&040000)!=0) ++i;
2854 @ @<Reduce to the case that |f>=0| and |q>0|@>=
2858 negate( f); negative=true;
2861 negate(q); negative=! negative;
2864 @ The invariant relations in this case are (i)~$\lfloor(qf+p)/2^k\rfloor
2865 =\lfloor qf_0/2^{28}+{1\over2}\rfloor$, where $k$ is an integer and
2866 $f_0$ is the original value of~$f$; (ii)~$2^k\L f<2^{k+1}$.
2869 @<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>=
2870 p=fraction_half; /* that's $2^{27}$; the invariants hold now with $k=28$ */
2871 if ( q<fraction_four ) {
2873 if ( odd(f) ) p=halfp(p+q); else p=halfp(p);
2878 if ( odd(f) ) p=p+halfp(q-p); else p=halfp(p);
2884 @ When we want to multiply something by a |scaled| quantity, we use a scheme
2885 analogous to |take_fraction| but with a different scaling.
2886 Given positive operands, |take_scaled|
2887 computes the quantity $p=\lfloor qf/2^{16}+{1\over2}\rfloor$.
2889 Once again it is a good idea to use a machine-language replacement if
2890 possible; otherwise |take_scaled| will use more than 2\pct! of the running time
2891 when the Computer Modern fonts are being generated.
2896 integer mp_take_scaled (MP mp,integer q, scaled f) {
2897 integer p; /* the fraction so far */
2898 boolean negative; /* should the result be negated? */
2899 integer n; /* additional multiple of $q$ */
2900 integer be_careful; /* disables certain compiler optimizations */
2901 @<Reduce to the case that |f>=0| and |q>0|@>;
2905 n=f / unity; f=f % unity;
2906 if ( q<=el_gordo / n ) {
2909 mp->arith_error=true; n=el_gordo;
2913 @<Compute $p=\lfloor qf/2^{16}+{1\over2}\rfloor-q$@>;
2914 be_careful=n-el_gordo;
2915 if ( be_careful+p>0 ) {
2916 mp->arith_error=true; n=el_gordo-p;
2918 return ( negative ?(-(n+p)) :(n+p));
2920 integer mp_take_scaled (MP mp,integer p, scaled q) {
2923 d = (double)p * (double)q * TWEXP_16;
2927 if (d!=TWEXP31 || (((p&077777)*(q&077777))&040000)==0)
2928 mp->arith_error = true;
2932 if (d==i && (((p&077777)*(q&077777))&040000)!=0) --i;
2936 if (d!= -TWEXP31 || ((-(p&077777)*(q&077777))&040000)==0)
2937 mp->arith_error = true;
2941 if (d==i && ((-(p&077777)*(q&077777))&040000)!=0) ++i;
2947 @ @<Compute $p=\lfloor qf/2^{16}+{1\over2}\rfloor-q$@>=
2948 p=half_unit; /* that's $2^{15}$; the invariants hold now with $k=16$ */
2950 if ( q<fraction_four ) {
2952 p = (odd(f) ? halfp(p+q) : halfp(p));
2957 p = (odd(f) ? p+halfp(q-p) : halfp(p));
2962 @ For completeness, there's also |make_scaled|, which computes a
2963 quotient as a |scaled| number instead of as a |fraction|.
2964 In other words, the result is $\lfloor2^{16}p/q+{1\over2}\rfloor$, if the
2965 operands are positive. \ (This procedure is not used especially often,
2966 so it is not part of \MP's inner loop.)
2968 @<Internal library ...@>=
2969 scaled mp_make_scaled (MP mp,integer p, integer q) ;
2972 scaled mp_make_scaled (MP mp,integer p, integer q) {
2974 integer f; /* the fraction bits, with a leading 1 bit */
2975 integer n; /* the integer part of $\vert p/q\vert$ */
2976 boolean negative; /* should the result be negated? */
2977 integer be_careful; /* disables certain compiler optimizations */
2978 if ( p>=0 ) negative=false;
2979 else { negate(p); negative=true; };
2982 if ( q==0 ) mp_confusion(mp, "/");
2983 @:this can't happen /}{\quad \./@>
2985 negate(q); negative=! negative;
2989 mp->arith_error=true;
2990 return (negative ? (-el_gordo) : el_gordo);
2993 @<Compute $f=\lfloor 2^{16}(1+p/q)+{1\over2}\rfloor$@>;
2994 return ( negative ? (-(f+n)) :(f+n));
3000 if (q==0) mp_confusion(mp,"/");
3002 d = TWEXP16 * (double)p /(double)q;
3005 if (d>=TWEXP31) {mp->arith_error=true; return ELGORDO;}
3007 if (d==i && ( ((q>0 ? -q : q)&077777)
3008 * (((i&037777)<<1)-1) & 04000)!=0) --i;
3011 if (d<= -TWEXP31) {mp->arith_error=true; return -ELGORDO;}
3013 if (d==i && ( ((q>0 ? q : -q)&077777)
3014 * (((i&037777)<<1)+1) & 04000)!=0) ++i;
3020 @ @<Compute $f=\lfloor 2^{16}(1+p/q)+{1\over2}\rfloor$@>=
3023 be_careful=p-q; p=be_careful+p;
3024 if ( p>=0 ) f=f+f+1;
3025 else { f+=f; p=p+q; };
3028 if ( be_careful+p>=0 ) incr(f)
3030 @ Here is a typical example of how the routines above can be used.
3031 It computes the function
3032 $${1\over3\tau}f(\theta,\phi)=
3033 {\tau^{-1}\bigl(2+\sqrt2\,(\sin\theta-{1\over16}\sin\phi)
3034 (\sin\phi-{1\over16}\sin\theta)(\cos\theta-\cos\phi)\bigr)\over
3035 3\,\bigl(1+{1\over2}(\sqrt5-1)\cos\theta+{1\over2}(3-\sqrt5\,)\cos\phi\bigr)},$$
3036 where $\tau$ is a |scaled| ``tension'' parameter. This is \MP's magic
3037 fudge factor for placing the first control point of a curve that starts
3038 at an angle $\theta$ and ends at an angle $\phi$ from the straight path.
3039 (Actually, if the stated quantity exceeds 4, \MP\ reduces it to~4.)
3041 The trigonometric quantity to be multiplied by $\sqrt2$ is less than $\sqrt2$.
3042 (It's a sum of eight terms whose absolute values can be bounded using
3043 relations such as $\sin\theta\cos\theta\L{1\over2}$.) Thus the numerator
3044 is positive; and since the tension $\tau$ is constrained to be at least
3045 $3\over4$, the numerator is less than $16\over3$. The denominator is
3046 nonnegative and at most~6. Hence the fixed-point calculations below
3047 are guaranteed to stay within the bounds of a 32-bit computer word.
3049 The angles $\theta$ and $\phi$ are given implicitly in terms of |fraction|
3050 arguments |st|, |ct|, |sf|, and |cf|, representing $\sin\theta$, $\cos\theta$,
3051 $\sin\phi$, and $\cos\phi$, respectively.
3054 fraction mp_velocity (MP mp,fraction st, fraction ct, fraction sf,
3055 fraction cf, scaled t) {
3056 integer acc,num,denom; /* registers for intermediate calculations */
3057 acc=mp_take_fraction(mp, st-(sf / 16), sf-(st / 16));
3058 acc=mp_take_fraction(mp, acc,ct-cf);
3059 num=fraction_two+mp_take_fraction(mp, acc,379625062);
3060 /* $2^{28}\sqrt2\approx379625062.497$ */
3061 denom=fraction_three+mp_take_fraction(mp, ct,497706707)+mp_take_fraction(mp, cf,307599661);
3062 /* $3\cdot2^{27}\cdot(\sqrt5-1)\approx497706706.78$ and
3063 $3\cdot2^{27}\cdot(3-\sqrt5\,)\approx307599661.22$ */
3064 if ( t!=unity ) num=mp_make_scaled(mp, num,t);
3065 /* |make_scaled(fraction,scaled)=fraction| */
3066 if ( num / 4>=denom )
3067 return fraction_four;
3069 return mp_make_fraction(mp, num, denom);
3072 @ The following somewhat different subroutine tests rigorously if $ab$ is
3073 greater than, equal to, or less than~$cd$,
3074 given integers $(a,b,c,d)$. In most cases a quick decision is reached.
3075 The result is $+1$, 0, or~$-1$ in the three respective cases.
3077 @d mp_ab_vs_cd(M,A,B,C,D) mp_do_ab_vs_cd(A,B,C,D)
3080 integer mp_do_ab_vs_cd (integer a,integer b, integer c, integer d) {
3081 integer q,r; /* temporary registers */
3082 @<Reduce to the case that |a,c>=0|, |b,d>0|@>;
3084 q = a / d; r = c / b;
3086 return ( q>r ? 1 : -1);
3087 q = a % d; r = c % b;
3090 if ( q==0 ) return -1;
3092 } /* now |a>d>0| and |c>b>0| */
3095 @ @<Reduce to the case that |a...@>=
3096 if ( a<0 ) { negate(a); negate(b); };
3097 if ( c<0 ) { negate(c); negate(d); };
3100 if ( (a==0||b==0)&&(c==0||d==0) ) return 0;
3104 return ( a==0 ? 0 : -1);
3105 q=a; a=c; c=q; q=-b; b=-d; d=q;
3106 } else if ( b<=0 ) {
3107 if ( b<0 ) if ( a>0 ) return -1;
3108 return (c==0 ? 0 : -1);
3111 @ We conclude this set of elementary routines with some simple rounding
3112 and truncation operations.
3114 @<Internal library declarations@>=
3115 #define mp_floor_scaled(M,i) ((i)&(-65536))
3116 #define mp_round_unscaled(M,i) (((i>>15)+1)>>1)
3117 #define mp_round_fraction(M,i) (((i>>11)+1)>>1)
3120 @* \[8] Algebraic and transcendental functions.
3121 \MP\ computes all of the necessary special functions from scratch, without
3122 relying on |real| arithmetic or system subroutines for sines, cosines, etc.
3124 @ To get the square root of a |scaled| number |x|, we want to calculate
3125 $s=\lfloor 2^8\!\sqrt x +{1\over2}\rfloor$. If $x>0$, this is the unique
3126 integer such that $2^{16}x-s\L s^2<2^{16}x+s$. The following subroutine
3127 determines $s$ by an iterative method that maintains the invariant
3128 relations $x=2^{46-2k}x_0\bmod 2^{30}$, $0<y=\lfloor 2^{16-2k}x_0\rfloor
3129 -s^2+s\L q=2s$, where $x_0$ is the initial value of $x$. The value of~$y$
3130 might, however, be zero at the start of the first iteration.
3133 scaled mp_square_rt (MP mp,scaled x) ;
3136 scaled mp_square_rt (MP mp,scaled x) {
3137 small_number k; /* iteration control counter */
3138 integer y,q; /* registers for intermediate calculations */
3140 @<Handle square root of zero or negative argument@>;
3143 while ( x<fraction_two ) { /* i.e., |while x<@t$2^{29}$@>|\unskip */
3146 if ( x<fraction_four ) y=0;
3147 else { x=x-fraction_four; y=1; };
3149 @<Decrease |k| by 1, maintaining the invariant
3150 relations between |x|, |y|, and~|q|@>;
3156 @ @<Handle square root of zero...@>=
3159 print_err("Square root of ");
3160 @.Square root...replaced by 0@>
3161 mp_print_scaled(mp, x); mp_print(mp, " has been replaced by 0");
3162 help2("Since I don't take square roots of negative numbers,")
3163 ("I'm zeroing this one. Proceed, with fingers crossed.");
3169 @ @<Decrease |k| by 1, maintaining...@>=
3171 if ( x>=fraction_four ) { /* note that |fraction_four=@t$2^{30}$@>| */
3172 x=x-fraction_four; incr(y);
3174 x+=x; y=y+y-q; q+=q;
3175 if ( x>=fraction_four ) { x=x-fraction_four; incr(y); };
3176 if ( y>q ){ y=y-q; q=q+2; }
3177 else if ( y<=0 ) { q=q-2; y=y+q; };
3180 @ Pythagorean addition $\psqrt{a^2+b^2}$ is implemented by an elegant
3181 iterative scheme due to Cleve Moler and Donald Morrison [{\sl IBM Journal
3182 @^Moler, Cleve Barry@>
3183 @^Morrison, Donald Ross@>
3184 of Research and Development\/ \bf27} (1983), 577--581]. It modifies |a| and~|b|
3185 in such a way that their Pythagorean sum remains invariant, while the
3186 smaller argument decreases.
3188 @<Internal library ...@>=
3189 integer mp_pyth_add (MP mp,integer a, integer b);
3193 integer mp_pyth_add (MP mp,integer a, integer b) {
3194 fraction r; /* register used to transform |a| and |b| */
3195 boolean big; /* is the result dangerously near $2^{31}$? */
3197 if ( a<b ) { r=b; b=a; a=r; }; /* now |0<=b<=a| */
3199 if ( a<fraction_two ) {
3202 a=a / 4; b=b / 4; big=true;
3203 }; /* we reduced the precision to avoid arithmetic overflow */
3204 @<Replace |a| by an approximation to $\psqrt{a^2+b^2}$@>;
3206 if ( a<fraction_two ) {
3209 mp->arith_error=true; a=el_gordo;
3216 @ The key idea here is to reflect the vector $(a,b)$ about the
3217 line through $(a,b/2)$.
3219 @<Replace |a| by an approximation to $\psqrt{a^2+b^2}$@>=
3221 r=mp_make_fraction(mp, b,a);
3222 r=mp_take_fraction(mp, r,r); /* now $r\approx b^2/a^2$ */
3224 r=mp_make_fraction(mp, r,fraction_four+r);
3225 a=a+mp_take_fraction(mp, a+a,r); b=mp_take_fraction(mp, b,r);
3229 @ Here is a similar algorithm for $\psqrt{a^2-b^2}$.
3230 It converges slowly when $b$ is near $a$, but otherwise it works fine.
3233 integer mp_pyth_sub (MP mp,integer a, integer b) {
3234 fraction r; /* register used to transform |a| and |b| */
3235 boolean big; /* is the input dangerously near $2^{31}$? */
3238 @<Handle erroneous |pyth_sub| and set |a:=0|@>;
3240 if ( a<fraction_four ) {
3243 a=halfp(a); b=halfp(b); big=true;
3245 @<Replace |a| by an approximation to $\psqrt{a^2-b^2}$@>;
3246 if ( big ) double(a);
3251 @ @<Replace |a| by an approximation to $\psqrt{a^2-b^2}$@>=
3253 r=mp_make_fraction(mp, b,a);
3254 r=mp_take_fraction(mp, r,r); /* now $r\approx b^2/a^2$ */
3256 r=mp_make_fraction(mp, r,fraction_four-r);
3257 a=a-mp_take_fraction(mp, a+a,r); b=mp_take_fraction(mp, b,r);
3260 @ @<Handle erroneous |pyth_sub| and set |a:=0|@>=
3263 print_err("Pythagorean subtraction "); mp_print_scaled(mp, a);
3264 mp_print(mp, "+-+"); mp_print_scaled(mp, b);
3265 mp_print(mp, " has been replaced by 0");
3267 help2("Since I don't take square roots of negative numbers,")
3268 ("I'm zeroing this one. Proceed, with fingers crossed.");
3274 @ The subroutines for logarithm and exponential involve two tables.
3275 The first is simple: |two_to_the[k]| equals $2^k$. The second involves
3276 a bit more calculation, which the author claims to have done correctly:
3277 |spec_log[k]| is $2^{27}$ times $\ln\bigl(1/(1-2^{-k})\bigr)=
3278 2^{-k}+{1\over2}2^{-2k}+{1\over3}2^{-3k}+\cdots\,$, rounded to the
3281 @d two_to_the(A) (1<<(A))
3284 static const integer spec_log[29] = { 0, /* special logarithms */
3285 93032640, 38612034, 17922280, 8662214, 4261238, 2113709,
3286 1052693, 525315, 262400, 131136, 65552, 32772, 16385,
3287 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1, 1 };
3289 @ @<Local variables for initialization@>=
3290 integer k; /* all-purpose loop index */
3293 @ Here is the routine that calculates $2^8$ times the natural logarithm
3294 of a |scaled| quantity; it is an integer approximation to $2^{24}\ln(x/2^{16})$,
3295 when |x| is a given positive integer.
3297 The method is based on exercise 1.2.2--25 in {\sl The Art of Computer
3298 Programming\/}: During the main iteration we have $1\L 2^{-30}x<1/(1-2^{1-k})$,
3299 and the logarithm of $2^{30}x$ remains to be added to an accumulator
3300 register called~$y$. Three auxiliary bits of accuracy are retained in~$y$
3301 during the calculation, and sixteen auxiliary bits to extend |y| are
3302 kept in~|z| during the initial argument reduction. (We add
3303 $100\cdot2^{16}=6553600$ to~|z| and subtract 100 from~|y| so that |z| will
3304 not become negative; also, the actual amount subtracted from~|y| is~96,
3305 not~100, because we want to add~4 for rounding before the final division by~8.)
3308 scaled mp_m_log (MP mp,scaled x) {
3309 integer y,z; /* auxiliary registers */
3310 integer k; /* iteration counter */
3312 @<Handle non-positive logarithm@>;
3314 y=1302456956+4-100; /* $14\times2^{27}\ln2\approx1302456956.421063$ */
3315 z=27595+6553600; /* and $2^{16}\times .421063\approx 27595$ */
3316 while ( x<fraction_four ) {
3317 double(x); y-=93032639; z-=48782;
3318 } /* $2^{27}\ln2\approx 93032639.74436163$ and $2^{16}\times.74436163\approx 48782$ */
3319 y=y+(z / unity); k=2;
3320 while ( x>fraction_four+4 ) {
3321 @<Increase |k| until |x| can be multiplied by a
3322 factor of $2^{-k}$, and adjust $y$ accordingly@>;
3328 @ @<Increase |k| until |x| can...@>=
3330 z=((x-1) / two_to_the(k))+1; /* $z=\lceil x/2^k\rceil$ */
3331 while ( x<fraction_four+z ) { z=halfp(z+1); incr(k); };
3332 y+=spec_log[k]; x-=z;
3335 @ @<Handle non-positive logarithm@>=
3337 print_err("Logarithm of ");
3338 @.Logarithm...replaced by 0@>
3339 mp_print_scaled(mp, x); mp_print(mp, " has been replaced by 0");
3340 help2("Since I don't take logs of non-positive numbers,")
3341 ("I'm zeroing this one. Proceed, with fingers crossed.");
3346 @ Conversely, the exponential routine calculates $\exp(x/2^8)$,
3347 when |x| is |scaled|. The result is an integer approximation to
3348 $2^{16}\exp(x/2^{24})$, when |x| is regarded as an integer.
3351 scaled mp_m_exp (MP mp,scaled x) {
3352 small_number k; /* loop control index */
3353 integer y,z; /* auxiliary registers */
3354 if ( x>174436200 ) {
3355 /* $2^{24}\ln((2^{31}-1)/2^{16})\approx 174436199.51$ */
3356 mp->arith_error=true;
3358 } else if ( x<-197694359 ) {
3359 /* $2^{24}\ln(2^{-1}/2^{16})\approx-197694359.45$ */
3363 z=-8*x; y=04000000; /* $y=2^{20}$ */
3365 if ( x<=127919879 ) {
3367 /* $2^{27}\ln((2^{31}-1)/2^{20})\approx 1023359037.125$ */
3369 z=8*(174436200-x); /* |z| is always nonnegative */
3373 @<Multiply |y| by $\exp(-z/2^{27})$@>;
3375 return ((y+8) / 16);
3381 @ The idea here is that subtracting |spec_log[k]| from |z| corresponds
3382 to multiplying |y| by $1-2^{-k}$.
3384 A subtle point (which had to be checked) was that if $x=127919879$, the
3385 value of~|y| will decrease so that |y+8| doesn't overflow. In fact,
3386 $z$ will be 5 in this case, and |y| will decrease by~64 when |k=25|
3387 and by~16 when |k=27|.
3389 @<Multiply |y| by...@>=
3392 while ( z>=spec_log[k] ) {
3394 y=y-1-((y-two_to_the(k-1)) / two_to_the(k));
3399 @ The trigonometric subroutines use an auxiliary table such that
3400 |spec_atan[k]| contains an approximation to the |angle| whose tangent
3401 is~$1/2^k$. $\arctan2^{-k}$ times $2^{20}\cdot180/\pi$
3404 static const angle spec_atan[27] = { 0, 27855475, 14718068, 7471121, 3750058,
3405 1876857, 938658, 469357, 234682, 117342, 58671, 29335, 14668, 7334, 3667,
3406 1833, 917, 458, 229, 115, 57, 29, 14, 7, 4, 2, 1 };
3408 @ Given integers |x| and |y|, not both zero, the |n_arg| function
3409 returns the |angle| whose tangent points in the direction $(x,y)$.
3410 This subroutine first determines the correct octant, then solves the
3411 problem for |0<=y<=x|, then converts the result appropriately to
3412 return an answer in the range |-one_eighty_deg<=@t$\theta$@><=one_eighty_deg|.
3413 (The answer is |+one_eighty_deg| if |y=0| and |x<0|, but an answer of
3414 |-one_eighty_deg| is possible if, for example, |y=-1| and $x=-2^{30}$.)
3416 The octants are represented in a ``Gray code,'' since that turns out
3417 to be computationally simplest.
3423 @d second_octant (first_octant+switch_x_and_y)
3424 @d third_octant (first_octant+switch_x_and_y+negate_x)
3425 @d fourth_octant (first_octant+negate_x)
3426 @d fifth_octant (first_octant+negate_x+negate_y)
3427 @d sixth_octant (first_octant+switch_x_and_y+negate_x+negate_y)
3428 @d seventh_octant (first_octant+switch_x_and_y+negate_y)
3429 @d eighth_octant (first_octant+negate_y)
3432 angle mp_n_arg (MP mp,integer x, integer y) {
3433 angle z; /* auxiliary register */
3434 integer t; /* temporary storage */
3435 small_number k; /* loop counter */
3436 int octant; /* octant code */
3438 octant=first_octant;
3440 negate(x); octant=first_octant+negate_x;
3443 negate(y); octant=octant+negate_y;
3446 t=y; y=x; x=t; octant=octant+switch_x_and_y;
3449 @<Handle undefined arg@>;
3451 @<Set variable |z| to the arg of $(x,y)$@>;
3452 @<Return an appropriate answer based on |z| and |octant|@>;
3456 @ @<Handle undefined arg@>=
3458 print_err("angle(0,0) is taken as zero");
3459 @.angle(0,0)...zero@>
3460 help2("The `angle' between two identical points is undefined.")
3461 ("I'm zeroing this one. Proceed, with fingers crossed.");
3466 @ @<Return an appropriate answer...@>=
3468 case first_octant: return z;
3469 case second_octant: return (ninety_deg-z);
3470 case third_octant: return (ninety_deg+z);
3471 case fourth_octant: return (one_eighty_deg-z);
3472 case fifth_octant: return (z-one_eighty_deg);
3473 case sixth_octant: return (-z-ninety_deg);
3474 case seventh_octant: return (z-ninety_deg);
3475 case eighth_octant: return (-z);
3476 }; /* there are no other cases */
3479 @ At this point we have |x>=y>=0|, and |x>0|. The numbers are scaled up
3480 or down until $2^{28}\L x<2^{29}$, so that accurate fixed-point calculations
3483 @<Set variable |z| to the arg...@>=
3484 while ( x>=fraction_two ) {
3485 x=halfp(x); y=halfp(y);
3489 while ( x<fraction_one ) {
3492 @<Increase |z| to the arg of $(x,y)$@>;
3495 @ During the calculations of this section, variables |x| and~|y|
3496 represent actual coordinates $(x,2^{-k}y)$. We will maintain the
3497 condition |x>=y|, so that the tangent will be at most $2^{-k}$.
3498 If $x<2y$, the tangent is greater than $2^{-k-1}$. The transformation
3499 $(a,b)\mapsto(a+b\tan\phi,b-a\tan\phi)$ replaces $(a,b)$ by
3500 coordinates whose angle has decreased by~$\phi$; in the special case
3501 $a=x$, $b=2^{-k}y$, and $\tan\phi=2^{-k-1}$, this operation reduces
3502 to the particularly simple iteration shown here. [Cf.~John E. Meggitt,
3503 @^Meggitt, John E.@>
3504 {\sl IBM Journal of Research and Development\/ \bf6} (1962), 210--226.]
3506 The initial value of |x| will be multiplied by at most
3507 $(1+{1\over2})(1+{1\over8})(1+{1\over32})\cdots\approx 1.7584$; hence
3508 there is no chance of integer overflow.
3510 @<Increase |z|...@>=
3515 z=z+spec_atan[k]; t=x; x=x+(y / two_to_the(k+k)); y=y-t;
3520 if ( y>x ) { z=z+spec_atan[k]; y=y-x; };
3523 @ Conversely, the |n_sin_cos| routine takes an |angle| and produces the sine
3524 and cosine of that angle. The results of this routine are
3525 stored in global integer variables |n_sin| and |n_cos|.
3528 fraction n_sin;fraction n_cos; /* results computed by |n_sin_cos| */
3530 @ Given an integer |z| that is $2^{20}$ times an angle $\theta$ in degrees,
3531 the purpose of |n_sin_cos(z)| is to set
3532 |x=@t$r\cos\theta$@>| and |y=@t$r\sin\theta$@>| (approximately),
3533 for some rather large number~|r|. The maximum of |x| and |y|
3534 will be between $2^{28}$ and $2^{30}$, so that there will be hardly
3535 any loss of accuracy. Then |x| and~|y| are divided by~|r|.
3538 void mp_n_sin_cos (MP mp,angle z) { /* computes a multiple of the sine
3540 small_number k; /* loop control variable */
3541 int q; /* specifies the quadrant */
3542 fraction r; /* magnitude of |(x,y)| */
3543 integer x,y,t; /* temporary registers */
3544 while ( z<0 ) z=z+three_sixty_deg;
3545 z=z % three_sixty_deg; /* now |0<=z<three_sixty_deg| */
3546 q=z / forty_five_deg; z=z % forty_five_deg;
3547 x=fraction_one; y=x;
3548 if ( ! odd(q) ) z=forty_five_deg-z;
3549 @<Subtract angle |z| from |(x,y)|@>;
3550 @<Convert |(x,y)| to the octant determined by~|q|@>;
3551 r=mp_pyth_add(mp, x,y);
3552 mp->n_cos=mp_make_fraction(mp, x,r);
3553 mp->n_sin=mp_make_fraction(mp, y,r);
3556 @ In this case the octants are numbered sequentially.
3558 @<Convert |(x,...@>=
3561 case 1: t=x; x=y; y=t; break;
3562 case 2: t=x; x=-y; y=t; break;
3563 case 3: negate(x); break;
3564 case 4: negate(x); negate(y); break;
3565 case 5: t=x; x=-y; y=-t; break;
3566 case 6: t=x; x=y; y=-t; break;
3567 case 7: negate(y); break;
3568 } /* there are no other cases */
3570 @ The main iteration of |n_sin_cos| is similar to that of |n_arg| but
3571 applied in reverse. The values of |spec_atan[k]| decrease slowly enough
3572 that this loop is guaranteed to terminate before the (nonexistent) value
3573 |spec_atan[27]| would be required.
3575 @<Subtract angle |z|...@>=
3578 if ( z>=spec_atan[k] ) {
3579 z=z-spec_atan[k]; t=x;
3580 x=t+y / two_to_the(k);
3581 y=y-t / two_to_the(k);
3585 if ( y<0 ) y=0 /* this precaution may never be needed */
3587 @ And now let's complete our collection of numeric utility routines
3588 by considering random number generation.
3589 \MP\ generates pseudo-random numbers with the additive scheme recommended
3590 in Section 3.6 of {\sl The Art of Computer Programming}; however, the
3591 results are random fractions between 0 and |fraction_one-1|, inclusive.
3593 There's an auxiliary array |randoms| that contains 55 pseudo-random
3594 fractions. Using the recurrence $x_n=(x_{n-55}-x_{n-31})\bmod 2^{28}$,
3595 we generate batches of 55 new $x_n$'s at a time by calling |new_randoms|.
3596 The global variable |j_random| tells which element has most recently
3598 The global variable |random_seed| was introduced in version 0.9,
3599 for the sole reason of stressing the fact that the initial value of the
3600 random seed is system-dependant. The initialization code below will initialize
3601 this variable to |(internal[mp_time] div unity)+internal[mp_day]|, but this
3602 is not good enough on modern fast machines that are capable of running
3603 multiple MetaPost processes within the same second.
3604 @^system dependencies@>
3607 fraction randoms[55]; /* the last 55 random values generated */
3608 int j_random; /* the number of unused |randoms| */
3610 @ @<Option variables@>=
3611 int random_seed; /* the default random seed */
3613 @ @<Allocate or initialize ...@>=
3614 mp->random_seed = (scaled)opt->random_seed;
3616 @ To consume a random fraction, the program below will say `|next_random|'
3617 and then it will fetch |randoms[j_random]|.
3619 @d next_random { if ( mp->j_random==0 ) mp_new_randoms(mp);
3620 else decr(mp->j_random); }
3623 void mp_new_randoms (MP mp) {
3624 int k; /* index into |randoms| */
3625 fraction x; /* accumulator */
3626 for (k=0;k<=23;k++) {
3627 x=mp->randoms[k]-mp->randoms[k+31];
3628 if ( x<0 ) x=x+fraction_one;
3631 for (k=24;k<= 54;k++){
3632 x=mp->randoms[k]-mp->randoms[k-24];
3633 if ( x<0 ) x=x+fraction_one;
3640 void mp_init_randoms (MP mp,scaled seed);
3642 @ To initialize the |randoms| table, we call the following routine.
3645 void mp_init_randoms (MP mp,scaled seed) {
3646 fraction j,jj,k; /* more or less random integers */
3647 int i; /* index into |randoms| */
3649 while ( j>=fraction_one ) j=halfp(j);
3651 for (i=0;i<=54;i++ ){
3653 if ( k<0 ) k=k+fraction_one;
3654 mp->randoms[(i*21)% 55]=j;
3658 mp_new_randoms(mp); /* ``warm up'' the array */
3661 @ To produce a uniform random number in the range |0<=u<x| or |0>=u>x|
3662 or |0=u=x|, given a |scaled| value~|x|, we proceed as shown here.
3664 Note that the call of |take_fraction| will produce the values 0 and~|x|
3665 with about half the probability that it will produce any other particular
3666 values between 0 and~|x|, because it rounds its answers.
3669 scaled mp_unif_rand (MP mp,scaled x) {
3670 scaled y; /* trial value */
3671 next_random; y=mp_take_fraction(mp, abs(x),mp->randoms[mp->j_random]);
3672 if ( y==abs(x) ) return 0;
3673 else if ( x>0 ) return y;
3677 @ Finally, a normal deviate with mean zero and unit standard deviation
3678 can readily be obtained with the ratio method (Algorithm 3.4.1R in
3679 {\sl The Art of Computer Programming\/}).
3682 scaled mp_norm_rand (MP mp) {
3683 integer x,u,l; /* what the book would call $2^{16}X$, $2^{28}U$, and $-2^{24}\ln U$ */
3687 x=mp_take_fraction(mp, 112429,mp->randoms[mp->j_random]-fraction_half);
3688 /* $2^{16}\sqrt{8/e}\approx 112428.82793$ */
3689 next_random; u=mp->randoms[mp->j_random];
3690 } while (abs(x)>=u);
3691 x=mp_make_fraction(mp, x,u);
3692 l=139548960-mp_m_log(mp, u); /* $2^{24}\cdot12\ln2\approx139548959.6165$ */
3693 } while (mp_ab_vs_cd(mp, 1024,l,x,x)<0);
3697 @* \[9] Packed data.
3698 In order to make efficient use of storage space, \MP\ bases its major data
3699 structures on a |memory_word|, which contains either a (signed) integer,
3700 possibly scaled, or a small number of fields that are one half or one
3701 quarter of the size used for storing integers.
3703 If |x| is a variable of type |memory_word|, it contains up to four
3704 fields that can be referred to as follows:
3705 $$\vbox{\halign{\hfil#&#\hfil&#\hfil\cr
3706 |x|&.|int|&(an |integer|)\cr
3707 |x|&.|sc|\qquad&(a |scaled| integer)\cr
3708 |x.hh.lh|, |x.hh|&.|rh|&(two halfword fields)\cr
3709 |x.hh.b0|, |x.hh.b1|, |x.hh|&.|rh|&(two quarterword fields, one halfword
3711 |x.qqqq.b0|, |x.qqqq.b1|, |x.qqqq|&.|b2|, |x.qqqq.b3|\hskip-100pt
3712 &\qquad\qquad\qquad(four quarterword fields)\cr}}$$
3713 This is somewhat cumbersome to write, and not very readable either, but
3714 macros will be used to make the notation shorter and more transparent.
3715 The code below gives a formal definition of |memory_word| and
3716 its subsidiary types, using packed variant records. \MP\ makes no
3717 assumptions about the relative positions of the fields within a word.
3719 @d max_quarterword 0x3FFF /* largest allowable value in a |quarterword| */
3720 @d max_halfword 0xFFFFFFF /* largest allowable value in a |halfword| */
3722 @ Here are the inequalities that the quarterword and halfword values
3723 must satisfy (or rather, the inequalities that they mustn't satisfy):
3725 @<Check the ``constant''...@>=
3726 if (mp->ini_version) {
3727 if ( mp->mem_max!=mp->mem_top ) mp->bad=8;
3729 if ( mp->mem_max<mp->mem_top ) mp->bad=8;
3731 if ( max_quarterword<255 ) mp->bad=9;
3732 if ( max_halfword<65535 ) mp->bad=10;
3733 if ( max_quarterword>max_halfword ) mp->bad=11;
3734 if ( mp->mem_max>=max_halfword ) mp->bad=12;
3735 if ( mp->max_strings>max_halfword ) mp->bad=13;
3737 @ The macros |qi| and |qo| are used for input to and output
3738 from quarterwords. These are legacy macros.
3739 @^system dependencies@>
3741 @d qo(A) (A) /* to read eight bits from a quarterword */
3742 @d qi(A) (A) /* to store eight bits in a quarterword */
3744 @ The reader should study the following definitions closely:
3745 @^system dependencies@>
3747 @d sc cint /* |scaled| data is equivalent to |integer| */
3750 typedef short quarterword; /* 1/4 of a word */
3751 typedef int halfword; /* 1/2 of a word */
3756 struct { /* Make B0,B1 overlap the most significant bytes of LH. */
3763 quarterword B2, B3, B0, B1;
3778 @ When debugging, we may want to print a |memory_word| without knowing
3779 what type it is; so we print it in all modes.
3783 void mp_print_word (MP mp,memory_word w) {
3784 /* prints |w| in all ways */
3785 mp_print_int(mp, w.cint); mp_print_char(mp, ' ');
3786 mp_print_scaled(mp, w.sc); mp_print_char(mp, ' ');
3787 mp_print_scaled(mp, w.sc / 010000); mp_print_ln(mp);
3788 mp_print_int(mp, w.hh.lh); mp_print_char(mp, '=');
3789 mp_print_int(mp, w.hh.b0); mp_print_char(mp, ':');
3790 mp_print_int(mp, w.hh.b1); mp_print_char(mp, ';');
3791 mp_print_int(mp, w.hh.rh); mp_print_char(mp, ' ');
3792 mp_print_int(mp, w.qqqq.b0); mp_print_char(mp, ':');
3793 mp_print_int(mp, w.qqqq.b1); mp_print_char(mp, ':');
3794 mp_print_int(mp, w.qqqq.b2); mp_print_char(mp, ':');
3795 mp_print_int(mp, w.qqqq.b3);
3799 @* \[10] Dynamic memory allocation.
3801 The \MP\ system does nearly all of its own memory allocation, so that it
3802 can readily be transported into environments that do not have automatic
3803 facilities for strings, garbage collection, etc., and so that it can be in
3804 control of what error messages the user receives. The dynamic storage
3805 requirements of \MP\ are handled by providing a large array |mem| in
3806 which consecutive blocks of words are used as nodes by the \MP\ routines.
3808 Pointer variables are indices into this array, or into another array
3809 called |eqtb| that will be explained later. A pointer variable might
3810 also be a special flag that lies outside the bounds of |mem|, so we
3811 allow pointers to assume any |halfword| value. The minimum memory
3812 index represents a null pointer.
3814 @d null 0 /* the null pointer */
3815 @d mp_void (null+1) /* a null pointer different from |null| */
3819 typedef halfword pointer; /* a flag or a location in |mem| or |eqtb| */
3821 @ The |mem| array is divided into two regions that are allocated separately,
3822 but the dividing line between these two regions is not fixed; they grow
3823 together until finding their ``natural'' size in a particular job.
3824 Locations less than or equal to |lo_mem_max| are used for storing
3825 variable-length records consisting of two or more words each. This region
3826 is maintained using an algorithm similar to the one described in exercise
3827 2.5--19 of {\sl The Art of Computer Programming}. However, no size field
3828 appears in the allocated nodes; the program is responsible for knowing the
3829 relevant size when a node is freed. Locations greater than or equal to
3830 |hi_mem_min| are used for storing one-word records; a conventional
3831 \.{AVAIL} stack is used for allocation in this region.
3833 Locations of |mem| between |0| and |mem_top| may be dumped as part
3834 of preloaded format files, by the \.{INIMP} preprocessor.
3836 Production versions of \MP\ may extend the memory at the top end in order to
3837 provide more space; these locations, between |mem_top| and |mem_max|,
3838 are always used for single-word nodes.
3840 The key pointers that govern |mem| allocation have a prescribed order:
3841 $$\hbox{|null=0<lo_mem_max<hi_mem_min<mem_top<=mem_end<=mem_max|.}$$
3844 memory_word *mem; /* the big dynamic storage area */
3845 pointer lo_mem_max; /* the largest location of variable-size memory in use */
3846 pointer hi_mem_min; /* the smallest location of one-word memory in use */
3850 @d xfree(A) do { mp_xfree(A); A=NULL; } while (0)
3851 @d xrealloc(P,A,B) mp_xrealloc(mp,P,A,B)
3852 @d xmalloc(A,B) mp_xmalloc(mp,A,B)
3853 @d xstrdup(A) mp_xstrdup(mp,A)
3854 @d XREALLOC(a,b,c) a = xrealloc(a,(b+1),sizeof(c));
3856 @<Declare helpers@>=
3857 void mp_xfree (void *x);
3858 void *mp_xrealloc (MP mp, void *p, size_t nmem, size_t size) ;
3859 void *mp_xmalloc (MP mp, size_t nmem, size_t size) ;
3860 char *mp_xstrdup(MP mp, const char *s);
3862 @ The |max_size_test| guards against overflow, on the assumption that
3863 |size_t| is at least 31bits wide.
3865 @d max_size_test 0x7FFFFFFF
3868 void mp_xfree (void *x) {
3869 if (x!=NULL) free(x);
3871 void *mp_xrealloc (MP mp, void *p, size_t nmem, size_t size) {
3873 if ((max_size_test/size)<nmem) {
3874 do_fprintf(mp->err_out,"Memory size overflow!\n");
3875 mp->history =mp_fatal_error_stop; mp_jump_out(mp);
3877 w = realloc (p,(nmem*size));
3879 do_fprintf(mp->err_out,"Out of memory!\n");
3880 mp->history =mp_fatal_error_stop; mp_jump_out(mp);
3884 void *mp_xmalloc (MP mp, size_t nmem, size_t size) {
3886 if ((max_size_test/size)<nmem) {
3887 do_fprintf(mp->err_out,"Memory size overflow!\n");
3888 mp->history =mp_fatal_error_stop; mp_jump_out(mp);
3890 w = malloc (nmem*size);
3892 do_fprintf(mp->err_out,"Out of memory!\n");
3893 mp->history =mp_fatal_error_stop; mp_jump_out(mp);
3897 char *mp_xstrdup(MP mp, const char *s) {
3903 do_fprintf(mp->err_out,"Out of memory!\n");
3904 mp->history =mp_fatal_error_stop; mp_jump_out(mp);
3911 @<Allocate or initialize ...@>=
3912 mp->mem = xmalloc ((mp->mem_max+1),sizeof (memory_word));
3913 memset(mp->mem,0,(mp->mem_max+1)*sizeof (memory_word));
3915 @ @<Dealloc variables@>=
3918 @ Users who wish to study the memory requirements of particular applications can
3919 can use optional special features that keep track of current and
3920 maximum memory usage. When code between the delimiters |stat| $\ldots$
3921 |tats| is not ``commented out,'' \MP\ will run a bit slower but it will
3922 report these statistics when |mp_tracing_stats| is positive.
3925 integer var_used; integer dyn_used; /* how much memory is in use */
3927 @ Let's consider the one-word memory region first, since it's the
3928 simplest. The pointer variable |mem_end| holds the highest-numbered location
3929 of |mem| that has ever been used. The free locations of |mem| that
3930 occur between |hi_mem_min| and |mem_end|, inclusive, are of type
3931 |two_halves|, and we write |info(p)| and |link(p)| for the |lh|
3932 and |rh| fields of |mem[p]| when it is of this type. The single-word
3933 free locations form a linked list
3934 $$|avail|,\;\hbox{|link(avail)|},\;\hbox{|link(link(avail))|},\;\ldots$$
3935 terminated by |null|.
3937 @d link(A) mp->mem[(A)].hh.rh /* the |link| field of a memory word */
3938 @d info(A) mp->mem[(A)].hh.lh /* the |info| field of a memory word */
3941 pointer avail; /* head of the list of available one-word nodes */
3942 pointer mem_end; /* the last one-word node used in |mem| */
3944 @ If one-word memory is exhausted, it might mean that the user has forgotten
3945 a token like `\&{enddef}' or `\&{endfor}'. We will define some procedures
3946 later that try to help pinpoint the trouble.
3949 @<Declare the procedure called |show_token_list|@>;
3950 @<Declare the procedure called |runaway|@>
3952 @ The function |get_avail| returns a pointer to a new one-word node whose
3953 |link| field is null. However, \MP\ will halt if there is no more room left.
3957 pointer mp_get_avail (MP mp) { /* single-word node allocation */
3958 pointer p; /* the new node being got */
3959 p=mp->avail; /* get top location in the |avail| stack */
3961 mp->avail=link(mp->avail); /* and pop it off */
3962 } else if ( mp->mem_end<mp->mem_max ) { /* or go into virgin territory */
3963 incr(mp->mem_end); p=mp->mem_end;
3965 decr(mp->hi_mem_min); p=mp->hi_mem_min;
3966 if ( mp->hi_mem_min<=mp->lo_mem_max ) {
3967 mp_runaway(mp); /* if memory is exhausted, display possible runaway text */
3968 mp_overflow(mp, "main memory size",mp->mem_max);
3969 /* quit; all one-word nodes are busy */
3970 @:MetaPost capacity exceeded main memory size}{\quad main memory size@>
3973 link(p)=null; /* provide an oft-desired initialization of the new node */
3974 incr(mp->dyn_used);/* maintain statistics */
3978 @ Conversely, a one-word node is recycled by calling |free_avail|.
3980 @d free_avail(A) /* single-word node liberation */
3981 { link((A))=mp->avail; mp->avail=(A); decr(mp->dyn_used); }
3983 @ There's also a |fast_get_avail| routine, which saves the procedure-call
3984 overhead at the expense of extra programming. This macro is used in
3985 the places that would otherwise account for the most calls of |get_avail|.
3988 @d fast_get_avail(A) {
3989 (A)=mp->avail; /* avoid |get_avail| if possible, to save time */
3990 if ( (A)==null ) { (A)=mp_get_avail(mp); }
3991 else { mp->avail=link((A)); link((A))=null; incr(mp->dyn_used); }
3994 @ The available-space list that keeps track of the variable-size portion
3995 of |mem| is a nonempty, doubly-linked circular list of empty nodes,
3996 pointed to by the roving pointer |rover|.
3998 Each empty node has size 2 or more; the first word contains the special
3999 value |max_halfword| in its |link| field and the size in its |info| field;
4000 the second word contains the two pointers for double linking.
4002 Each nonempty node also has size 2 or more. Its first word is of type
4003 |two_halves|\kern-1pt, and its |link| field is never equal to |max_halfword|.
4004 Otherwise there is complete flexibility with respect to the contents
4005 of its other fields and its other words.
4007 (We require |mem_max<max_halfword| because terrible things can happen
4008 when |max_halfword| appears in the |link| field of a nonempty node.)
4010 @d empty_flag max_halfword /* the |link| of an empty variable-size node */
4011 @d is_empty(A) (link((A))==empty_flag) /* tests for empty node */
4012 @d node_size info /* the size field in empty variable-size nodes */
4013 @d llink(A) info((A)+1) /* left link in doubly-linked list of empty nodes */
4014 @d rlink(A) link((A)+1) /* right link in doubly-linked list of empty nodes */
4017 pointer rover; /* points to some node in the list of empties */
4019 @ A call to |get_node| with argument |s| returns a pointer to a new node
4020 of size~|s|, which must be 2~or more. The |link| field of the first word
4021 of this new node is set to null. An overflow stop occurs if no suitable
4024 If |get_node| is called with $s=2^{30}$, it simply merges adjacent free
4025 areas and returns the value |max_halfword|.
4027 @<Internal library declarations@>=
4028 pointer mp_get_node (MP mp,integer s) ;
4031 pointer mp_get_node (MP mp,integer s) { /* variable-size node allocation */
4032 pointer p; /* the node currently under inspection */
4033 pointer q; /* the node physically after node |p| */
4034 integer r; /* the newly allocated node, or a candidate for this honor */
4035 integer t,tt; /* temporary registers */
4038 p=mp->rover; /* start at some free node in the ring */
4040 @<Try to allocate within node |p| and its physical successors,
4041 and |goto found| if allocation was possible@>;
4042 if (rlink(p)==null || rlink(p)==p) {
4043 print_err("Free list garbled");
4044 help3("I found an entry in the list of free nodes that links")
4045 ("badly. I will try to ignore the broken link, but something")
4046 ("is seriously amiss. It is wise to warn the maintainers.")
4050 p=rlink(p); /* move to the next node in the ring */
4051 } while (p!=mp->rover); /* repeat until the whole list has been traversed */
4052 if ( s==010000000000 ) {
4053 return max_halfword;
4055 if ( mp->lo_mem_max+2<mp->hi_mem_min ) {
4056 if ( mp->lo_mem_max+2<=max_halfword ) {
4057 @<Grow more variable-size memory and |goto restart|@>;
4060 mp_overflow(mp, "main memory size",mp->mem_max);
4061 /* sorry, nothing satisfactory is left */
4062 @:MetaPost capacity exceeded main memory size}{\quad main memory size@>
4064 link(r)=null; /* this node is now nonempty */
4065 mp->var_used+=s; /* maintain usage statistics */
4069 @ The lower part of |mem| grows by 1000 words at a time, unless
4070 we are very close to going under. When it grows, we simply link
4071 a new node into the available-space list. This method of controlled
4072 growth helps to keep the |mem| usage consecutive when \MP\ is
4073 implemented on ``virtual memory'' systems.
4076 @<Grow more variable-size memory and |goto restart|@>=
4078 if ( mp->hi_mem_min-mp->lo_mem_max>=1998 ) {
4079 t=mp->lo_mem_max+1000;
4081 t=mp->lo_mem_max+1+(mp->hi_mem_min-mp->lo_mem_max) / 2;
4082 /* |lo_mem_max+2<=t<hi_mem_min| */
4084 if ( t>max_halfword ) t=max_halfword;
4085 p=llink(mp->rover); q=mp->lo_mem_max; rlink(p)=q; llink(mp->rover)=q;
4086 rlink(q)=mp->rover; llink(q)=p; link(q)=empty_flag;
4087 node_size(q)=t-mp->lo_mem_max;
4088 mp->lo_mem_max=t; link(mp->lo_mem_max)=null; info(mp->lo_mem_max)=null;
4093 @ @<Try to allocate...@>=
4094 q=p+node_size(p); /* find the physical successor */
4095 while ( is_empty(q) ) { /* merge node |p| with node |q| */
4096 t=rlink(q); tt=llink(q);
4098 if ( q==mp->rover ) mp->rover=t;
4099 llink(t)=tt; rlink(tt)=t;
4104 @<Allocate from the top of node |p| and |goto found|@>;
4107 if ( rlink(p)!=p ) {
4108 @<Allocate entire node |p| and |goto found|@>;
4111 node_size(p)=q-p /* reset the size in case it grew */
4113 @ @<Allocate from the top...@>=
4115 node_size(p)=r-p; /* store the remaining size */
4116 mp->rover=p; /* start searching here next time */
4120 @ Here we delete node |p| from the ring, and let |rover| rove around.
4122 @<Allocate entire...@>=
4124 mp->rover=rlink(p); t=llink(p);
4125 llink(mp->rover)=t; rlink(t)=mp->rover;
4129 @ Conversely, when some variable-size node |p| of size |s| is no longer needed,
4130 the operation |free_node(p,s)| will make its words available, by inserting
4131 |p| as a new empty node just before where |rover| now points.
4133 @<Internal library declarations@>=
4134 void mp_free_node (MP mp, pointer p, halfword s) ;
4137 void mp_free_node (MP mp, pointer p, halfword s) { /* variable-size node
4139 pointer q; /* |llink(rover)| */
4140 node_size(p)=s; link(p)=empty_flag;
4142 q=llink(mp->rover); llink(p)=q; rlink(p)=mp->rover; /* set both links */
4143 llink(mp->rover)=p; rlink(q)=p; /* insert |p| into the ring */
4144 mp->var_used-=s; /* maintain statistics */
4147 @ Just before \.{INIMP} writes out the memory, it sorts the doubly linked
4148 available space list. The list is probably very short at such times, so a
4149 simple insertion sort is used. The smallest available location will be
4150 pointed to by |rover|, the next-smallest by |rlink(rover)|, etc.
4153 void mp_sort_avail (MP mp) { /* sorts the available variable-size nodes
4155 pointer p,q,r; /* indices into |mem| */
4156 pointer old_rover; /* initial |rover| setting */
4157 p=mp_get_node(mp, 010000000000); /* merge adjacent free areas */
4158 p=rlink(mp->rover); rlink(mp->rover)=max_halfword; old_rover=mp->rover;
4159 while ( p!=old_rover ) {
4160 @<Sort |p| into the list starting at |rover|
4161 and advance |p| to |rlink(p)|@>;
4164 while ( rlink(p)!=max_halfword ) {
4165 llink(rlink(p))=p; p=rlink(p);
4167 rlink(p)=mp->rover; llink(mp->rover)=p;
4170 @ The following |while| loop is guaranteed to
4171 terminate, since the list that starts at
4172 |rover| ends with |max_halfword| during the sorting procedure.
4175 if ( p<mp->rover ) {
4176 q=p; p=rlink(q); rlink(q)=mp->rover; mp->rover=q;
4179 while ( rlink(q)<p ) q=rlink(q);
4180 r=rlink(p); rlink(p)=rlink(q); rlink(q)=p; p=r;
4183 @* \[11] Memory layout.
4184 Some areas of |mem| are dedicated to fixed usage, since static allocation is
4185 more efficient than dynamic allocation when we can get away with it. For
4186 example, locations |0| to |1| are always used to store a
4187 two-word dummy token whose second word is zero.
4188 The following macro definitions accomplish the static allocation by giving
4189 symbolic names to the fixed positions. Static variable-size nodes appear
4190 in locations |0| through |lo_mem_stat_max|, and static single-word nodes
4191 appear in locations |hi_mem_stat_min| through |mem_top|, inclusive.
4193 @d null_dash (2) /* the first two words are reserved for a null value */
4194 @d dep_head (null_dash+3) /* we will define |dash_node_size=3| */
4195 @d zero_val (dep_head+2) /* two words for a permanently zero value */
4196 @d temp_val (zero_val+2) /* two words for a temporary value node */
4197 @d end_attr temp_val /* we use |end_attr+2| only */
4198 @d inf_val (end_attr+2) /* and |inf_val+1| only */
4199 @d test_pen (inf_val+2)
4200 /* nine words for a pen used when testing the turning number */
4201 @d bad_vardef (test_pen+9) /* two words for \&{vardef} error recovery */
4202 @d lo_mem_stat_max (bad_vardef+1) /* largest statically
4203 allocated word in the variable-size |mem| */
4205 @d sentinel mp->mem_top /* end of sorted lists */
4206 @d temp_head (mp->mem_top-1) /* head of a temporary list of some kind */
4207 @d hold_head (mp->mem_top-2) /* head of a temporary list of another kind */
4208 @d spec_head (mp->mem_top-3) /* head of a list of unprocessed \&{special} items */
4209 @d hi_mem_stat_min (mp->mem_top-3) /* smallest statically allocated word in
4210 the one-word |mem| */
4212 @ The following code gets the dynamic part of |mem| off to a good start,
4213 when \MP\ is initializing itself the slow way.
4215 @<Initialize table entries (done by \.{INIMP} only)@>=
4216 @^data structure assumptions@>
4217 mp->rover=lo_mem_stat_max+1; /* initialize the dynamic memory */
4218 link(mp->rover)=empty_flag;
4219 node_size(mp->rover)=1000; /* which is a 1000-word available node */
4220 llink(mp->rover)=mp->rover; rlink(mp->rover)=mp->rover;
4221 mp->lo_mem_max=mp->rover+1000;
4222 link(mp->lo_mem_max)=null; info(mp->lo_mem_max)=null;
4223 for (k=hi_mem_stat_min;k<=(int)mp->mem_top;k++) {
4224 mp->mem[k]=mp->mem[mp->lo_mem_max]; /* clear list heads */
4226 mp->avail=null; mp->mem_end=mp->mem_top;
4227 mp->hi_mem_min=hi_mem_stat_min; /* initialize the one-word memory */
4228 mp->var_used=lo_mem_stat_max+1;
4229 mp->dyn_used=mp->mem_top+1-(hi_mem_stat_min); /* initialize statistics */
4230 @<Initialize a pen at |test_pen| so that it fits in nine words@>;
4232 @ The procedure |flush_list(p)| frees an entire linked list of one-word
4233 nodes that starts at a given position, until coming to |sentinel| or a
4234 pointer that is not in the one-word region. Another procedure,
4235 |flush_node_list|, frees an entire linked list of one-word and two-word
4236 nodes, until coming to a |null| pointer.
4240 void mp_flush_list (MP mp,pointer p) { /* makes list of single-word nodes available */
4241 pointer q,r; /* list traversers */
4242 if ( p>=mp->hi_mem_min ) if ( p!=sentinel ) {
4247 if ( r<mp->hi_mem_min ) break;
4248 } while (r!=sentinel);
4249 /* now |q| is the last node on the list */
4250 link(q)=mp->avail; mp->avail=p;
4254 void mp_flush_node_list (MP mp,pointer p) {
4255 pointer q; /* the node being recycled */
4258 if ( q<mp->hi_mem_min )
4259 mp_free_node(mp, q,2);
4265 @ If \MP\ is extended improperly, the |mem| array might get screwed up.
4266 For example, some pointers might be wrong, or some ``dead'' nodes might not
4267 have been freed when the last reference to them disappeared. Procedures
4268 |check_mem| and |search_mem| are available to help diagnose such
4269 problems. These procedures make use of two arrays called |free| and
4270 |was_free| that are present only if \MP's debugging routines have
4271 been included. (You may want to decrease the size of |mem| while you
4275 Because |boolean|s are typedef-d as ints, it is better to use
4276 unsigned chars here.
4279 unsigned char *free; /* free cells */
4280 unsigned char *was_free; /* previously free cells */
4281 pointer was_mem_end; pointer was_lo_max; pointer was_hi_min;
4282 /* previous |mem_end|, |lo_mem_max|,and |hi_mem_min| */
4283 boolean panicking; /* do we want to check memory constantly? */
4285 @ @<Allocate or initialize ...@>=
4286 mp->free = xmalloc ((mp->mem_max+1),sizeof (unsigned char));
4287 mp->was_free = xmalloc ((mp->mem_max+1), sizeof (unsigned char));
4289 @ @<Dealloc variables@>=
4291 xfree(mp->was_free);
4293 @ @<Allocate or ...@>=
4294 mp->was_mem_end=0; /* indicate that everything was previously free */
4295 mp->was_lo_max=0; mp->was_hi_min=mp->mem_max;
4296 mp->panicking=false;
4298 @ @<Declare |mp_reallocate| functions@>=
4299 void mp_reallocate_memory(MP mp, int l) ;
4302 void mp_reallocate_memory(MP mp, int l) {
4303 XREALLOC(mp->free, l, unsigned char);
4304 XREALLOC(mp->was_free, l, unsigned char);
4306 int newarea = l-mp->mem_max;
4307 XREALLOC(mp->mem, l, memory_word);
4308 memset (mp->mem+(mp->mem_max+1),0,sizeof(memory_word)*(newarea));
4310 XREALLOC(mp->mem, l, memory_word);
4311 memset(mp->mem,0,sizeof(memory_word)*(l+1));
4314 if (mp->ini_version)
4320 @ Procedure |check_mem| makes sure that the available space lists of
4321 |mem| are well formed, and it optionally prints out all locations
4322 that are reserved now but were free the last time this procedure was called.
4325 void mp_check_mem (MP mp,boolean print_locs ) {
4326 pointer p,q,r; /* current locations of interest in |mem| */
4327 boolean clobbered; /* is something amiss? */
4328 for (p=0;p<=mp->lo_mem_max;p++) {
4329 mp->free[p]=false; /* you can probably do this faster */
4331 for (p=mp->hi_mem_min;p<= mp->mem_end;p++) {
4332 mp->free[p]=false; /* ditto */
4334 @<Check single-word |avail| list@>;
4335 @<Check variable-size |avail| list@>;
4336 @<Check flags of unavailable nodes@>;
4337 @<Check the list of linear dependencies@>;
4339 @<Print newly busy locations@>;
4341 memcpy(mp->was_free,mp->free, sizeof(char)*(mp->mem_end+1));
4342 mp->was_mem_end=mp->mem_end;
4343 mp->was_lo_max=mp->lo_mem_max;
4344 mp->was_hi_min=mp->hi_mem_min;
4347 @ @<Check single-word...@>=
4348 p=mp->avail; q=null; clobbered=false;
4350 if ( (p>mp->mem_end)||(p<mp->hi_mem_min) ) clobbered=true;
4351 else if ( mp->free[p] ) clobbered=true;
4353 mp_print_nl(mp, "AVAIL list clobbered at ");
4354 @.AVAIL list clobbered...@>
4355 mp_print_int(mp, q); break;
4357 mp->free[p]=true; q=p; p=link(q);
4360 @ @<Check variable-size...@>=
4361 p=mp->rover; q=null; clobbered=false;
4363 if ( (p>=mp->lo_mem_max)||(p<0) ) clobbered=true;
4364 else if ( (rlink(p)>=mp->lo_mem_max)||(rlink(p)<0) ) clobbered=true;
4365 else if ( !(is_empty(p))||(node_size(p)<2)||
4366 (p+node_size(p)>mp->lo_mem_max)|| (llink(rlink(p))!=p) ) clobbered=true;
4368 mp_print_nl(mp, "Double-AVAIL list clobbered at ");
4369 @.Double-AVAIL list clobbered...@>
4370 mp_print_int(mp, q); break;
4372 for (q=p;q<=p+node_size(p)-1;q++) { /* mark all locations free */
4373 if ( mp->free[q] ) {
4374 mp_print_nl(mp, "Doubly free location at ");
4375 @.Doubly free location...@>
4376 mp_print_int(mp, q); break;
4381 } while (p!=mp->rover)
4384 @ @<Check flags...@>=
4386 while ( p<=mp->lo_mem_max ) { /* node |p| should not be empty */
4387 if ( is_empty(p) ) {
4388 mp_print_nl(mp, "Bad flag at "); mp_print_int(mp, p);
4391 while ( (p<=mp->lo_mem_max) && ! mp->free[p] ) incr(p);
4392 while ( (p<=mp->lo_mem_max) && mp->free[p] ) incr(p);
4395 @ @<Print newly busy...@>=
4397 @<Do intialization required before printing new busy locations@>;
4398 mp_print_nl(mp, "New busy locs:");
4400 for (p=0;p<= mp->lo_mem_max;p++ ) {
4401 if ( ! mp->free[p] && ((p>mp->was_lo_max) || mp->was_free[p]) ) {
4402 @<Indicate that |p| is a new busy location@>;
4405 for (p=mp->hi_mem_min;p<=mp->mem_end;p++ ) {
4406 if ( ! mp->free[p] &&
4407 ((p<mp->was_hi_min) || (p>mp->was_mem_end) || mp->was_free[p]) ) {
4408 @<Indicate that |p| is a new busy location@>;
4411 @<Finish printing new busy locations@>;
4414 @ There might be many new busy locations so we are careful to print contiguous
4415 blocks compactly. During this operation |q| is the last new busy location and
4416 |r| is the start of the block containing |q|.
4418 @<Indicate that |p| is a new busy location@>=
4422 mp_print(mp, ".."); mp_print_int(mp, q);
4424 mp_print_char(mp, ' '); mp_print_int(mp, p);
4430 @ @<Do intialization required before printing new busy locations@>=
4431 q=mp->mem_max; r=mp->mem_max
4433 @ @<Finish printing new busy locations@>=
4435 mp_print(mp, ".."); mp_print_int(mp, q);
4438 @ The |search_mem| procedure attempts to answer the question ``Who points
4439 to node~|p|?'' In doing so, it fetches |link| and |info| fields of |mem|
4440 that might not be of type |two_halves|. Strictly speaking, this is
4441 undefined, and it can lead to ``false drops'' (words that seem to
4442 point to |p| purely by coincidence). But for debugging purposes, we want
4443 to rule out the places that do {\sl not\/} point to |p|, so a few false
4444 drops are tolerable.
4447 void mp_search_mem (MP mp, pointer p) { /* look for pointers to |p| */
4448 integer q; /* current position being searched */
4449 for (q=0;q<=mp->lo_mem_max;q++) {
4451 mp_print_nl(mp, "LINK("); mp_print_int(mp, q); mp_print_char(mp, ')');
4454 mp_print_nl(mp, "INFO("); mp_print_int(mp, q); mp_print_char(mp, ')');
4457 for (q=mp->hi_mem_min;q<=mp->mem_end;q++) {
4459 mp_print_nl(mp, "LINK("); mp_print_int(mp, q); mp_print_char(mp, ')');
4462 mp_print_nl(mp, "INFO("); mp_print_int(mp, q); mp_print_char(mp, ')');
4465 @<Search |eqtb| for equivalents equal to |p|@>;
4468 @* \[12] The command codes.
4469 Before we can go much further, we need to define symbolic names for the internal
4470 code numbers that represent the various commands obeyed by \MP. These codes
4471 are somewhat arbitrary, but not completely so. For example,
4472 some codes have been made adjacent so that |case| statements in the
4473 program need not consider cases that are widely spaced, or so that |case|
4474 statements can be replaced by |if| statements. A command can begin an
4475 expression if and only if its code lies between |min_primary_command| and
4476 |max_primary_command|, inclusive. The first token of a statement that doesn't
4477 begin with an expression has a command code between |min_command| and
4478 |max_statement_command|, inclusive. Anything less than |min_command| is
4479 eliminated during macro expansions, and anything no more than |max_pre_command|
4480 is eliminated when expanding \TeX\ material. Ranges such as
4481 |min_secondary_command..max_secondary_command| are used when parsing
4482 expressions, but the relative ordering within such a range is generally not
4485 The ordering of the highest-numbered commands
4486 (|comma<semicolon<end_group<stop|) is crucial for the parsing and
4487 error-recovery methods of this program as is the ordering |if_test<fi_or_else|
4488 for the smallest two commands. The ordering is also important in the ranges
4489 |numeric_token..plus_or_minus| and |left_brace..ampersand|.
4491 At any rate, here is the list, for future reference.
4493 @d start_tex 1 /* begin \TeX\ material (\&{btex}, \&{verbatimtex}) */
4494 @d etex_marker 2 /* end \TeX\ material (\&{etex}) */
4495 @d mpx_break 3 /* stop reading an \.{MPX} file (\&{mpxbreak}) */
4496 @d max_pre_command mpx_break
4497 @d if_test 4 /* conditional text (\&{if}) */
4498 @d fi_or_else 5 /* delimiters for conditionals (\&{elseif}, \&{else}, \&{fi} */
4499 @d input 6 /* input a source file (\&{input}, \&{endinput}) */
4500 @d iteration 7 /* iterate (\&{for}, \&{forsuffixes}, \&{forever}, \&{endfor}) */
4501 @d repeat_loop 8 /* special command substituted for \&{endfor} */
4502 @d exit_test 9 /* premature exit from a loop (\&{exitif}) */
4503 @d relax 10 /* do nothing (\.{\char`\\}) */
4504 @d scan_tokens 11 /* put a string into the input buffer */
4505 @d expand_after 12 /* look ahead one token */
4506 @d defined_macro 13 /* a macro defined by the user */
4507 @d min_command (defined_macro+1)
4508 @d save_command 14 /* save a list of tokens (\&{save}) */
4509 @d interim_command 15 /* save an internal quantity (\&{interim}) */
4510 @d let_command 16 /* redefine a symbolic token (\&{let}) */
4511 @d new_internal 17 /* define a new internal quantity (\&{newinternal}) */
4512 @d macro_def 18 /* define a macro (\&{def}, \&{vardef}, etc.) */
4513 @d ship_out_command 19 /* output a character (\&{shipout}) */
4514 @d add_to_command 20 /* add to edges (\&{addto}) */
4515 @d bounds_command 21 /* add bounding path to edges (\&{setbounds}, \&{clip}) */
4516 @d tfm_command 22 /* command for font metric info (\&{ligtable}, etc.) */
4517 @d protection_command 23 /* set protection flag (\&{outer}, \&{inner}) */
4518 @d show_command 24 /* diagnostic output (\&{show}, \&{showvariable}, etc.) */
4519 @d mode_command 25 /* set interaction level (\&{batchmode}, etc.) */
4520 @d mp_random_seed 26 /* initialize random number generator (\&{randomseed}) */
4521 @d message_command 27 /* communicate to user (\&{message}, \&{errmessage}) */
4522 @d every_job_command 28 /* designate a starting token (\&{everyjob}) */
4523 @d delimiters 29 /* define a pair of delimiters (\&{delimiters}) */
4524 @d special_command 30 /* output special info (\&{special})
4525 or font map info (\&{fontmapfile}, \&{fontmapline}) */
4526 @d write_command 31 /* write text to a file (\&{write}) */
4527 @d type_name 32 /* declare a type (\&{numeric}, \&{pair}, etc. */
4528 @d max_statement_command type_name
4529 @d min_primary_command type_name
4530 @d left_delimiter 33 /* the left delimiter of a matching pair */
4531 @d begin_group 34 /* beginning of a group (\&{begingroup}) */
4532 @d nullary 35 /* an operator without arguments (e.g., \&{normaldeviate}) */
4533 @d unary 36 /* an operator with one argument (e.g., \&{sqrt}) */
4534 @d str_op 37 /* convert a suffix to a string (\&{str}) */
4535 @d cycle 38 /* close a cyclic path (\&{cycle}) */
4536 @d primary_binary 39 /* binary operation taking `\&{of}' (e.g., \&{point}) */
4537 @d capsule_token 40 /* a value that has been put into a token list */
4538 @d string_token 41 /* a string constant (e.g., |"hello"|) */
4539 @d internal_quantity 42 /* internal numeric parameter (e.g., \&{pausing}) */
4540 @d min_suffix_token internal_quantity
4541 @d tag_token 43 /* a symbolic token without a primitive meaning */
4542 @d numeric_token 44 /* a numeric constant (e.g., \.{3.14159}) */
4543 @d max_suffix_token numeric_token
4544 @d plus_or_minus 45 /* either `\.+' or `\.-' */
4545 @d max_primary_command plus_or_minus /* should also be |numeric_token+1| */
4546 @d min_tertiary_command plus_or_minus
4547 @d tertiary_secondary_macro 46 /* a macro defined by \&{secondarydef} */
4548 @d tertiary_binary 47 /* an operator at the tertiary level (e.g., `\.{++}') */
4549 @d max_tertiary_command tertiary_binary
4550 @d left_brace 48 /* the operator `\.{\char`\{}' */
4551 @d min_expression_command left_brace
4552 @d path_join 49 /* the operator `\.{..}' */
4553 @d ampersand 50 /* the operator `\.\&' */
4554 @d expression_tertiary_macro 51 /* a macro defined by \&{tertiarydef} */
4555 @d expression_binary 52 /* an operator at the expression level (e.g., `\.<') */
4556 @d equals 53 /* the operator `\.=' */
4557 @d max_expression_command equals
4558 @d and_command 54 /* the operator `\&{and}' */
4559 @d min_secondary_command and_command
4560 @d secondary_primary_macro 55 /* a macro defined by \&{primarydef} */
4561 @d slash 56 /* the operator `\./' */
4562 @d secondary_binary 57 /* an operator at the binary level (e.g., \&{shifted}) */
4563 @d max_secondary_command secondary_binary
4564 @d param_type 58 /* type of parameter (\&{primary}, \&{expr}, \&{suffix}, etc.) */
4565 @d controls 59 /* specify control points explicitly (\&{controls}) */
4566 @d tension 60 /* specify tension between knots (\&{tension}) */
4567 @d at_least 61 /* bounded tension value (\&{atleast}) */
4568 @d curl_command 62 /* specify curl at an end knot (\&{curl}) */
4569 @d macro_special 63 /* special macro operators (\&{quote}, \.{\#\AT!}, etc.) */
4570 @d right_delimiter 64 /* the right delimiter of a matching pair */
4571 @d left_bracket 65 /* the operator `\.[' */
4572 @d right_bracket 66 /* the operator `\.]' */
4573 @d right_brace 67 /* the operator `\.{\char`\}}' */
4574 @d with_option 68 /* option for filling (\&{withpen}, \&{withweight}, etc.) */
4576 /* variant of \&{addto} (\&{contour}, \&{doublepath}, \&{also}) */
4577 @d of_token 70 /* the operator `\&{of}' */
4578 @d to_token 71 /* the operator `\&{to}' */
4579 @d step_token 72 /* the operator `\&{step}' */
4580 @d until_token 73 /* the operator `\&{until}' */
4581 @d within_token 74 /* the operator `\&{within}' */
4582 @d lig_kern_token 75
4583 /* the operators `\&{kern}' and `\.{=:}' and `\.{=:\char'174}, etc. */
4584 @d assignment 76 /* the operator `\.{:=}' */
4585 @d skip_to 77 /* the operation `\&{skipto}' */
4586 @d bchar_label 78 /* the operator `\.{\char'174\char'174:}' */
4587 @d double_colon 79 /* the operator `\.{::}' */
4588 @d colon 80 /* the operator `\.:' */
4590 @d comma 81 /* the operator `\.,', must be |colon+1| */
4591 @d end_of_statement (mp->cur_cmd>comma)
4592 @d semicolon 82 /* the operator `\.;', must be |comma+1| */
4593 @d end_group 83 /* end a group (\&{endgroup}), must be |semicolon+1| */
4594 @d stop 84 /* end a job (\&{end}, \&{dump}), must be |end_group+1| */
4595 @d max_command_code stop
4596 @d outer_tag (max_command_code+1) /* protection code added to command code */
4599 typedef int command_code;
4601 @ Variables and capsules in \MP\ have a variety of ``types,''
4602 distinguished by the code numbers defined here. These numbers are also
4603 not completely arbitrary. Things that get expanded must have types
4604 |>mp_independent|; a type remaining after expansion is numeric if and only if
4605 its code number is at least |numeric_type|; objects containing numeric
4606 parts must have types between |transform_type| and |pair_type|;
4607 all other types must be smaller than |transform_type|; and among the types
4608 that are not unknown or vacuous, the smallest two must be |boolean_type|
4609 and |string_type| in that order.
4611 @d undefined 0 /* no type has been declared */
4612 @d unknown_tag 1 /* this constant is added to certain type codes below */
4613 @d unknown_types mp_unknown_boolean: case mp_unknown_string:
4614 case mp_unknown_pen: case mp_unknown_picture: case mp_unknown_path
4617 enum mp_variable_type {
4618 mp_vacuous=1, /* no expression was present */
4619 mp_boolean_type, /* \&{boolean} with a known value */
4621 mp_string_type, /* \&{string} with a known value */
4623 mp_pen_type, /* \&{pen} with a known value */
4625 mp_path_type, /* \&{path} with a known value */
4627 mp_picture_type, /* \&{picture} with a known value */
4629 mp_transform_type, /* \&{transform} variable or capsule */
4630 mp_color_type, /* \&{color} variable or capsule */
4631 mp_cmykcolor_type, /* \&{cmykcolor} variable or capsule */
4632 mp_pair_type, /* \&{pair} variable or capsule */
4633 mp_numeric_type, /* variable that has been declared \&{numeric} but not used */
4634 mp_known, /* \&{numeric} with a known value */
4635 mp_dependent, /* a linear combination with |fraction| coefficients */
4636 mp_proto_dependent, /* a linear combination with |scaled| coefficients */
4637 mp_independent, /* \&{numeric} with unknown value */
4638 mp_token_list, /* variable name or suffix argument or text argument */
4639 mp_structured, /* variable with subscripts and attributes */
4640 mp_unsuffixed_macro, /* variable defined with \&{vardef} but no \.{\AT!\#} */
4641 mp_suffixed_macro /* variable defined with \&{vardef} and \.{\AT!\#} */
4645 void mp_print_type (MP mp,small_number t) ;
4647 @ @<Basic printing procedures@>=
4648 void mp_print_type (MP mp,small_number t) {
4650 case mp_vacuous:mp_print(mp, "mp_vacuous"); break;
4651 case mp_boolean_type:mp_print(mp, "boolean"); break;
4652 case mp_unknown_boolean:mp_print(mp, "unknown boolean"); break;
4653 case mp_string_type:mp_print(mp, "string"); break;
4654 case mp_unknown_string:mp_print(mp, "unknown string"); break;
4655 case mp_pen_type:mp_print(mp, "pen"); break;
4656 case mp_unknown_pen:mp_print(mp, "unknown pen"); break;
4657 case mp_path_type:mp_print(mp, "path"); break;
4658 case mp_unknown_path:mp_print(mp, "unknown path"); break;
4659 case mp_picture_type:mp_print(mp, "picture"); break;
4660 case mp_unknown_picture:mp_print(mp, "unknown picture"); break;
4661 case mp_transform_type:mp_print(mp, "transform"); break;
4662 case mp_color_type:mp_print(mp, "color"); break;
4663 case mp_cmykcolor_type:mp_print(mp, "cmykcolor"); break;
4664 case mp_pair_type:mp_print(mp, "pair"); break;
4665 case mp_known:mp_print(mp, "known numeric"); break;
4666 case mp_dependent:mp_print(mp, "dependent"); break;
4667 case mp_proto_dependent:mp_print(mp, "proto-dependent"); break;
4668 case mp_numeric_type:mp_print(mp, "numeric"); break;
4669 case mp_independent:mp_print(mp, "independent"); break;
4670 case mp_token_list:mp_print(mp, "token list"); break;
4671 case mp_structured:mp_print(mp, "mp_structured"); break;
4672 case mp_unsuffixed_macro:mp_print(mp, "unsuffixed macro"); break;
4673 case mp_suffixed_macro:mp_print(mp, "suffixed macro"); break;
4674 default: mp_print(mp, "undefined"); break;
4678 @ Values inside \MP\ are stored in two-word nodes that have a |name_type|
4679 as well as a |type|. The possibilities for |name_type| are defined
4680 here; they will be explained in more detail later.
4684 mp_root=0, /* |name_type| at the top level of a variable */
4685 mp_saved_root, /* same, when the variable has been saved */
4686 mp_structured_root, /* |name_type| where a |mp_structured| branch occurs */
4687 mp_subscr, /* |name_type| in a subscript node */
4688 mp_attr, /* |name_type| in an attribute node */
4689 mp_x_part_sector, /* |name_type| in the \&{xpart} of a node */
4690 mp_y_part_sector, /* |name_type| in the \&{ypart} of a node */
4691 mp_xx_part_sector, /* |name_type| in the \&{xxpart} of a node */
4692 mp_xy_part_sector, /* |name_type| in the \&{xypart} of a node */
4693 mp_yx_part_sector, /* |name_type| in the \&{yxpart} of a node */
4694 mp_yy_part_sector, /* |name_type| in the \&{yypart} of a node */
4695 mp_red_part_sector, /* |name_type| in the \&{redpart} of a node */
4696 mp_green_part_sector, /* |name_type| in the \&{greenpart} of a node */
4697 mp_blue_part_sector, /* |name_type| in the \&{bluepart} of a node */
4698 mp_cyan_part_sector, /* |name_type| in the \&{redpart} of a node */
4699 mp_magenta_part_sector, /* |name_type| in the \&{greenpart} of a node */
4700 mp_yellow_part_sector, /* |name_type| in the \&{bluepart} of a node */
4701 mp_black_part_sector, /* |name_type| in the \&{greenpart} of a node */
4702 mp_grey_part_sector, /* |name_type| in the \&{bluepart} of a node */
4703 mp_capsule, /* |name_type| in stashed-away subexpressions */
4704 mp_token /* |name_type| in a numeric token or string token */
4707 @ Primitive operations that produce values have a secondary identification
4708 code in addition to their command code; it's something like genera and species.
4709 For example, `\.*' has the command code |primary_binary|, and its
4710 secondary identification is |times|. The secondary codes start at 30 so that
4711 they don't overlap with the type codes; some type codes (e.g., |mp_string_type|)
4712 are used as operators as well as type identifications. The relative values
4713 are not critical, except for |true_code..false_code|, |or_op..and_op|,
4714 and |filled_op..bounded_op|. The restrictions are that
4715 |and_op-false_code=or_op-true_code|, that the ordering of
4716 |x_part...blue_part| must match that of |x_part_sector..mp_blue_part_sector|,
4717 and the ordering of |filled_op..bounded_op| must match that of the code
4718 values they test for.
4720 @d true_code 30 /* operation code for \.{true} */
4721 @d false_code 31 /* operation code for \.{false} */
4722 @d null_picture_code 32 /* operation code for \.{nullpicture} */
4723 @d null_pen_code 33 /* operation code for \.{nullpen} */
4724 @d job_name_op 34 /* operation code for \.{jobname} */
4725 @d read_string_op 35 /* operation code for \.{readstring} */
4726 @d pen_circle 36 /* operation code for \.{pencircle} */
4727 @d normal_deviate 37 /* operation code for \.{normaldeviate} */
4728 @d read_from_op 38 /* operation code for \.{readfrom} */
4729 @d close_from_op 39 /* operation code for \.{closefrom} */
4730 @d odd_op 40 /* operation code for \.{odd} */
4731 @d known_op 41 /* operation code for \.{known} */
4732 @d unknown_op 42 /* operation code for \.{unknown} */
4733 @d not_op 43 /* operation code for \.{not} */
4734 @d decimal 44 /* operation code for \.{decimal} */
4735 @d reverse 45 /* operation code for \.{reverse} */
4736 @d make_path_op 46 /* operation code for \.{makepath} */
4737 @d make_pen_op 47 /* operation code for \.{makepen} */
4738 @d oct_op 48 /* operation code for \.{oct} */
4739 @d hex_op 49 /* operation code for \.{hex} */
4740 @d ASCII_op 50 /* operation code for \.{ASCII} */
4741 @d char_op 51 /* operation code for \.{char} */
4742 @d length_op 52 /* operation code for \.{length} */
4743 @d turning_op 53 /* operation code for \.{turningnumber} */
4744 @d color_model_part 54 /* operation code for \.{colormodel} */
4745 @d x_part 55 /* operation code for \.{xpart} */
4746 @d y_part 56 /* operation code for \.{ypart} */
4747 @d xx_part 57 /* operation code for \.{xxpart} */
4748 @d xy_part 58 /* operation code for \.{xypart} */
4749 @d yx_part 59 /* operation code for \.{yxpart} */
4750 @d yy_part 60 /* operation code for \.{yypart} */
4751 @d red_part 61 /* operation code for \.{redpart} */
4752 @d green_part 62 /* operation code for \.{greenpart} */
4753 @d blue_part 63 /* operation code for \.{bluepart} */
4754 @d cyan_part 64 /* operation code for \.{cyanpart} */
4755 @d magenta_part 65 /* operation code for \.{magentapart} */
4756 @d yellow_part 66 /* operation code for \.{yellowpart} */
4757 @d black_part 67 /* operation code for \.{blackpart} */
4758 @d grey_part 68 /* operation code for \.{greypart} */
4759 @d font_part 69 /* operation code for \.{fontpart} */
4760 @d text_part 70 /* operation code for \.{textpart} */
4761 @d path_part 71 /* operation code for \.{pathpart} */
4762 @d pen_part 72 /* operation code for \.{penpart} */
4763 @d dash_part 73 /* operation code for \.{dashpart} */
4764 @d sqrt_op 74 /* operation code for \.{sqrt} */
4765 @d m_exp_op 75 /* operation code for \.{mexp} */
4766 @d m_log_op 76 /* operation code for \.{mlog} */
4767 @d sin_d_op 77 /* operation code for \.{sind} */
4768 @d cos_d_op 78 /* operation code for \.{cosd} */
4769 @d floor_op 79 /* operation code for \.{floor} */
4770 @d uniform_deviate 80 /* operation code for \.{uniformdeviate} */
4771 @d char_exists_op 81 /* operation code for \.{charexists} */
4772 @d font_size 82 /* operation code for \.{fontsize} */
4773 @d ll_corner_op 83 /* operation code for \.{llcorner} */
4774 @d lr_corner_op 84 /* operation code for \.{lrcorner} */
4775 @d ul_corner_op 85 /* operation code for \.{ulcorner} */
4776 @d ur_corner_op 86 /* operation code for \.{urcorner} */
4777 @d arc_length 87 /* operation code for \.{arclength} */
4778 @d angle_op 88 /* operation code for \.{angle} */
4779 @d cycle_op 89 /* operation code for \.{cycle} */
4780 @d filled_op 90 /* operation code for \.{filled} */
4781 @d stroked_op 91 /* operation code for \.{stroked} */
4782 @d textual_op 92 /* operation code for \.{textual} */
4783 @d clipped_op 93 /* operation code for \.{clipped} */
4784 @d bounded_op 94 /* operation code for \.{bounded} */
4785 @d plus 95 /* operation code for \.+ */
4786 @d minus 96 /* operation code for \.- */
4787 @d times 97 /* operation code for \.* */
4788 @d over 98 /* operation code for \./ */
4789 @d pythag_add 99 /* operation code for \.{++} */
4790 @d pythag_sub 100 /* operation code for \.{+-+} */
4791 @d or_op 101 /* operation code for \.{or} */
4792 @d and_op 102 /* operation code for \.{and} */
4793 @d less_than 103 /* operation code for \.< */
4794 @d less_or_equal 104 /* operation code for \.{<=} */
4795 @d greater_than 105 /* operation code for \.> */
4796 @d greater_or_equal 106 /* operation code for \.{>=} */
4797 @d equal_to 107 /* operation code for \.= */
4798 @d unequal_to 108 /* operation code for \.{<>} */
4799 @d concatenate 109 /* operation code for \.\& */
4800 @d rotated_by 110 /* operation code for \.{rotated} */
4801 @d slanted_by 111 /* operation code for \.{slanted} */
4802 @d scaled_by 112 /* operation code for \.{scaled} */
4803 @d shifted_by 113 /* operation code for \.{shifted} */
4804 @d transformed_by 114 /* operation code for \.{transformed} */
4805 @d x_scaled 115 /* operation code for \.{xscaled} */
4806 @d y_scaled 116 /* operation code for \.{yscaled} */
4807 @d z_scaled 117 /* operation code for \.{zscaled} */
4808 @d in_font 118 /* operation code for \.{infont} */
4809 @d intersect 119 /* operation code for \.{intersectiontimes} */
4810 @d double_dot 120 /* operation code for improper \.{..} */
4811 @d substring_of 121 /* operation code for \.{substring} */
4812 @d min_of substring_of
4813 @d subpath_of 122 /* operation code for \.{subpath} */
4814 @d direction_time_of 123 /* operation code for \.{directiontime} */
4815 @d point_of 124 /* operation code for \.{point} */
4816 @d precontrol_of 125 /* operation code for \.{precontrol} */
4817 @d postcontrol_of 126 /* operation code for \.{postcontrol} */
4818 @d pen_offset_of 127 /* operation code for \.{penoffset} */
4819 @d arc_time_of 128 /* operation code for \.{arctime} */
4820 @d mp_version 129 /* operation code for \.{mpversion} */
4821 @d envelope_of 130 /* operation code for \.{envelope} */
4823 @c void mp_print_op (MP mp,quarterword c) {
4824 if (c<=mp_numeric_type ) {
4825 mp_print_type(mp, c);
4828 case true_code:mp_print(mp, "true"); break;
4829 case false_code:mp_print(mp, "false"); break;
4830 case null_picture_code:mp_print(mp, "nullpicture"); break;
4831 case null_pen_code:mp_print(mp, "nullpen"); break;
4832 case job_name_op:mp_print(mp, "jobname"); break;
4833 case read_string_op:mp_print(mp, "readstring"); break;
4834 case pen_circle:mp_print(mp, "pencircle"); break;
4835 case normal_deviate:mp_print(mp, "normaldeviate"); break;
4836 case read_from_op:mp_print(mp, "readfrom"); break;
4837 case close_from_op:mp_print(mp, "closefrom"); break;
4838 case odd_op:mp_print(mp, "odd"); break;
4839 case known_op:mp_print(mp, "known"); break;
4840 case unknown_op:mp_print(mp, "unknown"); break;
4841 case not_op:mp_print(mp, "not"); break;
4842 case decimal:mp_print(mp, "decimal"); break;
4843 case reverse:mp_print(mp, "reverse"); break;
4844 case make_path_op:mp_print(mp, "makepath"); break;
4845 case make_pen_op:mp_print(mp, "makepen"); break;
4846 case oct_op:mp_print(mp, "oct"); break;
4847 case hex_op:mp_print(mp, "hex"); break;
4848 case ASCII_op:mp_print(mp, "ASCII"); break;
4849 case char_op:mp_print(mp, "char"); break;
4850 case length_op:mp_print(mp, "length"); break;
4851 case turning_op:mp_print(mp, "turningnumber"); break;
4852 case x_part:mp_print(mp, "xpart"); break;
4853 case y_part:mp_print(mp, "ypart"); break;
4854 case xx_part:mp_print(mp, "xxpart"); break;
4855 case xy_part:mp_print(mp, "xypart"); break;
4856 case yx_part:mp_print(mp, "yxpart"); break;
4857 case yy_part:mp_print(mp, "yypart"); break;
4858 case red_part:mp_print(mp, "redpart"); break;
4859 case green_part:mp_print(mp, "greenpart"); break;
4860 case blue_part:mp_print(mp, "bluepart"); break;
4861 case cyan_part:mp_print(mp, "cyanpart"); break;
4862 case magenta_part:mp_print(mp, "magentapart"); break;
4863 case yellow_part:mp_print(mp, "yellowpart"); break;
4864 case black_part:mp_print(mp, "blackpart"); break;
4865 case grey_part:mp_print(mp, "greypart"); break;
4866 case color_model_part:mp_print(mp, "colormodel"); break;
4867 case font_part:mp_print(mp, "fontpart"); break;
4868 case text_part:mp_print(mp, "textpart"); break;
4869 case path_part:mp_print(mp, "pathpart"); break;
4870 case pen_part:mp_print(mp, "penpart"); break;
4871 case dash_part:mp_print(mp, "dashpart"); break;
4872 case sqrt_op:mp_print(mp, "sqrt"); break;
4873 case m_exp_op:mp_print(mp, "mexp"); break;
4874 case m_log_op:mp_print(mp, "mlog"); break;
4875 case sin_d_op:mp_print(mp, "sind"); break;
4876 case cos_d_op:mp_print(mp, "cosd"); break;
4877 case floor_op:mp_print(mp, "floor"); break;
4878 case uniform_deviate:mp_print(mp, "uniformdeviate"); break;
4879 case char_exists_op:mp_print(mp, "charexists"); break;
4880 case font_size:mp_print(mp, "fontsize"); break;
4881 case ll_corner_op:mp_print(mp, "llcorner"); break;
4882 case lr_corner_op:mp_print(mp, "lrcorner"); break;
4883 case ul_corner_op:mp_print(mp, "ulcorner"); break;
4884 case ur_corner_op:mp_print(mp, "urcorner"); break;
4885 case arc_length:mp_print(mp, "arclength"); break;
4886 case angle_op:mp_print(mp, "angle"); break;
4887 case cycle_op:mp_print(mp, "cycle"); break;
4888 case filled_op:mp_print(mp, "filled"); break;
4889 case stroked_op:mp_print(mp, "stroked"); break;
4890 case textual_op:mp_print(mp, "textual"); break;
4891 case clipped_op:mp_print(mp, "clipped"); break;
4892 case bounded_op:mp_print(mp, "bounded"); break;
4893 case plus:mp_print_char(mp, '+'); break;
4894 case minus:mp_print_char(mp, '-'); break;
4895 case times:mp_print_char(mp, '*'); break;
4896 case over:mp_print_char(mp, '/'); break;
4897 case pythag_add:mp_print(mp, "++"); break;
4898 case pythag_sub:mp_print(mp, "+-+"); break;
4899 case or_op:mp_print(mp, "or"); break;
4900 case and_op:mp_print(mp, "and"); break;
4901 case less_than:mp_print_char(mp, '<'); break;
4902 case less_or_equal:mp_print(mp, "<="); break;
4903 case greater_than:mp_print_char(mp, '>'); break;
4904 case greater_or_equal:mp_print(mp, ">="); break;
4905 case equal_to:mp_print_char(mp, '='); break;
4906 case unequal_to:mp_print(mp, "<>"); break;
4907 case concatenate:mp_print(mp, "&"); break;
4908 case rotated_by:mp_print(mp, "rotated"); break;
4909 case slanted_by:mp_print(mp, "slanted"); break;
4910 case scaled_by:mp_print(mp, "scaled"); break;
4911 case shifted_by:mp_print(mp, "shifted"); break;
4912 case transformed_by:mp_print(mp, "transformed"); break;
4913 case x_scaled:mp_print(mp, "xscaled"); break;
4914 case y_scaled:mp_print(mp, "yscaled"); break;
4915 case z_scaled:mp_print(mp, "zscaled"); break;
4916 case in_font:mp_print(mp, "infont"); break;
4917 case intersect:mp_print(mp, "intersectiontimes"); break;
4918 case substring_of:mp_print(mp, "substring"); break;
4919 case subpath_of:mp_print(mp, "subpath"); break;
4920 case direction_time_of:mp_print(mp, "directiontime"); break;
4921 case point_of:mp_print(mp, "point"); break;
4922 case precontrol_of:mp_print(mp, "precontrol"); break;
4923 case postcontrol_of:mp_print(mp, "postcontrol"); break;
4924 case pen_offset_of:mp_print(mp, "penoffset"); break;
4925 case arc_time_of:mp_print(mp, "arctime"); break;
4926 case mp_version:mp_print(mp, "mpversion"); break;
4927 case envelope_of:mp_print(mp, "envelope"); break;
4928 default: mp_print(mp, ".."); break;
4933 @ \MP\ also has a bunch of internal parameters that a user might want to
4934 fuss with. Every such parameter has an identifying code number, defined here.
4937 enum mp_given_internal {
4938 mp_tracing_titles=1, /* show titles online when they appear */
4939 mp_tracing_equations, /* show each variable when it becomes known */
4940 mp_tracing_capsules, /* show capsules too */
4941 mp_tracing_choices, /* show the control points chosen for paths */
4942 mp_tracing_specs, /* show path subdivision prior to filling with polygonal a pen */
4943 mp_tracing_commands, /* show commands and operations before they are performed */
4944 mp_tracing_restores, /* show when a variable or internal is restored */
4945 mp_tracing_macros, /* show macros before they are expanded */
4946 mp_tracing_output, /* show digitized edges as they are output */
4947 mp_tracing_stats, /* show memory usage at end of job */
4948 mp_tracing_lost_chars, /* show characters that aren't \&{infont} */
4949 mp_tracing_online, /* show long diagnostics on terminal and in the log file */
4950 mp_year, /* the current year (e.g., 1984) */
4951 mp_month, /* the current month (e.g, 3 $\equiv$ March) */
4952 mp_day, /* the current day of the month */
4953 mp_time, /* the number of minutes past midnight when this job started */
4954 mp_char_code, /* the number of the next character to be output */
4955 mp_char_ext, /* the extension code of the next character to be output */
4956 mp_char_wd, /* the width of the next character to be output */
4957 mp_char_ht, /* the height of the next character to be output */
4958 mp_char_dp, /* the depth of the next character to be output */
4959 mp_char_ic, /* the italic correction of the next character to be output */
4960 mp_design_size, /* the unit of measure used for |mp_char_wd..mp_char_ic|, in points */
4961 mp_pausing, /* positive to display lines on the terminal before they are read */
4962 mp_showstopping, /* positive to stop after each \&{show} command */
4963 mp_fontmaking, /* positive if font metric output is to be produced */
4964 mp_linejoin, /* as in \ps: 0 for mitered, 1 for round, 2 for beveled */
4965 mp_linecap, /* as in \ps: 0 for butt, 1 for round, 2 for square */
4966 mp_miterlimit, /* controls miter length as in \ps */
4967 mp_warning_check, /* controls error message when variable value is large */
4968 mp_boundary_char, /* the right boundary character for ligatures */
4969 mp_prologues, /* positive to output conforming PostScript using built-in fonts */
4970 mp_true_corners, /* positive to make \&{llcorner} etc. ignore \&{setbounds} */
4971 mp_default_color_model, /* the default color model for unspecified items */
4972 mp_restore_clip_color,
4973 mp_procset, /* wether or not create PostScript command shortcuts */
4974 mp_gtroffmode, /* whether the user specified |-troff| on the command line */
4979 @d max_given_internal mp_gtroffmode
4982 scaled *internal; /* the values of internal quantities */
4983 char **int_name; /* their names */
4984 int int_ptr; /* the maximum internal quantity defined so far */
4985 int max_internal; /* current maximum number of internal quantities */
4987 @ @<Option variables@>=
4990 @ @<Allocate or initialize ...@>=
4991 mp->max_internal=2*max_given_internal;
4992 mp->internal = xmalloc ((mp->max_internal+1), sizeof(scaled));
4993 mp->int_name = xmalloc ((mp->max_internal+1), sizeof(char *));
4994 mp->troff_mode=(opt->troff_mode>0 ? true : false);
4996 @ @<Exported function ...@>=
4997 int mp_troff_mode(MP mp);
5000 int mp_troff_mode(MP mp) { return mp->troff_mode; }
5002 @ @<Set initial ...@>=
5003 for (k=0;k<= mp->max_internal; k++ ) {
5005 mp->int_name[k]=NULL;
5007 mp->int_ptr=max_given_internal;
5009 @ The symbolic names for internal quantities are put into \MP's hash table
5010 by using a routine called |primitive|, which will be defined later. Let us
5011 enter them now, so that we don't have to list all those names again
5014 @<Put each of \MP's primitives into the hash table@>=
5015 mp_primitive(mp, "tracingtitles",internal_quantity,mp_tracing_titles);
5016 @:tracingtitles_}{\&{tracingtitles} primitive@>
5017 mp_primitive(mp, "tracingequations",internal_quantity,mp_tracing_equations);
5018 @:mp_tracing_equations_}{\&{tracingequations} primitive@>
5019 mp_primitive(mp, "tracingcapsules",internal_quantity,mp_tracing_capsules);
5020 @:mp_tracing_capsules_}{\&{tracingcapsules} primitive@>
5021 mp_primitive(mp, "tracingchoices",internal_quantity,mp_tracing_choices);
5022 @:mp_tracing_choices_}{\&{tracingchoices} primitive@>
5023 mp_primitive(mp, "tracingspecs",internal_quantity,mp_tracing_specs);
5024 @:mp_tracing_specs_}{\&{tracingspecs} primitive@>
5025 mp_primitive(mp, "tracingcommands",internal_quantity,mp_tracing_commands);
5026 @:mp_tracing_commands_}{\&{tracingcommands} primitive@>
5027 mp_primitive(mp, "tracingrestores",internal_quantity,mp_tracing_restores);
5028 @:mp_tracing_restores_}{\&{tracingrestores} primitive@>
5029 mp_primitive(mp, "tracingmacros",internal_quantity,mp_tracing_macros);
5030 @:mp_tracing_macros_}{\&{tracingmacros} primitive@>
5031 mp_primitive(mp, "tracingoutput",internal_quantity,mp_tracing_output);
5032 @:mp_tracing_output_}{\&{tracingoutput} primitive@>
5033 mp_primitive(mp, "tracingstats",internal_quantity,mp_tracing_stats);
5034 @:mp_tracing_stats_}{\&{tracingstats} primitive@>
5035 mp_primitive(mp, "tracinglostchars",internal_quantity,mp_tracing_lost_chars);
5036 @:mp_tracing_lost_chars_}{\&{tracinglostchars} primitive@>
5037 mp_primitive(mp, "tracingonline",internal_quantity,mp_tracing_online);
5038 @:mp_tracing_online_}{\&{tracingonline} primitive@>
5039 mp_primitive(mp, "year",internal_quantity,mp_year);
5040 @:mp_year_}{\&{year} primitive@>
5041 mp_primitive(mp, "month",internal_quantity,mp_month);
5042 @:mp_month_}{\&{month} primitive@>
5043 mp_primitive(mp, "day",internal_quantity,mp_day);
5044 @:mp_day_}{\&{day} primitive@>
5045 mp_primitive(mp, "time",internal_quantity,mp_time);
5046 @:time_}{\&{time} primitive@>
5047 mp_primitive(mp, "charcode",internal_quantity,mp_char_code);
5048 @:mp_char_code_}{\&{charcode} primitive@>
5049 mp_primitive(mp, "charext",internal_quantity,mp_char_ext);
5050 @:mp_char_ext_}{\&{charext} primitive@>
5051 mp_primitive(mp, "charwd",internal_quantity,mp_char_wd);
5052 @:mp_char_wd_}{\&{charwd} primitive@>
5053 mp_primitive(mp, "charht",internal_quantity,mp_char_ht);
5054 @:mp_char_ht_}{\&{charht} primitive@>
5055 mp_primitive(mp, "chardp",internal_quantity,mp_char_dp);
5056 @:mp_char_dp_}{\&{chardp} primitive@>
5057 mp_primitive(mp, "charic",internal_quantity,mp_char_ic);
5058 @:mp_char_ic_}{\&{charic} primitive@>
5059 mp_primitive(mp, "designsize",internal_quantity,mp_design_size);
5060 @:mp_design_size_}{\&{designsize} primitive@>
5061 mp_primitive(mp, "pausing",internal_quantity,mp_pausing);
5062 @:mp_pausing_}{\&{pausing} primitive@>
5063 mp_primitive(mp, "showstopping",internal_quantity,mp_showstopping);
5064 @:mp_showstopping_}{\&{showstopping} primitive@>
5065 mp_primitive(mp, "fontmaking",internal_quantity,mp_fontmaking);
5066 @:mp_fontmaking_}{\&{fontmaking} primitive@>
5067 mp_primitive(mp, "linejoin",internal_quantity,mp_linejoin);
5068 @:mp_linejoin_}{\&{linejoin} primitive@>
5069 mp_primitive(mp, "linecap",internal_quantity,mp_linecap);
5070 @:mp_linecap_}{\&{linecap} primitive@>
5071 mp_primitive(mp, "miterlimit",internal_quantity,mp_miterlimit);
5072 @:mp_miterlimit_}{\&{miterlimit} primitive@>
5073 mp_primitive(mp, "warningcheck",internal_quantity,mp_warning_check);
5074 @:mp_warning_check_}{\&{warningcheck} primitive@>
5075 mp_primitive(mp, "boundarychar",internal_quantity,mp_boundary_char);
5076 @:mp_boundary_char_}{\&{boundarychar} primitive@>
5077 mp_primitive(mp, "prologues",internal_quantity,mp_prologues);
5078 @:mp_prologues_}{\&{prologues} primitive@>
5079 mp_primitive(mp, "truecorners",internal_quantity,mp_true_corners);
5080 @:mp_true_corners_}{\&{truecorners} primitive@>
5081 mp_primitive(mp, "mpprocset",internal_quantity,mp_procset);
5082 @:mp_procset_}{\&{mpprocset} primitive@>
5083 mp_primitive(mp, "troffmode",internal_quantity,mp_gtroffmode);
5084 @:troffmode_}{\&{troffmode} primitive@>
5085 mp_primitive(mp, "defaultcolormodel",internal_quantity,mp_default_color_model);
5086 @:mp_default_color_model_}{\&{defaultcolormodel} primitive@>
5087 mp_primitive(mp, "restoreclipcolor",internal_quantity,mp_restore_clip_color);
5088 @:mp_restore_clip_color_}{\&{restoreclipcolor} primitive@>
5090 @ Colors can be specified in four color models. In the special
5091 case of |no_model|, MetaPost does not output any color operator to
5092 the postscript output.
5094 Note: these values are passed directly on to |with_option|. This only
5095 works because the other possible values passed to |with_option| are
5096 8 and 10 respectively (from |with_pen| and |with_picture|).
5098 There is a first state, that is only used for |gs_colormodel|. It flags
5099 the fact that there has not been any kind of color specification by
5100 the user so far in the game.
5103 enum mp_color_model {
5108 mp_uninitialized_model=9,
5112 @ @<Initialize table entries (done by \.{INIMP} only)@>=
5113 mp->internal[mp_default_color_model]=(mp_rgb_model*unity);
5114 mp->internal[mp_restore_clip_color]=unity;
5116 @ Well, we do have to list the names one more time, for use in symbolic
5119 @<Initialize table...@>=
5120 mp->int_name[mp_tracing_titles]=xstrdup("tracingtitles");
5121 mp->int_name[mp_tracing_equations]=xstrdup("tracingequations");
5122 mp->int_name[mp_tracing_capsules]=xstrdup("tracingcapsules");
5123 mp->int_name[mp_tracing_choices]=xstrdup("tracingchoices");
5124 mp->int_name[mp_tracing_specs]=xstrdup("tracingspecs");
5125 mp->int_name[mp_tracing_commands]=xstrdup("tracingcommands");
5126 mp->int_name[mp_tracing_restores]=xstrdup("tracingrestores");
5127 mp->int_name[mp_tracing_macros]=xstrdup("tracingmacros");
5128 mp->int_name[mp_tracing_output]=xstrdup("tracingoutput");
5129 mp->int_name[mp_tracing_stats]=xstrdup("tracingstats");
5130 mp->int_name[mp_tracing_lost_chars]=xstrdup("tracinglostchars");
5131 mp->int_name[mp_tracing_online]=xstrdup("tracingonline");
5132 mp->int_name[mp_year]=xstrdup("year");
5133 mp->int_name[mp_month]=xstrdup("month");
5134 mp->int_name[mp_day]=xstrdup("day");
5135 mp->int_name[mp_time]=xstrdup("time");
5136 mp->int_name[mp_char_code]=xstrdup("charcode");
5137 mp->int_name[mp_char_ext]=xstrdup("charext");
5138 mp->int_name[mp_char_wd]=xstrdup("charwd");
5139 mp->int_name[mp_char_ht]=xstrdup("charht");
5140 mp->int_name[mp_char_dp]=xstrdup("chardp");
5141 mp->int_name[mp_char_ic]=xstrdup("charic");
5142 mp->int_name[mp_design_size]=xstrdup("designsize");
5143 mp->int_name[mp_pausing]=xstrdup("pausing");
5144 mp->int_name[mp_showstopping]=xstrdup("showstopping");
5145 mp->int_name[mp_fontmaking]=xstrdup("fontmaking");
5146 mp->int_name[mp_linejoin]=xstrdup("linejoin");
5147 mp->int_name[mp_linecap]=xstrdup("linecap");
5148 mp->int_name[mp_miterlimit]=xstrdup("miterlimit");
5149 mp->int_name[mp_warning_check]=xstrdup("warningcheck");
5150 mp->int_name[mp_boundary_char]=xstrdup("boundarychar");
5151 mp->int_name[mp_prologues]=xstrdup("prologues");
5152 mp->int_name[mp_true_corners]=xstrdup("truecorners");
5153 mp->int_name[mp_default_color_model]=xstrdup("defaultcolormodel");
5154 mp->int_name[mp_procset]=xstrdup("mpprocset");
5155 mp->int_name[mp_gtroffmode]=xstrdup("troffmode");
5156 mp->int_name[mp_restore_clip_color]=xstrdup("restoreclipcolor");
5158 @ The following procedure, which is called just before \MP\ initializes its
5159 input and output, establishes the initial values of the date and time.
5160 @^system dependencies@>
5162 Note that the values are |scaled| integers. Hence \MP\ can no longer
5163 be used after the year 32767.
5166 void mp_fix_date_and_time (MP mp) {
5167 time_t clock = time ((time_t *) 0);
5168 struct tm *tmptr = localtime (&clock);
5169 mp->internal[mp_time]=
5170 (tmptr->tm_hour*60+tmptr->tm_min)*unity; /* minutes since midnight */
5171 mp->internal[mp_day]=(tmptr->tm_mday)*unity; /* fourth day of the month */
5172 mp->internal[mp_month]=(tmptr->tm_mon+1)*unity; /* seventh month of the year */
5173 mp->internal[mp_year]=(tmptr->tm_year+1900)*unity; /* Anno Domini */
5177 void mp_fix_date_and_time (MP mp) ;
5179 @ \MP\ is occasionally supposed to print diagnostic information that
5180 goes only into the transcript file, unless |mp_tracing_online| is positive.
5181 Now that we have defined |mp_tracing_online| we can define
5182 two routines that adjust the destination of print commands:
5185 void mp_begin_diagnostic (MP mp) ;
5186 void mp_end_diagnostic (MP mp,boolean blank_line);
5187 void mp_print_diagnostic (MP mp, char *s, char *t, boolean nuline) ;
5189 @ @<Basic printing...@>=
5190 @<Declare a function called |true_line|@>;
5191 void mp_begin_diagnostic (MP mp) { /* prepare to do some tracing */
5192 mp->old_setting=mp->selector;
5193 if ((mp->internal[mp_tracing_online]<=0)&&(mp->selector==term_and_log)){
5195 if ( mp->history==mp_spotless ) mp->history=mp_warning_issued;
5199 void mp_end_diagnostic (MP mp,boolean blank_line) {
5200 /* restore proper conditions after tracing */
5201 mp_print_nl(mp, "");
5202 if ( blank_line ) mp_print_ln(mp);
5203 mp->selector=mp->old_setting;
5209 unsigned int old_setting;
5211 @ We will occasionally use |begin_diagnostic| in connection with line-number
5212 printing, as follows. (The parameter |s| is typically |"Path"| or
5213 |"Cycle spec"|, etc.)
5215 @<Basic printing...@>=
5216 void mp_print_diagnostic (MP mp, char *s, char *t, boolean nuline) {
5217 mp_begin_diagnostic(mp);
5218 if ( nuline ) mp_print_nl(mp, s); else mp_print(mp, s);
5219 mp_print(mp, " at line ");
5220 mp_print_int(mp, mp_true_line(mp));
5221 mp_print(mp, t); mp_print_char(mp, ':');
5224 @ The 256 |ASCII_code| characters are grouped into classes by means of
5225 the |char_class| table. Individual class numbers have no semantic
5226 or syntactic significance, except in a few instances defined here.
5227 There's also |max_class|, which can be used as a basis for additional
5228 class numbers in nonstandard extensions of \MP.
5230 @d digit_class 0 /* the class number of \.{0123456789} */
5231 @d period_class 1 /* the class number of `\..' */
5232 @d space_class 2 /* the class number of spaces and nonstandard characters */
5233 @d percent_class 3 /* the class number of `\.\%' */
5234 @d string_class 4 /* the class number of `\."' */
5235 @d right_paren_class 8 /* the class number of `\.)' */
5236 @d isolated_classes 5: case 6: case 7: case 8 /* characters that make length-one tokens only */
5237 @d letter_class 9 /* letters and the underline character */
5238 @d left_bracket_class 17 /* `\.[' */
5239 @d right_bracket_class 18 /* `\.]' */
5240 @d invalid_class 20 /* bad character in the input */
5241 @d max_class 20 /* the largest class number */
5244 int char_class[256]; /* the class numbers */
5246 @ If changes are made to accommodate non-ASCII character sets, they should
5247 follow the guidelines in Appendix~C of {\sl The {\logos METAFONT\/}book}.
5248 @:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
5249 @^system dependencies@>
5251 @<Set initial ...@>=
5252 for (k='0';k<='9';k++)
5253 mp->char_class[k]=digit_class;
5254 mp->char_class['.']=period_class;
5255 mp->char_class[' ']=space_class;
5256 mp->char_class['%']=percent_class;
5257 mp->char_class['"']=string_class;
5258 mp->char_class[',']=5;
5259 mp->char_class[';']=6;
5260 mp->char_class['(']=7;
5261 mp->char_class[')']=right_paren_class;
5262 for (k='A';k<= 'Z';k++ )
5263 mp->char_class[k]=letter_class;
5264 for (k='a';k<='z';k++)
5265 mp->char_class[k]=letter_class;
5266 mp->char_class['_']=letter_class;
5267 mp->char_class['<']=10;
5268 mp->char_class['=']=10;
5269 mp->char_class['>']=10;
5270 mp->char_class[':']=10;
5271 mp->char_class['|']=10;
5272 mp->char_class['`']=11;
5273 mp->char_class['\'']=11;
5274 mp->char_class['+']=12;
5275 mp->char_class['-']=12;
5276 mp->char_class['/']=13;
5277 mp->char_class['*']=13;
5278 mp->char_class['\\']=13;
5279 mp->char_class['!']=14;
5280 mp->char_class['?']=14;
5281 mp->char_class['#']=15;
5282 mp->char_class['&']=15;
5283 mp->char_class['@@']=15;
5284 mp->char_class['$']=15;
5285 mp->char_class['^']=16;
5286 mp->char_class['~']=16;
5287 mp->char_class['[']=left_bracket_class;
5288 mp->char_class[']']=right_bracket_class;
5289 mp->char_class['{']=19;
5290 mp->char_class['}']=19;
5292 mp->char_class[k]=invalid_class;
5293 mp->char_class['\t']=space_class;
5294 mp->char_class['\f']=space_class;
5295 for (k=127;k<=255;k++)
5296 mp->char_class[k]=invalid_class;
5298 @* \[13] The hash table.
5299 Symbolic tokens are stored and retrieved by means of a fairly standard hash
5300 table algorithm called the method of ``coalescing lists'' (cf.\ Algorithm 6.4C
5301 in {\sl The Art of Computer Programming\/}). Once a symbolic token enters the
5302 table, it is never removed.
5304 The actual sequence of characters forming a symbolic token is
5305 stored in the |str_pool| array together with all the other strings. An
5306 auxiliary array |hash| consists of items with two halfword fields per
5307 word. The first of these, called |next(p)|, points to the next identifier
5308 belonging to the same coalesced list as the identifier corresponding to~|p|;
5309 and the other, called |text(p)|, points to the |str_start| entry for
5310 |p|'s identifier. If position~|p| of the hash table is empty, we have
5311 |text(p)=0|; if position |p| is either empty or the end of a coalesced
5312 hash list, we have |next(p)=0|.
5314 An auxiliary pointer variable called |hash_used| is maintained in such a
5315 way that all locations |p>=hash_used| are nonempty. The global variable
5316 |st_count| tells how many symbolic tokens have been defined, if statistics
5319 The first 256 locations of |hash| are reserved for symbols of length one.
5321 There's a parallel array called |eqtb| that contains the current equivalent
5322 values of each symbolic token. The entries of this array consist of
5323 two halfwords called |eq_type| (a command code) and |equiv| (a secondary
5324 piece of information that qualifies the |eq_type|).
5326 @d next(A) mp->hash[(A)].lh /* link for coalesced lists */
5327 @d text(A) mp->hash[(A)].rh /* string number for symbolic token name */
5328 @d eq_type(A) mp->eqtb[(A)].lh /* the current ``meaning'' of a symbolic token */
5329 @d equiv(A) mp->eqtb[(A)].rh /* parametric part of a token's meaning */
5330 @d hash_base 257 /* hashing actually starts here */
5331 @d hash_is_full (mp->hash_used==hash_base) /* are all positions occupied? */
5334 pointer hash_used; /* allocation pointer for |hash| */
5335 integer st_count; /* total number of known identifiers */
5337 @ Certain entries in the hash table are ``frozen'' and not redefinable,
5338 since they are used in error recovery.
5340 @d hash_top (hash_base+mp->hash_size) /* the first location of the frozen area */
5341 @d frozen_inaccessible hash_top /* |hash| location to protect the frozen area */
5342 @d frozen_repeat_loop (hash_top+1) /* |hash| location of a loop-repeat token */
5343 @d frozen_right_delimiter (hash_top+2) /* |hash| location of a permanent `\.)' */
5344 @d frozen_left_bracket (hash_top+3) /* |hash| location of a permanent `\.[' */
5345 @d frozen_slash (hash_top+4) /* |hash| location of a permanent `\./' */
5346 @d frozen_colon (hash_top+5) /* |hash| location of a permanent `\.:' */
5347 @d frozen_semicolon (hash_top+6) /* |hash| location of a permanent `\.;' */
5348 @d frozen_end_for (hash_top+7) /* |hash| location of a permanent \&{endfor} */
5349 @d frozen_end_def (hash_top+8) /* |hash| location of a permanent \&{enddef} */
5350 @d frozen_fi (hash_top+9) /* |hash| location of a permanent \&{fi} */
5351 @d frozen_end_group (hash_top+10) /* |hash| location of a permanent `\.{endgroup}' */
5352 @d frozen_etex (hash_top+11) /* |hash| location of a permanent \&{etex} */
5353 @d frozen_mpx_break (hash_top+12) /* |hash| location of a permanent \&{mpxbreak} */
5354 @d frozen_bad_vardef (hash_top+13) /* |hash| location of `\.{a bad variable}' */
5355 @d frozen_undefined (hash_top+14) /* |hash| location that never gets defined */
5356 @d hash_end (hash_top+14) /* the actual size of the |hash| and |eqtb| arrays */
5359 two_halves *hash; /* the hash table */
5360 two_halves *eqtb; /* the equivalents */
5362 @ @<Allocate or initialize ...@>=
5363 mp->hash = xmalloc((hash_end+1),sizeof(two_halves));
5364 mp->eqtb = xmalloc((hash_end+1),sizeof(two_halves));
5366 @ @<Dealloc variables@>=
5371 next(1)=0; text(1)=0; eq_type(1)=tag_token; equiv(1)=null;
5372 for (k=2;k<=hash_end;k++) {
5373 mp->hash[k]=mp->hash[1]; mp->eqtb[k]=mp->eqtb[1];
5376 @ @<Initialize table entries...@>=
5377 mp->hash_used=frozen_inaccessible; /* nothing is used */
5379 text(frozen_bad_vardef)=intern("a bad variable");
5380 text(frozen_etex)=intern("etex");
5381 text(frozen_mpx_break)=intern("mpxbreak");
5382 text(frozen_fi)=intern("fi");
5383 text(frozen_end_group)=intern("endgroup");
5384 text(frozen_end_def)=intern("enddef");
5385 text(frozen_end_for)=intern("endfor");
5386 text(frozen_semicolon)=intern(";");
5387 text(frozen_colon)=intern(":");
5388 text(frozen_slash)=intern("/");
5389 text(frozen_left_bracket)=intern("[");
5390 text(frozen_right_delimiter)=intern(")");
5391 text(frozen_inaccessible)=intern(" INACCESSIBLE");
5392 eq_type(frozen_right_delimiter)=right_delimiter;
5394 @ @<Check the ``constant'' values...@>=
5395 if ( hash_end+mp->max_internal>max_halfword ) mp->bad=17;
5397 @ Here is the subroutine that searches the hash table for an identifier
5398 that matches a given string of length~|l| appearing in |buffer[j..
5399 (j+l-1)]|. If the identifier is not found, it is inserted; hence it
5400 will always be found, and the corresponding hash table address
5404 pointer mp_id_lookup (MP mp,integer j, integer l) { /* search the hash table */
5405 integer h; /* hash code */
5406 pointer p; /* index in |hash| array */
5407 pointer k; /* index in |buffer| array */
5409 @<Treat special case of length 1 and |break|@>;
5411 @<Compute the hash code |h|@>;
5412 p=h+hash_base; /* we start searching here; note that |0<=h<hash_prime| */
5414 if (text(p)>0 && length(text(p))==l && mp_str_eq_buf(mp, text(p),j))
5417 @<Insert a new symbolic token after |p|, then
5418 make |p| point to it and |break|@>;
5425 @ @<Treat special case of length 1...@>=
5426 p=mp->buffer[j]+1; text(p)=p-1; return p;
5429 @ @<Insert a new symbolic...@>=
5434 mp_overflow(mp, "hash size",mp->hash_size);
5435 @:MetaPost capacity exceeded hash size}{\quad hash size@>
5436 decr(mp->hash_used);
5437 } while (text(mp->hash_used)!=0); /* search for an empty location in |hash| */
5438 next(p)=mp->hash_used;
5442 for (k=j;k<=j+l-1;k++) {
5443 append_char(mp->buffer[k]);
5445 text(p)=mp_make_string(mp);
5446 mp->str_ref[text(p)]=max_str_ref;
5452 @ The value of |hash_prime| should be roughly 85\pct! of |hash_size|, and it
5453 should be a prime number. The theory of hashing tells us to expect fewer
5454 than two table probes, on the average, when the search is successful.
5455 [See J.~S. Vitter, {\sl Journal of the ACM\/ \bf30} (1983), 231--258.]
5456 @^Vitter, Jeffrey Scott@>
5458 @<Compute the hash code |h|@>=
5460 for (k=j+1;k<=j+l-1;k++){
5461 h=h+h+mp->buffer[k];
5462 while ( h>=mp->hash_prime ) h=h-mp->hash_prime;
5465 @ @<Search |eqtb| for equivalents equal to |p|@>=
5466 for (q=1;q<=hash_end;q++) {
5467 if ( equiv(q)==p ) {
5468 mp_print_nl(mp, "EQUIV(");
5469 mp_print_int(mp, q);
5470 mp_print_char(mp, ')');
5474 @ We need to put \MP's ``primitive'' symbolic tokens into the hash
5475 table, together with their command code (which will be the |eq_type|)
5476 and an operand (which will be the |equiv|). The |primitive| procedure
5477 does this, in a way that no \MP\ user can. The global value |cur_sym|
5478 contains the new |eqtb| pointer after |primitive| has acted.
5481 void mp_primitive (MP mp, char *ss, halfword c, halfword o) {
5482 pool_pointer k; /* index into |str_pool| */
5483 small_number j; /* index into |buffer| */
5484 small_number l; /* length of the string */
5487 k=mp->str_start[s]; l=str_stop(s)-k;
5488 /* we will move |s| into the (empty) |buffer| */
5489 for (j=0;j<=l-1;j++) {
5490 mp->buffer[j]=mp->str_pool[k+j];
5492 mp->cur_sym=mp_id_lookup(mp, 0,l);
5493 if ( s>=256 ) { /* we don't want to have the string twice */
5494 mp_flush_string(mp, text(mp->cur_sym)); text(mp->cur_sym)=s;
5496 eq_type(mp->cur_sym)=c;
5497 equiv(mp->cur_sym)=o;
5501 @ Many of \MP's primitives need no |equiv|, since they are identifiable
5502 by their |eq_type| alone. These primitives are loaded into the hash table
5505 @<Put each of \MP's primitives into the hash table@>=
5506 mp_primitive(mp, "..",path_join,0);
5507 @:.._}{\.{..} primitive@>
5508 mp_primitive(mp, "[",left_bracket,0); mp->eqtb[frozen_left_bracket]=mp->eqtb[mp->cur_sym];
5509 @:[ }{\.{[} primitive@>
5510 mp_primitive(mp, "]",right_bracket,0);
5511 @:] }{\.{]} primitive@>
5512 mp_primitive(mp, "}",right_brace,0);
5513 @:]]}{\.{\char`\}} primitive@>
5514 mp_primitive(mp, "{",left_brace,0);
5515 @:][}{\.{\char`\{} primitive@>
5516 mp_primitive(mp, ":",colon,0); mp->eqtb[frozen_colon]=mp->eqtb[mp->cur_sym];
5517 @:: }{\.{:} primitive@>
5518 mp_primitive(mp, "::",double_colon,0);
5519 @::: }{\.{::} primitive@>
5520 mp_primitive(mp, "||:",bchar_label,0);
5521 @:::: }{\.{\char'174\char'174:} primitive@>
5522 mp_primitive(mp, ":=",assignment,0);
5523 @::=_}{\.{:=} primitive@>
5524 mp_primitive(mp, ",",comma,0);
5525 @:, }{\., primitive@>
5526 mp_primitive(mp, ";",semicolon,0); mp->eqtb[frozen_semicolon]=mp->eqtb[mp->cur_sym];
5527 @:; }{\.; primitive@>
5528 mp_primitive(mp, "\\",relax,0);
5529 @:]]\\}{\.{\char`\\} primitive@>
5531 mp_primitive(mp, "addto",add_to_command,0);
5532 @:add_to_}{\&{addto} primitive@>
5533 mp_primitive(mp, "atleast",at_least,0);
5534 @:at_least_}{\&{atleast} primitive@>
5535 mp_primitive(mp, "begingroup",begin_group,0); mp->bg_loc=mp->cur_sym;
5536 @:begin_group_}{\&{begingroup} primitive@>
5537 mp_primitive(mp, "controls",controls,0);
5538 @:controls_}{\&{controls} primitive@>
5539 mp_primitive(mp, "curl",curl_command,0);
5540 @:curl_}{\&{curl} primitive@>
5541 mp_primitive(mp, "delimiters",delimiters,0);
5542 @:delimiters_}{\&{delimiters} primitive@>
5543 mp_primitive(mp, "endgroup",end_group,0);
5544 mp->eqtb[frozen_end_group]=mp->eqtb[mp->cur_sym]; mp->eg_loc=mp->cur_sym;
5545 @:endgroup_}{\&{endgroup} primitive@>
5546 mp_primitive(mp, "everyjob",every_job_command,0);
5547 @:every_job_}{\&{everyjob} primitive@>
5548 mp_primitive(mp, "exitif",exit_test,0);
5549 @:exit_if_}{\&{exitif} primitive@>
5550 mp_primitive(mp, "expandafter",expand_after,0);
5551 @:expand_after_}{\&{expandafter} primitive@>
5552 mp_primitive(mp, "interim",interim_command,0);
5553 @:interim_}{\&{interim} primitive@>
5554 mp_primitive(mp, "let",let_command,0);
5555 @:let_}{\&{let} primitive@>
5556 mp_primitive(mp, "newinternal",new_internal,0);
5557 @:new_internal_}{\&{newinternal} primitive@>
5558 mp_primitive(mp, "of",of_token,0);
5559 @:of_}{\&{of} primitive@>
5560 mp_primitive(mp, "randomseed",mp_random_seed,0);
5561 @:mp_random_seed_}{\&{randomseed} primitive@>
5562 mp_primitive(mp, "save",save_command,0);
5563 @:save_}{\&{save} primitive@>
5564 mp_primitive(mp, "scantokens",scan_tokens,0);
5565 @:scan_tokens_}{\&{scantokens} primitive@>
5566 mp_primitive(mp, "shipout",ship_out_command,0);
5567 @:ship_out_}{\&{shipout} primitive@>
5568 mp_primitive(mp, "skipto",skip_to,0);
5569 @:skip_to_}{\&{skipto} primitive@>
5570 mp_primitive(mp, "special",special_command,0);
5571 @:special}{\&{special} primitive@>
5572 mp_primitive(mp, "fontmapfile",special_command,1);
5573 @:fontmapfile}{\&{fontmapfile} primitive@>
5574 mp_primitive(mp, "fontmapline",special_command,2);
5575 @:fontmapline}{\&{fontmapline} primitive@>
5576 mp_primitive(mp, "step",step_token,0);
5577 @:step_}{\&{step} primitive@>
5578 mp_primitive(mp, "str",str_op,0);
5579 @:str_}{\&{str} primitive@>
5580 mp_primitive(mp, "tension",tension,0);
5581 @:tension_}{\&{tension} primitive@>
5582 mp_primitive(mp, "to",to_token,0);
5583 @:to_}{\&{to} primitive@>
5584 mp_primitive(mp, "until",until_token,0);
5585 @:until_}{\&{until} primitive@>
5586 mp_primitive(mp, "within",within_token,0);
5587 @:within_}{\&{within} primitive@>
5588 mp_primitive(mp, "write",write_command,0);
5589 @:write_}{\&{write} primitive@>
5591 @ Each primitive has a corresponding inverse, so that it is possible to
5592 display the cryptic numeric contents of |eqtb| in symbolic form.
5593 Every call of |primitive| in this program is therefore accompanied by some
5594 straightforward code that forms part of the |print_cmd_mod| routine
5597 @<Cases of |print_cmd_mod| for symbolic printing of primitives@>=
5598 case add_to_command:mp_print(mp, "addto"); break;
5599 case assignment:mp_print(mp, ":="); break;
5600 case at_least:mp_print(mp, "atleast"); break;
5601 case bchar_label:mp_print(mp, "||:"); break;
5602 case begin_group:mp_print(mp, "begingroup"); break;
5603 case colon:mp_print(mp, ":"); break;
5604 case comma:mp_print(mp, ","); break;
5605 case controls:mp_print(mp, "controls"); break;
5606 case curl_command:mp_print(mp, "curl"); break;
5607 case delimiters:mp_print(mp, "delimiters"); break;
5608 case double_colon:mp_print(mp, "::"); break;
5609 case end_group:mp_print(mp, "endgroup"); break;
5610 case every_job_command:mp_print(mp, "everyjob"); break;
5611 case exit_test:mp_print(mp, "exitif"); break;
5612 case expand_after:mp_print(mp, "expandafter"); break;
5613 case interim_command:mp_print(mp, "interim"); break;
5614 case left_brace:mp_print(mp, "{"); break;
5615 case left_bracket:mp_print(mp, "["); break;
5616 case let_command:mp_print(mp, "let"); break;
5617 case new_internal:mp_print(mp, "newinternal"); break;
5618 case of_token:mp_print(mp, "of"); break;
5619 case path_join:mp_print(mp, ".."); break;
5620 case mp_random_seed:mp_print(mp, "randomseed"); break;
5621 case relax:mp_print_char(mp, '\\'); break;
5622 case right_brace:mp_print(mp, "}"); break;
5623 case right_bracket:mp_print(mp, "]"); break;
5624 case save_command:mp_print(mp, "save"); break;
5625 case scan_tokens:mp_print(mp, "scantokens"); break;
5626 case semicolon:mp_print(mp, ";"); break;
5627 case ship_out_command:mp_print(mp, "shipout"); break;
5628 case skip_to:mp_print(mp, "skipto"); break;
5629 case special_command: if ( m==2 ) mp_print(mp, "fontmapline"); else
5630 if ( m==1 ) mp_print(mp, "fontmapfile"); else
5631 mp_print(mp, "special"); break;
5632 case step_token:mp_print(mp, "step"); break;
5633 case str_op:mp_print(mp, "str"); break;
5634 case tension:mp_print(mp, "tension"); break;
5635 case to_token:mp_print(mp, "to"); break;
5636 case until_token:mp_print(mp, "until"); break;
5637 case within_token:mp_print(mp, "within"); break;
5638 case write_command:mp_print(mp, "write"); break;
5640 @ We will deal with the other primitives later, at some point in the program
5641 where their |eq_type| and |equiv| values are more meaningful. For example,
5642 the primitives for macro definitions will be loaded when we consider the
5643 routines that define macros.
5644 It is easy to find where each particular
5645 primitive was treated by looking in the index at the end; for example, the
5646 section where |"def"| entered |eqtb| is listed under `\&{def} primitive'.
5648 @* \[14] Token lists.
5649 A \MP\ token is either symbolic or numeric or a string, or it denotes
5650 a macro parameter or capsule; so there are five corresponding ways to encode it
5652 internally: (1)~A symbolic token whose hash code is~|p|
5653 is represented by the number |p|, in the |info| field of a single-word
5654 node in~|mem|. (2)~A numeric token whose |scaled| value is~|v| is
5655 represented in a two-word node of~|mem|; the |type| field is |known|,
5656 the |name_type| field is |token|, and the |value| field holds~|v|.
5657 The fact that this token appears in a two-word node rather than a
5658 one-word node is, of course, clear from the node address.
5659 (3)~A string token is also represented in a two-word node; the |type|
5660 field is |mp_string_type|, the |name_type| field is |token|, and the
5661 |value| field holds the corresponding |str_number|. (4)~Capsules have
5662 |name_type=capsule|, and their |type| and |value| fields represent
5663 arbitrary values (in ways to be explained later). (5)~Macro parameters
5664 are like symbolic tokens in that they appear in |info| fields of
5665 one-word nodes. The $k$th parameter is represented by |expr_base+k| if it
5666 is of type \&{expr}, or by |suffix_base+k| if it is of type \&{suffix}, or
5667 by |text_base+k| if it is of type \&{text}. (Here |0<=k<param_size|.)
5668 Actual values of these parameters are kept in a separate stack, as we will
5669 see later. The constants |expr_base|, |suffix_base|, and |text_base| are,
5670 of course, chosen so that there will be no confusion between symbolic
5671 tokens and parameters of various types.
5674 the `\\{type}' field of a node has nothing to do with ``type'' in a
5675 printer's sense. It's curious that the same word is used in such different ways.
5677 @d type(A) mp->mem[(A)].hh.b0 /* identifies what kind of value this is */
5678 @d name_type(A) mp->mem[(A)].hh.b1 /* a clue to the name of this value */
5679 @d token_node_size 2 /* the number of words in a large token node */
5680 @d value_loc(A) ((A)+1) /* the word that contains the |value| field */
5681 @d value(A) mp->mem[value_loc((A))].cint /* the value stored in a large token node */
5682 @d expr_base (hash_end+1) /* code for the zeroth \&{expr} parameter */
5683 @d suffix_base (expr_base+mp->param_size) /* code for the zeroth \&{suffix} parameter */
5684 @d text_base (suffix_base+mp->param_size) /* code for the zeroth \&{text} parameter */
5686 @<Check the ``constant''...@>=
5687 if ( text_base+mp->param_size>max_halfword ) mp->bad=18;
5689 @ We have set aside a two word node beginning at |null| so that we can have
5690 |value(null)=0|. We will make use of this coincidence later.
5692 @<Initialize table entries...@>=
5693 link(null)=null; value(null)=0;
5695 @ A numeric token is created by the following trivial routine.
5698 pointer mp_new_num_tok (MP mp,scaled v) {
5699 pointer p; /* the new node */
5700 p=mp_get_node(mp, token_node_size); value(p)=v;
5701 type(p)=mp_known; name_type(p)=mp_token;
5705 @ A token list is a singly linked list of nodes in |mem|, where
5706 each node contains a token and a link. Here's a subroutine that gets rid
5707 of a token list when it is no longer needed.
5709 @c void mp_flush_token_list (MP mp,pointer p) {
5710 pointer q; /* the node being recycled */
5713 if ( q>=mp->hi_mem_min ) {
5717 case mp_vacuous: case mp_boolean_type: case mp_known:
5719 case mp_string_type:
5720 delete_str_ref(value(q));
5722 case unknown_types: case mp_pen_type: case mp_path_type:
5723 case mp_picture_type: case mp_pair_type: case mp_color_type:
5724 case mp_cmykcolor_type: case mp_transform_type: case mp_dependent:
5725 case mp_proto_dependent: case mp_independent:
5726 mp_recycle_value(mp,q);
5728 default: mp_confusion(mp, "token");
5729 @:this can't happen token}{\quad token@>
5731 mp_free_node(mp, q,token_node_size);
5736 @ The procedure |show_token_list|, which prints a symbolic form of
5737 the token list that starts at a given node |p|, illustrates these
5738 conventions. The token list being displayed should not begin with a reference
5739 count. However, the procedure is intended to be fairly robust, so that if the
5740 memory links are awry or if |p| is not really a pointer to a token list,
5741 almost nothing catastrophic can happen.
5743 An additional parameter |q| is also given; this parameter is either null
5744 or it points to a node in the token list where a certain magic computation
5745 takes place that will be explained later. (Basically, |q| is non-null when
5746 we are printing the two-line context information at the time of an error
5747 message; |q| marks the place corresponding to where the second line
5750 The generation will stop, and `\.{\char`\ ETC.}' will be printed, if the length
5751 of printing exceeds a given limit~|l|; the length of printing upon entry is
5752 assumed to be a given amount called |null_tally|. (Note that
5753 |show_token_list| sometimes uses itself recursively to print
5754 variable names within a capsule.)
5757 Unusual entries are printed in the form of all-caps tokens
5758 preceded by a space, e.g., `\.{\char`\ BAD}'.
5760 @<Declare the procedure called |show_token_list|@>=
5761 void mp_show_token_list (MP mp, integer p, integer q, integer l,
5762 integer null_tally) ;
5765 void mp_show_token_list (MP mp, integer p, integer q, integer l,
5766 integer null_tally) {
5767 small_number class,c; /* the |char_class| of previous and new tokens */
5768 integer r,v; /* temporary registers */
5769 class=percent_class;
5770 mp->tally=null_tally;
5771 while ( (p!=null) && (mp->tally<l) ) {
5773 @<Do magic computation@>;
5774 @<Display token |p| and set |c| to its class;
5775 but |return| if there are problems@>;
5779 mp_print(mp, " ETC.");
5784 @ @<Display token |p| and set |c| to its class...@>=
5785 c=letter_class; /* the default */
5786 if ( (p<0)||(p>mp->mem_end) ) {
5787 mp_print(mp, " CLOBBERED"); return;
5790 if ( p<mp->hi_mem_min ) {
5791 @<Display two-word token@>;
5794 if ( r>=expr_base ) {
5795 @<Display a parameter token@>;
5799 @<Display a collective subscript@>
5801 mp_print(mp, " IMPOSSIBLE");
5806 if ( (r<0)||(r>mp->max_str_ptr) ) {
5807 mp_print(mp, " NONEXISTENT");
5810 @<Print string |r| as a symbolic token
5811 and set |c| to its class@>;
5817 @ @<Display two-word token@>=
5818 if ( name_type(p)==mp_token ) {
5819 if ( type(p)==mp_known ) {
5820 @<Display a numeric token@>;
5821 } else if ( type(p)!=mp_string_type ) {
5822 mp_print(mp, " BAD");
5825 mp_print_char(mp, '"'); mp_print_str(mp, value(p)); mp_print_char(mp, '"');
5828 } else if ((name_type(p)!=mp_capsule)||(type(p)<mp_vacuous)||(type(p)>mp_independent) ) {
5829 mp_print(mp, " BAD");
5831 mp_print_capsule(mp,p); c=right_paren_class;
5834 @ @<Display a numeric token@>=
5835 if ( class==digit_class )
5836 mp_print_char(mp, ' ');
5839 if ( class==left_bracket_class )
5840 mp_print_char(mp, ' ');
5841 mp_print_char(mp, '['); mp_print_scaled(mp, v); mp_print_char(mp, ']');
5842 c=right_bracket_class;
5844 mp_print_scaled(mp, v); c=digit_class;
5848 @ Strictly speaking, a genuine token will never have |info(p)=0|.
5849 But we will see later (in the |print_variable_name| routine) that
5850 it is convenient to let |info(p)=0| stand for `\.{[]}'.
5852 @<Display a collective subscript@>=
5854 if ( class==left_bracket_class )
5855 mp_print_char(mp, ' ');
5856 mp_print(mp, "[]"); c=right_bracket_class;
5859 @ @<Display a parameter token@>=
5861 if ( r<suffix_base ) {
5862 mp_print(mp, "(EXPR"); r=r-(expr_base);
5864 } else if ( r<text_base ) {
5865 mp_print(mp, "(SUFFIX"); r=r-(suffix_base);
5868 mp_print(mp, "(TEXT"); r=r-(text_base);
5871 mp_print_int(mp, r); mp_print_char(mp, ')'); c=right_paren_class;
5875 @ @<Print string |r| as a symbolic token...@>=
5877 c=mp->char_class[mp->str_pool[mp->str_start[r]]];
5880 case letter_class:mp_print_char(mp, '.'); break;
5881 case isolated_classes: break;
5882 default: mp_print_char(mp, ' '); break;
5885 mp_print_str(mp, r);
5889 void mp_print_capsule (MP mp, pointer p);
5891 @ @<Declare miscellaneous procedures that were declared |forward|@>=
5892 void mp_print_capsule (MP mp, pointer p) {
5893 mp_print_char(mp, '('); mp_print_exp(mp,p,0); mp_print_char(mp, ')');
5896 @ Macro definitions are kept in \MP's memory in the form of token lists
5897 that have a few extra one-word nodes at the beginning.
5899 The first node contains a reference count that is used to tell when the
5900 list is no longer needed. To emphasize the fact that a reference count is
5901 present, we shall refer to the |info| field of this special node as the
5903 @^reference counts@>
5905 The next node or nodes after the reference count serve to describe the
5906 formal parameters. They either contain a code word that specifies all
5907 of the parameters, or they contain zero or more parameter tokens followed
5908 by the code `|general_macro|'.
5911 /* reference count preceding a macro definition or picture header */
5912 @d add_mac_ref(A) incr(ref_count((A))) /* make a new reference to a macro list */
5913 @d general_macro 0 /* preface to a macro defined with a parameter list */
5914 @d primary_macro 1 /* preface to a macro with a \&{primary} parameter */
5915 @d secondary_macro 2 /* preface to a macro with a \&{secondary} parameter */
5916 @d tertiary_macro 3 /* preface to a macro with a \&{tertiary} parameter */
5917 @d expr_macro 4 /* preface to a macro with an undelimited \&{expr} parameter */
5918 @d of_macro 5 /* preface to a macro with
5919 undelimited `\&{expr} |x| \&{of}~|y|' parameters */
5920 @d suffix_macro 6 /* preface to a macro with an undelimited \&{suffix} parameter */
5921 @d text_macro 7 /* preface to a macro with an undelimited \&{text} parameter */
5924 void mp_delete_mac_ref (MP mp,pointer p) {
5925 /* |p| points to the reference count of a macro list that is
5926 losing one reference */
5927 if ( ref_count(p)==null ) mp_flush_token_list(mp, p);
5928 else decr(ref_count(p));
5931 @ The following subroutine displays a macro, given a pointer to its
5935 @<Declare the procedure called |print_cmd_mod|@>;
5936 void mp_show_macro (MP mp, pointer p, integer q, integer l) {
5937 pointer r; /* temporary storage */
5938 p=link(p); /* bypass the reference count */
5939 while ( info(p)>text_macro ){
5940 r=link(p); link(p)=null;
5941 mp_show_token_list(mp, p,null,l,0); link(p)=r; p=r;
5942 if ( l>0 ) l=l-mp->tally; else return;
5943 } /* control printing of `\.{ETC.}' */
5947 case general_macro:mp_print(mp, "->"); break;
5949 case primary_macro: case secondary_macro: case tertiary_macro:
5950 mp_print_char(mp, '<');
5951 mp_print_cmd_mod(mp, param_type,info(p));
5952 mp_print(mp, ">->");
5954 case expr_macro:mp_print(mp, "<expr>->"); break;
5955 case of_macro:mp_print(mp, "<expr>of<primary>->"); break;
5956 case suffix_macro:mp_print(mp, "<suffix>->"); break;
5957 case text_macro:mp_print(mp, "<text>->"); break;
5958 } /* there are no other cases */
5959 mp_show_token_list(mp, link(p),q,l-mp->tally,0);
5962 @* \[15] Data structures for variables.
5963 The variables of \MP\ programs can be simple, like `\.x', or they can
5964 combine the structural properties of arrays and records, like `\.{x20a.b}'.
5965 A \MP\ user assigns a type to a variable like \.{x20a.b} by saying, for
5966 example, `\.{boolean} \.{x20a.b}'. It's time for us to study how such
5967 things are represented inside of the computer.
5969 Each variable value occupies two consecutive words, either in a two-word
5970 node called a value node, or as a two-word subfield of a larger node. One
5971 of those two words is called the |value| field; it is an integer,
5972 containing either a |scaled| numeric value or the representation of some
5973 other type of quantity. (It might also be subdivided into halfwords, in
5974 which case it is referred to by other names instead of |value|.) The other
5975 word is broken into subfields called |type|, |name_type|, and |link|. The
5976 |type| field is a quarterword that specifies the variable's type, and
5977 |name_type| is a quarterword from which \MP\ can reconstruct the
5978 variable's name (sometimes by using the |link| field as well). Thus, only
5979 1.25 words are actually devoted to the value itself; the other
5980 three-quarters of a word are overhead, but they aren't wasted because they
5981 allow \MP\ to deal with sparse arrays and to provide meaningful diagnostics.
5983 In this section we shall be concerned only with the structural aspects of
5984 variables, not their values. Later parts of the program will change the
5985 |type| and |value| fields, but we shall treat those fields as black boxes
5986 whose contents should not be touched.
5988 However, if the |type| field is |mp_structured|, there is no |value| field,
5989 and the second word is broken into two pointer fields called |attr_head|
5990 and |subscr_head|. Those fields point to additional nodes that
5991 contain structural information, as we shall see.
5993 @d subscr_head_loc(A) (A)+1 /* where |value|, |subscr_head| and |attr_head| are */
5994 @d attr_head(A) info(subscr_head_loc((A))) /* pointer to attribute info */
5995 @d subscr_head(A) link(subscr_head_loc((A))) /* pointer to subscript info */
5996 @d value_node_size 2 /* the number of words in a value node */
5998 @ An attribute node is three words long. Two of these words contain |type|
5999 and |value| fields as described above, and the third word contains
6000 additional information: There is an |attr_loc| field, which contains the
6001 hash address of the token that names this attribute; and there's also a
6002 |parent| field, which points to the value node of |mp_structured| type at the
6003 next higher level (i.e., at the level to which this attribute is
6004 subsidiary). The |name_type| in an attribute node is `|attr|'. The
6005 |link| field points to the next attribute with the same parent; these are
6006 arranged in increasing order, so that |attr_loc(link(p))>attr_loc(p)|. The
6007 final attribute node links to the constant |end_attr|, whose |attr_loc|
6008 field is greater than any legal hash address. The |attr_head| in the
6009 parent points to a node whose |name_type| is |mp_structured_root|; this
6010 node represents the null attribute, i.e., the variable that is relevant
6011 when no attributes are attached to the parent. The |attr_head| node is either
6012 a value node, a subscript node, or an attribute node, depending on what
6013 the parent would be if it were not structured; but the subscript and
6014 attribute fields are ignored, so it effectively contains only the data of
6015 a value node. The |link| field in this special node points to an attribute
6016 node whose |attr_loc| field is zero; the latter node represents a collective
6017 subscript `\.{[]}' attached to the parent, and its |link| field points to
6018 the first non-special attribute node (or to |end_attr| if there are none).
6020 A subscript node likewise occupies three words, with |type| and |value| fields
6021 plus extra information; its |name_type| is |subscr|. In this case the
6022 third word is called the |subscript| field, which is a |scaled| integer.
6023 The |link| field points to the subscript node with the next larger
6024 subscript, if any; otherwise the |link| points to the attribute node
6025 for collective subscripts at this level. We have seen that the latter node
6026 contains an upward pointer, so that the parent can be deduced.
6028 The |name_type| in a parent-less value node is |root|, and the |link|
6029 is the hash address of the token that names this value.
6031 In other words, variables have a hierarchical structure that includes
6032 enough threads running around so that the program is able to move easily
6033 between siblings, parents, and children. An example should be helpful:
6034 (The reader is advised to draw a picture while reading the following
6035 description, since that will help to firm up the ideas.)
6036 Suppose that `\.x' and `\.{x.a}' and `\.{x[]b}' and `\.{x5}'
6037 and `\.{x20b}' have been mentioned in a user's program, where
6038 \.{x[]b} has been declared to be of \&{boolean} type. Let |h(x)|, |h(a)|,
6039 and |h(b)| be the hash addresses of \.x, \.a, and~\.b. Then
6040 |eq_type(h(x))=name| and |equiv(h(x))=p|, where |p|~is a two-word value
6041 node with |name_type(p)=root| and |link(p)=h(x)|. We have |type(p)=mp_structured|,
6042 |attr_head(p)=q|, and |subscr_head(p)=r|, where |q| points to a value
6043 node and |r| to a subscript node. (Are you still following this? Use
6044 a pencil to draw a diagram.) The lone variable `\.x' is represented by
6045 |type(q)| and |value(q)|; furthermore
6046 |name_type(q)=mp_structured_root| and |link(q)=q1|, where |q1| points
6047 to an attribute node representing `\.{x[]}'. Thus |name_type(q1)=attr|,
6048 |attr_loc(q1)=collective_subscript=0|, |parent(q1)=p|,
6049 |type(q1)=mp_structured|, |attr_head(q1)=qq|, and |subscr_head(q1)=qq1|;
6050 |qq| is a value node with |type(qq)=mp_numeric_type| (assuming that \.{x5} is
6051 numeric, because |qq| represents `\.{x[]}' with no further attributes),
6052 |name_type(qq)=mp_structured_root|, and
6053 |link(qq)=qq1|. (Now pay attention to the next part.) Node |qq1| is
6054 an attribute node representing `\.{x[][]}', which has never yet
6055 occurred; its |type| field is |undefined|, and its |value| field is
6056 undefined. We have |name_type(qq1)=attr|, |attr_loc(qq1)=collective_subscript|,
6057 |parent(qq1)=q1|, and |link(qq1)=qq2|. Since |qq2| represents
6058 `\.{x[]b}', |type(qq2)=mp_unknown_boolean|; also |attr_loc(qq2)=h(b)|,
6059 |parent(qq2)=q1|, |name_type(qq2)=attr|, |link(qq2)=end_attr|.
6060 (Maybe colored lines will help untangle your picture.)
6061 Node |r| is a subscript node with |type| and |value|
6062 representing `\.{x5}'; |name_type(r)=subscr|, |subscript(r)=5.0|,
6063 and |link(r)=r1| is another subscript node. To complete the picture,
6064 see if you can guess what |link(r1)| is; give up? It's~|q1|.
6065 Furthermore |subscript(r1)=20.0|, |name_type(r1)=subscr|,
6066 |type(r1)=mp_structured|, |attr_head(r1)=qqq|, |subscr_head(r1)=qqq1|,
6067 and we finish things off with three more nodes
6068 |qqq|, |qqq1|, and |qqq2| hung onto~|r1|. (Perhaps you should start again
6069 with a larger sheet of paper.) The value of variable \.{x20b}
6070 appears in node~|qqq2|, as you can well imagine.
6072 If the example in the previous paragraph doesn't make things crystal
6073 clear, a glance at some of the simpler subroutines below will reveal how
6074 things work out in practice.
6076 The only really unusual thing about these conventions is the use of
6077 collective subscript attributes. The idea is to avoid repeating a lot of
6078 type information when many elements of an array are identical macros
6079 (for which distinct values need not be stored) or when they don't have
6080 all of the possible attributes. Branches of the structure below collective
6081 subscript attributes do not carry actual values except for macro identifiers;
6082 branches of the structure below subscript nodes do not carry significant
6083 information in their collective subscript attributes.
6085 @d attr_loc_loc(A) ((A)+2) /* where the |attr_loc| and |parent| fields are */
6086 @d attr_loc(A) info(attr_loc_loc((A))) /* hash address of this attribute */
6087 @d parent(A) link(attr_loc_loc((A))) /* pointer to |mp_structured| variable */
6088 @d subscript_loc(A) ((A)+2) /* where the |subscript| field lives */
6089 @d subscript(A) mp->mem[subscript_loc((A))].sc /* subscript of this variable */
6090 @d attr_node_size 3 /* the number of words in an attribute node */
6091 @d subscr_node_size 3 /* the number of words in a subscript node */
6092 @d collective_subscript 0 /* code for the attribute `\.{[]}' */
6094 @<Initialize table...@>=
6095 attr_loc(end_attr)=hash_end+1; parent(end_attr)=null;
6097 @ Variables of type \&{pair} will have values that point to four-word
6098 nodes containing two numeric values. The first of these values has
6099 |name_type=mp_x_part_sector| and the second has |name_type=mp_y_part_sector|;
6100 the |link| in the first points back to the node whose |value| points
6101 to this four-word node.
6103 Variables of type \&{transform} are similar, but in this case their
6104 |value| points to a 12-word node containing six values, identified by
6105 |x_part_sector|, |y_part_sector|, |mp_xx_part_sector|, |mp_xy_part_sector|,
6106 |mp_yx_part_sector|, and |mp_yy_part_sector|.
6107 Finally, variables of type \&{color} have 3~values in 6~words
6108 identified by |mp_red_part_sector|, |mp_green_part_sector|, and |mp_blue_part_sector|.
6110 When an entire structured variable is saved, the |root| indication
6111 is temporarily replaced by |saved_root|.
6113 Some variables have no name; they just are used for temporary storage
6114 while expressions are being evaluated. We call them {\sl capsules}.
6116 @d x_part_loc(A) (A) /* where the \&{xpart} is found in a pair or transform node */
6117 @d y_part_loc(A) ((A)+2) /* where the \&{ypart} is found in a pair or transform node */
6118 @d xx_part_loc(A) ((A)+4) /* where the \&{xxpart} is found in a transform node */
6119 @d xy_part_loc(A) ((A)+6) /* where the \&{xypart} is found in a transform node */
6120 @d yx_part_loc(A) ((A)+8) /* where the \&{yxpart} is found in a transform node */
6121 @d yy_part_loc(A) ((A)+10) /* where the \&{yypart} is found in a transform node */
6122 @d red_part_loc(A) (A) /* where the \&{redpart} is found in a color node */
6123 @d green_part_loc(A) ((A)+2) /* where the \&{greenpart} is found in a color node */
6124 @d blue_part_loc(A) ((A)+4) /* where the \&{bluepart} is found in a color node */
6125 @d cyan_part_loc(A) (A) /* where the \&{cyanpart} is found in a color node */
6126 @d magenta_part_loc(A) ((A)+2) /* where the \&{magentapart} is found in a color node */
6127 @d yellow_part_loc(A) ((A)+4) /* where the \&{yellowpart} is found in a color node */
6128 @d black_part_loc(A) ((A)+6) /* where the \&{blackpart} is found in a color node */
6129 @d grey_part_loc(A) (A) /* where the \&{greypart} is found in a color node */
6131 @d pair_node_size 4 /* the number of words in a pair node */
6132 @d transform_node_size 12 /* the number of words in a transform node */
6133 @d color_node_size 6 /* the number of words in a color node */
6134 @d cmykcolor_node_size 8 /* the number of words in a color node */
6137 small_number big_node_size[mp_pair_type+1];
6138 small_number sector0[mp_pair_type+1];
6139 small_number sector_offset[mp_black_part_sector+1];
6141 @ The |sector0| array gives for each big node type, |name_type| values
6142 for its first subfield; the |sector_offset| array gives for each
6143 |name_type| value, the offset from the first subfield in words;
6144 and the |big_node_size| array gives the size in words for each type of
6148 mp->big_node_size[mp_transform_type]=transform_node_size;
6149 mp->big_node_size[mp_pair_type]=pair_node_size;
6150 mp->big_node_size[mp_color_type]=color_node_size;
6151 mp->big_node_size[mp_cmykcolor_type]=cmykcolor_node_size;
6152 mp->sector0[mp_transform_type]=mp_x_part_sector;
6153 mp->sector0[mp_pair_type]=mp_x_part_sector;
6154 mp->sector0[mp_color_type]=mp_red_part_sector;
6155 mp->sector0[mp_cmykcolor_type]=mp_cyan_part_sector;
6156 for (k=mp_x_part_sector;k<= mp_yy_part_sector;k++ ) {
6157 mp->sector_offset[k]=2*(k-mp_x_part_sector);
6159 for (k=mp_red_part_sector;k<= mp_blue_part_sector ; k++) {
6160 mp->sector_offset[k]=2*(k-mp_red_part_sector);
6162 for (k=mp_cyan_part_sector;k<= mp_black_part_sector;k++ ) {
6163 mp->sector_offset[k]=2*(k-mp_cyan_part_sector);
6166 @ If |type(p)=mp_pair_type| or |mp_transform_type| and if |value(p)=null|, the
6167 procedure call |init_big_node(p)| will allocate a pair or transform node
6168 for~|p|. The individual parts of such nodes are initially of type
6172 void mp_init_big_node (MP mp,pointer p) {
6173 pointer q; /* the new node */
6174 small_number s; /* its size */
6175 s=mp->big_node_size[type(p)]; q=mp_get_node(mp, s);
6178 @<Make variable |q+s| newly independent@>;
6179 name_type(q+s)=halfp(s)+mp->sector0[type(p)];
6182 link(q)=p; value(p)=q;
6185 @ The |id_transform| function creates a capsule for the
6186 identity transformation.
6189 pointer mp_id_transform (MP mp) {
6190 pointer p,q,r; /* list manipulation registers */
6191 p=mp_get_node(mp, value_node_size); type(p)=mp_transform_type;
6192 name_type(p)=mp_capsule; value(p)=null; mp_init_big_node(mp, p); q=value(p);
6193 r=q+transform_node_size;
6196 type(r)=mp_known; value(r)=0;
6198 value(xx_part_loc(q))=unity;
6199 value(yy_part_loc(q))=unity;
6203 @ Tokens are of type |tag_token| when they first appear, but they point
6204 to |null| until they are first used as the root of a variable.
6205 The following subroutine establishes the root node on such grand occasions.
6208 void mp_new_root (MP mp,pointer x) {
6209 pointer p; /* the new node */
6210 p=mp_get_node(mp, value_node_size); type(p)=undefined; name_type(p)=mp_root;
6211 link(p)=x; equiv(x)=p;
6214 @ These conventions for variable representation are illustrated by the
6215 |print_variable_name| routine, which displays the full name of a
6216 variable given only a pointer to its two-word value packet.
6219 void mp_print_variable_name (MP mp, pointer p);
6222 void mp_print_variable_name (MP mp, pointer p) {
6223 pointer q; /* a token list that will name the variable's suffix */
6224 pointer r; /* temporary for token list creation */
6225 while ( name_type(p)>=mp_x_part_sector ) {
6226 @<Preface the output with a part specifier; |return| in the
6227 case of a capsule@>;
6230 while ( name_type(p)>mp_saved_root ) {
6231 @<Ascend one level, pushing a token onto list |q|
6232 and replacing |p| by its parent@>;
6234 r=mp_get_avail(mp); info(r)=link(p); link(r)=q;
6235 if ( name_type(p)==mp_saved_root ) mp_print(mp, "(SAVED)");
6237 mp_show_token_list(mp, r,null,el_gordo,mp->tally);
6238 mp_flush_token_list(mp, r);
6241 @ @<Ascend one level, pushing a token onto list |q|...@>=
6243 if ( name_type(p)==mp_subscr ) {
6244 r=mp_new_num_tok(mp, subscript(p));
6247 } while (name_type(p)!=mp_attr);
6248 } else if ( name_type(p)==mp_structured_root ) {
6249 p=link(p); goto FOUND;
6251 if ( name_type(p)!=mp_attr ) mp_confusion(mp, "var");
6252 @:this can't happen var}{\quad var@>
6253 r=mp_get_avail(mp); info(r)=attr_loc(p);
6260 @ @<Preface the output with a part specifier...@>=
6261 { switch (name_type(p)) {
6262 case mp_x_part_sector: mp_print_char(mp, 'x'); break;
6263 case mp_y_part_sector: mp_print_char(mp, 'y'); break;
6264 case mp_xx_part_sector: mp_print(mp, "xx"); break;
6265 case mp_xy_part_sector: mp_print(mp, "xy"); break;
6266 case mp_yx_part_sector: mp_print(mp, "yx"); break;
6267 case mp_yy_part_sector: mp_print(mp, "yy"); break;
6268 case mp_red_part_sector: mp_print(mp, "red"); break;
6269 case mp_green_part_sector: mp_print(mp, "green"); break;
6270 case mp_blue_part_sector: mp_print(mp, "blue"); break;
6271 case mp_cyan_part_sector: mp_print(mp, "cyan"); break;
6272 case mp_magenta_part_sector: mp_print(mp, "magenta"); break;
6273 case mp_yellow_part_sector: mp_print(mp, "yellow"); break;
6274 case mp_black_part_sector: mp_print(mp, "black"); break;
6275 case mp_grey_part_sector: mp_print(mp, "grey"); break;
6277 mp_print(mp, "%CAPSULE"); mp_print_int(mp, p-null); return;
6280 } /* there are no other cases */
6281 mp_print(mp, "part ");
6282 p=link(p-mp->sector_offset[name_type(p)]);
6285 @ The |interesting| function returns |true| if a given variable is not
6286 in a capsule, or if the user wants to trace capsules.
6289 boolean mp_interesting (MP mp,pointer p) {
6290 small_number t; /* a |name_type| */
6291 if ( mp->internal[mp_tracing_capsules]>0 ) {
6295 if ( t>=mp_x_part_sector ) if ( t!=mp_capsule )
6296 t=name_type(link(p-mp->sector_offset[t]));
6297 return (t!=mp_capsule);
6301 @ Now here is a subroutine that converts an unstructured type into an
6302 equivalent structured type, by inserting a |mp_structured| node that is
6303 capable of growing. This operation is done only when |name_type(p)=root|,
6304 |subscr|, or |attr|.
6306 The procedure returns a pointer to the new node that has taken node~|p|'s
6307 place in the structure. Node~|p| itself does not move, nor are its
6308 |value| or |type| fields changed in any way.
6311 pointer mp_new_structure (MP mp,pointer p) {
6312 pointer q,r=0; /* list manipulation registers */
6313 switch (name_type(p)) {
6315 q=link(p); r=mp_get_node(mp, value_node_size); equiv(q)=r;
6318 @<Link a new subscript node |r| in place of node |p|@>;
6321 @<Link a new attribute node |r| in place of node |p|@>;
6324 mp_confusion(mp, "struct");
6325 @:this can't happen struct}{\quad struct@>
6328 link(r)=link(p); type(r)=mp_structured; name_type(r)=name_type(p);
6329 attr_head(r)=p; name_type(p)=mp_structured_root;
6330 q=mp_get_node(mp, attr_node_size); link(p)=q; subscr_head(r)=q;
6331 parent(q)=r; type(q)=undefined; name_type(q)=mp_attr; link(q)=end_attr;
6332 attr_loc(q)=collective_subscript;
6336 @ @<Link a new subscript node |r| in place of node |p|@>=
6341 } while (name_type(q)!=mp_attr);
6342 q=parent(q); r=subscr_head_loc(q); /* |link(r)=subscr_head(q)| */
6346 r=mp_get_node(mp, subscr_node_size);
6347 link(q)=r; subscript(r)=subscript(p);
6350 @ If the attribute is |collective_subscript|, there are two pointers to
6351 node~|p|, so we must change both of them.
6353 @<Link a new attribute node |r| in place of node |p|@>=
6355 q=parent(p); r=attr_head(q);
6359 r=mp_get_node(mp, attr_node_size); link(q)=r;
6360 mp->mem[attr_loc_loc(r)]=mp->mem[attr_loc_loc(p)]; /* copy |attr_loc| and |parent| */
6361 if ( attr_loc(p)==collective_subscript ) {
6362 q=subscr_head_loc(parent(p));
6363 while ( link(q)!=p ) q=link(q);
6368 @ The |find_variable| routine is given a pointer~|t| to a nonempty token
6369 list of suffixes; it returns a pointer to the corresponding two-word
6370 value. For example, if |t| points to token \.x followed by a numeric
6371 token containing the value~7, |find_variable| finds where the value of
6372 \.{x7} is stored in memory. This may seem a simple task, and it
6373 usually is, except when \.{x7} has never been referenced before.
6374 Indeed, \.x may never have even been subscripted before; complexities
6375 arise with respect to updating the collective subscript information.
6377 If a macro type is detected anywhere along path~|t|, or if the first
6378 item on |t| isn't a |tag_token|, the value |null| is returned.
6379 Otherwise |p| will be a non-null pointer to a node such that
6380 |undefined<type(p)<mp_structured|.
6382 @d abort_find { return null; }
6385 pointer mp_find_variable (MP mp,pointer t) {
6386 pointer p,q,r,s; /* nodes in the ``value'' line */
6387 pointer pp,qq,rr,ss; /* nodes in the ``collective'' line */
6388 integer n; /* subscript or attribute */
6389 memory_word save_word; /* temporary storage for a word of |mem| */
6391 p=info(t); t=link(t);
6392 if ( (eq_type(p) % outer_tag) != tag_token ) abort_find;
6393 if ( equiv(p)==null ) mp_new_root(mp, p);
6396 @<Make sure that both nodes |p| and |pp| are of |mp_structured| type@>;
6397 if ( t<mp->hi_mem_min ) {
6398 @<Descend one level for the subscript |value(t)|@>
6400 @<Descend one level for the attribute |info(t)|@>;
6404 if ( type(pp)>=mp_structured ) {
6405 if ( type(pp)==mp_structured ) pp=attr_head(pp); else abort_find;
6407 if ( type(p)==mp_structured ) p=attr_head(p);
6408 if ( type(p)==undefined ) {
6409 if ( type(pp)==undefined ) { type(pp)=mp_numeric_type; value(pp)=null; };
6410 type(p)=type(pp); value(p)=null;
6415 @ Although |pp| and |p| begin together, they diverge when a subscript occurs;
6416 |pp|~stays in the collective line while |p|~goes through actual subscript
6419 @<Make sure that both nodes |p| and |pp|...@>=
6420 if ( type(pp)!=mp_structured ) {
6421 if ( type(pp)>mp_structured ) abort_find;
6422 ss=mp_new_structure(mp, pp);
6425 }; /* now |type(pp)=mp_structured| */
6426 if ( type(p)!=mp_structured ) /* it cannot be |>mp_structured| */
6427 p=mp_new_structure(mp, p) /* now |type(p)=mp_structured| */
6429 @ We want this part of the program to be reasonably fast, in case there are
6431 lots of subscripts at the same level of the data structure. Therefore
6432 we store an ``infinite'' value in the word that appears at the end of the
6433 subscript list, even though that word isn't part of a subscript node.
6435 @<Descend one level for the subscript |value(t)|@>=
6438 pp=link(attr_head(pp)); /* now |attr_loc(pp)=collective_subscript| */
6439 q=link(attr_head(p)); save_word=mp->mem[subscript_loc(q)];
6440 subscript(q)=el_gordo; s=subscr_head_loc(p); /* |link(s)=subscr_head(p)| */
6443 } while (n>subscript(s));
6444 if ( n==subscript(s) ) {
6447 p=mp_get_node(mp, subscr_node_size); link(r)=p; link(p)=s;
6448 subscript(p)=n; name_type(p)=mp_subscr; type(p)=undefined;
6450 mp->mem[subscript_loc(q)]=save_word;
6453 @ @<Descend one level for the attribute |info(t)|@>=
6459 } while (n>attr_loc(ss));
6460 if ( n<attr_loc(ss) ) {
6461 qq=mp_get_node(mp, attr_node_size); link(rr)=qq; link(qq)=ss;
6462 attr_loc(qq)=n; name_type(qq)=mp_attr; type(qq)=undefined;
6463 parent(qq)=pp; ss=qq;
6468 pp=ss; s=attr_head(p);
6471 } while (n>attr_loc(s));
6472 if ( n==attr_loc(s) ) {
6475 q=mp_get_node(mp, attr_node_size); link(r)=q; link(q)=s;
6476 attr_loc(q)=n; name_type(q)=mp_attr; type(q)=undefined;
6482 @ Variables lose their former values when they appear in a type declaration,
6483 or when they are defined to be macros or \&{let} equal to something else.
6484 A subroutine will be defined later that recycles the storage associated
6485 with any particular |type| or |value|; our goal now is to study a higher
6486 level process called |flush_variable|, which selectively frees parts of a
6489 This routine has some complexity because of examples such as
6490 `\hbox{\tt numeric x[]a[]b}'
6491 which recycles all variables of the form \.{x[i]a[j]b} (and no others), while
6492 `\hbox{\tt vardef x[]a[]=...}'
6493 discards all variables of the form \.{x[i]a[j]} followed by an arbitrary
6494 suffix, except for the collective node \.{x[]a[]} itself. The obvious way
6495 to handle such examples is to use recursion; so that's what we~do.
6498 Parameter |p| points to the root information of the variable;
6499 parameter |t| points to a list of one-word nodes that represent
6500 suffixes, with |info=collective_subscript| for subscripts.
6503 @<Declare subroutines for printing expressions@>
6504 @<Declare basic dependency-list subroutines@>
6505 @<Declare the recycling subroutines@>
6506 void mp_flush_cur_exp (MP mp,scaled v) ;
6507 @<Declare the procedure called |flush_below_variable|@>
6510 void mp_flush_variable (MP mp,pointer p, pointer t, boolean discard_suffixes) {
6511 pointer q,r; /* list manipulation */
6512 halfword n; /* attribute to match */
6514 if ( type(p)!=mp_structured ) return;
6515 n=info(t); t=link(t);
6516 if ( n==collective_subscript ) {
6517 r=subscr_head_loc(p); q=link(r); /* |q=subscr_head(p)| */
6518 while ( name_type(q)==mp_subscr ){
6519 mp_flush_variable(mp, q,t,discard_suffixes);
6521 if ( type(q)==mp_structured ) r=q;
6522 else { link(r)=link(q); mp_free_node(mp, q,subscr_node_size); }
6532 } while (attr_loc(p)<n);
6533 if ( attr_loc(p)!=n ) return;
6535 if ( discard_suffixes ) {
6536 mp_flush_below_variable(mp, p);
6538 if ( type(p)==mp_structured ) p=attr_head(p);
6539 mp_recycle_value(mp, p);
6543 @ The next procedure is simpler; it wipes out everything but |p| itself,
6544 which becomes undefined.
6546 @<Declare the procedure called |flush_below_variable|@>=
6547 void mp_flush_below_variable (MP mp, pointer p);
6550 void mp_flush_below_variable (MP mp,pointer p) {
6551 pointer q,r; /* list manipulation registers */
6552 if ( type(p)!=mp_structured ) {
6553 mp_recycle_value(mp, p); /* this sets |type(p)=undefined| */
6556 while ( name_type(q)==mp_subscr ) {
6557 mp_flush_below_variable(mp, q); r=q; q=link(q);
6558 mp_free_node(mp, r,subscr_node_size);
6560 r=attr_head(p); q=link(r); mp_recycle_value(mp, r);
6561 if ( name_type(p)<=mp_saved_root ) mp_free_node(mp, r,value_node_size);
6562 else mp_free_node(mp, r,subscr_node_size);
6563 /* we assume that |subscr_node_size=attr_node_size| */
6565 mp_flush_below_variable(mp, q); r=q; q=link(q); mp_free_node(mp, r,attr_node_size);
6566 } while (q!=end_attr);
6571 @ Just before assigning a new value to a variable, we will recycle the
6572 old value and make the old value undefined. The |und_type| routine
6573 determines what type of undefined value should be given, based on
6574 the current type before recycling.
6577 small_number mp_und_type (MP mp,pointer p) {
6579 case undefined: case mp_vacuous:
6581 case mp_boolean_type: case mp_unknown_boolean:
6582 return mp_unknown_boolean;
6583 case mp_string_type: case mp_unknown_string:
6584 return mp_unknown_string;
6585 case mp_pen_type: case mp_unknown_pen:
6586 return mp_unknown_pen;
6587 case mp_path_type: case mp_unknown_path:
6588 return mp_unknown_path;
6589 case mp_picture_type: case mp_unknown_picture:
6590 return mp_unknown_picture;
6591 case mp_transform_type: case mp_color_type: case mp_cmykcolor_type:
6592 case mp_pair_type: case mp_numeric_type:
6594 case mp_known: case mp_dependent: case mp_proto_dependent: case mp_independent:
6595 return mp_numeric_type;
6596 } /* there are no other cases */
6600 @ The |clear_symbol| routine is used when we want to redefine the equivalent
6601 of a symbolic token. It must remove any variable structure or macro
6602 definition that is currently attached to that symbol. If the |saving|
6603 parameter is true, a subsidiary structure is saved instead of destroyed.
6606 void mp_clear_symbol (MP mp,pointer p, boolean saving) {
6607 pointer q; /* |equiv(p)| */
6609 switch (eq_type(p) % outer_tag) {
6611 case secondary_primary_macro:
6612 case tertiary_secondary_macro:
6613 case expression_tertiary_macro:
6614 if ( ! saving ) mp_delete_mac_ref(mp, q);
6619 name_type(q)=mp_saved_root;
6621 mp_flush_below_variable(mp, q); mp_free_node(mp,q,value_node_size);
6628 mp->eqtb[p]=mp->eqtb[frozen_undefined];
6631 @* \[16] Saving and restoring equivalents.
6632 The nested structure given by \&{begingroup} and \&{endgroup}
6633 allows |eqtb| entries to be saved and restored, so that temporary changes
6634 can be made without difficulty. When the user requests a current value to
6635 be saved, \MP\ puts that value into its ``save stack.'' An appearance of
6636 \&{endgroup} ultimately causes the old values to be removed from the save
6637 stack and put back in their former places.
6639 The save stack is a linked list containing three kinds of entries,
6640 distinguished by their |info| fields. If |p| points to a saved item,
6644 |info(p)=0| stands for a group boundary; each \&{begingroup} contributes
6645 such an item to the save stack and each \&{endgroup} cuts back the stack
6646 until the most recent such entry has been removed.
6649 |info(p)=q|, where |1<=q<=hash_end|, means that |mem[p+1]| holds the former
6650 contents of |eqtb[q]|. Such save stack entries are generated by \&{save}
6651 commands or suitable \&{interim} commands.
6654 |info(p)=hash_end+q|, where |q>0|, means that |value(p)| is a |scaled|
6655 integer to be restored to internal parameter number~|q|. Such entries
6656 are generated by \&{interim} commands.
6659 The global variable |save_ptr| points to the top item on the save stack.
6661 @d save_node_size 2 /* number of words per non-boundary save-stack node */
6662 @d saved_equiv(A) mp->mem[(A)+1].hh /* where an |eqtb| entry gets saved */
6663 @d save_boundary_item(A) { (A)=mp_get_avail(mp); info((A))=0;
6664 link((A))=mp->save_ptr; mp->save_ptr=(A);
6668 pointer save_ptr; /* the most recently saved item */
6670 @ @<Set init...@>=mp->save_ptr=null;
6672 @ The |save_variable| routine is given a hash address |q|; it salts this
6673 address in the save stack, together with its current equivalent,
6674 then makes token~|q| behave as though it were brand new.
6676 Nothing is stacked when |save_ptr=null|, however; there's no way to remove
6677 things from the stack when the program is not inside a group, so there's
6678 no point in wasting the space.
6680 @c void mp_save_variable (MP mp,pointer q) {
6681 pointer p; /* temporary register */
6682 if ( mp->save_ptr!=null ){
6683 p=mp_get_node(mp, save_node_size); info(p)=q; link(p)=mp->save_ptr;
6684 saved_equiv(p)=mp->eqtb[q]; mp->save_ptr=p;
6686 mp_clear_symbol(mp, q,(mp->save_ptr!=null));
6689 @ Similarly, |save_internal| is given the location |q| of an internal
6690 quantity like |mp_tracing_pens|. It creates a save stack entry of the
6693 @c void mp_save_internal (MP mp,halfword q) {
6694 pointer p; /* new item for the save stack */
6695 if ( mp->save_ptr!=null ){
6696 p=mp_get_node(mp, save_node_size); info(p)=hash_end+q;
6697 link(p)=mp->save_ptr; value(p)=mp->internal[q]; mp->save_ptr=p;
6701 @ At the end of a group, the |unsave| routine restores all of the saved
6702 equivalents in reverse order. This routine will be called only when there
6703 is at least one boundary item on the save stack.
6706 void mp_unsave (MP mp) {
6707 pointer q; /* index to saved item */
6708 pointer p; /* temporary register */
6709 while ( info(mp->save_ptr)!=0 ) {
6710 q=info(mp->save_ptr);
6712 if ( mp->internal[mp_tracing_restores]>0 ) {
6713 mp_begin_diagnostic(mp); mp_print_nl(mp, "{restoring ");
6714 mp_print(mp, mp->int_name[q-(hash_end)]); mp_print_char(mp, '=');
6715 mp_print_scaled(mp, value(mp->save_ptr)); mp_print_char(mp, '}');
6716 mp_end_diagnostic(mp, false);
6718 mp->internal[q-(hash_end)]=value(mp->save_ptr);
6720 if ( mp->internal[mp_tracing_restores]>0 ) {
6721 mp_begin_diagnostic(mp); mp_print_nl(mp, "{restoring ");
6722 mp_print_text(q); mp_print_char(mp, '}');
6723 mp_end_diagnostic(mp, false);
6725 mp_clear_symbol(mp, q,false);
6726 mp->eqtb[q]=saved_equiv(mp->save_ptr);
6727 if ( eq_type(q) % outer_tag==tag_token ) {
6729 if ( p!=null ) name_type(p)=mp_root;
6732 p=link(mp->save_ptr);
6733 mp_free_node(mp, mp->save_ptr,save_node_size); mp->save_ptr=p;
6735 p=link(mp->save_ptr); free_avail(mp->save_ptr); mp->save_ptr=p;
6738 @* \[17] Data structures for paths.
6739 When a \MP\ user specifies a path, \MP\ will create a list of knots
6740 and control points for the associated cubic spline curves. If the
6741 knots are $z_0$, $z_1$, \dots, $z_n$, there are control points
6742 $z_k^+$ and $z_{k+1}^-$ such that the cubic splines between knots
6743 $z_k$ and $z_{k+1}$ are defined by B\'ezier's formula
6744 @:Bezier}{B\'ezier, Pierre Etienne@>
6745 $$\eqalign{z(t)&=B(z_k,z_k^+,z_{k+1}^-,z_{k+1};t)\cr
6746 &=(1-t)^3z_k+3(1-t)^2tz_k^++3(1-t)t^2z_{k+1}^-+t^3z_{k+1}\cr}$$
6749 There is a 8-word node for each knot $z_k$, containing one word of
6750 control information and six words for the |x| and |y| coordinates of
6751 $z_k^-$ and $z_k$ and~$z_k^+$. The control information appears in the
6752 |left_type| and |right_type| fields, which each occupy a quarter of
6753 the first word in the node; they specify properties of the curve as it
6754 enters and leaves the knot. There's also a halfword |link| field,
6755 which points to the following knot, and a final supplementary word (of
6756 which only a quarter is used).
6758 If the path is a closed contour, knots 0 and |n| are identical;
6759 i.e., the |link| in knot |n-1| points to knot~0. But if the path
6760 is not closed, the |left_type| of knot~0 and the |right_type| of knot~|n|
6761 are equal to |endpoint|. In the latter case the |link| in knot~|n| points
6762 to knot~0, and the control points $z_0^-$ and $z_n^+$ are not used.
6764 @d left_type(A) mp->mem[(A)].hh.b0 /* characterizes the path entering this knot */
6765 @d right_type(A) mp->mem[(A)].hh.b1 /* characterizes the path leaving this knot */
6766 @d x_coord(A) mp->mem[(A)+1].sc /* the |x| coordinate of this knot */
6767 @d y_coord(A) mp->mem[(A)+2].sc /* the |y| coordinate of this knot */
6768 @d left_x(A) mp->mem[(A)+3].sc /* the |x| coordinate of previous control point */
6769 @d left_y(A) mp->mem[(A)+4].sc /* the |y| coordinate of previous control point */
6770 @d right_x(A) mp->mem[(A)+5].sc /* the |x| coordinate of next control point */
6771 @d right_y(A) mp->mem[(A)+6].sc /* the |y| coordinate of next control point */
6772 @d x_loc(A) ((A)+1) /* where the |x| coordinate is stored in a knot */
6773 @d y_loc(A) ((A)+2) /* where the |y| coordinate is stored in a knot */
6774 @d knot_coord(A) mp->mem[(A)].sc /* |x| or |y| coordinate given |x_loc| or |y_loc| */
6775 @d left_coord(A) mp->mem[(A)+2].sc
6776 /* coordinate of previous control point given |x_loc| or |y_loc| */
6777 @d right_coord(A) mp->mem[(A)+4].sc
6778 /* coordinate of next control point given |x_loc| or |y_loc| */
6779 @d knot_node_size 8 /* number of words in a knot node */
6783 mp_endpoint=0, /* |left_type| at path beginning and |right_type| at path end */
6784 mp_explicit, /* |left_type| or |right_type| when control points are known */
6785 mp_given, /* |left_type| or |right_type| when a direction is given */
6786 mp_curl, /* |left_type| or |right_type| when a curl is desired */
6787 mp_open, /* |left_type| or |right_type| when \MP\ should choose the direction */
6791 @ Before the B\'ezier control points have been calculated, the memory
6792 space they will ultimately occupy is taken up by information that can be
6793 used to compute them. There are four cases:
6796 \textindent{$\bullet$} If |right_type=mp_open|, the curve should leave
6797 the knot in the same direction it entered; \MP\ will figure out a
6801 \textindent{$\bullet$} If |right_type=mp_curl|, the curve should leave the
6802 knot in a direction depending on the angle at which it enters the next
6803 knot and on the curl parameter stored in |right_curl|.
6806 \textindent{$\bullet$} If |right_type=mp_given|, the curve should leave the
6807 knot in a nonzero direction stored as an |angle| in |right_given|.
6810 \textindent{$\bullet$} If |right_type=mp_explicit|, the B\'ezier control
6811 point for leaving this knot has already been computed; it is in the
6812 |right_x| and |right_y| fields.
6815 The rules for |left_type| are similar, but they refer to the curve entering
6816 the knot, and to \\{left} fields instead of \\{right} fields.
6818 Non-|explicit| control points will be chosen based on ``tension'' parameters
6819 in the |left_tension| and |right_tension| fields. The
6820 `\&{atleast}' option is represented by negative tension values.
6821 @:at_least_}{\&{atleast} primitive@>
6823 For example, the \MP\ path specification
6824 $$\.{z0..z1..tension atleast 1..\{curl 2\}z2..z3\{-1,-2\}..tension
6826 where \.p is the path `\.{z4..controls z45 and z54..z5}', will be represented
6828 \def\lodash{\hbox to 1.1em{\thinspace\hrulefill\thinspace}}
6829 $$\vbox{\halign{#\hfil&&\qquad#\hfil\cr
6830 |left_type|&\\{left} info&|x_coord,y_coord|&|right_type|&\\{right} info\cr
6832 |endpoint|&\lodash$,\,$\lodash&$x_0,y_0$&|curl|&$1.0,1.0$\cr
6833 |open|&\lodash$,1.0$&$x_1,y_1$&|open|&\lodash$,-1.0$\cr
6834 |curl|&$2.0,-1.0$&$x_2,y_2$&|curl|&$2.0,1.0$\cr
6835 |given|&$d,1.0$&$x_3,y_3$&|given|&$d,3.0$\cr
6836 |open|&\lodash$,4.0$&$x_4,y_4$&|explicit|&$x_{45},y_{45}$\cr
6837 |explicit|&$x_{54},y_{54}$&$x_5,y_5$&|endpoint|&\lodash$,\,$\lodash\cr}}$$
6838 Here |d| is the |angle| obtained by calling |n_arg(-unity,-two)|.
6839 Of course, this example is more complicated than anything a normal user
6842 These types must satisfy certain restrictions because of the form of \MP's
6844 (i)~|open| type never appears in the same node together with |endpoint|,
6846 (ii)~The |right_type| of a node is |explicit| if and only if the
6847 |left_type| of the following node is |explicit|.
6848 (iii)~|endpoint| types occur only at the ends, as mentioned above.
6850 @d left_curl left_x /* curl information when entering this knot */
6851 @d left_given left_x /* given direction when entering this knot */
6852 @d left_tension left_y /* tension information when entering this knot */
6853 @d right_curl right_x /* curl information when leaving this knot */
6854 @d right_given right_x /* given direction when leaving this knot */
6855 @d right_tension right_y /* tension information when leaving this knot */
6857 @ Knots can be user-supplied, or they can be created by program code,
6858 like the |split_cubic| function, or |copy_path|. The distinction is
6859 needed for the cleanup routine that runs after |split_cubic|, because
6860 it should only delete knots it has previously inserted, and never
6861 anything that was user-supplied. In order to be able to differentiate
6862 one knot from another, we will set |originator(p):=mp_metapost_user| when
6863 it appeared in the actual metapost program, and
6864 |originator(p):=mp_program_code| in all other cases.
6866 @d originator(A) mp->mem[(A)+7].hh.b0 /* the creator of this knot */
6870 mp_program_code=0, /* not created by a user */
6871 mp_metapost_user, /* created by a user */
6874 @ Here is a routine that prints a given knot list
6875 in symbolic form. It illustrates the conventions discussed above,
6876 and checks for anomalies that might arise while \MP\ is being debugged.
6878 @<Declare subroutines for printing expressions@>=
6879 void mp_pr_path (MP mp,pointer h);
6882 void mp_pr_path (MP mp,pointer h) {
6883 pointer p,q; /* for list traversal */
6887 if ( (p==null)||(q==null) ) {
6888 mp_print_nl(mp, "???"); return; /* this won't happen */
6891 @<Print information for adjacent knots |p| and |q|@>;
6894 if ( (p!=h)||(left_type(h)!=mp_endpoint) ) {
6895 @<Print two dots, followed by |given| or |curl| if present@>;
6898 if ( left_type(h)!=mp_endpoint )
6899 mp_print(mp, "cycle");
6902 @ @<Print information for adjacent knots...@>=
6903 mp_print_two(mp, x_coord(p),y_coord(p));
6904 switch (right_type(p)) {
6906 if ( left_type(p)==mp_open ) mp_print(mp, "{open?}"); /* can't happen */
6908 if ( (left_type(q)!=mp_endpoint)||(q!=h) ) q=null; /* force an error */
6912 @<Print control points between |p| and |q|, then |goto done1|@>;
6915 @<Print information for a curve that begins |open|@>;
6919 @<Print information for a curve that begins |curl| or |given|@>;
6922 mp_print(mp, "???"); /* can't happen */
6926 if ( left_type(q)<=mp_explicit ) {
6927 mp_print(mp, "..control?"); /* can't happen */
6929 } else if ( (right_tension(p)!=unity)||(left_tension(q)!=unity) ) {
6930 @<Print tension between |p| and |q|@>;
6933 @ Since |n_sin_cos| produces |fraction| results, which we will print as if they
6934 were |scaled|, the magnitude of a |given| direction vector will be~4096.
6936 @<Print two dots...@>=
6938 mp_print_nl(mp, " ..");
6939 if ( left_type(p)==mp_given ) {
6940 mp_n_sin_cos(mp, left_given(p)); mp_print_char(mp, '{');
6941 mp_print_scaled(mp, mp->n_cos); mp_print_char(mp, ',');
6942 mp_print_scaled(mp, mp->n_sin); mp_print_char(mp, '}');
6943 } else if ( left_type(p)==mp_curl ){
6944 mp_print(mp, "{curl ");
6945 mp_print_scaled(mp, left_curl(p)); mp_print_char(mp, '}');
6949 @ @<Print tension between |p| and |q|@>=
6951 mp_print(mp, "..tension ");
6952 if ( right_tension(p)<0 ) mp_print(mp, "atleast");
6953 mp_print_scaled(mp, abs(right_tension(p)));
6954 if ( right_tension(p)!=left_tension(q) ){
6955 mp_print(mp, " and ");
6956 if ( left_tension(q)<0 ) mp_print(mp, "atleast");
6957 mp_print_scaled(mp, abs(left_tension(q)));
6961 @ @<Print control points between |p| and |q|, then |goto done1|@>=
6963 mp_print(mp, "..controls ");
6964 mp_print_two(mp, right_x(p),right_y(p));
6965 mp_print(mp, " and ");
6966 if ( left_type(q)!=mp_explicit ) {
6967 mp_print(mp, "??"); /* can't happen */
6970 mp_print_two(mp, left_x(q),left_y(q));
6975 @ @<Print information for a curve that begins |open|@>=
6976 if ( (left_type(p)!=mp_explicit)&&(left_type(p)!=mp_open) ) {
6977 mp_print(mp, "{open?}"); /* can't happen */
6981 @ A curl of 1 is shown explicitly, so that the user sees clearly that
6982 \MP's default curl is present.
6984 The code here uses the fact that |left_curl==left_given| and
6985 |right_curl==right_given|.
6987 @<Print information for a curve that begins |curl|...@>=
6989 if ( left_type(p)==mp_open )
6990 mp_print(mp, "??"); /* can't happen */
6992 if ( right_type(p)==mp_curl ) {
6993 mp_print(mp, "{curl "); mp_print_scaled(mp, right_curl(p));
6995 mp_n_sin_cos(mp, right_given(p)); mp_print_char(mp, '{');
6996 mp_print_scaled(mp, mp->n_cos); mp_print_char(mp, ',');
6997 mp_print_scaled(mp, mp->n_sin);
6999 mp_print_char(mp, '}');
7002 @ It is convenient to have another version of |pr_path| that prints the path
7003 as a diagnostic message.
7005 @<Declare subroutines for printing expressions@>=
7006 void mp_print_path (MP mp,pointer h, char *s, boolean nuline) {
7007 mp_print_diagnostic(mp, "Path", s, nuline); mp_print_ln(mp);
7010 mp_end_diagnostic(mp, true);
7013 @ If we want to duplicate a knot node, we can say |copy_knot|:
7016 pointer mp_copy_knot (MP mp,pointer p) {
7017 pointer q; /* the copy */
7018 int k; /* runs through the words of a knot node */
7019 q=mp_get_node(mp, knot_node_size);
7020 for (k=0;k<knot_node_size;k++) {
7021 mp->mem[q+k]=mp->mem[p+k];
7023 originator(q)=originator(p);
7027 @ The |copy_path| routine makes a clone of a given path.
7030 pointer mp_copy_path (MP mp, pointer p) {
7031 pointer q,pp,qq; /* for list manipulation */
7032 q=mp_copy_knot(mp, p);
7035 link(qq)=mp_copy_knot(mp, pp);
7044 @ Just before |ship_out|, knot lists are exported for printing.
7046 The |gr_XXXX| macros are defined in |mppsout.h|.
7049 struct mp_knot *mp_export_knot (MP mp,pointer p) {
7050 struct mp_knot *q; /* the copy */
7053 q = mp_xmalloc(mp, 1, sizeof (struct mp_knot));
7054 memset(q,0,sizeof (struct mp_knot));
7055 gr_left_type(q) = left_type(p);
7056 gr_right_type(q) = right_type(p);
7057 gr_x_coord(q) = x_coord(p);
7058 gr_y_coord(q) = y_coord(p);
7059 gr_left_x(q) = left_x(p);
7060 gr_left_y(q) = left_y(p);
7061 gr_right_x(q) = right_x(p);
7062 gr_right_y(q) = right_y(p);
7063 gr_originator(q) = originator(p);
7067 @ The |export_knot_list| routine therefore also makes a clone
7071 struct mp_knot *mp_export_knot_list (MP mp, pointer p) {
7072 struct mp_knot *q, *qq; /* for list manipulation */
7073 pointer pp; /* for list manipulation */
7076 q=mp_export_knot(mp, p);
7079 gr_next_knot(qq)=mp_export_knot(mp, pp);
7080 qq=gr_next_knot(qq);
7088 @ Similarly, there's a way to copy the {\sl reverse\/} of a path. This procedure
7089 returns a pointer to the first node of the copy, if the path is a cycle,
7090 but to the final node of a non-cyclic copy. The global
7091 variable |path_tail| will point to the final node of the original path;
7092 this trick makes it easier to implement `\&{doublepath}'.
7094 All node types are assumed to be |endpoint| or |explicit| only.
7097 pointer mp_htap_ypoc (MP mp,pointer p) {
7098 pointer q,pp,qq,rr; /* for list manipulation */
7099 q=mp_get_node(mp, knot_node_size); /* this will correspond to |p| */
7102 right_type(qq)=left_type(pp); left_type(qq)=right_type(pp);
7103 x_coord(qq)=x_coord(pp); y_coord(qq)=y_coord(pp);
7104 right_x(qq)=left_x(pp); right_y(qq)=left_y(pp);
7105 left_x(qq)=right_x(pp); left_y(qq)=right_y(pp);
7106 originator(qq)=originator(pp);
7107 if ( link(pp)==p ) {
7108 link(q)=qq; mp->path_tail=pp; return q;
7110 rr=mp_get_node(mp, knot_node_size); link(rr)=qq; qq=rr; pp=link(pp);
7115 pointer path_tail; /* the node that links to the beginning of a path */
7117 @ When a cyclic list of knot nodes is no longer needed, it can be recycled by
7118 calling the following subroutine.
7120 @<Declare the recycling subroutines@>=
7121 void mp_toss_knot_list (MP mp,pointer p) ;
7124 void mp_toss_knot_list (MP mp,pointer p) {
7125 pointer q; /* the node being freed */
7126 pointer r; /* the next node */
7130 mp_free_node(mp, q,knot_node_size); q=r;
7134 @* \[18] Choosing control points.
7135 Now we must actually delve into one of \MP's more difficult routines,
7136 the |make_choices| procedure that chooses angles and control points for
7137 the splines of a curve when the user has not specified them explicitly.
7138 The parameter to |make_choices| points to a list of knots and
7139 path information, as described above.
7141 A path decomposes into independent segments at ``breakpoint'' knots,
7142 which are knots whose left and right angles are both prespecified in
7143 some way (i.e., their |left_type| and |right_type| aren't both open).
7146 @<Declare the procedure called |solve_choices|@>;
7147 void mp_make_choices (MP mp,pointer knots) {
7148 pointer h; /* the first breakpoint */
7149 pointer p,q; /* consecutive breakpoints being processed */
7150 @<Other local variables for |make_choices|@>;
7151 check_arith; /* make sure that |arith_error=false| */
7152 if ( mp->internal[mp_tracing_choices]>0 )
7153 mp_print_path(mp, knots,", before choices",true);
7154 @<If consecutive knots are equal, join them explicitly@>;
7155 @<Find the first breakpoint, |h|, on the path;
7156 insert an artificial breakpoint if the path is an unbroken cycle@>;
7159 @<Fill in the control points between |p| and the next breakpoint,
7160 then advance |p| to that breakpoint@>;
7162 if ( mp->internal[mp_tracing_choices]>0 )
7163 mp_print_path(mp, knots,", after choices",true);
7164 if ( mp->arith_error ) {
7165 @<Report an unexpected problem during the choice-making@>;
7169 @ @<Report an unexpected problem during the choice...@>=
7171 print_err("Some number got too big");
7172 @.Some number got too big@>
7173 help2("The path that I just computed is out of range.")
7174 ("So it will probably look funny. Proceed, for a laugh.");
7175 mp_put_get_error(mp); mp->arith_error=false;
7178 @ Two knots in a row with the same coordinates will always be joined
7179 by an explicit ``curve'' whose control points are identical with the
7182 @<If consecutive knots are equal, join them explicitly@>=
7186 if ( x_coord(p)==x_coord(q) && y_coord(p)==y_coord(q) && right_type(p)>mp_explicit ) {
7187 right_type(p)=mp_explicit;
7188 if ( left_type(p)==mp_open ) {
7189 left_type(p)=mp_curl; left_curl(p)=unity;
7191 left_type(q)=mp_explicit;
7192 if ( right_type(q)==mp_open ) {
7193 right_type(q)=mp_curl; right_curl(q)=unity;
7195 right_x(p)=x_coord(p); left_x(q)=x_coord(p);
7196 right_y(p)=y_coord(p); left_y(q)=y_coord(p);
7201 @ If there are no breakpoints, it is necessary to compute the direction
7202 angles around an entire cycle. In this case the |left_type| of the first
7203 node is temporarily changed to |end_cycle|.
7205 @<Find the first breakpoint, |h|, on the path...@>=
7208 if ( left_type(h)!=mp_open ) break;
7209 if ( right_type(h)!=mp_open ) break;
7212 left_type(h)=mp_end_cycle; break;
7216 @ If |right_type(p)<given| and |q=link(p)|, we must have
7217 |right_type(p)=left_type(q)=mp_explicit| or |endpoint|.
7219 @<Fill in the control points between |p| and the next breakpoint...@>=
7221 if ( right_type(p)>=mp_given ) {
7222 while ( (left_type(q)==mp_open)&&(right_type(q)==mp_open) ) q=link(q);
7223 @<Fill in the control information between
7224 consecutive breakpoints |p| and |q|@>;
7225 } else if ( right_type(p)==mp_endpoint ) {
7226 @<Give reasonable values for the unused control points between |p| and~|q|@>;
7230 @ This step makes it possible to transform an explicitly computed path without
7231 checking the |left_type| and |right_type| fields.
7233 @<Give reasonable values for the unused control points between |p| and~|q|@>=
7235 right_x(p)=x_coord(p); right_y(p)=y_coord(p);
7236 left_x(q)=x_coord(q); left_y(q)=y_coord(q);
7239 @ Before we can go further into the way choices are made, we need to
7240 consider the underlying theory. The basic ideas implemented in |make_choices|
7241 are due to John Hobby, who introduced the notion of ``mock curvature''
7242 @^Hobby, John Douglas@>
7243 at a knot. Angles are chosen so that they preserve mock curvature when
7244 a knot is passed, and this has been found to produce excellent results.
7246 It is convenient to introduce some notations that simplify the necessary
7247 formulas. Let $d_{k,k+1}=\vert z\k-z_k\vert$ be the (nonzero) distance
7248 between knots |k| and |k+1|; and let
7249 $${z\k-z_k\over z_k-z_{k-1}}={d_{k,k+1}\over d_{k-1,k}}e^{i\psi_k}$$
7250 so that a polygonal line from $z_{k-1}$ to $z_k$ to $z\k$ turns left
7251 through an angle of~$\psi_k$. We assume that $\vert\psi_k\vert\L180^\circ$.
7252 The control points for the spline from $z_k$ to $z\k$ will be denoted by
7253 $$\eqalign{z_k^+&=z_k+
7254 \textstyle{1\over3}\rho_k e^{i\theta_k}(z\k-z_k),\cr
7256 \textstyle{1\over3}\sigma\k e^{-i\phi\k}(z\k-z_k),\cr}$$
7257 where $\rho_k$ and $\sigma\k$ are nonnegative ``velocity ratios'' at the
7258 beginning and end of the curve, while $\theta_k$ and $\phi\k$ are the
7259 corresponding ``offset angles.'' These angles satisfy the condition
7260 $$\theta_k+\phi_k+\psi_k=0,\eqno(*)$$
7261 whenever the curve leaves an intermediate knot~|k| in the direction that
7264 @ Let $\alpha_k$ and $\beta\k$ be the reciprocals of the ``tension'' of
7265 the curve at its beginning and ending points. This means that
7266 $\rho_k=\alpha_k f(\theta_k,\phi\k)$ and $\sigma\k=\beta\k f(\phi\k,\theta_k)$,
7267 where $f(\theta,\phi)$ is \MP's standard velocity function defined in
7268 the |velocity| subroutine. The cubic spline $B(z_k^{\phantom+},z_k^+,
7269 z\k^-,z\k^{\phantom+};t)$
7272 $${2\sigma\k\sin(\theta_k+\phi\k)-6\sin\theta_k\over\rho_k^2d_{k,k+1}}
7273 \qquad{\rm and}\qquad
7274 {2\rho_k\sin(\theta_k+\phi\k)-6\sin\phi\k\over\sigma\k^2d_{k,k+1}}$$
7275 at |t=0| and |t=1|, respectively. The mock curvature is the linear
7277 approximation to this true curvature that arises in the limit for
7278 small $\theta_k$ and~$\phi\k$, if second-order terms are discarded.
7279 The standard velocity function satisfies
7280 $$f(\theta,\phi)=1+O(\theta^2+\theta\phi+\phi^2);$$
7281 hence the mock curvatures are respectively
7282 $${2\beta\k(\theta_k+\phi\k)-6\theta_k\over\alpha_k^2d_{k,k+1}}
7283 \qquad{\rm and}\qquad
7284 {2\alpha_k(\theta_k+\phi\k)-6\phi\k\over\beta\k^2d_{k,k+1}}.\eqno(**)$$
7286 @ The turning angles $\psi_k$ are given, and equation $(*)$ above
7287 determines $\phi_k$ when $\theta_k$ is known, so the task of
7288 angle selection is essentially to choose appropriate values for each
7289 $\theta_k$. When equation~$(*)$ is used to eliminate $\phi$~variables
7290 from $(**)$, we obtain a system of linear equations of the form
7291 $$A_k\theta_{k-1}+(B_k+C_k)\theta_k+D_k\theta\k=-B_k\psi_k-D_k\psi\k,$$
7293 $$A_k={\alpha_{k-1}\over\beta_k^2d_{k-1,k}},
7294 \qquad B_k={3-\alpha_{k-1}\over\beta_k^2d_{k-1,k}},
7295 \qquad C_k={3-\beta\k\over\alpha_k^2d_{k,k+1}},
7296 \qquad D_k={\beta\k\over\alpha_k^2d_{k,k+1}}.$$
7297 The tensions are always $3\over4$ or more, hence each $\alpha$ and~$\beta$
7298 will be at most $4\over3$. It follows that $B_k\G{5\over4}A_k$ and
7299 $C_k\G{5\over4}D_k$; hence the equations are diagonally dominant;
7300 hence they have a unique solution. Moreover, in most cases the tensions
7301 are equal to~1, so that $B_k=2A_k$ and $C_k=2D_k$. This makes the
7302 solution numerically stable, and there is an exponential damping
7303 effect: The data at knot $k\pm j$ affects the angle at knot~$k$ by
7304 a factor of~$O(2^{-j})$.
7306 @ However, we still must consider the angles at the starting and ending
7307 knots of a non-cyclic path. These angles might be given explicitly, or
7308 they might be specified implicitly in terms of an amount of ``curl.''
7310 Let's assume that angles need to be determined for a non-cyclic path
7311 starting at $z_0$ and ending at~$z_n$. Then equations of the form
7312 $$A_k\theta_{k-1}+(B_k+C_k)\theta_k+D_k\theta_{k+1}=R_k$$
7313 have been given for $0<k<n$, and it will be convenient to introduce
7314 equations of the same form for $k=0$ and $k=n$, where
7315 $$A_0=B_0=C_n=D_n=0.$$
7316 If $\theta_0$ is supposed to have a given value $E_0$, we simply
7317 define $C_0=0$, $D_0=0$, and $R_0=E_0$. Otherwise a curl
7318 parameter, $\gamma_0$, has been specified at~$z_0$; this means
7319 that the mock curvature at $z_0$ should be $\gamma_0$ times the
7320 mock curvature at $z_1$; i.e.,
7321 $${2\beta_1(\theta_0+\phi_1)-6\theta_0\over\alpha_0^2d_{01}}
7322 =\gamma_0{2\alpha_0(\theta_0+\phi_1)-6\phi_1\over\beta_1^2d_{01}}.$$
7323 This equation simplifies to
7324 $$(\alpha_0\chi_0+3-\beta_1)\theta_0+
7325 \bigl((3-\alpha_0)\chi_0+\beta_1\bigr)\theta_1=
7326 -\bigl((3-\alpha_0)\chi_0+\beta_1\bigr)\psi_1,$$
7327 where $\chi_0=\alpha_0^2\gamma_0/\beta_1^2$; so we can set $C_0=
7328 \chi_0\alpha_0+3-\beta_1$, $D_0=(3-\alpha_0)\chi_0+\beta_1$, $R_0=-D_0\psi_1$.
7329 It can be shown that $C_0>0$ and $C_0B_1-A_1D_0>0$ when $\gamma_0\G0$,
7330 hence the linear equations remain nonsingular.
7332 Similar considerations apply at the right end, when the final angle $\phi_n$
7333 may or may not need to be determined. It is convenient to let $\psi_n=0$,
7334 hence $\theta_n=-\phi_n$. We either have an explicit equation $\theta_n=E_n$,
7336 $$\bigl((3-\beta_n)\chi_n+\alpha_{n-1}\bigr)\theta_{n-1}+
7337 (\beta_n\chi_n+3-\alpha_{n-1})\theta_n=0,\qquad
7338 \chi_n={\beta_n^2\gamma_n\over\alpha_{n-1}^2}.$$
7340 When |make_choices| chooses angles, it must compute the coefficients of
7341 these linear equations, then solve the equations. To compute the coefficients,
7342 it is necessary to compute arctangents of the given turning angles~$\psi_k$.
7343 When the equations are solved, the chosen directions $\theta_k$ are put
7344 back into the form of control points by essentially computing sines and
7347 @ OK, we are ready to make the hard choices of |make_choices|.
7348 Most of the work is relegated to an auxiliary procedure
7349 called |solve_choices|, which has been introduced to keep
7350 |make_choices| from being extremely long.
7352 @<Fill in the control information between...@>=
7353 @<Calculate the turning angles $\psi_k$ and the distances $d_{k,k+1}$;
7354 set $n$ to the length of the path@>;
7355 @<Remove |open| types at the breakpoints@>;
7356 mp_solve_choices(mp, p,q,n)
7358 @ It's convenient to precompute quantities that will be needed several
7359 times later. The values of |delta_x[k]| and |delta_y[k]| will be the
7360 coordinates of $z\k-z_k$, and the magnitude of this vector will be
7361 |delta[k]=@t$d_{k,k+1}$@>|. The path angle $\psi_k$ between $z_k-z_{k-1}$
7362 and $z\k-z_k$ will be stored in |psi[k]|.
7365 int path_size; /* maximum number of knots between breakpoints of a path */
7368 scaled *delta; /* knot differences */
7369 angle *psi; /* turning angles */
7371 @ @<Allocate or initialize ...@>=
7377 @ @<Dealloc variables@>=
7383 @ @<Other local variables for |make_choices|@>=
7384 int k,n; /* current and final knot numbers */
7385 pointer s,t; /* registers for list traversal */
7386 scaled delx,dely; /* directions where |open| meets |explicit| */
7387 fraction sine,cosine; /* trig functions of various angles */
7389 @ @<Calculate the turning angles...@>=
7392 k=0; s=p; n=mp->path_size;
7395 mp->delta_x[k]=x_coord(t)-x_coord(s);
7396 mp->delta_y[k]=y_coord(t)-y_coord(s);
7397 mp->delta[k]=mp_pyth_add(mp, mp->delta_x[k],mp->delta_y[k]);
7399 sine=mp_make_fraction(mp, mp->delta_y[k-1],mp->delta[k-1]);
7400 cosine=mp_make_fraction(mp, mp->delta_x[k-1],mp->delta[k-1]);
7401 mp->psi[k]=mp_n_arg(mp, mp_take_fraction(mp, mp->delta_x[k],cosine)+
7402 mp_take_fraction(mp, mp->delta_y[k],sine),
7403 mp_take_fraction(mp, mp->delta_y[k],cosine)-
7404 mp_take_fraction(mp, mp->delta_x[k],sine));
7407 if ( k==mp->path_size ) {
7408 mp_reallocate_paths(mp, mp->path_size+(mp->path_size>>2));
7409 goto RESTART; /* retry, loop size has changed */
7412 } while (!((k>=n)&&(left_type(s)!=mp_end_cycle)));
7413 if ( k==n ) mp->psi[n]=0; else mp->psi[k]=mp->psi[1];
7416 @ When we get to this point of the code, |right_type(p)| is either
7417 |given| or |curl| or |open|. If it is |open|, we must have
7418 |left_type(p)=mp_end_cycle| or |left_type(p)=mp_explicit|. In the latter
7419 case, the |open| type is converted to |given|; however, if the
7420 velocity coming into this knot is zero, the |open| type is
7421 converted to a |curl|, since we don't know the incoming direction.
7423 Similarly, |left_type(q)| is either |given| or |curl| or |open| or
7424 |mp_end_cycle|. The |open| possibility is reduced either to |given| or to |curl|.
7426 @<Remove |open| types at the breakpoints@>=
7427 if ( left_type(q)==mp_open ) {
7428 delx=right_x(q)-x_coord(q); dely=right_y(q)-y_coord(q);
7429 if ( (delx==0)&&(dely==0) ) {
7430 left_type(q)=mp_curl; left_curl(q)=unity;
7432 left_type(q)=mp_given; left_given(q)=mp_n_arg(mp, delx,dely);
7435 if ( (right_type(p)==mp_open)&&(left_type(p)==mp_explicit) ) {
7436 delx=x_coord(p)-left_x(p); dely=y_coord(p)-left_y(p);
7437 if ( (delx==0)&&(dely==0) ) {
7438 right_type(p)=mp_curl; right_curl(p)=unity;
7440 right_type(p)=mp_given; right_given(p)=mp_n_arg(mp, delx,dely);
7444 @ Linear equations need to be solved whenever |n>1|; and also when |n=1|
7445 and exactly one of the breakpoints involves a curl. The simplest case occurs
7446 when |n=1| and there is a curl at both breakpoints; then we simply draw
7449 But before coding up the simple cases, we might as well face the general case,
7450 since we must deal with it sooner or later, and since the general case
7451 is likely to give some insight into the way simple cases can be handled best.
7453 When there is no cycle, the linear equations to be solved form a tridiagonal
7454 system, and we can apply the standard technique of Gaussian elimination
7455 to convert that system to a sequence of equations of the form
7456 $$\theta_0+u_0\theta_1=v_0,\quad
7457 \theta_1+u_1\theta_2=v_1,\quad\ldots,\quad
7458 \theta_{n-1}+u_{n-1}\theta_n=v_{n-1},\quad
7460 It is possible to do this diagonalization while generating the equations.
7461 Once $\theta_n$ is known, it is easy to determine $\theta_{n-1}$, \dots,
7462 $\theta_1$, $\theta_0$; thus, the equations will be solved.
7464 The procedure is slightly more complex when there is a cycle, but the
7465 basic idea will be nearly the same. In the cyclic case the right-hand
7466 sides will be $v_k+w_k\theta_0$ instead of simply $v_k$, and we will start
7467 the process off with $u_0=v_0=0$, $w_0=1$. The final equation will be not
7468 $\theta_n=v_n$ but $\theta_n+u_n\theta_1=v_n+w_n\theta_0$; an appropriate
7469 ending routine will take account of the fact that $\theta_n=\theta_0$ and
7470 eliminate the $w$'s from the system, after which the solution can be
7473 When $u_k$, $v_k$, and $w_k$ are being computed, the three pointer
7474 variables |r|, |s|,~|t| will point respectively to knots |k-1|, |k|,
7475 and~|k+1|. The $u$'s and $w$'s are scaled by $2^{28}$, i.e., they are
7476 of type |fraction|; the $\theta$'s and $v$'s are of type |angle|.
7479 angle *theta; /* values of $\theta_k$ */
7480 fraction *uu; /* values of $u_k$ */
7481 angle *vv; /* values of $v_k$ */
7482 fraction *ww; /* values of $w_k$ */
7484 @ @<Allocate or initialize ...@>=
7490 @ @<Dealloc variables@>=
7496 @ @<Declare |mp_reallocate| functions@>=
7497 void mp_reallocate_paths (MP mp, int l);
7500 void mp_reallocate_paths (MP mp, int l) {
7501 XREALLOC (mp->delta_x, l, scaled);
7502 XREALLOC (mp->delta_y, l, scaled);
7503 XREALLOC (mp->delta, l, scaled);
7504 XREALLOC (mp->psi, l, angle);
7505 XREALLOC (mp->theta, l, angle);
7506 XREALLOC (mp->uu, l, fraction);
7507 XREALLOC (mp->vv, l, angle);
7508 XREALLOC (mp->ww, l, fraction);
7512 @ Our immediate problem is to get the ball rolling by setting up the
7513 first equation or by realizing that no equations are needed, and to fit
7514 this initialization into a framework suitable for the overall computation.
7516 @<Declare the procedure called |solve_choices|@>=
7517 @<Declare subroutines needed by |solve_choices|@>;
7518 void mp_solve_choices (MP mp,pointer p, pointer q, halfword n) {
7519 int k; /* current knot number */
7520 pointer r,s,t; /* registers for list traversal */
7521 @<Other local variables for |solve_choices|@>;
7526 @<Get the linear equations started; or |return|
7527 with the control points in place, if linear equations
7530 switch (left_type(s)) {
7531 case mp_end_cycle: case mp_open:
7532 @<Set up equation to match mock curvatures
7533 at $z_k$; then |goto found| with $\theta_n$
7534 adjusted to equal $\theta_0$, if a cycle has ended@>;
7537 @<Set up equation for a curl at $\theta_n$
7541 @<Calculate the given value of $\theta_n$
7544 } /* there are no other cases */
7549 @<Finish choosing angles and assigning control points@>;
7552 @ On the first time through the loop, we have |k=0| and |r| is not yet
7553 defined. The first linear equation, if any, will have $A_0=B_0=0$.
7555 @<Get the linear equations started...@>=
7556 switch (right_type(s)) {
7558 if ( left_type(t)==mp_given ) {
7559 @<Reduce to simple case of two givens and |return|@>
7561 @<Set up the equation for a given value of $\theta_0$@>;
7565 if ( left_type(t)==mp_curl ) {
7566 @<Reduce to simple case of straight line and |return|@>
7568 @<Set up the equation for a curl at $\theta_0$@>;
7572 mp->uu[0]=0; mp->vv[0]=0; mp->ww[0]=fraction_one;
7573 /* this begins a cycle */
7575 } /* there are no other cases */
7577 @ The general equation that specifies equality of mock curvature at $z_k$ is
7578 $$A_k\theta_{k-1}+(B_k+C_k)\theta_k+D_k\theta\k=-B_k\psi_k-D_k\psi\k,$$
7579 as derived above. We want to combine this with the already-derived equation
7580 $\theta_{k-1}+u_{k-1}\theta_k=v_{k-1}+w_{k-1}\theta_0$ in order to obtain
7582 $\theta_k+u_k\theta\k=v_k+w_k\theta_0$. This can be done by dividing the
7584 $$(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}
7585 -A_kw_{k-1}\theta_0$$
7586 by $B_k-u_{k-1}A_k+C_k$. The trick is to do this carefully with
7587 fixed-point arithmetic, avoiding the chance of overflow while retaining
7590 The calculations will be performed in several registers that
7591 provide temporary storage for intermediate quantities.
7593 @<Other local variables for |solve_choices|@>=
7594 fraction aa,bb,cc,ff,acc; /* temporary registers */
7595 scaled dd,ee; /* likewise, but |scaled| */
7596 scaled lt,rt; /* tension values */
7598 @ @<Set up equation to match mock curvatures...@>=
7599 { @<Calculate the values $\\{aa}=A_k/B_k$, $\\{bb}=D_k/C_k$,
7600 $\\{dd}=(3-\alpha_{k-1})d_{k,k+1}$, $\\{ee}=(3-\beta\k)d_{k-1,k}$,
7601 and $\\{cc}=(B_k-u_{k-1}A_k)/B_k$@>;
7602 @<Calculate the ratio $\\{ff}=C_k/(C_k+B_k-u_{k-1}A_k)$@>;
7603 mp->uu[k]=mp_take_fraction(mp, ff,bb);
7604 @<Calculate the values of $v_k$ and $w_k$@>;
7605 if ( left_type(s)==mp_end_cycle ) {
7606 @<Adjust $\theta_n$ to equal $\theta_0$ and |goto found|@>;
7610 @ Since tension values are never less than 3/4, the values |aa| and
7611 |bb| computed here are never more than 4/5.
7613 @<Calculate the values $\\{aa}=...@>=
7614 if ( abs(right_tension(r))==unity) {
7615 aa=fraction_half; dd=2*mp->delta[k];
7617 aa=mp_make_fraction(mp, unity,3*abs(right_tension(r))-unity);
7618 dd=mp_take_fraction(mp, mp->delta[k],
7619 fraction_three-mp_make_fraction(mp, unity,abs(right_tension(r))));
7621 if ( abs(left_tension(t))==unity ){
7622 bb=fraction_half; ee=2*mp->delta[k-1];
7624 bb=mp_make_fraction(mp, unity,3*abs(left_tension(t))-unity);
7625 ee=mp_take_fraction(mp, mp->delta[k-1],
7626 fraction_three-mp_make_fraction(mp, unity,abs(left_tension(t))));
7628 cc=fraction_one-mp_take_fraction(mp, mp->uu[k-1],aa)
7630 @ The ratio to be calculated in this step can be written in the form
7631 $$\beta_k^2\cdot\\{ee}\over\beta_k^2\cdot\\{ee}+\alpha_k^2\cdot
7632 \\{cc}\cdot\\{dd},$$
7633 because of the quantities just calculated. The values of |dd| and |ee|
7634 will not be needed after this step has been performed.
7636 @<Calculate the ratio $\\{ff}=C_k/(C_k+B_k-u_{k-1}A_k)$@>=
7637 dd=mp_take_fraction(mp, dd,cc); lt=abs(left_tension(s)); rt=abs(right_tension(s));
7638 if ( lt!=rt ) { /* $\beta_k^{-1}\ne\alpha_k^{-1}$ */
7640 ff=mp_make_fraction(mp, lt,rt);
7641 ff=mp_take_fraction(mp, ff,ff); /* $\alpha_k^2/\beta_k^2$ */
7642 dd=mp_take_fraction(mp, dd,ff);
7644 ff=mp_make_fraction(mp, rt,lt);
7645 ff=mp_take_fraction(mp, ff,ff); /* $\beta_k^2/\alpha_k^2$ */
7646 ee=mp_take_fraction(mp, ee,ff);
7649 ff=mp_make_fraction(mp, ee,ee+dd)
7651 @ The value of $u_{k-1}$ will be |<=1| except when $k=1$ and the previous
7652 equation was specified by a curl. In that case we must use a special
7653 method of computation to prevent overflow.
7655 Fortunately, the calculations turn out to be even simpler in this ``hard''
7656 case. The curl equation makes $w_0=0$ and $v_0=-u_0\psi_1$, hence
7657 $-B_1\psi_1-A_1v_0=-(B_1-u_0A_1)\psi_1=-\\{cc}\cdot B_1\psi_1$.
7659 @<Calculate the values of $v_k$ and $w_k$@>=
7660 acc=-mp_take_fraction(mp, mp->psi[k+1],mp->uu[k]);
7661 if ( right_type(r)==mp_curl ) {
7663 mp->vv[k]=acc-mp_take_fraction(mp, mp->psi[1],fraction_one-ff);
7665 ff=mp_make_fraction(mp, fraction_one-ff,cc); /* this is
7666 $B_k/(C_k+B_k-u_{k-1}A_k)<5$ */
7667 acc=acc-mp_take_fraction(mp, mp->psi[k],ff);
7668 ff=mp_take_fraction(mp, ff,aa); /* this is $A_k/(C_k+B_k-u_{k-1}A_k)$ */
7669 mp->vv[k]=acc-mp_take_fraction(mp, mp->vv[k-1],ff);
7670 if ( mp->ww[k-1]==0 ) mp->ww[k]=0;
7671 else mp->ww[k]=-mp_take_fraction(mp, mp->ww[k-1],ff);
7674 @ When a complete cycle has been traversed, we have $\theta_k+u_k\theta\k=
7675 v_k+w_k\theta_0$, for |1<=k<=n|. We would like to determine the value of
7676 $\theta_n$ and reduce the system to the form $\theta_k+u_k\theta\k=v_k$
7677 for |0<=k<n|, so that the cyclic case can be finished up just as if there
7680 The idea in the following code is to observe that
7681 $$\eqalign{\theta_n&=v_n+w_n\theta_0-u_n\theta_1=\cdots\cr
7682 &=v_n+w_n\theta_0-u_n\bigl(v_1+w_1\theta_0-u_1(v_2+\cdots
7683 -u_{n-2}(v_{n-1}+w_{n-1}\theta_0-u_{n-1}\theta_0))\bigr),\cr}$$
7684 so we can solve for $\theta_n=\theta_0$.
7686 @<Adjust $\theta_n$ to equal $\theta_0$ and |goto found|@>=
7688 aa=0; bb=fraction_one; /* we have |k=n| */
7691 aa=mp->vv[k]-mp_take_fraction(mp, aa,mp->uu[k]);
7692 bb=mp->ww[k]-mp_take_fraction(mp, bb,mp->uu[k]);
7693 } while (k!=n); /* now $\theta_n=\\{aa}+\\{bb}\cdot\theta_n$ */
7694 aa=mp_make_fraction(mp, aa,fraction_one-bb);
7695 mp->theta[n]=aa; mp->vv[0]=aa;
7696 for (k=1;k<=n-1;k++) {
7697 mp->vv[k]=mp->vv[k]+mp_take_fraction(mp, aa,mp->ww[k]);
7702 @ @d reduce_angle(A) if ( abs((A))>one_eighty_deg ) {
7703 if ( (A)>0 ) (A)=(A)-three_sixty_deg; else (A)=(A)+three_sixty_deg; }
7705 @<Calculate the given value of $\theta_n$...@>=
7707 mp->theta[n]=left_given(s)-mp_n_arg(mp, mp->delta_x[n-1],mp->delta_y[n-1]);
7708 reduce_angle(mp->theta[n]);
7712 @ @<Set up the equation for a given value of $\theta_0$@>=
7714 mp->vv[0]=right_given(s)-mp_n_arg(mp, mp->delta_x[0],mp->delta_y[0]);
7715 reduce_angle(mp->vv[0]);
7716 mp->uu[0]=0; mp->ww[0]=0;
7719 @ @<Set up the equation for a curl at $\theta_0$@>=
7720 { cc=right_curl(s); lt=abs(left_tension(t)); rt=abs(right_tension(s));
7721 if ( (rt==unity)&&(lt==unity) )
7722 mp->uu[0]=mp_make_fraction(mp, cc+cc+unity,cc+two);
7724 mp->uu[0]=mp_curl_ratio(mp, cc,rt,lt);
7725 mp->vv[0]=-mp_take_fraction(mp, mp->psi[1],mp->uu[0]); mp->ww[0]=0;
7728 @ @<Set up equation for a curl at $\theta_n$...@>=
7729 { cc=left_curl(s); lt=abs(left_tension(s)); rt=abs(right_tension(r));
7730 if ( (rt==unity)&&(lt==unity) )
7731 ff=mp_make_fraction(mp, cc+cc+unity,cc+two);
7733 ff=mp_curl_ratio(mp, cc,lt,rt);
7734 mp->theta[n]=-mp_make_fraction(mp, mp_take_fraction(mp, mp->vv[n-1],ff),
7735 fraction_one-mp_take_fraction(mp, ff,mp->uu[n-1]));
7739 @ The |curl_ratio| subroutine has three arguments, which our previous notation
7740 encourages us to call $\gamma$, $\alpha^{-1}$, and $\beta^{-1}$. It is
7741 a somewhat tedious program to calculate
7742 $${(3-\alpha)\alpha^2\gamma+\beta^3\over
7743 \alpha^3\gamma+(3-\beta)\beta^2},$$
7744 with the result reduced to 4 if it exceeds 4. (This reduction of curl
7745 is necessary only if the curl and tension are both large.)
7746 The values of $\alpha$ and $\beta$ will be at most~4/3.
7748 @<Declare subroutines needed by |solve_choices|@>=
7749 fraction mp_curl_ratio (MP mp,scaled gamma, scaled a_tension,
7751 fraction alpha,beta,num,denom,ff; /* registers */
7752 alpha=mp_make_fraction(mp, unity,a_tension);
7753 beta=mp_make_fraction(mp, unity,b_tension);
7754 if ( alpha<=beta ) {
7755 ff=mp_make_fraction(mp, alpha,beta); ff=mp_take_fraction(mp, ff,ff);
7756 gamma=mp_take_fraction(mp, gamma,ff);
7757 beta=beta / 010000; /* convert |fraction| to |scaled| */
7758 denom=mp_take_fraction(mp, gamma,alpha)+three-beta;
7759 num=mp_take_fraction(mp, gamma,fraction_three-alpha)+beta;
7761 ff=mp_make_fraction(mp, beta,alpha); ff=mp_take_fraction(mp, ff,ff);
7762 beta=mp_take_fraction(mp, beta,ff) / 010000; /* convert |fraction| to |scaled| */
7763 denom=mp_take_fraction(mp, gamma,alpha)+(ff / 1365)-beta;
7764 /* $1365\approx 2^{12}/3$ */
7765 num=mp_take_fraction(mp, gamma,fraction_three-alpha)+beta;
7767 if ( num>=denom+denom+denom+denom ) return fraction_four;
7768 else return mp_make_fraction(mp, num,denom);
7771 @ We're in the home stretch now.
7773 @<Finish choosing angles and assigning control points@>=
7774 for (k=n-1;k>=0;k--) {
7775 mp->theta[k]=mp->vv[k]-mp_take_fraction(mp,mp->theta[k+1],mp->uu[k]);
7780 mp_n_sin_cos(mp, mp->theta[k]); mp->st=mp->n_sin; mp->ct=mp->n_cos;
7781 mp_n_sin_cos(mp, -mp->psi[k+1]-mp->theta[k+1]); mp->sf=mp->n_sin; mp->cf=mp->n_cos;
7782 mp_set_controls(mp, s,t,k);
7786 @ The |set_controls| routine actually puts the control points into
7787 a pair of consecutive nodes |p| and~|q|. Global variables are used to
7788 record the values of $\sin\theta$, $\cos\theta$, $\sin\phi$, and
7789 $\cos\phi$ needed in this calculation.
7795 fraction cf; /* sines and cosines */
7797 @ @<Declare subroutines needed by |solve_choices|@>=
7798 void mp_set_controls (MP mp,pointer p, pointer q, integer k) {
7799 fraction rr,ss; /* velocities, divided by thrice the tension */
7800 scaled lt,rt; /* tensions */
7801 fraction sine; /* $\sin(\theta+\phi)$ */
7802 lt=abs(left_tension(q)); rt=abs(right_tension(p));
7803 rr=mp_velocity(mp, mp->st,mp->ct,mp->sf,mp->cf,rt);
7804 ss=mp_velocity(mp, mp->sf,mp->cf,mp->st,mp->ct,lt);
7805 if ( (right_tension(p)<0)||(left_tension(q)<0) ) {
7806 @<Decrease the velocities,
7807 if necessary, to stay inside the bounding triangle@>;
7809 right_x(p)=x_coord(p)+mp_take_fraction(mp,
7810 mp_take_fraction(mp, mp->delta_x[k],mp->ct)-
7811 mp_take_fraction(mp, mp->delta_y[k],mp->st),rr);
7812 right_y(p)=y_coord(p)+mp_take_fraction(mp,
7813 mp_take_fraction(mp, mp->delta_y[k],mp->ct)+
7814 mp_take_fraction(mp, mp->delta_x[k],mp->st),rr);
7815 left_x(q)=x_coord(q)-mp_take_fraction(mp,
7816 mp_take_fraction(mp, mp->delta_x[k],mp->cf)+
7817 mp_take_fraction(mp, mp->delta_y[k],mp->sf),ss);
7818 left_y(q)=y_coord(q)-mp_take_fraction(mp,
7819 mp_take_fraction(mp, mp->delta_y[k],mp->cf)-
7820 mp_take_fraction(mp, mp->delta_x[k],mp->sf),ss);
7821 right_type(p)=mp_explicit; left_type(q)=mp_explicit;
7824 @ The boundedness conditions $\\{rr}\L\sin\phi\,/\sin(\theta+\phi)$ and
7825 $\\{ss}\L\sin\theta\,/\sin(\theta+\phi)$ are to be enforced if $\sin\theta$,
7826 $\sin\phi$, and $\sin(\theta+\phi)$ all have the same sign. Otherwise
7827 there is no ``bounding triangle.''
7828 @:at_least_}{\&{atleast} primitive@>
7830 @<Decrease the velocities, if necessary...@>=
7831 if (((mp->st>=0)&&(mp->sf>=0))||((mp->st<=0)&&(mp->sf<=0)) ) {
7832 sine=mp_take_fraction(mp, abs(mp->st),mp->cf)+
7833 mp_take_fraction(mp, abs(mp->sf),mp->ct);
7835 sine=mp_take_fraction(mp, sine,fraction_one+unity); /* safety factor */
7836 if ( right_tension(p)<0 )
7837 if ( mp_ab_vs_cd(mp, abs(mp->sf),fraction_one,rr,sine)<0 )
7838 rr=mp_make_fraction(mp, abs(mp->sf),sine);
7839 if ( left_tension(q)<0 )
7840 if ( mp_ab_vs_cd(mp, abs(mp->st),fraction_one,ss,sine)<0 )
7841 ss=mp_make_fraction(mp, abs(mp->st),sine);
7845 @ Only the simple cases remain to be handled.
7847 @<Reduce to simple case of two givens and |return|@>=
7849 aa=mp_n_arg(mp, mp->delta_x[0],mp->delta_y[0]);
7850 mp_n_sin_cos(mp, right_given(p)-aa); mp->ct=mp->n_cos; mp->st=mp->n_sin;
7851 mp_n_sin_cos(mp, left_given(q)-aa); mp->cf=mp->n_cos; mp->sf=-mp->n_sin;
7852 mp_set_controls(mp, p,q,0); return;
7855 @ @<Reduce to simple case of straight line and |return|@>=
7857 right_type(p)=mp_explicit; left_type(q)=mp_explicit;
7858 lt=abs(left_tension(q)); rt=abs(right_tension(p));
7860 if ( mp->delta_x[0]>=0 ) right_x(p)=x_coord(p)+((mp->delta_x[0]+1) / 3);
7861 else right_x(p)=x_coord(p)+((mp->delta_x[0]-1) / 3);
7862 if ( mp->delta_y[0]>=0 ) right_y(p)=y_coord(p)+((mp->delta_y[0]+1) / 3);
7863 else right_y(p)=y_coord(p)+((mp->delta_y[0]-1) / 3);
7865 ff=mp_make_fraction(mp, unity,3*rt); /* $\alpha/3$ */
7866 right_x(p)=x_coord(p)+mp_take_fraction(mp, mp->delta_x[0],ff);
7867 right_y(p)=y_coord(p)+mp_take_fraction(mp, mp->delta_y[0],ff);
7870 if ( mp->delta_x[0]>=0 ) left_x(q)=x_coord(q)-((mp->delta_x[0]+1) / 3);
7871 else left_x(q)=x_coord(q)-((mp->delta_x[0]-1) / 3);
7872 if ( mp->delta_y[0]>=0 ) left_y(q)=y_coord(q)-((mp->delta_y[0]+1) / 3);
7873 else left_y(q)=y_coord(q)-((mp->delta_y[0]-1) / 3);
7875 ff=mp_make_fraction(mp, unity,3*lt); /* $\beta/3$ */
7876 left_x(q)=x_coord(q)-mp_take_fraction(mp, mp->delta_x[0],ff);
7877 left_y(q)=y_coord(q)-mp_take_fraction(mp, mp->delta_y[0],ff);
7882 @* \[19] Measuring paths.
7883 \MP's \&{llcorner}, \&{lrcorner}, \&{ulcorner}, and \&{urcorner} operators
7884 allow the user to measure the bounding box of anything that can go into a
7885 picture. It's easy to get rough bounds on the $x$ and $y$ extent of a path
7886 by just finding the bounding box of the knots and the control points. We
7887 need a more accurate version of the bounding box, but we can still use the
7888 easy estimate to save time by focusing on the interesting parts of the path.
7890 @ Computing an accurate bounding box involves a theme that will come up again
7891 and again. Given a Bernshte{\u\i}n polynomial
7892 @^Bernshte{\u\i}n, Serge{\u\i} Natanovich@>
7893 $$B(z_0,z_1,\ldots,z_n;t)=\sum_k{n\choose k}t^k(1-t)^{n-k}z_k,$$
7894 we can conveniently bisect its range as follows:
7897 \textindent{1)} Let $z_k^{(0)}=z_k$, for |0<=k<=n|.
7900 \textindent{2)} Let $z_k^{(j+1)}={1\over2}(z_k^{(j)}+z\k^{(j)})$, for
7901 |0<=k<n-j|, for |0<=j<n|.
7905 $$B(z_0,z_1,\ldots,z_n;t)=B(z_0^{(0)},z_0^{(1)},\ldots,z_0^{(n)};2t)
7906 =B(z_0^{(n)},z_1^{(n-1)},\ldots,z_n^{(0)};2t-1).$$
7907 This formula gives us the coefficients of polynomials to use over the ranges
7908 $0\L t\L{1\over2}$ and ${1\over2}\L t\L1$.
7910 @ Now here's a subroutine that's handy for all sorts of path computations:
7911 Given a quadratic polynomial $B(a,b,c;t)$, the |crossing_point| function
7912 returns the unique |fraction| value |t| between 0 and~1 at which
7913 $B(a,b,c;t)$ changes from positive to negative, or returns
7914 |t=fraction_one+1| if no such value exists. If |a<0| (so that $B(a,b,c;t)$
7915 is already negative at |t=0|), |crossing_point| returns the value zero.
7917 @d no_crossing { return (fraction_one+1); }
7918 @d one_crossing { return fraction_one; }
7919 @d zero_crossing { return 0; }
7920 @d mp_crossing_point(M,A,B,C) mp_do_crossing_point(A,B,C)
7922 @c fraction mp_do_crossing_point (integer a, integer b, integer c) {
7923 integer d; /* recursive counter */
7924 integer x,xx,x0,x1,x2; /* temporary registers for bisection */
7925 if ( a<0 ) zero_crossing;
7928 if ( c>0 ) { no_crossing; }
7929 else if ( (a==0)&&(b==0) ) { no_crossing;}
7930 else { one_crossing; }
7932 if ( a==0 ) zero_crossing;
7933 } else if ( a==0 ) {
7934 if ( b<=0 ) zero_crossing;
7936 @<Use bisection to find the crossing point, if one exists@>;
7939 @ The general bisection method is quite simple when $n=2$, hence
7940 |crossing_point| does not take much time. At each stage in the
7941 recursion we have a subinterval defined by |l| and~|j| such that
7942 $B(a,b,c;2^{-l}(j+t))=B(x_0,x_1,x_2;t)$, and we want to ``zero in'' on
7943 the subinterval where $x_0\G0$ and $\min(x_1,x_2)<0$.
7945 It is convenient for purposes of calculation to combine the values
7946 of |l| and~|j| in a single variable $d=2^l+j$, because the operation
7947 of bisection then corresponds simply to doubling $d$ and possibly
7948 adding~1. Furthermore it proves to be convenient to modify
7949 our previous conventions for bisection slightly, maintaining the
7950 variables $X_0=2^lx_0$, $X_1=2^l(x_0-x_1)$, and $X_2=2^l(x_1-x_2)$.
7951 With these variables the conditions $x_0\ge0$ and $\min(x_1,x_2)<0$ are
7952 equivalent to $\max(X_1,X_1+X_2)>X_0\ge0$.
7954 The following code maintains the invariant relations
7955 $0\L|x0|<\max(|x1|,|x1|+|x2|)$,
7956 $\vert|x1|\vert<2^{30}$, $\vert|x2|\vert<2^{30}$;
7957 it has been constructed in such a way that no arithmetic overflow
7958 will occur if the inputs satisfy
7959 $a<2^{30}$, $\vert a-b\vert<2^{30}$, and $\vert b-c\vert<2^{30}$.
7961 @<Use bisection to find the crossing point...@>=
7962 d=1; x0=a; x1=a-b; x2=b-c;
7973 if ( x<=x0 ) { if ( x+x2<=x0 ) no_crossing; }
7977 } while (d<fraction_one);
7978 return (d-fraction_one)
7980 @ Here is a routine that computes the $x$ or $y$ coordinate of the point on
7981 a cubic corresponding to the |fraction| value~|t|.
7983 It is convenient to define a \.{WEB} macro |t_of_the_way| such that
7984 |t_of_the_way(a,b)| expands to |a-(a-b)*t|, i.e., to |t[a,b]|.
7986 @d t_of_the_way(A,B) ((A)-mp_take_fraction(mp,((A)-(B)),t))
7988 @c scaled mp_eval_cubic (MP mp,pointer p, pointer q, fraction t) {
7989 scaled x1,x2,x3; /* intermediate values */
7990 x1=t_of_the_way(knot_coord(p),right_coord(p));
7991 x2=t_of_the_way(right_coord(p),left_coord(q));
7992 x3=t_of_the_way(left_coord(q),knot_coord(q));
7993 x1=t_of_the_way(x1,x2);
7994 x2=t_of_the_way(x2,x3);
7995 return t_of_the_way(x1,x2);
7998 @ The actual bounding box information is stored in global variables.
7999 Since it is convenient to address the $x$ and $y$ information
8000 separately, we define arrays indexed by |x_code..y_code| and use
8001 macros to give them more convenient names.
8005 mp_x_code=0, /* index for |minx| and |maxx| */
8006 mp_y_code /* index for |miny| and |maxy| */
8010 @d minx mp->bbmin[mp_x_code]
8011 @d maxx mp->bbmax[mp_x_code]
8012 @d miny mp->bbmin[mp_y_code]
8013 @d maxy mp->bbmax[mp_y_code]
8016 scaled bbmin[mp_y_code+1];
8017 scaled bbmax[mp_y_code+1];
8018 /* the result of procedures that compute bounding box information */
8020 @ Now we're ready for the key part of the bounding box computation.
8021 The |bound_cubic| procedure updates |bbmin[c]| and |bbmax[c]| based on
8022 $$B(\hbox{|knot_coord(p)|}, \hbox{|right_coord(p)|},
8023 \hbox{|left_coord(q)|}, \hbox{|knot_coord(q)|};t)
8025 for $0<t\le1$. In other words, the procedure adjusts the bounds to
8026 accommodate |knot_coord(q)| and any extremes over the range $0<t<1$.
8027 The |c| parameter is |x_code| or |y_code|.
8029 @c void mp_bound_cubic (MP mp,pointer p, pointer q, small_number c) {
8030 boolean wavy; /* whether we need to look for extremes */
8031 scaled del1,del2,del3,del,dmax; /* proportional to the control
8032 points of a quadratic derived from a cubic */
8033 fraction t,tt; /* where a quadratic crosses zero */
8034 scaled x; /* a value that |bbmin[c]| and |bbmax[c]| must accommodate */
8036 @<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>;
8037 @<Check the control points against the bounding box and set |wavy:=true|
8038 if any of them lie outside@>;
8040 del1=right_coord(p)-knot_coord(p);
8041 del2=left_coord(q)-right_coord(p);
8042 del3=knot_coord(q)-left_coord(q);
8043 @<Scale up |del1|, |del2|, and |del3| for greater accuracy;
8044 also set |del| to the first nonzero element of |(del1,del2,del3)|@>;
8046 negate(del1); negate(del2); negate(del3);
8048 t=mp_crossing_point(mp, del1,del2,del3);
8049 if ( t<fraction_one ) {
8050 @<Test the extremes of the cubic against the bounding box@>;
8055 @ @<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>=
8056 if ( x<mp->bbmin[c] ) mp->bbmin[c]=x;
8057 if ( x>mp->bbmax[c] ) mp->bbmax[c]=x
8059 @ @<Check the control points against the bounding box and set...@>=
8061 if ( mp->bbmin[c]<=right_coord(p) )
8062 if ( right_coord(p)<=mp->bbmax[c] )
8063 if ( mp->bbmin[c]<=left_coord(q) )
8064 if ( left_coord(q)<=mp->bbmax[c] )
8067 @ If |del1=del2=del3=0|, it's impossible to obey the title of this
8068 section. We just set |del=0| in that case.
8070 @<Scale up |del1|, |del2|, and |del3| for greater accuracy...@>=
8071 if ( del1!=0 ) del=del1;
8072 else if ( del2!=0 ) del=del2;
8076 if ( abs(del2)>dmax ) dmax=abs(del2);
8077 if ( abs(del3)>dmax ) dmax=abs(del3);
8078 while ( dmax<fraction_half ) {
8079 dmax+=dmax; del1+=del1; del2+=del2; del3+=del3;
8083 @ Since |crossing_point| has tried to choose |t| so that
8084 $B(|del1|,|del2|,|del3|;\tau)$ crosses zero at $\tau=|t|$ with negative
8085 slope, the value of |del2| computed below should not be positive.
8086 But rounding error could make it slightly positive in which case we
8087 must cut it to zero to avoid confusion.
8089 @<Test the extremes of the cubic against the bounding box@>=
8091 x=mp_eval_cubic(mp, p,q,t);
8092 @<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>;
8093 del2=t_of_the_way(del2,del3);
8094 /* now |0,del2,del3| represent the derivative on the remaining interval */
8095 if ( del2>0 ) del2=0;
8096 tt=mp_crossing_point(mp, 0,-del2,-del3);
8097 if ( tt<fraction_one ) {
8098 @<Test the second extreme against the bounding box@>;
8102 @ @<Test the second extreme against the bounding box@>=
8104 x=mp_eval_cubic(mp, p,q,t_of_the_way(tt,fraction_one));
8105 @<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>;
8108 @ Finding the bounding box of a path is basically a matter of applying
8109 |bound_cubic| twice for each pair of adjacent knots.
8111 @c void mp_path_bbox (MP mp,pointer h) {
8112 pointer p,q; /* a pair of adjacent knots */
8113 minx=x_coord(h); miny=y_coord(h);
8114 maxx=minx; maxy=miny;
8117 if ( right_type(p)==mp_endpoint ) return;
8119 mp_bound_cubic(mp, x_loc(p),x_loc(q),mp_x_code);
8120 mp_bound_cubic(mp, y_loc(p),y_loc(q),mp_y_code);
8125 @ Another important way to measure a path is to find its arc length. This
8126 is best done by using the general bisection algorithm to subdivide the path
8127 until obtaining ``well behaved'' subpaths whose arc lengths can be approximated
8130 Since the arc length is the integral with respect to time of the magnitude of
8131 the velocity, it is natural to use Simpson's rule for the approximation.
8133 If $\dot B(t)$ is the spline velocity, Simpson's rule gives
8134 $$ \vb\dot B(0)\vb + 4\vb\dot B({1\over2})\vb + \vb\dot B(1)\vb \over 6 $$
8135 for the arc length of a path of length~1. For a cubic spline
8136 $B(z_0,z_1,z_2,z_3;t)$, the time derivative $\dot B(t)$ is
8137 $3B(dz_0,dz_1,dz_2;t)$, where $dz_i=z_{i+1}-z_i$. Hence the arc length
8139 $$ {\vb dz_0\vb \over 2} + 2\vb dz_{02}\vb + {\vb dz_2\vb \over 2}, $$
8141 $$ dz_{02}={1\over2}\left({dz_0+dz_1\over 2}+{dz_1+dz_2\over 2}\right)$$
8142 is the result of the bisection algorithm.
8144 @ The remaining problem is how to decide when a subpath is ``well behaved.''
8145 This could be done via the theoretical error bound for Simpson's rule,
8147 but this is impractical because it requires an estimate of the fourth
8148 derivative of the quantity being integrated. It is much easier to just perform
8149 a bisection step and see how much the arc length estimate changes. Since the
8150 error for Simpson's rule is proportional to the fourth power of the sample
8151 spacing, the remaining error is typically about $1\over16$ of the amount of
8152 the change. We say ``typically'' because the error has a pseudo-random behavior
8153 that could cause the two estimates to agree when each contain large errors.
8155 To protect against disasters such as undetected cusps, the bisection process
8156 should always continue until all the $dz_i$ vectors belong to a single
8157 $90^\circ$ sector. This ensures that no point on the spline can have velocity
8158 less than 70\% of the minimum of $\vb dz_0\vb$, $\vb dz_1\vb$ and $\vb dz_2\vb$.
8159 If such a spline happens to produce an erroneous arc length estimate that
8160 is little changed by bisection, the amount of the error is likely to be fairly
8161 small. We will try to arrange things so that freak accidents of this type do
8162 not destroy the inverse relationship between the \&{arclength} and
8163 \&{arctime} operations.
8164 @:arclength_}{\&{arclength} primitive@>
8165 @:arctime_}{\&{arctime} primitive@>
8167 @ The \&{arclength} and \&{arctime} operations are both based on a recursive
8169 function that finds the arc length of a cubic spline given $dz_0$, $dz_1$,
8170 $dz_2$. This |arc_test| routine also takes an arc length goal |a_goal| and
8171 returns the time when the arc length reaches |a_goal| if there is such a time.
8172 Thus the return value is either an arc length less than |a_goal| or, if the
8173 arc length would be at least |a_goal|, it returns a time value decreased by
8174 |two|. This allows the caller to use the sign of the result to distinguish
8175 between arc lengths and time values. On certain types of overflow, it is
8176 possible for |a_goal| and the result of |arc_test| both to be |el_gordo|.
8177 Otherwise, the result is always less than |a_goal|.
8179 Rather than halving the control point coordinates on each recursive call to
8180 |arc_test|, it is better to keep them proportional to velocity on the original
8181 curve and halve the results instead. This means that recursive calls can
8182 potentially use larger error tolerances in their arc length estimates. How
8183 much larger depends on to what extent the errors behave as though they are
8184 independent of each other. To save computing time, we use optimistic assumptions
8185 and increase the tolerance by a factor of about $\sqrt2$ for each recursive
8188 In addition to the tolerance parameter, |arc_test| should also have parameters
8189 for ${1\over3}\vb\dot B(0)\vb$, ${2\over3}\vb\dot B({1\over2})\vb$, and
8190 ${1\over3}\vb\dot B(1)\vb$. These quantities are relatively expensive to compute
8191 and they are needed in different instances of |arc_test|.
8193 @c @t\4@>@<Declare subroutines needed by |arc_test|@>;
8194 scaled mp_arc_test (MP mp, scaled dx0, scaled dy0, scaled dx1, scaled dy1,
8195 scaled dx2, scaled dy2, scaled v0, scaled v02,
8196 scaled v2, scaled a_goal, scaled tol) {
8197 boolean simple; /* are the control points confined to a $90^\circ$ sector? */
8198 scaled dx01, dy01, dx12, dy12, dx02, dy02; /* bisection results */
8200 /* twice the velocity magnitudes at $t={1\over4}$ and $t={3\over4}$ */
8201 scaled arc; /* best arc length estimate before recursion */
8202 @<Other local variables in |arc_test|@>;
8203 @<Bisect the B\'ezier quadratic given by |dx0|, |dy0|, |dx1|, |dy1|,
8205 @<Initialize |v002|, |v022|, and the arc length estimate |arc|; if it overflows
8206 set |arc_test| and |return|@>;
8207 @<Test if the control points are confined to one quadrant or rotating them
8208 $45^\circ$ would put them in one quadrant. Then set |simple| appropriately@>;
8209 if ( simple && (abs(arc-v02-halfp(v0+v2)) <= tol) ) {
8210 if ( arc < a_goal ) {
8213 @<Estimate when the arc length reaches |a_goal| and set |arc_test| to
8214 that time minus |two|@>;
8217 @<Use one or two recursive calls to compute the |arc_test| function@>;
8221 @ The |tol| value should by multiplied by $\sqrt 2$ before making recursive
8222 calls, but $1.5$ is an adequate approximation. It is best to avoid using
8223 |make_fraction| in this inner loop.
8226 @<Use one or two recursive calls to compute the |arc_test| function@>=
8228 @<Set |a_new| and |a_aux| so their sum is |2*a_goal| and |a_new| is as
8229 large as possible@>;
8230 tol = tol + halfp(tol);
8231 a = mp_arc_test(mp, dx0,dy0, dx01,dy01, dx02,dy02, v0, v002,
8232 halfp(v02), a_new, tol);
8234 return (-halfp(two-a));
8236 @<Update |a_new| to reduce |a_new+a_aux| by |a|@>;
8237 b = mp_arc_test(mp, dx02,dy02, dx12,dy12, dx2,dy2,
8238 halfp(v02), v022, v2, a_new, tol);
8240 return (-halfp(-b) - half_unit);
8242 return (a + half(b-a));
8246 @ @<Other local variables in |arc_test|@>=
8247 scaled a,b; /* results of recursive calls */
8248 scaled a_new,a_aux; /* the sum of these gives the |a_goal| */
8250 @ @<Set |a_new| and |a_aux| so their sum is |2*a_goal| and |a_new| is...@>=
8251 a_aux = el_gordo - a_goal;
8252 if ( a_goal > a_aux ) {
8253 a_aux = a_goal - a_aux;
8256 a_new = a_goal + a_goal;
8260 @ There is no need to maintain |a_aux| at this point so we use it as a temporary
8261 to force the additions and subtractions to be done in an order that avoids
8264 @<Update |a_new| to reduce |a_new+a_aux| by |a|@>=
8267 a_new = a_new + a_aux;
8270 @ This code assumes all {\it dx} and {\it dy} variables have magnitude less than
8271 |fraction_four|. To simplify the rest of the |arc_test| routine, we strengthen
8272 this assumption by requiring the norm of each $({\it dx},{\it dy})$ pair to obey
8273 this bound. Note that recursive calls will maintain this invariant.
8275 @<Bisect the B\'ezier quadratic given by |dx0|, |dy0|, |dx1|, |dy1|,...@>=
8276 dx01 = half(dx0 + dx1);
8277 dx12 = half(dx1 + dx2);
8278 dx02 = half(dx01 + dx12);
8279 dy01 = half(dy0 + dy1);
8280 dy12 = half(dy1 + dy2);
8281 dy02 = half(dy01 + dy12)
8283 @ We should be careful to keep |arc<el_gordo| so that calling |arc_test| with
8284 |a_goal=el_gordo| is guaranteed to yield the arc length.
8286 @<Initialize |v002|, |v022|, and the arc length estimate |arc|;...@>=
8287 v002 = mp_pyth_add(mp, dx01+half(dx0+dx02), dy01+half(dy0+dy02));
8288 v022 = mp_pyth_add(mp, dx12+half(dx02+dx2), dy12+half(dy02+dy2));
8290 arc1 = v002 + half(halfp(v0+tmp) - v002);
8291 arc = v022 + half(halfp(v2+tmp) - v022);
8292 if ( (arc < el_gordo-arc1) ) {
8295 mp->arith_error = true;
8296 if ( a_goal==el_gordo ) return (el_gordo);
8300 @ @<Other local variables in |arc_test|@>=
8301 scaled tmp, tmp2; /* all purpose temporary registers */
8302 scaled arc1; /* arc length estimate for the first half */
8304 @ @<Test if the control points are confined to one quadrant or rotating...@>=
8305 simple = ((dx0>=0) && (dx1>=0) && (dx2>=0)) ||
8306 ((dx0<=0) && (dx1<=0) && (dx2<=0));
8308 simple = ((dy0>=0) && (dy1>=0) && (dy2>=0)) ||
8309 ((dy0<=0) && (dy1<=0) && (dy2<=0));
8311 simple = ((dx0>=dy0) && (dx1>=dy1) && (dx2>=dy2)) ||
8312 ((dx0<=dy0) && (dx1<=dy1) && (dx2<=dy2));
8314 simple = ((-dx0>=dy0) && (-dx1>=dy1) && (-dx2>=dy2)) ||
8315 ((-dx0<=dy0) && (-dx1<=dy1) && (-dx2<=dy2));
8318 @ Since Simpson's rule is based on approximating the integrand by a parabola,
8320 it is appropriate to use the same approximation to decide when the integral
8321 reaches the intermediate value |a_goal|. At this point
8323 {\vb\dot B(0)\vb\over 3} &= \hbox{|v0|}, \qquad
8324 {\vb\dot B({1\over4})\vb\over 3} = {\hbox{|v002|}\over 2}, \qquad
8325 {\vb\dot B({1\over2})\vb\over 3} = {\hbox{|v02|}\over 2}, \cr
8326 {\vb\dot B({3\over4})\vb\over 3} &= {\hbox{|v022|}\over 2}, \qquad
8327 {\vb\dot B(1)\vb\over 3} = \hbox{|v2|} \cr
8331 $$ {\vb\dot B(t)\vb\over 3} \approx
8332 \cases{B\left(\hbox{|v0|},
8333 \hbox{|v002|}-{1\over 2}\hbox{|v0|}-{1\over 4}\hbox{|v02|},
8334 {1\over 2}\hbox{|v02|}; 2t \right)&
8335 if $t\le{1\over 2}$\cr
8336 B\left({1\over 2}\hbox{|v02|},
8337 \hbox{|v022|}-{1\over 4}\hbox{|v02|}-{1\over 2}\hbox{|v2|},
8338 \hbox{|v2|}; 2t-1 \right)&
8339 if $t\ge{1\over 2}$.\cr}
8342 We can integrate $\vb\dot B(t)\vb$ by using
8343 $$\int 3B(a,b,c;\tau)\,dt =
8344 {B(0,a,a+b,a+b+c;\tau) + {\rm constant} \over {d\tau\over dt}}.
8347 This construction allows us to find the time when the arc length reaches
8348 |a_goal| by solving a cubic equation of the form
8349 $$ B(0,a,a+b,a+b+c;\tau) = x, $$
8350 where $\tau$ is $2t$ or $2t+1$, $x$ is |a_goal| or |a_goal-arc1|, and $a$, $b$,
8351 and $c$ are the Bernshte{\u\i}n coefficients from $(*)$ divided by
8352 @^Bernshte{\u\i}n, Serge{\u\i} Natanovich@>
8353 $d\tau\over dt$. We shall define a function |solve_rising_cubic| that finds
8354 $\tau$ given $a$, $b$, $c$, and $x$.
8356 @<Estimate when the arc length reaches |a_goal| and set |arc_test| to...@>=
8358 tmp = (v02 + 2) / 4;
8359 if ( a_goal<=arc1 ) {
8362 (halfp(mp_solve_rising_cubic(mp, tmp2, arc1-tmp2-tmp, tmp, a_goal))- two);
8365 return ((half_unit - two) +
8366 halfp(mp_solve_rising_cubic(mp, tmp, arc-arc1-tmp-tmp2, tmp2, a_goal-arc1)));
8370 @ Here is the |solve_rising_cubic| routine that finds the time~$t$ when
8371 $$ B(0, a, a+b, a+b+c; t) = x. $$
8372 This routine is based on |crossing_point| but is simplified by the
8373 assumptions that $B(a,b,c;t)\ge0$ for $0\le t\le1$ and that |0<=x<=a+b+c|.
8374 If rounding error causes this condition to be violated slightly, we just ignore
8375 it and proceed with binary search. This finds a time when the function value
8376 reaches |x| and the slope is positive.
8378 @<Declare subroutines needed by |arc_test|@>=
8379 scaled mp_solve_rising_cubic (MP mp,scaled a, scaled b, scaled c, scaled x) {
8380 scaled ab, bc, ac; /* bisection results */
8381 integer t; /* $2^k+q$ where unscaled answer is in $[q2^{-k},(q+1)2^{-k})$ */
8382 integer xx; /* temporary for updating |x| */
8383 if ( (a<0) || (c<0) ) mp_confusion(mp, "rising?");
8384 @:this can't happen rising?}{\quad rising?@>
8387 } else if ( x >= a+b+c ) {
8391 @<Rescale if necessary to make sure |a|, |b|, and |c| are all less than
8395 @<Subdivide the B\'ezier quadratic defined by |a|, |b|, |c|@>;
8396 xx = x - a - ab - ac;
8397 if ( xx < -x ) { x+=x; b=ab; c=ac; }
8398 else { x = x + xx; a=ac; b=mp->bc; t = t+1; };
8399 } while (t < unity);
8404 @ @<Subdivide the B\'ezier quadratic defined by |a|, |b|, |c|@>=
8409 @ @d one_third_el_gordo 05252525252 /* upper bound on |a|, |b|, and |c| */
8411 @<Rescale if necessary to make sure |a|, |b|, and |c| are all less than...@>=
8412 while ((a>one_third_el_gordo)||(b>one_third_el_gordo)||(c>one_third_el_gordo)) {
8419 @ It is convenient to have a simpler interface to |arc_test| that requires no
8420 unnecessary arguments and ensures that each $({\it dx},{\it dy})$ pair has
8421 length less than |fraction_four|.
8423 @d arc_tol 16 /* quit when change in arc length estimate reaches this */
8425 @c scaled mp_do_arc_test (MP mp,scaled dx0, scaled dy0, scaled dx1,
8426 scaled dy1, scaled dx2, scaled dy2, scaled a_goal) {
8427 scaled v0,v1,v2; /* length of each $({\it dx},{\it dy})$ pair */
8428 scaled v02; /* twice the norm of the quadratic at $t={1\over2}$ */
8429 v0 = mp_pyth_add(mp, dx0,dy0);
8430 v1 = mp_pyth_add(mp, dx1,dy1);
8431 v2 = mp_pyth_add(mp, dx2,dy2);
8432 if ( (v0>=fraction_four) || (v1>=fraction_four) || (v2>=fraction_four) ) {
8433 mp->arith_error = true;
8434 if ( a_goal==el_gordo ) return el_gordo;
8437 v02 = mp_pyth_add(mp, dx1+half(dx0+dx2), dy1+half(dy0+dy2));
8438 return (mp_arc_test(mp, dx0,dy0, dx1,dy1, dx2,dy2,
8439 v0, v02, v2, a_goal, arc_tol));
8443 @ Now it is easy to find the arc length of an entire path.
8445 @c scaled mp_get_arc_length (MP mp,pointer h) {
8446 pointer p,q; /* for traversing the path */
8447 scaled a,a_tot; /* current and total arc lengths */
8450 while ( right_type(p)!=mp_endpoint ){
8452 a = mp_do_arc_test(mp, right_x(p)-x_coord(p), right_y(p)-y_coord(p),
8453 left_x(q)-right_x(p), left_y(q)-right_y(p),
8454 x_coord(q)-left_x(q), y_coord(q)-left_y(q), el_gordo);
8455 a_tot = mp_slow_add(mp, a, a_tot);
8456 if ( q==h ) break; else p=q;
8462 @ The inverse operation of finding the time on a path~|h| when the arc length
8463 reaches some value |arc0| can also be accomplished via |do_arc_test|. Some care
8464 is required to handle very large times or negative times on cyclic paths. For
8465 non-cyclic paths, |arc0| values that are negative or too large cause
8466 |get_arc_time| to return 0 or the length of path~|h|.
8468 If |arc0| is greater than the arc length of a cyclic path~|h|, the result is a
8469 time value greater than the length of the path. Since it could be much greater,
8470 we must be prepared to compute the arc length of path~|h| and divide this into
8471 |arc0| to find how many multiples of the length of path~|h| to add.
8473 @c scaled mp_get_arc_time (MP mp,pointer h, scaled arc0) {
8474 pointer p,q; /* for traversing the path */
8475 scaled t_tot; /* accumulator for the result */
8476 scaled t; /* the result of |do_arc_test| */
8477 scaled arc; /* portion of |arc0| not used up so far */
8478 integer n; /* number of extra times to go around the cycle */
8480 @<Deal with a negative |arc0| value and |return|@>;
8482 if ( arc0==el_gordo ) decr(arc0);
8486 while ( (right_type(p)!=mp_endpoint) && (arc>0) ) {
8488 t = mp_do_arc_test(mp, right_x(p)-x_coord(p), right_y(p)-y_coord(p),
8489 left_x(q)-right_x(p), left_y(q)-right_y(p),
8490 x_coord(q)-left_x(q), y_coord(q)-left_y(q), arc);
8491 @<Update |arc| and |t_tot| after |do_arc_test| has just returned |t|@>;
8493 @<Update |t_tot| and |arc| to avoid going around the cyclic
8494 path too many times but set |arith_error:=true| and |goto done| on
8503 @ @<Update |arc| and |t_tot| after |do_arc_test| has just returned |t|@>=
8504 if ( t<0 ) { t_tot = t_tot + t + two; arc = 0; }
8505 else { t_tot = t_tot + unity; arc = arc - t; }
8507 @ @<Deal with a negative |arc0| value and |return|@>=
8509 if ( left_type(h)==mp_endpoint ) {
8512 p = mp_htap_ypoc(mp, h);
8513 t_tot = -mp_get_arc_time(mp, p, -arc0);
8514 mp_toss_knot_list(mp, p);
8520 @ @<Update |t_tot| and |arc| to avoid going around the cyclic...@>=
8522 n = arc / (arc0 - arc);
8523 arc = arc - n*(arc0 - arc);
8524 if ( t_tot > el_gordo / (n+1) ) {
8525 mp->arith_error = true;
8529 t_tot = (n + 1)*t_tot;
8532 @* \[20] Data structures for pens.
8533 A Pen in \MP\ can be either elliptical or polygonal. Elliptical pens result
8534 in \ps\ \&{stroke} commands, while anything drawn with a polygonal pen is
8535 @:stroke}{\&{stroke} command@>
8536 converted into an area fill as described in the next part of this program.
8537 The mathematics behind this process is based on simple aspects of the theory
8538 of tracings developed by Leo Guibas, Lyle Ramshaw, and Jorge Stolfi
8539 [``A kinematic framework for computational geometry,'' Proc.\ IEEE Symp.\
8540 Foundations of Computer Science {\bf 24} (1983), 100--111].
8542 Polygonal pens are created from paths via \MP's \&{makepen} primitive.
8543 @:makepen_}{\&{makepen} primitive@>
8544 This path representation is almost sufficient for our purposes except that
8545 a pen path should always be a convex polygon with the vertices in
8546 counter-clockwise order.
8547 Since we will need to scan pen polygons both forward and backward, a pen
8548 should be represented as a doubly linked ring of knot nodes. There is
8549 room for the extra back pointer because we do not need the
8550 |left_type| or |right_type| fields. In fact, we don't need the |left_x|,
8551 |left_y|, |right_x|, or |right_y| fields either but we leave these alone
8552 so that certain procedures can operate on both pens and paths. In particular,
8553 pens can be copied using |copy_path| and recycled using |toss_knot_list|.
8556 /* this replaces the |left_type| and |right_type| fields in a pen knot */
8558 @ The |make_pen| procedure turns a path into a pen by initializing
8559 the |knil| pointers and making sure the knots form a convex polygon.
8560 Thus each cubic in the given path becomes a straight line and the control
8561 points are ignored. If the path is not cyclic, the ends are connected by a
8564 @d copy_pen(A) mp_make_pen(mp, mp_copy_path(mp, (A)),false)
8566 @c @<Declare a function called |convex_hull|@>;
8567 pointer mp_make_pen (MP mp,pointer h, boolean need_hull) {
8568 pointer p,q; /* two consecutive knots */
8575 h=mp_convex_hull(mp, h);
8576 @<Make sure |h| isn't confused with an elliptical pen@>;
8581 @ The only information required about an elliptical pen is the overall
8582 transformation that has been applied to the original \&{pencircle}.
8583 @:pencircle_}{\&{pencircle} primitive@>
8584 Since it suffices to keep track of how the three points $(0,0)$, $(1,0)$,
8585 and $(0,1)$ are transformed, an elliptical pen can be stored in a single
8586 knot node and transformed as if it were a path.
8588 @d pen_is_elliptical(A) ((A)==link((A)))
8590 @c pointer mp_get_pen_circle (MP mp,scaled diam) {
8591 pointer h; /* the knot node to return */
8592 h=mp_get_node(mp, knot_node_size);
8593 link(h)=h; knil(h)=h;
8594 originator(h)=mp_program_code;
8595 x_coord(h)=0; y_coord(h)=0;
8596 left_x(h)=diam; left_y(h)=0;
8597 right_x(h)=0; right_y(h)=diam;
8601 @ If the polygon being returned by |make_pen| has only one vertex, it will
8602 be interpreted as an elliptical pen. This is no problem since a degenerate
8603 polygon can equally well be thought of as a degenerate ellipse. We need only
8604 initialize the |left_x|, |left_y|, |right_x|, and |right_y| fields.
8606 @<Make sure |h| isn't confused with an elliptical pen@>=
8607 if ( pen_is_elliptical( h) ){
8608 left_x(h)=x_coord(h); left_y(h)=y_coord(h);
8609 right_x(h)=x_coord(h); right_y(h)=y_coord(h);
8612 @ We have to cheat a little here but most operations on pens only use
8613 the first three words in each knot node.
8614 @^data structure assumptions@>
8616 @<Initialize a pen at |test_pen| so that it fits in nine words@>=
8617 x_coord(test_pen)=-half_unit;
8618 y_coord(test_pen)=0;
8619 x_coord(test_pen+3)=half_unit;
8620 y_coord(test_pen+3)=0;
8621 x_coord(test_pen+6)=0;
8622 y_coord(test_pen+6)=unity;
8623 link(test_pen)=test_pen+3;
8624 link(test_pen+3)=test_pen+6;
8625 link(test_pen+6)=test_pen;
8626 knil(test_pen)=test_pen+6;
8627 knil(test_pen+3)=test_pen;
8628 knil(test_pen+6)=test_pen+3
8630 @ Printing a polygonal pen is very much like printing a path
8632 @<Declare subroutines for printing expressions@>=
8633 void mp_pr_pen (MP mp,pointer h) {
8634 pointer p,q; /* for list traversal */
8635 if ( pen_is_elliptical(h) ) {
8636 @<Print the elliptical pen |h|@>;
8640 mp_print_two(mp, x_coord(p),y_coord(p));
8641 mp_print_nl(mp, " .. ");
8642 @<Advance |p| making sure the links are OK and |return| if there is
8645 mp_print(mp, "cycle");
8649 @ @<Advance |p| making sure the links are OK and |return| if there is...@>=
8651 if ( (q==null) || (knil(q)!=p) ) {
8652 mp_print_nl(mp, "???"); return; /* this won't happen */
8657 @ @<Print the elliptical pen |h|@>=
8659 mp_print(mp, "pencircle transformed (");
8660 mp_print_scaled(mp, x_coord(h));
8661 mp_print_char(mp, ',');
8662 mp_print_scaled(mp, y_coord(h));
8663 mp_print_char(mp, ',');
8664 mp_print_scaled(mp, left_x(h)-x_coord(h));
8665 mp_print_char(mp, ',');
8666 mp_print_scaled(mp, right_x(h)-x_coord(h));
8667 mp_print_char(mp, ',');
8668 mp_print_scaled(mp, left_y(h)-y_coord(h));
8669 mp_print_char(mp, ',');
8670 mp_print_scaled(mp, right_y(h)-y_coord(h));
8671 mp_print_char(mp, ')');
8674 @ Here us another version of |pr_pen| that prints the pen as a diagnostic
8677 @<Declare subroutines for printing expressions@>=
8678 void mp_print_pen (MP mp,pointer h, char *s, boolean nuline) {
8679 mp_print_diagnostic(mp, "Pen",s,nuline); mp_print_ln(mp);
8682 mp_end_diagnostic(mp, true);
8685 @ Making a polygonal pen into a path involves restoring the |left_type| and
8686 |right_type| fields and setting the control points so as to make a polygonal
8690 void mp_make_path (MP mp,pointer h) {
8691 pointer p; /* for traversing the knot list */
8692 small_number k; /* a loop counter */
8693 @<Other local variables in |make_path|@>;
8694 if ( pen_is_elliptical(h) ) {
8695 @<Make the elliptical pen |h| into a path@>;
8699 left_type(p)=mp_explicit;
8700 right_type(p)=mp_explicit;
8701 @<copy the coordinates of knot |p| into its control points@>;
8707 @ @<copy the coordinates of knot |p| into its control points@>=
8708 left_x(p)=x_coord(p);
8709 left_y(p)=y_coord(p);
8710 right_x(p)=x_coord(p);
8711 right_y(p)=y_coord(p)
8713 @ We need an eight knot path to get a good approximation to an ellipse.
8715 @<Make the elliptical pen |h| into a path@>=
8717 @<Extract the transformation parameters from the elliptical pen~|h|@>;
8719 for (k=0;k<=7;k++ ) {
8720 @<Initialize |p| as the |k|th knot of a circle of unit diameter,
8721 transforming it appropriately@>;
8722 if ( k==7 ) link(p)=h; else link(p)=mp_get_node(mp, knot_node_size);
8727 @ @<Extract the transformation parameters from the elliptical pen~|h|@>=
8728 center_x=x_coord(h);
8729 center_y=y_coord(h);
8730 width_x=left_x(h)-center_x;
8731 width_y=left_y(h)-center_y;
8732 height_x=right_x(h)-center_x;
8733 height_y=right_y(h)-center_y
8735 @ @<Other local variables in |make_path|@>=
8736 scaled center_x,center_y; /* translation parameters for an elliptical pen */
8737 scaled width_x,width_y; /* the effect of a unit change in $x$ */
8738 scaled height_x,height_y; /* the effect of a unit change in $y$ */
8739 scaled dx,dy; /* the vector from knot |p| to its right control point */
8741 /* |k| advanced $270^\circ$ around the ring (cf. $\sin\theta=\cos(\theta+270)$) */
8743 @ The only tricky thing here are the tables |half_cos| and |d_cos| used to
8744 find the point $k/8$ of the way around the circle and the direction vector
8747 @<Initialize |p| as the |k|th knot of a circle of unit diameter,...@>=
8749 x_coord(p)=center_x+mp_take_fraction(mp, mp->half_cos[k],width_x)
8750 +mp_take_fraction(mp, mp->half_cos[kk],height_x);
8751 y_coord(p)=center_y+mp_take_fraction(mp, mp->half_cos[k],width_y)
8752 +mp_take_fraction(mp, mp->half_cos[kk],height_y);
8753 dx=-mp_take_fraction(mp, mp->d_cos[kk],width_x)
8754 +mp_take_fraction(mp, mp->d_cos[k],height_x);
8755 dy=-mp_take_fraction(mp, mp->d_cos[kk],width_y)
8756 +mp_take_fraction(mp, mp->d_cos[k],height_y);
8757 right_x(p)=x_coord(p)+dx;
8758 right_y(p)=y_coord(p)+dy;
8759 left_x(p)=x_coord(p)-dx;
8760 left_y(p)=y_coord(p)-dy;
8761 left_type(p)=mp_explicit;
8762 right_type(p)=mp_explicit;
8763 originator(p)=mp_program_code
8766 fraction half_cos[8]; /* ${1\over2}\cos(45k)$ */
8767 fraction d_cos[8]; /* a magic constant times $\cos(45k)$ */
8769 @ The magic constant for |d_cos| is the distance between $({1\over2},0)$ and
8770 $({1\over4}\sqrt2,{1\over4}\sqrt2)$ times the result of the |velocity|
8771 function for $\theta=\phi=22.5^\circ$. This comes out to be
8772 $$ d = {\sqrt{2-\sqrt2}\over 3+3\cos22.5^\circ}
8773 \approx 0.132608244919772.
8777 mp->half_cos[0]=fraction_half;
8778 mp->half_cos[1]=94906266; /* $2^{26}\sqrt2\approx94906265.62$ */
8780 mp->d_cos[0]=35596755; /* $2^{28}d\approx35596754.69$ */
8781 mp->d_cos[1]=25170707; /* $2^{27}\sqrt2\,d\approx25170706.63$ */
8783 for (k=3;k<= 4;k++ ) {
8784 mp->half_cos[k]=-mp->half_cos[4-k];
8785 mp->d_cos[k]=-mp->d_cos[4-k];
8787 for (k=5;k<= 7;k++ ) {
8788 mp->half_cos[k]=mp->half_cos[8-k];
8789 mp->d_cos[k]=mp->d_cos[8-k];
8792 @ The |convex_hull| function forces a pen polygon to be convex when it is
8793 returned by |make_pen| and after any subsequent transformation where rounding
8794 error might allow the convexity to be lost.
8795 The convex hull algorithm used here is described by F.~P. Preparata and
8796 M.~I. Shamos [{\sl Computational Geometry}, Springer-Verlag, 1985].
8798 @<Declare a function called |convex_hull|@>=
8799 @<Declare a procedure called |move_knot|@>;
8800 pointer mp_convex_hull (MP mp,pointer h) { /* Make a polygonal pen convex */
8801 pointer l,r; /* the leftmost and rightmost knots */
8802 pointer p,q; /* knots being scanned */
8803 pointer s; /* the starting point for an upcoming scan */
8804 scaled dx,dy; /* a temporary pointer */
8805 if ( pen_is_elliptical(h) ) {
8808 @<Set |l| to the leftmost knot in polygon~|h|@>;
8809 @<Set |r| to the rightmost knot in polygon~|h|@>;
8812 @<Find any knots on the path from |l| to |r| above the |l|-|r| line and
8813 move them past~|r|@>;
8814 @<Find any knots on the path from |s| to |l| below the |l|-|r| line and
8815 move them past~|l|@>;
8816 @<Sort the path from |l| to |r| by increasing $x$@>;
8817 @<Sort the path from |r| to |l| by decreasing $x$@>;
8820 @<Do a Gramm scan and remove vertices where there is no left turn@>;
8826 @ All comparisons are done primarily on $x$ and secondarily on $y$.
8828 @<Set |l| to the leftmost knot in polygon~|h|@>=
8832 if ( x_coord(p)<=x_coord(l) )
8833 if ( (x_coord(p)<x_coord(l)) || (y_coord(p)<y_coord(l)) )
8838 @ @<Set |r| to the rightmost knot in polygon~|h|@>=
8842 if ( x_coord(p)>=x_coord(r) )
8843 if ( (x_coord(p)>x_coord(r)) || (y_coord(p)>y_coord(r)) )
8848 @ @<Find any knots on the path from |l| to |r| above the |l|-|r| line...@>=
8849 dx=x_coord(r)-x_coord(l);
8850 dy=y_coord(r)-y_coord(l);
8854 if ( mp_ab_vs_cd(mp, dx,y_coord(p)-y_coord(l),dy,x_coord(p)-x_coord(l))>0 )
8855 mp_move_knot(mp, p, r);
8859 @ The |move_knot| procedure removes |p| from a doubly linked list and inserts
8862 @ @<Declare a procedure called |move_knot|@>=
8863 void mp_move_knot (MP mp,pointer p, pointer q) {
8864 link(knil(p))=link(p);
8865 knil(link(p))=knil(p);
8872 @ @<Find any knots on the path from |s| to |l| below the |l|-|r| line...@>=
8876 if ( mp_ab_vs_cd(mp, dx,y_coord(p)-y_coord(l),dy,x_coord(p)-x_coord(l))<0 )
8877 mp_move_knot(mp, p,l);
8881 @ The list is likely to be in order already so we just do linear insertions.
8882 Secondary comparisons on $y$ ensure that the sort is consistent with the
8883 choice of |l| and |r|.
8885 @<Sort the path from |l| to |r| by increasing $x$@>=
8889 while ( x_coord(q)>x_coord(p) ) q=knil(q);
8890 while ( x_coord(q)==x_coord(p) ) {
8891 if ( y_coord(q)>y_coord(p) ) q=knil(q); else break;
8893 if ( q==knil(p) ) p=link(p);
8894 else { p=link(p); mp_move_knot(mp, knil(p),q); };
8897 @ @<Sort the path from |r| to |l| by decreasing $x$@>=
8901 while ( x_coord(q)<x_coord(p) ) q=knil(q);
8902 while ( x_coord(q)==x_coord(p) ) {
8903 if ( y_coord(q)<y_coord(p) ) q=knil(q); else break;
8905 if ( q==knil(p) ) p=link(p);
8906 else { p=link(p); mp_move_knot(mp, knil(p),q); };
8909 @ The condition involving |ab_vs_cd| tests if there is not a left turn
8910 at knot |q|. There usually will be a left turn so we streamline the case
8911 where the |then| clause is not executed.
8913 @<Do a Gramm scan and remove vertices where there...@>=
8917 dx=x_coord(q)-x_coord(p);
8918 dy=y_coord(q)-y_coord(p);
8922 if ( mp_ab_vs_cd(mp, dx,y_coord(q)-y_coord(p),dy,x_coord(q)-x_coord(p))<=0 ) {
8923 @<Remove knot |p| and back up |p| and |q| but don't go past |l|@>;
8928 @ @<Remove knot |p| and back up |p| and |q| but don't go past |l|@>=
8931 mp_free_node(mp, p,knot_node_size);
8932 link(s)=q; knil(q)=s;
8934 else { p=knil(s); q=s; };
8937 @ The |find_offset| procedure sets global variables |(cur_x,cur_y)| to the
8938 offset associated with the given direction |(x,y)|. If two different offsets
8939 apply, it chooses one of them.
8942 void mp_find_offset (MP mp,scaled x, scaled y, pointer h) {
8943 pointer p,q; /* consecutive knots */
8945 /* the transformation matrix for an elliptical pen */
8946 fraction xx,yy; /* untransformed offset for an elliptical pen */
8947 fraction d; /* a temporary register */
8948 if ( pen_is_elliptical(h) ) {
8949 @<Find the offset for |(x,y)| on the elliptical pen~|h|@>
8954 } while (!(mp_ab_vs_cd(mp, x_coord(q)-x_coord(p),y, y_coord(q)-y_coord(p),x)>=0));
8957 } while (!(mp_ab_vs_cd(mp, x_coord(q)-x_coord(p),y, y_coord(q)-y_coord(p),x)<=0));
8958 mp->cur_x=x_coord(p);
8959 mp->cur_y=y_coord(p);
8965 scaled cur_y; /* all-purpose return value registers */
8967 @ @<Find the offset for |(x,y)| on the elliptical pen~|h|@>=
8968 if ( (x==0) && (y==0) ) {
8969 mp->cur_x=x_coord(h); mp->cur_y=y_coord(h);
8971 @<Find the non-constant part of the transformation for |h|@>;
8972 while ( (abs(x)<fraction_half) && (abs(y)<fraction_half) ){
8975 @<Make |(xx,yy)| the offset on the untransformed \&{pencircle} for the
8976 untransformed version of |(x,y)|@>;
8977 mp->cur_x=x_coord(h)+mp_take_fraction(mp, xx,wx)+mp_take_fraction(mp, yy,hx);
8978 mp->cur_y=y_coord(h)+mp_take_fraction(mp, xx,wy)+mp_take_fraction(mp, yy,hy);
8981 @ @<Find the non-constant part of the transformation for |h|@>=
8982 wx=left_x(h)-x_coord(h);
8983 wy=left_y(h)-y_coord(h);
8984 hx=right_x(h)-x_coord(h);
8985 hy=right_y(h)-y_coord(h)
8987 @ @<Make |(xx,yy)| the offset on the untransformed \&{pencircle} for the...@>=
8988 yy=-(mp_take_fraction(mp, x,hy)+mp_take_fraction(mp, y,-hx));
8989 xx=mp_take_fraction(mp, x,-wy)+mp_take_fraction(mp, y,wx);
8990 d=mp_pyth_add(mp, xx,yy);
8992 xx=half(mp_make_fraction(mp, xx,d));
8993 yy=half(mp_make_fraction(mp, yy,d));
8996 @ Finding the bounding box of a pen is easy except if the pen is elliptical.
8997 But we can handle that case by just calling |find_offset| twice. The answer
8998 is stored in the global variables |minx|, |maxx|, |miny|, and |maxy|.
9001 void mp_pen_bbox (MP mp,pointer h) {
9002 pointer p; /* for scanning the knot list */
9003 if ( pen_is_elliptical(h) ) {
9004 @<Find the bounding box of an elliptical pen@>;
9006 minx=x_coord(h); maxx=minx;
9007 miny=y_coord(h); maxy=miny;
9010 if ( x_coord(p)<minx ) minx=x_coord(p);
9011 if ( y_coord(p)<miny ) miny=y_coord(p);
9012 if ( x_coord(p)>maxx ) maxx=x_coord(p);
9013 if ( y_coord(p)>maxy ) maxy=y_coord(p);
9019 @ @<Find the bounding box of an elliptical pen@>=
9021 mp_find_offset(mp, 0,fraction_one,h);
9023 minx=2*x_coord(h)-mp->cur_x;
9024 mp_find_offset(mp, -fraction_one,0,h);
9026 miny=2*y_coord(h)-mp->cur_y;
9029 @* \[21] Edge structures.
9030 Now we come to \MP's internal scheme for representing pictures.
9031 The representation is very different from \MF's edge structures
9032 because \MP\ pictures contain \ps\ graphics objects instead of pixel
9033 images. However, the basic idea is somewhat similar in that shapes
9034 are represented via their boundaries.
9036 The main purpose of edge structures is to keep track of graphical objects
9037 until it is time to translate them into \ps. Since \MP\ does not need to
9038 know anything about an edge structure other than how to translate it into
9039 \ps\ and how to find its bounding box, edge structures can be just linked
9040 lists of graphical objects. \MP\ has no easy way to determine whether
9041 two such objects overlap, but it suffices to draw the first one first and
9042 let the second one overwrite it if necessary.
9045 enum mp_graphical_object_code {
9046 @<Graphical object codes@>
9049 @ Let's consider the types of graphical objects one at a time.
9050 First of all, a filled contour is represented by a eight-word node. The first
9051 word contains |type| and |link| fields, and the next six words contain a
9052 pointer to a cyclic path and the value to use for \ps' \&{currentrgbcolor}
9053 parameter. If a pen is used for filling |pen_p|, |ljoin_val| and |miterlim_val|
9054 give the relevant information.
9056 @d path_p(A) link((A)+1)
9057 /* a pointer to the path that needs filling */
9058 @d pen_p(A) info((A)+1)
9059 /* a pointer to the pen to fill or stroke with */
9060 @d color_model(A) type((A)+2) /* the color model */
9061 @d obj_red_loc(A) ((A)+3) /* the first of three locations for the color */
9062 @d obj_cyan_loc obj_red_loc /* the first of four locations for the color */
9063 @d obj_grey_loc obj_red_loc /* the location for the color */
9064 @d red_val(A) mp->mem[(A)+3].sc
9065 /* the red component of the color in the range $0\ldots1$ */
9068 @d green_val(A) mp->mem[(A)+4].sc
9069 /* the green component of the color in the range $0\ldots1$ */
9070 @d magenta_val green_val
9071 @d blue_val(A) mp->mem[(A)+5].sc
9072 /* the blue component of the color in the range $0\ldots1$ */
9073 @d yellow_val blue_val
9074 @d black_val(A) mp->mem[(A)+6].sc
9075 /* the blue component of the color in the range $0\ldots1$ */
9076 @d ljoin_val(A) name_type((A)) /* the value of \&{linejoin} */
9077 @:mp_linejoin_}{\&{linejoin} primitive@>
9078 @d miterlim_val(A) mp->mem[(A)+7].sc /* the value of \&{miterlimit} */
9079 @:mp_miterlimit_}{\&{miterlimit} primitive@>
9080 @d obj_color_part(A) mp->mem[(A)+3-red_part].sc
9081 /* interpret an object pointer that has been offset by |red_part..blue_part| */
9082 @d pre_script(A) mp->mem[(A)+8].hh.lh
9083 @d post_script(A) mp->mem[(A)+8].hh.rh
9086 @ @<Graphical object codes@>=
9090 pointer mp_new_fill_node (MP mp,pointer p) {
9091 /* make a fill node for cyclic path |p| and color black */
9092 pointer t; /* the new node */
9093 t=mp_get_node(mp, fill_node_size);
9094 type(t)=mp_fill_code;
9096 pen_p(t)=null; /* |null| means don't use a pen */
9101 color_model(t)=mp_uninitialized_model;
9103 post_script(t)=null;
9104 @<Set the |ljoin_val| and |miterlim_val| fields in object |t|@>;
9108 @ @<Set the |ljoin_val| and |miterlim_val| fields in object |t|@>=
9109 if ( mp->internal[mp_linejoin]>unity ) ljoin_val(t)=2;
9110 else if ( mp->internal[mp_linejoin]>0 ) ljoin_val(t)=1;
9111 else ljoin_val(t)=0;
9112 if ( mp->internal[mp_miterlimit]<unity )
9113 miterlim_val(t)=unity;
9115 miterlim_val(t)=mp->internal[mp_miterlimit]
9117 @ A stroked path is represented by an eight-word node that is like a filled
9118 contour node except that it contains the current \&{linecap} value, a scale
9119 factor for the dash pattern, and a pointer that is non-null if the stroke
9120 is to be dashed. The purpose of the scale factor is to allow a picture to
9121 be transformed without touching the picture that |dash_p| points to.
9123 @d dash_p(A) link((A)+9)
9124 /* a pointer to the edge structure that gives the dash pattern */
9125 @d lcap_val(A) type((A)+9)
9126 /* the value of \&{linecap} */
9127 @:mp_linecap_}{\&{linecap} primitive@>
9128 @d dash_scale(A) mp->mem[(A)+10].sc /* dash lengths are scaled by this factor */
9129 @d stroked_node_size 11
9131 @ @<Graphical object codes@>=
9135 pointer mp_new_stroked_node (MP mp,pointer p) {
9136 /* make a stroked node for path |p| with |pen_p(p)| temporarily |null| */
9137 pointer t; /* the new node */
9138 t=mp_get_node(mp, stroked_node_size);
9139 type(t)=mp_stroked_code;
9140 path_p(t)=p; pen_p(t)=null;
9142 dash_scale(t)=unity;
9147 color_model(t)=mp_uninitialized_model;
9149 post_script(t)=null;
9150 @<Set the |ljoin_val| and |miterlim_val| fields in object |t|@>;
9151 if ( mp->internal[mp_linecap]>unity ) lcap_val(t)=2;
9152 else if ( mp->internal[mp_linecap]>0 ) lcap_val(t)=1;
9157 @ When a dashed line is computed in a transformed coordinate system, the dash
9158 lengths get scaled like the pen shape and we need to compensate for this. Since
9159 there is no unique scale factor for an arbitrary transformation, we use the
9160 the square root of the determinant. The properties of the determinant make it
9161 easier to maintain the |dash_scale|. The computation is fairly straight-forward
9162 except for the initialization of the scale factor |s|. The factor of 64 is
9163 needed because |square_rt| scales its result by $2^8$ while we need $2^{14}$
9164 to counteract the effect of |take_fraction|.
9166 @<Declare subroutines needed by |print_edges|@>=
9167 scaled mp_sqrt_det (MP mp,scaled a, scaled b, scaled c, scaled d) {
9168 scaled maxabs; /* $max(|a|,|b|,|c|,|d|)$ */
9169 integer s; /* amount by which the result of |square_rt| needs to be scaled */
9170 @<Initialize |maxabs|@>;
9172 while ( (maxabs<fraction_one) && (s>1) ){
9173 a+=a; b+=b; c+=c; d+=d;
9174 maxabs+=maxabs; s=halfp(s);
9176 return s*mp_square_rt(mp, abs(mp_take_fraction(mp, a,d)-mp_take_fraction(mp, b,c)));
9179 scaled mp_get_pen_scale (MP mp,pointer p) {
9180 return mp_sqrt_det(mp,
9181 left_x(p)-x_coord(p), right_x(p)-x_coord(p),
9182 left_y(p)-y_coord(p), right_y(p)-y_coord(p));
9185 @ @<Internal library ...@>=
9186 scaled mp_sqrt_det (MP mp,scaled a, scaled b, scaled c, scaled d) ;
9189 @ @<Initialize |maxabs|@>=
9191 if ( abs(b)>maxabs ) maxabs=abs(b);
9192 if ( abs(c)>maxabs ) maxabs=abs(c);
9193 if ( abs(d)>maxabs ) maxabs=abs(d)
9195 @ When a picture contains text, this is represented by a fourteen-word node
9196 where the color information and |type| and |link| fields are augmented by
9197 additional fields that describe the text and how it is transformed.
9198 The |path_p| and |pen_p| pointers are replaced by a number that identifies
9199 the font and a string number that gives the text to be displayed.
9200 The |width|, |height|, and |depth| fields
9201 give the dimensions of the text at its design size, and the remaining six
9202 words give a transformation to be applied to the text. The |new_text_node|
9203 function initializes everything to default values so that the text comes out
9204 black with its reference point at the origin.
9206 @d text_p(A) link((A)+1) /* a string pointer for the text to display */
9207 @d font_n(A) info((A)+1) /* the font number */
9208 @d width_val(A) mp->mem[(A)+7].sc /* unscaled width of the text */
9209 @d height_val(A) mp->mem[(A)+9].sc /* unscaled height of the text */
9210 @d depth_val(A) mp->mem[(A)+10].sc /* unscaled depth of the text */
9211 @d text_tx_loc(A) ((A)+11)
9212 /* the first of six locations for transformation parameters */
9213 @d tx_val(A) mp->mem[(A)+11].sc /* $x$ shift amount */
9214 @d ty_val(A) mp->mem[(A)+12].sc /* $y$ shift amount */
9215 @d txx_val(A) mp->mem[(A)+13].sc /* |txx| transformation parameter */
9216 @d txy_val(A) mp->mem[(A)+14].sc /* |txy| transformation parameter */
9217 @d tyx_val(A) mp->mem[(A)+15].sc /* |tyx| transformation parameter */
9218 @d tyy_val(A) mp->mem[(A)+16].sc /* |tyy| transformation parameter */
9219 @d text_trans_part(A) mp->mem[(A)+11-x_part].sc
9220 /* interpret a text node pointer that has been offset by |x_part..yy_part| */
9221 @d text_node_size 17
9223 @ @<Graphical object codes@>=
9226 @ @c @<Declare text measuring subroutines@>;
9227 pointer mp_new_text_node (MP mp,char *f,str_number s) {
9228 /* make a text node for font |f| and text string |s| */
9229 pointer t; /* the new node */
9230 t=mp_get_node(mp, text_node_size);
9231 type(t)=mp_text_code;
9233 font_n(t)=mp_find_font(mp, f); /* this identifies the font */
9238 color_model(t)=mp_uninitialized_model;
9240 post_script(t)=null;
9241 tx_val(t)=0; ty_val(t)=0;
9242 txx_val(t)=unity; txy_val(t)=0;
9243 tyx_val(t)=0; tyy_val(t)=unity;
9244 mp_set_text_box(mp, t); /* this finds the bounding box */
9248 @ The last two types of graphical objects that can occur in an edge structure
9249 are clipping paths and \&{setbounds} paths. These are slightly more difficult
9250 @:set_bounds_}{\&{setbounds} primitive@>
9251 to implement because we must keep track of exactly what is being clipped or
9252 bounded when pictures get merged together. For this reason, each clipping or
9253 \&{setbounds} operation is represented by a pair of nodes: first comes a
9254 two-word node whose |path_p| gives the relevant path, then there is the list
9255 of objects to clip or bound followed by a two-word node whose second word is
9258 Using at least two words for each graphical object node allows them all to be
9259 allocated and deallocated similarly with a global array |gr_object_size| to
9260 give the size in words for each object type.
9262 @d start_clip_size 2
9263 @d start_bounds_size 2
9264 @d stop_clip_size 2 /* the second word is not used here */
9265 @d stop_bounds_size 2 /* the second word is not used here */
9267 @d stop_type(A) ((A)+2)
9268 /* matching |type| for |start_clip_code| or |start_bounds_code| */
9269 @d has_color(A) (type((A))<mp_start_clip_code)
9270 /* does a graphical object have color fields? */
9271 @d has_pen(A) (type((A))<mp_text_code)
9272 /* does a graphical object have a |pen_p| field? */
9273 @d is_start_or_stop(A) (type((A))>=mp_start_clip_code)
9274 @d is_stop(A) (type((A))>=mp_stop_clip_code)
9276 @ @<Graphical object codes@>=
9277 mp_start_clip_code=4, /* |type| of a node that starts clipping */
9278 mp_start_bounds_code=5, /* |type| of a node that gives a \&{setbounds} path */
9279 mp_stop_clip_code=6, /* |type| of a node that stops clipping */
9280 mp_stop_bounds_code=7, /* |type| of a node that stops \&{setbounds} */
9283 pointer mp_new_bounds_node (MP mp,pointer p, small_number c) {
9284 /* make a node of type |c| where |p| is the clipping or \&{setbounds} path */
9285 pointer t; /* the new node */
9286 t=mp_get_node(mp, mp->gr_object_size[c]);
9292 @ We need an array to keep track of the sizes of graphical objects.
9295 small_number gr_object_size[mp_stop_bounds_code+1];
9298 mp->gr_object_size[mp_fill_code]=fill_node_size;
9299 mp->gr_object_size[mp_stroked_code]=stroked_node_size;
9300 mp->gr_object_size[mp_text_code]=text_node_size;
9301 mp->gr_object_size[mp_start_clip_code]=start_clip_size;
9302 mp->gr_object_size[mp_stop_clip_code]=stop_clip_size;
9303 mp->gr_object_size[mp_start_bounds_code]=start_bounds_size;
9304 mp->gr_object_size[mp_stop_bounds_code]=stop_bounds_size;
9306 @ All the essential information in an edge structure is encoded as a linked list
9307 of graphical objects as we have just seen, but it is helpful to add some
9308 redundant information. A single edge structure might be used as a dash pattern
9309 many times, and it would be nice to avoid scanning the same structure
9310 repeatedly. Thus, an edge structure known to be a suitable dash pattern
9311 has a header that gives a list of dashes in a sorted order designed for rapid
9312 translation into \ps.
9314 Each dash is represented by a three-word node containing the initial and final
9315 $x$~coordinates as well as the usual |link| field. The |link| fields points to
9316 the dash node with the next higher $x$-coordinates and the final link points
9317 to a special location called |null_dash|. (There should be no overlap between
9318 dashes). Since the $y$~coordinate of the dash pattern is needed to determine
9319 the period of repetition, this needs to be stored in the edge header along
9320 with a pointer to the list of dash nodes.
9322 @d start_x(A) mp->mem[(A)+1].sc /* the starting $x$~coordinate in a dash node */
9323 @d stop_x(A) mp->mem[(A)+2].sc /* the ending $x$~coordinate in a dash node */
9326 /* in an edge header this points to the first dash node */
9327 @d dash_y(A) mp->mem[(A)+1].sc /* $y$ value for the dash list in an edge header */
9329 @ It is also convenient for an edge header to contain the bounding
9330 box information needed by the \&{llcorner} and \&{urcorner} operators
9331 so that this does not have to be recomputed unnecessarily. This is done by
9332 adding fields for the $x$~and $y$ extremes as well as a pointer that indicates
9333 how far the bounding box computation has gotten. Thus if the user asks for
9334 the bounding box and then adds some more text to the picture before asking
9335 for more bounding box information, the second computation need only look at
9336 the additional text.
9338 When the bounding box has not been computed, the |bblast| pointer points
9339 to a dummy link at the head of the graphical object list while the |minx_val|
9340 and |miny_val| fields contain |el_gordo| and the |maxx_val| and |maxy_val|
9341 fields contain |-el_gordo|.
9343 Since the bounding box of pictures containing objects of type
9344 |mp_start_bounds_code| depends on the value of \&{truecorners}, the bounding box
9345 @:mp_true_corners_}{\&{truecorners} primitive@>
9346 data might not be valid for all values of this parameter. Hence, the |bbtype|
9347 field is needed to keep track of this.
9349 @d minx_val(A) mp->mem[(A)+2].sc
9350 @d miny_val(A) mp->mem[(A)+3].sc
9351 @d maxx_val(A) mp->mem[(A)+4].sc
9352 @d maxy_val(A) mp->mem[(A)+5].sc
9353 @d bblast(A) link((A)+6) /* last item considered in bounding box computation */
9354 @d bbtype(A) info((A)+6) /* tells how bounding box data depends on \&{truecorners} */
9355 @d dummy_loc(A) ((A)+7) /* where the object list begins in an edge header */
9357 /* |bbtype| value when bounding box data is valid for all \&{truecorners} values */
9359 /* |bbtype| value when bounding box data is for \&{truecorners}${}\le 0$ */
9361 /* |bbtype| value when bounding box data is for \&{truecorners}${}>0$ */
9364 void mp_init_bbox (MP mp,pointer h) {
9365 /* Initialize the bounding box information in edge structure |h| */
9366 bblast(h)=dummy_loc(h);
9367 bbtype(h)=no_bounds;
9368 minx_val(h)=el_gordo;
9369 miny_val(h)=el_gordo;
9370 maxx_val(h)=-el_gordo;
9371 maxy_val(h)=-el_gordo;
9374 @ The only other entries in an edge header are a reference count in the first
9375 word and a pointer to the tail of the object list in the last word.
9377 @d obj_tail(A) info((A)+7) /* points to the last entry in the object list */
9378 @d edge_header_size 8
9381 void mp_init_edges (MP mp,pointer h) {
9382 /* initialize an edge header to null values */
9383 dash_list(h)=null_dash;
9384 obj_tail(h)=dummy_loc(h);
9385 link(dummy_loc(h))=null;
9387 mp_init_bbox(mp, h);
9390 @ Here is how edge structures are deleted. The process can be recursive because
9391 of the need to dereference edge structures that are used as dash patterns.
9394 @d add_edge_ref(A) incr(ref_count(A))
9395 @d delete_edge_ref(A) {
9396 if ( ref_count((A))==null )
9397 mp_toss_edges(mp, A);
9402 @<Declare the recycling subroutines@>=
9403 void mp_flush_dash_list (MP mp,pointer h);
9404 pointer mp_toss_gr_object (MP mp,pointer p) ;
9405 void mp_toss_edges (MP mp,pointer h) ;
9407 @ @c void mp_toss_edges (MP mp,pointer h) {
9408 pointer p,q; /* pointers that scan the list being recycled */
9409 pointer r; /* an edge structure that object |p| refers to */
9410 mp_flush_dash_list(mp, h);
9411 q=link(dummy_loc(h));
9412 while ( (q!=null) ) {
9414 r=mp_toss_gr_object(mp, p);
9415 if ( r!=null ) delete_edge_ref(r);
9417 mp_free_node(mp, h,edge_header_size);
9419 void mp_flush_dash_list (MP mp,pointer h) {
9420 pointer p,q; /* pointers that scan the list being recycled */
9422 while ( q!=null_dash ) {
9424 mp_free_node(mp, p,dash_node_size);
9426 dash_list(h)=null_dash;
9428 pointer mp_toss_gr_object (MP mp,pointer p) {
9429 /* returns an edge structure that needs to be dereferenced */
9430 pointer e; /* the edge structure to return */
9432 @<Prepare to recycle graphical object |p|@>;
9433 mp_free_node(mp, p,mp->gr_object_size[type(p)]);
9437 @ @<Prepare to recycle graphical object |p|@>=
9440 mp_toss_knot_list(mp, path_p(p));
9441 if ( pen_p(p)!=null ) mp_toss_knot_list(mp, pen_p(p));
9442 if ( pre_script(p)!=null ) delete_str_ref(pre_script(p));
9443 if ( post_script(p)!=null ) delete_str_ref(post_script(p));
9445 case mp_stroked_code:
9446 mp_toss_knot_list(mp, path_p(p));
9447 if ( pen_p(p)!=null ) mp_toss_knot_list(mp, pen_p(p));
9448 if ( pre_script(p)!=null ) delete_str_ref(pre_script(p));
9449 if ( post_script(p)!=null ) delete_str_ref(post_script(p));
9453 delete_str_ref(text_p(p));
9454 if ( pre_script(p)!=null ) delete_str_ref(pre_script(p));
9455 if ( post_script(p)!=null ) delete_str_ref(post_script(p));
9457 case mp_start_clip_code:
9458 case mp_start_bounds_code:
9459 mp_toss_knot_list(mp, path_p(p));
9461 case mp_stop_clip_code:
9462 case mp_stop_bounds_code:
9464 } /* there are no other cases */
9466 @ If we use |add_edge_ref| to ``copy'' edge structures, the real copying needs
9467 to be done before making a significant change to an edge structure. Much of
9468 the work is done in a separate routine |copy_objects| that copies a list of
9469 graphical objects into a new edge header.
9471 @c @<Declare a function called |copy_objects|@>;
9472 pointer mp_private_edges (MP mp,pointer h) {
9473 /* make a private copy of the edge structure headed by |h| */
9474 pointer hh; /* the edge header for the new copy */
9475 pointer p,pp; /* pointers for copying the dash list */
9476 if ( ref_count(h)==null ) {
9480 hh=mp_copy_objects(mp, link(dummy_loc(h)),null);
9481 @<Copy the dash list from |h| to |hh|@>;
9482 @<Copy the bounding box information from |h| to |hh| and make |bblast(hh)|
9483 point into the new object list@>;
9488 @ Here we use the fact that |dash_list(hh)=link(hh)|.
9489 @^data structure assumptions@>
9491 @<Copy the dash list from |h| to |hh|@>=
9492 pp=hh; p=dash_list(h);
9493 while ( (p!=null_dash) ) {
9494 link(pp)=mp_get_node(mp, dash_node_size);
9496 start_x(pp)=start_x(p);
9497 stop_x(pp)=stop_x(p);
9501 dash_y(hh)=dash_y(h)
9504 @ |h| is an edge structure
9506 @d gr_start_x(A) (A)->start_x_field
9507 @d gr_stop_x(A) (A)->stop_x_field
9508 @d gr_dash_link(A) (A)->next_field
9510 @d gr_dash_list(A) (A)->list_field
9511 @d gr_dash_y(A) (A)->y_field
9514 struct mp_dash_list *mp_export_dashes (MP mp, pointer h) {
9515 struct mp_dash_list *dl;
9516 struct mp_dash_item *dh, *di;
9518 if (h==null || dash_list(h)==null_dash)
9521 dl = mp_xmalloc(mp,1,sizeof(struct mp_dash_list));
9522 gr_dash_list(dl) = NULL;
9523 gr_dash_y(dl) = dash_y(h);
9525 while (p != null_dash) {
9526 di=mp_xmalloc(mp,1,sizeof(struct mp_dash_item));
9527 gr_dash_link(di) = NULL;
9528 gr_start_x(di) = start_x(p);
9529 gr_stop_x(di) = stop_x(p);
9531 gr_dash_list(dl) = di;
9533 gr_dash_link(dh) = di;
9542 @ @<Copy the bounding box information from |h| to |hh|...@>=
9543 minx_val(hh)=minx_val(h);
9544 miny_val(hh)=miny_val(h);
9545 maxx_val(hh)=maxx_val(h);
9546 maxy_val(hh)=maxy_val(h);
9547 bbtype(hh)=bbtype(h);
9548 p=dummy_loc(h); pp=dummy_loc(hh);
9549 while ((p!=bblast(h)) ) {
9550 if ( p==null ) mp_confusion(mp, "bblast");
9551 @:this can't happen bblast}{\quad bblast@>
9552 p=link(p); pp=link(pp);
9556 @ Here is the promised routine for copying graphical objects into a new edge
9557 structure. It starts copying at object~|p| and stops just before object~|q|.
9558 If |q| is null, it copies the entire sublist headed at |p|. The resulting edge
9559 structure requires further initialization by |init_bbox|.
9561 @<Declare a function called |copy_objects|@>=
9562 pointer mp_copy_objects (MP mp, pointer p, pointer q) {
9563 pointer hh; /* the new edge header */
9564 pointer pp; /* the last newly copied object */
9565 small_number k; /* temporary register */
9566 hh=mp_get_node(mp, edge_header_size);
9567 dash_list(hh)=null_dash;
9571 @<Make |link(pp)| point to a copy of object |p|, and update |p| and |pp|@>;
9578 @ @<Make |link(pp)| point to a copy of object |p|, and update |p| and |pp|@>=
9579 { k=mp->gr_object_size[type(p)];
9580 link(pp)=mp_get_node(mp, k);
9582 while ( (k>0) ) { decr(k); mp->mem[pp+k]=mp->mem[p+k]; };
9583 @<Fix anything in graphical object |pp| that should differ from the
9584 corresponding field in |p|@>;
9588 @ @<Fix anything in graphical object |pp| that should differ from the...@>=
9590 case mp_start_clip_code:
9591 case mp_start_bounds_code:
9592 path_p(pp)=mp_copy_path(mp, path_p(p));
9595 path_p(pp)=mp_copy_path(mp, path_p(p));
9596 if ( pen_p(p)!=null ) pen_p(pp)=copy_pen(pen_p(p));
9598 case mp_stroked_code:
9599 path_p(pp)=mp_copy_path(mp, path_p(p));
9600 pen_p(pp)=copy_pen(pen_p(p));
9601 if ( dash_p(p)!=null ) add_edge_ref(dash_p(pp));
9604 add_str_ref(text_p(pp));
9606 case mp_stop_clip_code:
9607 case mp_stop_bounds_code:
9609 } /* there are no other cases */
9611 @ Here is one way to find an acceptable value for the second argument to
9612 |copy_objects|. Given a non-null graphical object list, |skip_1component|
9613 skips past one picture component, where a ``picture component'' is a single
9614 graphical object, or a start bounds or start clip object and everything up
9615 through the matching stop bounds or stop clip object. The macro version avoids
9616 procedure call overhead and error handling: |skip_component(p)(e)| advances |p|
9617 unless |p| points to a stop bounds or stop clip node, in which case it executes
9620 @d skip_component(A)
9621 if ( ! is_start_or_stop((A)) ) (A)=link((A));
9622 else if ( ! is_stop((A)) ) (A)=mp_skip_1component(mp, (A));
9626 pointer mp_skip_1component (MP mp,pointer p) {
9627 integer lev; /* current nesting level */
9630 if ( is_start_or_stop(p) ) {
9631 if ( is_stop(p) ) decr(lev); else incr(lev);
9638 @ Here is a diagnostic routine for printing an edge structure in symbolic form.
9640 @<Declare subroutines for printing expressions@>=
9641 @<Declare subroutines needed by |print_edges|@>;
9642 void mp_print_edges (MP mp,pointer h, char *s, boolean nuline) {
9643 pointer p; /* a graphical object to be printed */
9644 pointer hh,pp; /* temporary pointers */
9645 scaled scf; /* a scale factor for the dash pattern */
9646 boolean ok_to_dash; /* |false| for polygonal pen strokes */
9647 mp_print_diagnostic(mp, "Edge structure",s,nuline);
9649 while ( link(p)!=null ) {
9653 @<Cases for printing graphical object node |p|@>;
9655 mp_print(mp, "[unknown object type!]");
9659 mp_print_nl(mp, "End edges");
9660 if ( p!=obj_tail(h) ) mp_print(mp, "?");
9662 mp_end_diagnostic(mp, true);
9665 @ @<Cases for printing graphical object node |p|@>=
9667 mp_print(mp, "Filled contour ");
9668 mp_print_obj_color(mp, p);
9669 mp_print_char(mp, ':'); mp_print_ln(mp);
9670 mp_pr_path(mp, path_p(p)); mp_print_ln(mp);
9671 if ( (pen_p(p)!=null) ) {
9672 @<Print join type for graphical object |p|@>;
9673 mp_print(mp, " with pen"); mp_print_ln(mp);
9674 mp_pr_pen(mp, pen_p(p));
9678 @ @<Print join type for graphical object |p|@>=
9679 switch (ljoin_val(p)) {
9681 mp_print(mp, "mitered joins limited ");
9682 mp_print_scaled(mp, miterlim_val(p));
9685 mp_print(mp, "round joins");
9688 mp_print(mp, "beveled joins");
9691 mp_print(mp, "?? joins");
9696 @ For stroked nodes, we need to print |lcap_val(p)| as well.
9698 @<Print join and cap types for stroked node |p|@>=
9699 switch (lcap_val(p)) {
9700 case 0:mp_print(mp, "butt"); break;
9701 case 1:mp_print(mp, "round"); break;
9702 case 2:mp_print(mp, "square"); break;
9703 default: mp_print(mp, "??"); break;
9706 mp_print(mp, " ends, ");
9707 @<Print join type for graphical object |p|@>
9709 @ Here is a routine that prints the color of a graphical object if it isn't
9710 black (the default color).
9712 @<Declare subroutines needed by |print_edges|@>=
9713 @<Declare a procedure called |print_compact_node|@>;
9714 void mp_print_obj_color (MP mp,pointer p) {
9715 if ( color_model(p)==mp_grey_model ) {
9716 if ( grey_val(p)>0 ) {
9717 mp_print(mp, "greyed ");
9718 mp_print_compact_node(mp, obj_grey_loc(p),1);
9720 } else if ( color_model(p)==mp_cmyk_model ) {
9721 if ( (cyan_val(p)>0) || (magenta_val(p)>0) ||
9722 (yellow_val(p)>0) || (black_val(p)>0) ) {
9723 mp_print(mp, "processcolored ");
9724 mp_print_compact_node(mp, obj_cyan_loc(p),4);
9726 } else if ( color_model(p)==mp_rgb_model ) {
9727 if ( (red_val(p)>0) || (green_val(p)>0) || (blue_val(p)>0) ) {
9728 mp_print(mp, "colored ");
9729 mp_print_compact_node(mp, obj_red_loc(p),3);
9734 @ We also need a procedure for printing consecutive scaled values as if they
9735 were a known big node.
9737 @<Declare a procedure called |print_compact_node|@>=
9738 void mp_print_compact_node (MP mp,pointer p, small_number k) {
9739 pointer q; /* last location to print */
9741 mp_print_char(mp, '(');
9743 mp_print_scaled(mp, mp->mem[p].sc);
9744 if ( p<q ) mp_print_char(mp, ',');
9747 mp_print_char(mp, ')');
9750 @ @<Cases for printing graphical object node |p|@>=
9751 case mp_stroked_code:
9752 mp_print(mp, "Filled pen stroke ");
9753 mp_print_obj_color(mp, p);
9754 mp_print_char(mp, ':'); mp_print_ln(mp);
9755 mp_pr_path(mp, path_p(p));
9756 if ( dash_p(p)!=null ) {
9757 mp_print_nl(mp, "dashed (");
9758 @<Finish printing the dash pattern that |p| refers to@>;
9761 @<Print join and cap types for stroked node |p|@>;
9762 mp_print(mp, " with pen"); mp_print_ln(mp);
9763 if ( pen_p(p)==null ) mp_print(mp, "???"); /* shouldn't happen */
9765 else mp_pr_pen(mp, pen_p(p));
9768 @ Normally, the |dash_list| field in an edge header is set to |null_dash|
9769 when it is not known to define a suitable dash pattern. This is disallowed
9770 here because the |dash_p| field should never point to such an edge header.
9771 Note that memory is allocated for |start_x(null_dash)| and we are free to
9772 give it any convenient value.
9774 @<Finish printing the dash pattern that |p| refers to@>=
9775 ok_to_dash=pen_is_elliptical(pen_p(p));
9776 if ( ! ok_to_dash ) scf=unity; else scf=dash_scale(p);
9779 if ( (pp==null_dash) || (dash_y(hh)<0) ) {
9780 mp_print(mp, " ??");
9781 } else { start_x(null_dash)=start_x(pp)+dash_y(hh);
9782 while ( pp!=null_dash ) {
9783 mp_print(mp, "on ");
9784 mp_print_scaled(mp, mp_take_scaled(mp, stop_x(pp)-start_x(pp),scf));
9785 mp_print(mp, " off ");
9786 mp_print_scaled(mp, mp_take_scaled(mp, start_x(link(pp))-stop_x(pp),scf));
9788 if ( pp!=null_dash ) mp_print_char(mp, ' ');
9790 mp_print(mp, ") shifted ");
9791 mp_print_scaled(mp, -mp_take_scaled(mp, mp_dash_offset(mp, hh),scf));
9792 if ( ! ok_to_dash || (dash_y(hh)==0) ) mp_print(mp, " (this will be ignored)");
9795 @ @<Declare subroutines needed by |print_edges|@>=
9796 scaled mp_dash_offset (MP mp,pointer h) {
9797 scaled x; /* the answer */
9798 if (dash_list(h)==null_dash || dash_y(h)<0) mp_confusion(mp, "dash0");
9799 @:this can't happen dash0}{\quad dash0@>
9800 if ( dash_y(h)==0 ) {
9803 x=-(start_x(dash_list(h)) % dash_y(h));
9804 if ( x<0 ) x=x+dash_y(h);
9809 @ @<Cases for printing graphical object node |p|@>=
9811 mp_print_char(mp, '"'); mp_print_str(mp,text_p(p));
9812 mp_print(mp, "\" infont \""); mp_print(mp, mp->font_name[font_n(p)]);
9813 mp_print_char(mp, '"'); mp_print_ln(mp);
9814 mp_print_obj_color(mp, p);
9815 mp_print(mp, "transformed ");
9816 mp_print_compact_node(mp, text_tx_loc(p),6);
9819 @ @<Cases for printing graphical object node |p|@>=
9820 case mp_start_clip_code:
9821 mp_print(mp, "clipping path:");
9823 mp_pr_path(mp, path_p(p));
9825 case mp_stop_clip_code:
9826 mp_print(mp, "stop clipping");
9829 @ @<Cases for printing graphical object node |p|@>=
9830 case mp_start_bounds_code:
9831 mp_print(mp, "setbounds path:");
9833 mp_pr_path(mp, path_p(p));
9835 case mp_stop_bounds_code:
9836 mp_print(mp, "end of setbounds");
9839 @ To initialize the |dash_list| field in an edge header~|h|, we need a
9840 subroutine that scans an edge structure and tries to interpret it as a dash
9841 pattern. This can only be done when there are no filled regions or clipping
9842 paths and all the pen strokes have the same color. The first step is to let
9843 $y_0$ be the initial $y$~coordinate of the first pen stroke. Then we implicitly
9844 project all the pen stroke paths onto the line $y=y_0$ and require that there
9845 be no retracing. If the resulting paths cover a range of $x$~coordinates of
9846 length $\Delta x$, we set |dash_y(h)| to the length of the dash pattern by
9847 finding the maximum of $\Delta x$ and the absolute value of~$y_0$.
9849 @c @<Declare a procedure called |x_retrace_error|@>;
9850 pointer mp_make_dashes (MP mp,pointer h) { /* returns |h| or |null| */
9851 pointer p; /* this scans the stroked nodes in the object list */
9852 pointer p0; /* if not |null| this points to the first stroked node */
9853 pointer pp,qq,rr; /* pointers into |path_p(p)| */
9854 pointer d,dd; /* pointers used to create the dash list */
9855 @<Other local variables in |make_dashes|@>;
9856 scaled y0=0; /* the initial $y$ coordinate */
9857 if ( dash_list(h)!=null_dash )
9860 p=link(dummy_loc(h));
9862 if ( type(p)!=mp_stroked_code ) {
9863 @<Compain that the edge structure contains a node of the wrong type
9864 and |goto not_found|@>;
9867 if ( p0==null ){ p0=p; y0=y_coord(pp); };
9868 @<Make |d| point to a new dash node created from stroke |p| and path |pp|
9869 or |goto not_found| if there is an error@>;
9870 @<Insert |d| into the dash list and |goto not_found| if there is an error@>;
9873 if ( dash_list(h)==null_dash )
9874 goto NOT_FOUND; /* No error message */
9875 @<Scan |dash_list(h)| and deal with any dashes that are themselves dashed@>;
9876 @<Set |dash_y(h)| and merge the first and last dashes if necessary@>;
9879 @<Flush the dash list, recycle |h| and return |null|@>;
9882 @ @<Compain that the edge structure contains a node of the wrong type...@>=
9884 print_err("Picture is too complicated to use as a dash pattern");
9885 help3("When you say `dashed p', picture p should not contain any")
9886 ("text, filled regions, or clipping paths. This time it did")
9887 ("so I'll just make it a solid line instead.");
9888 mp_put_get_error(mp);
9892 @ A similar error occurs when monotonicity fails.
9894 @<Declare a procedure called |x_retrace_error|@>=
9895 void mp_x_retrace_error (MP mp) {
9896 print_err("Picture is too complicated to use as a dash pattern");
9897 help3("When you say `dashed p', every path in p should be monotone")
9898 ("in x and there must be no overlapping. This failed")
9899 ("so I'll just make it a solid line instead.");
9900 mp_put_get_error(mp);
9903 @ We stash |p| in |info(d)| if |dash_p(p)<>0| so that subsequent processing can
9904 handle the case where the pen stroke |p| is itself dashed.
9906 @<Make |d| point to a new dash node created from stroke |p| and path...@>=
9907 @<Make sure |p| and |p0| are the same color and |goto not_found| if there is
9910 if ( link(pp)!=pp ) {
9913 @<Check for retracing between knots |qq| and |rr| and |goto not_found|
9914 if there is a problem@>;
9915 } while (right_type(rr)!=mp_endpoint);
9917 d=mp_get_node(mp, dash_node_size);
9918 if ( dash_p(p)==0 ) info(d)=0; else info(d)=p;
9919 if ( x_coord(pp)<x_coord(rr) ) {
9920 start_x(d)=x_coord(pp);
9921 stop_x(d)=x_coord(rr);
9923 start_x(d)=x_coord(rr);
9924 stop_x(d)=x_coord(pp);
9927 @ We also need to check for the case where the segment from |qq| to |rr| is
9928 monotone in $x$ but is reversed relative to the path from |pp| to |qq|.
9930 @<Check for retracing between knots |qq| and |rr| and |goto not_found|...@>=
9935 if ( (x0>x1) || (x1>x2) || (x2>x3) ) {
9936 if ( (x0<x1) || (x1<x2) || (x2<x3) ) {
9937 if ( mp_ab_vs_cd(mp, x2-x1,x2-x1,x1-x0,x3-x2)>0 ) {
9938 mp_x_retrace_error(mp); goto NOT_FOUND;
9942 if ( (x_coord(pp)>x0) || (x0>x3) ) {
9943 if ( (x_coord(pp)<x0) || (x0<x3) ) {
9944 mp_x_retrace_error(mp); goto NOT_FOUND;
9948 @ @<Other local variables in |make_dashes|@>=
9949 scaled x0,x1,x2,x3; /* $x$ coordinates of the segment from |qq| to |rr| */
9951 @ @<Make sure |p| and |p0| are the same color and |goto not_found|...@>=
9952 if ( (red_val(p)!=red_val(p0)) || (black_val(p)!=black_val(p0)) ||
9953 (green_val(p)!=green_val(p0)) || (blue_val(p)!=blue_val(p0)) ) {
9954 print_err("Picture is too complicated to use as a dash pattern");
9955 help3("When you say `dashed p', everything in picture p should")
9956 ("be the same color. I can\'t handle your color changes")
9957 ("so I'll just make it a solid line instead.");
9958 mp_put_get_error(mp);
9962 @ @<Insert |d| into the dash list and |goto not_found| if there is an error@>=
9963 start_x(null_dash)=stop_x(d);
9964 dd=h; /* this makes |link(dd)=dash_list(h)| */
9965 while ( start_x(link(dd))<stop_x(d) )
9968 if ( (stop_x(dd)>start_x(d)) )
9969 { mp_x_retrace_error(mp); goto NOT_FOUND; };
9974 @ @<Set |dash_y(h)| and merge the first and last dashes if necessary@>=
9976 while ( (link(d)!=null_dash) )
9979 dash_y(h)=stop_x(d)-start_x(dd);
9980 if ( abs(y0)>dash_y(h) ) {
9982 } else if ( d!=dd ) {
9983 dash_list(h)=link(dd);
9984 stop_x(d)=stop_x(dd)+dash_y(h);
9985 mp_free_node(mp, dd,dash_node_size);
9988 @ We get here when the argument is a null picture or when there is an error.
9989 Recovering from an error involves making |dash_list(h)| empty to indicate
9990 that |h| is not known to be a valid dash pattern. We also dereference |h|
9991 since it is not being used for the return value.
9993 @<Flush the dash list, recycle |h| and return |null|@>=
9994 mp_flush_dash_list(mp, h);
9998 @ Having carefully saved the dashed stroked nodes in the
9999 corresponding dash nodes, we must be prepared to break up these dashes into
10002 @<Scan |dash_list(h)| and deal with any dashes that are themselves dashed@>=
10003 d=h; /* now |link(d)=dash_list(h)| */
10004 while ( link(d)!=null_dash ) {
10010 hsf=dash_scale(ds);
10011 if ( (hh==null) ) mp_confusion(mp, "dash1");
10012 @:this can't happen dash0}{\quad dash1@>
10013 if ( dash_y(hh)==0 ) {
10016 if ( dash_list(hh)==null ) mp_confusion(mp, "dash1");
10017 @:this can't happen dash0}{\quad dash1@>
10018 @<Replace |link(d)| by a dashed version as determined by edge header
10019 |hh| and scale factor |ds|@>;
10024 @ @<Other local variables in |make_dashes|@>=
10025 pointer dln; /* |link(d)| */
10026 pointer hh; /* an edge header that tells how to break up |dln| */
10027 scaled hsf; /* the dash pattern from |hh| gets scaled by this */
10028 pointer ds; /* the stroked node from which |hh| and |hsf| are derived */
10029 scaled xoff; /* added to $x$ values in |dash_list(hh)| to match |dln| */
10031 @ @<Replace |link(d)| by a dashed version as determined by edge header...@>=
10034 xoff=start_x(dln)-mp_take_scaled(mp, hsf,start_x(dd))-
10035 mp_take_scaled(mp, hsf,mp_dash_offset(mp, hh));
10036 start_x(null_dash)=mp_take_scaled(mp, hsf,start_x(dd))
10037 +mp_take_scaled(mp, hsf,dash_y(hh));
10038 stop_x(null_dash)=start_x(null_dash);
10039 @<Advance |dd| until finding the first dash that overlaps |dln| when
10040 offset by |xoff|@>;
10041 while ( start_x(dln)<=stop_x(dln) ) {
10042 @<If |dd| has `fallen off the end', back up to the beginning and fix |xoff|@>;
10043 @<Insert a dash between |d| and |dln| for the overlap with the offset version
10046 start_x(dln)=xoff+mp_take_scaled(mp, hsf,start_x(dd));
10049 mp_free_node(mp, dln,dash_node_size)
10051 @ The name of this module is a bit of a lie because we just find the
10052 first |dd| where |take_scaled (hsf, stop_x(dd))| is large enough to make an
10053 overlap possible. It could be that the unoffset version of dash |dln| falls
10054 in the gap between |dd| and its predecessor.
10056 @<Advance |dd| until finding the first dash that overlaps |dln| when...@>=
10057 while ( xoff+mp_take_scaled(mp, hsf,stop_x(dd))<start_x(dln) ) {
10061 @ @<If |dd| has `fallen off the end', back up to the beginning and fix...@>=
10062 if ( dd==null_dash ) {
10064 xoff=xoff+mp_take_scaled(mp, hsf,dash_y(hh));
10067 @ At this point we already know that
10068 |start_x(dln)<=xoff+take_scaled(hsf,stop_x(dd))|.
10070 @<Insert a dash between |d| and |dln| for the overlap with the offset...@>=
10071 if ( (xoff+mp_take_scaled(mp, hsf,start_x(dd)))<=stop_x(dln) ) {
10072 link(d)=mp_get_node(mp, dash_node_size);
10075 if ( start_x(dln)>(xoff+mp_take_scaled(mp, hsf,start_x(dd))))
10076 start_x(d)=start_x(dln);
10078 start_x(d)=xoff+mp_take_scaled(mp, hsf,start_x(dd));
10079 if ( stop_x(dln)<(xoff+mp_take_scaled(mp, hsf,stop_x(dd))))
10080 stop_x(d)=stop_x(dln);
10082 stop_x(d)=xoff+mp_take_scaled(mp, hsf,stop_x(dd));
10085 @ The next major task is to update the bounding box information in an edge
10086 header~|h|. This is done via a procedure |adjust_bbox| that enlarges an edge
10087 header's bounding box to accommodate the box computed by |path_bbox| or
10088 |pen_bbox|. (This is stored in global variables |minx|, |miny|, |maxx|, and
10091 @c void mp_adjust_bbox (MP mp,pointer h) {
10092 if ( minx<minx_val(h) ) minx_val(h)=minx;
10093 if ( miny<miny_val(h) ) miny_val(h)=miny;
10094 if ( maxx>maxx_val(h) ) maxx_val(h)=maxx;
10095 if ( maxy>maxy_val(h) ) maxy_val(h)=maxy;
10098 @ Here is a special routine for updating the bounding box information in
10099 edge header~|h| to account for the squared-off ends of a non-cyclic path~|p|
10100 that is to be stroked with the pen~|pp|.
10102 @c void mp_box_ends (MP mp, pointer p, pointer pp, pointer h) {
10103 pointer q; /* a knot node adjacent to knot |p| */
10104 fraction dx,dy; /* a unit vector in the direction out of the path at~|p| */
10105 scaled d; /* a factor for adjusting the length of |(dx,dy)| */
10106 scaled z; /* a coordinate being tested against the bounding box */
10107 scaled xx,yy; /* the extreme pen vertex in the |(dx,dy)| direction */
10108 integer i; /* a loop counter */
10109 if ( right_type(p)!=mp_endpoint ) {
10112 @<Make |(dx,dy)| the final direction for the path segment from
10113 |q| to~|p|; set~|d|@>;
10114 d=mp_pyth_add(mp, dx,dy);
10116 @<Normalize the direction |(dx,dy)| and find the pen offset |(xx,yy)|@>;
10117 for (i=1;i<= 2;i++) {
10118 @<Use |(dx,dy)| to generate a vertex of the square end cap and
10119 update the bounding box to accommodate it@>;
10123 if ( right_type(p)==mp_endpoint ) {
10126 @<Advance |p| to the end of the path and make |q| the previous knot@>;
10132 @ @<Make |(dx,dy)| the final direction for the path segment from...@>=
10133 if ( q==link(p) ) {
10134 dx=x_coord(p)-right_x(p);
10135 dy=y_coord(p)-right_y(p);
10136 if ( (dx==0)&&(dy==0) ) {
10137 dx=x_coord(p)-left_x(q);
10138 dy=y_coord(p)-left_y(q);
10141 dx=x_coord(p)-left_x(p);
10142 dy=y_coord(p)-left_y(p);
10143 if ( (dx==0)&&(dy==0) ) {
10144 dx=x_coord(p)-right_x(q);
10145 dy=y_coord(p)-right_y(q);
10148 dx=x_coord(p)-x_coord(q);
10149 dy=y_coord(p)-y_coord(q)
10151 @ @<Normalize the direction |(dx,dy)| and find the pen offset |(xx,yy)|@>=
10152 dx=mp_make_fraction(mp, dx,d);
10153 dy=mp_make_fraction(mp, dy,d);
10154 mp_find_offset(mp, -dy,dx,pp);
10155 xx=mp->cur_x; yy=mp->cur_y
10157 @ @<Use |(dx,dy)| to generate a vertex of the square end cap and...@>=
10158 mp_find_offset(mp, dx,dy,pp);
10159 d=mp_take_fraction(mp, xx-mp->cur_x,dx)+mp_take_fraction(mp, yy-mp->cur_y,dy);
10160 if ( ((d<0)&&(i==1)) || ((d>0)&&(i==2)))
10161 mp_confusion(mp, "box_ends");
10162 @:this can't happen box ends}{\quad\\{box\_ends}@>
10163 z=x_coord(p)+mp->cur_x+mp_take_fraction(mp, d,dx);
10164 if ( z<minx_val(h) ) minx_val(h)=z;
10165 if ( z>maxx_val(h) ) maxx_val(h)=z;
10166 z=y_coord(p)+mp->cur_y+mp_take_fraction(mp, d,dy);
10167 if ( z<miny_val(h) ) miny_val(h)=z;
10168 if ( z>maxy_val(h) ) maxy_val(h)=z
10170 @ @<Advance |p| to the end of the path and make |q| the previous knot@>=
10174 } while (right_type(p)!=mp_endpoint)
10176 @ The major difficulty in finding the bounding box of an edge structure is the
10177 effect of clipping paths. We treat them conservatively by only clipping to the
10178 clipping path's bounding box, but this still
10179 requires recursive calls to |set_bbox| in order to find the bounding box of
10181 the objects to be clipped. Such calls are distinguished by the fact that the
10182 boolean parameter |top_level| is false.
10184 @c void mp_set_bbox (MP mp,pointer h, boolean top_level) {
10185 pointer p; /* a graphical object being considered */
10186 scaled sminx,sminy,smaxx,smaxy;
10187 /* for saving the bounding box during recursive calls */
10188 scaled x0,x1,y0,y1; /* temporary registers */
10189 integer lev; /* nesting level for |mp_start_bounds_code| nodes */
10190 @<Wipe out any existing bounding box information if |bbtype(h)| is
10191 incompatible with |internal[mp_true_corners]|@>;
10192 while ( link(bblast(h))!=null ) {
10196 case mp_stop_clip_code:
10197 if ( top_level ) mp_confusion(mp, "bbox"); else return;
10198 @:this can't happen bbox}{\quad bbox@>
10200 @<Other cases for updating the bounding box based on the type of object |p|@>;
10201 } /* all cases are enumerated above */
10203 if ( ! top_level ) mp_confusion(mp, "bbox");
10206 @ @<Internal library declarations@>=
10207 void mp_set_bbox (MP mp,pointer h, boolean top_level);
10209 @ @<Wipe out any existing bounding box information if |bbtype(h)| is...@>=
10210 switch (bbtype(h)) {
10214 if ( mp->internal[mp_true_corners]>0 ) mp_init_bbox(mp, h);
10217 if ( mp->internal[mp_true_corners]<=0 ) mp_init_bbox(mp, h);
10219 } /* there are no other cases */
10221 @ @<Other cases for updating the bounding box...@>=
10223 mp_path_bbox(mp, path_p(p));
10224 if ( pen_p(p)!=null ) {
10227 mp_pen_bbox(mp, pen_p(p));
10233 mp_adjust_bbox(mp, h);
10236 @ @<Other cases for updating the bounding box...@>=
10237 case mp_start_bounds_code:
10238 if ( mp->internal[mp_true_corners]>0 ) {
10239 bbtype(h)=bounds_unset;
10241 bbtype(h)=bounds_set;
10242 mp_path_bbox(mp, path_p(p));
10243 mp_adjust_bbox(mp, h);
10244 @<Scan to the matching |mp_stop_bounds_code| node and update |p| and
10248 case mp_stop_bounds_code:
10249 if ( mp->internal[mp_true_corners]<=0 ) mp_confusion(mp, "bbox2");
10250 @:this can't happen bbox2}{\quad bbox2@>
10253 @ @<Scan to the matching |mp_stop_bounds_code| node and update |p| and...@>=
10256 if ( link(p)==null ) mp_confusion(mp, "bbox2");
10257 @:this can't happen bbox2}{\quad bbox2@>
10259 if ( type(p)==mp_start_bounds_code ) incr(lev);
10260 else if ( type(p)==mp_stop_bounds_code ) decr(lev);
10264 @ It saves a lot of grief here to be slightly conservative and not account for
10265 omitted parts of dashed lines. We also don't worry about the material omitted
10266 when using butt end caps. The basic computation is for round end caps and
10267 |box_ends| augments it for square end caps.
10269 @<Other cases for updating the bounding box...@>=
10270 case mp_stroked_code:
10271 mp_path_bbox(mp, path_p(p));
10274 mp_pen_bbox(mp, pen_p(p));
10279 mp_adjust_bbox(mp, h);
10280 if ( (left_type(path_p(p))==mp_endpoint)&&(lcap_val(p)==2) )
10281 mp_box_ends(mp, path_p(p), pen_p(p), h);
10284 @ The height width and depth information stored in a text node determines a
10285 rectangle that needs to be transformed according to the transformation
10286 parameters stored in the text node.
10288 @<Other cases for updating the bounding box...@>=
10290 x1=mp_take_scaled(mp, txx_val(p),width_val(p));
10291 y0=mp_take_scaled(mp, txy_val(p),-depth_val(p));
10292 y1=mp_take_scaled(mp, txy_val(p),height_val(p));
10295 if ( y0<y1 ) { minx=minx+y0; maxx=maxx+y1; }
10296 else { minx=minx+y1; maxx=maxx+y0; }
10297 if ( x1<0 ) minx=minx+x1; else maxx=maxx+x1;
10298 x1=mp_take_scaled(mp, tyx_val(p),width_val(p));
10299 y0=mp_take_scaled(mp, tyy_val(p),-depth_val(p));
10300 y1=mp_take_scaled(mp, tyy_val(p),height_val(p));
10303 if ( y0<y1 ) { miny=miny+y0; maxy=maxy+y1; }
10304 else { miny=miny+y1; maxy=maxy+y0; }
10305 if ( x1<0 ) miny=miny+x1; else maxy=maxy+x1;
10306 mp_adjust_bbox(mp, h);
10309 @ This case involves a recursive call that advances |bblast(h)| to the node of
10310 type |mp_stop_clip_code| that matches |p|.
10312 @<Other cases for updating the bounding box...@>=
10313 case mp_start_clip_code:
10314 mp_path_bbox(mp, path_p(p));
10317 sminx=minx_val(h); sminy=miny_val(h);
10318 smaxx=maxx_val(h); smaxy=maxy_val(h);
10319 @<Reinitialize the bounding box in header |h| and call |set_bbox| recursively
10320 starting at |link(p)|@>;
10321 @<Clip the bounding box in |h| to the rectangle given by |x0|, |x1|,
10323 minx=sminx; miny=sminy;
10324 maxx=smaxx; maxy=smaxy;
10325 mp_adjust_bbox(mp, h);
10328 @ @<Reinitialize the bounding box in header |h| and call |set_bbox|...@>=
10329 minx_val(h)=el_gordo;
10330 miny_val(h)=el_gordo;
10331 maxx_val(h)=-el_gordo;
10332 maxy_val(h)=-el_gordo;
10333 mp_set_bbox(mp, h,false)
10335 @ @<Clip the bounding box in |h| to the rectangle given by |x0|, |x1|,...@>=
10336 if ( minx_val(h)<x0 ) minx_val(h)=x0;
10337 if ( miny_val(h)<y0 ) miny_val(h)=y0;
10338 if ( maxx_val(h)>x1 ) maxx_val(h)=x1;
10339 if ( maxy_val(h)>y1 ) maxy_val(h)=y1
10341 @* \[22] Finding an envelope.
10342 When \MP\ has a path and a polygonal pen, it needs to express the desired
10343 shape in terms of things \ps\ can understand. The present task is to compute
10344 a new path that describes the region to be filled. It is convenient to
10345 define this as a two step process where the first step is determining what
10346 offset to use for each segment of the path.
10348 @ Given a pointer |c| to a cyclic path,
10349 and a pointer~|h| to the first knot of a pen polygon,
10350 the |offset_prep| routine changes the path into cubics that are
10351 associated with particular pen offsets. Thus if the cubic between |p|
10352 and~|q| is associated with the |k|th offset and the cubic between |q| and~|r|
10353 has offset |l| then |info(q)=zero_off+l-k|. (The constant |zero_off| is added
10354 to because |l-k| could be negative.)
10356 After overwriting the type information with offset differences, we no longer
10357 have a true path so we refer to the knot list returned by |offset_prep| as an
10360 Since an envelope spec only determines relative changes in pen offsets,
10361 |offset_prep| sets a global variable |spec_offset| to the relative change from
10362 |h| to the first offset.
10364 @d zero_off 16384 /* added to offset changes to make them positive */
10367 integer spec_offset; /* number of pen edges between |h| and the initial offset */
10369 @ @c @<Declare subroutines needed by |offset_prep|@>;
10370 pointer mp_offset_prep (MP mp,pointer c, pointer h) {
10371 halfword n; /* the number of vertices in the pen polygon */
10372 pointer p,q,q0,r,w, ww; /* for list manipulation */
10373 integer k_needed; /* amount to be added to |info(p)| when it is computed */
10374 pointer w0; /* a pointer to pen offset to use just before |p| */
10375 scaled dxin,dyin; /* the direction into knot |p| */
10376 integer turn_amt; /* change in pen offsets for the current cubic */
10377 @<Other local variables for |offset_prep|@>;
10379 @<Initialize the pen size~|n|@>;
10380 @<Initialize the incoming direction and pen offset at |c|@>;
10384 @<Split the cubic between |p| and |q|, if necessary, into cubics
10385 associated with single offsets, after which |q| should
10386 point to the end of the final such cubic@>;
10388 @<Advance |p| to node |q|, removing any ``dead'' cubics that
10389 might have been introduced by the splitting process@>;
10391 @<Fix the offset change in |info(c)| and set |c| to the return value of
10396 @ We shall want to keep track of where certain knots on the cyclic path
10397 wind up in the envelope spec. It doesn't suffice just to keep pointers to
10398 knot nodes because some nodes are deleted while removing dead cubics. Thus
10399 |offset_prep| updates the following pointers
10403 pointer spec_p2; /* pointers to distinguished knots */
10406 mp->spec_p1=null; mp->spec_p2=null;
10408 @ @<Initialize the pen size~|n|@>=
10415 @ Since the true incoming direction isn't known yet, we just pick a direction
10416 consistent with the pen offset~|h|. If this is wrong, it can be corrected
10419 @<Initialize the incoming direction and pen offset at |c|@>=
10420 dxin=x_coord(link(h))-x_coord(knil(h));
10421 dyin=y_coord(link(h))-y_coord(knil(h));
10422 if ( (dxin==0)&&(dyin==0) ) {
10423 dxin=y_coord(knil(h))-y_coord(h);
10424 dyin=x_coord(h)-x_coord(knil(h));
10428 @ We must be careful not to remove the only cubic in a cycle.
10430 But we must also be careful for another reason. If the user-supplied
10431 path starts with a set of degenerate cubics, the target node |q| can
10432 be collapsed to the initial node |p| which might be the same as the
10433 initial node |c| of the curve. This would cause the |offset_prep| routine
10434 to bail out too early, causing distress later on. (See for example
10435 the testcase reported by Bogus\l{}aw Jackowski in tracker id 267, case 52c
10438 @<Advance |p| to node |q|, removing any ``dead'' cubics...@>=
10442 if ( x_coord(p)==right_x(p) && y_coord(p)==right_y(p) &&
10443 x_coord(p)==left_x(r) && y_coord(p)==left_y(r) &&
10444 x_coord(p)==x_coord(r) && y_coord(p)==y_coord(r) &&
10446 @<Remove the cubic following |p| and update the data structures
10447 to merge |r| into |p|@>;
10451 /* Check if we removed too much */
10455 @ @<Remove the cubic following |p| and update the data structures...@>=
10456 { k_needed=info(p)-zero_off;
10460 info(p)=k_needed+info(r);
10463 if ( r==c ) { info(p)=info(c); c=p; };
10464 if ( r==mp->spec_p1 ) mp->spec_p1=p;
10465 if ( r==mp->spec_p2 ) mp->spec_p2=p;
10466 r=p; mp_remove_cubic(mp, p);
10469 @ Not setting the |info| field of the newly created knot allows the splitting
10470 routine to work for paths.
10472 @<Declare subroutines needed by |offset_prep|@>=
10473 void mp_split_cubic (MP mp,pointer p, fraction t) { /* splits the cubic after |p| */
10474 scaled v; /* an intermediate value */
10475 pointer q,r; /* for list manipulation */
10476 q=link(p); r=mp_get_node(mp, knot_node_size); link(p)=r; link(r)=q;
10477 originator(r)=mp_program_code;
10478 left_type(r)=mp_explicit; right_type(r)=mp_explicit;
10479 v=t_of_the_way(right_x(p),left_x(q));
10480 right_x(p)=t_of_the_way(x_coord(p),right_x(p));
10481 left_x(q)=t_of_the_way(left_x(q),x_coord(q));
10482 left_x(r)=t_of_the_way(right_x(p),v);
10483 right_x(r)=t_of_the_way(v,left_x(q));
10484 x_coord(r)=t_of_the_way(left_x(r),right_x(r));
10485 v=t_of_the_way(right_y(p),left_y(q));
10486 right_y(p)=t_of_the_way(y_coord(p),right_y(p));
10487 left_y(q)=t_of_the_way(left_y(q),y_coord(q));
10488 left_y(r)=t_of_the_way(right_y(p),v);
10489 right_y(r)=t_of_the_way(v,left_y(q));
10490 y_coord(r)=t_of_the_way(left_y(r),right_y(r));
10493 @ This does not set |info(p)| or |right_type(p)|.
10495 @<Declare subroutines needed by |offset_prep|@>=
10496 void mp_remove_cubic (MP mp,pointer p) { /* removes the dead cubic following~|p| */
10497 pointer q; /* the node that disappears */
10498 q=link(p); link(p)=link(q);
10499 right_x(p)=right_x(q); right_y(p)=right_y(q);
10500 mp_free_node(mp, q,knot_node_size);
10503 @ Let $d\prec d'$ mean that the counter-clockwise angle from $d$ to~$d'$ is
10504 strictly between zero and $180^\circ$. Then we can define $d\preceq d'$ to
10505 mean that the angle could be zero or $180^\circ$. If $w_k=(u_k,v_k)$ is the
10506 $k$th pen offset, the $k$th pen edge direction is defined by the formula
10507 $$d_k=(u\k-u_k,\,v\k-v_k).$$
10508 When listed by increasing $k$, these directions occur in counter-clockwise
10509 order so that $d_k\preceq d\k$ for all~$k$.
10510 The goal of |offset_prep| is to find an offset index~|k| to associate with
10511 each cubic, such that the direction $d(t)$ of the cubic satisfies
10512 $$d_{k-1}\preceq d(t)\preceq d_k\qquad\hbox{for $0\le t\le 1$.}\eqno(*)$$
10513 We may have to split a cubic into many pieces before each
10514 piece corresponds to a unique offset.
10516 @<Split the cubic between |p| and |q|, if necessary, into cubics...@>=
10517 info(p)=zero_off+k_needed;
10519 @<Prepare for derivative computations;
10520 |goto not_found| if the current cubic is dead@>;
10521 @<Find the initial direction |(dx,dy)|@>;
10522 @<Update |info(p)| and find the offset $w_k$ such that
10523 $d_{k-1}\preceq(\\{dx},\\{dy})\prec d_k$; also advance |w0| for
10524 the direction change at |p|@>;
10525 @<Find the final direction |(dxin,dyin)|@>;
10526 @<Decide on the net change in pen offsets and set |turn_amt|@>;
10527 @<Complete the offset splitting process@>;
10528 w0=mp_pen_walk(mp, w0,turn_amt)
10530 @ @<Declare subroutines needed by |offset_prep|@>=
10531 pointer mp_pen_walk (MP mp,pointer w, integer k) {
10532 /* walk |k| steps around a pen from |w| */
10533 while ( k>0 ) { w=link(w); decr(k); };
10534 while ( k<0 ) { w=knil(w); incr(k); };
10538 @ The direction of a cubic $B(z_0,z_1,z_2,z_3;t)=\bigl(x(t),y(t)\bigr)$ can be
10539 calculated from the quadratic polynomials
10540 ${1\over3}x'(t)=B(x_1-x_0,x_2-x_1,x_3-x_2;t)$ and
10541 ${1\over3}y'(t)=B(y_1-y_0,y_2-y_1,y_3-y_2;t)$.
10542 Since we may be calculating directions from several cubics
10543 split from the current one, it is desirable to do these calculations
10544 without losing too much precision. ``Scaled up'' values of the
10545 derivatives, which will be less tainted by accumulated errors than
10546 derivatives found from the cubics themselves, are maintained in
10547 local variables |x0|, |x1|, and |x2|, representing $X_0=2^l(x_1-x_0)$,
10548 $X_1=2^l(x_2-x_1)$, and $X_2=2^l(x_3-x_2)$; similarly |y0|, |y1|, and~|y2|
10549 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)$.
10551 @<Other local variables for |offset_prep|@>=
10552 integer x0,x1,x2,y0,y1,y2; /* representatives of derivatives */
10553 integer t0,t1,t2; /* coefficients of polynomial for slope testing */
10554 integer du,dv,dx,dy; /* for directions of the pen and the curve */
10555 integer dx0,dy0; /* initial direction for the first cubic in the curve */
10556 integer max_coef; /* used while scaling */
10557 integer x0a,x1a,x2a,y0a,y1a,y2a; /* intermediate values */
10558 fraction t; /* where the derivative passes through zero */
10559 fraction s; /* a temporary value */
10561 @ @<Prepare for derivative computations...@>=
10562 x0=right_x(p)-x_coord(p);
10563 x2=x_coord(q)-left_x(q);
10564 x1=left_x(q)-right_x(p);
10565 y0=right_y(p)-y_coord(p); y2=y_coord(q)-left_y(q);
10566 y1=left_y(q)-right_y(p);
10568 if ( abs(x1)>max_coef ) max_coef=abs(x1);
10569 if ( abs(x2)>max_coef ) max_coef=abs(x2);
10570 if ( abs(y0)>max_coef ) max_coef=abs(y0);
10571 if ( abs(y1)>max_coef ) max_coef=abs(y1);
10572 if ( abs(y2)>max_coef ) max_coef=abs(y2);
10573 if ( max_coef==0 ) goto NOT_FOUND;
10574 while ( max_coef<fraction_half ) {
10576 double(x0); double(x1); double(x2);
10577 double(y0); double(y1); double(y2);
10580 @ Let us first solve a special case of the problem: Suppose we
10581 know an index~$k$ such that either (i)~$d(t)\succeq d_{k-1}$ for all~$t$
10582 and $d(0)\prec d_k$, or (ii)~$d(t)\preceq d_k$ for all~$t$ and
10583 $d(0)\succ d_{k-1}$.
10584 Then, in a sense, we're halfway done, since one of the two relations
10585 in $(*)$ is satisfied, and the other couldn't be satisfied for
10586 any other value of~|k|.
10588 Actually, the conditions can be relaxed somewhat since a relation such as
10589 $d(t)\succeq d_{k-1}$ restricts $d(t)$ to a half plane when all that really
10590 matters is whether $d(t)$ crosses the ray in the $d_{k-1}$ direction from
10591 the origin. The condition for case~(i) becomes $d_{k-1}\preceq d(0)\prec d_k$
10592 and $d(t)$ never crosses the $d_{k-1}$ ray in the clockwise direction.
10593 Case~(ii) is similar except $d(t)$ cannot cross the $d_k$ ray in the
10594 counterclockwise direction.
10596 The |fin_offset_prep| subroutine solves the stated subproblem.
10597 It has a parameter called |rise| that is |1| in
10598 case~(i), |-1| in case~(ii). Parameters |x0| through |y2| represent
10599 the derivative of the cubic following |p|.
10600 The |w| parameter should point to offset~$w_k$ and |info(p)| should already
10601 be set properly. The |turn_amt| parameter gives the absolute value of the
10602 overall net change in pen offsets.
10604 @<Declare subroutines needed by |offset_prep|@>=
10605 void mp_fin_offset_prep (MP mp,pointer p, pointer w, integer
10606 x0,integer x1, integer x2, integer y0, integer y1, integer y2,
10607 integer rise, integer turn_amt) {
10608 pointer ww; /* for list manipulation */
10609 scaled du,dv; /* for slope calculation */
10610 integer t0,t1,t2; /* test coefficients */
10611 fraction t; /* place where the derivative passes a critical slope */
10612 fraction s; /* slope or reciprocal slope */
10613 integer v; /* intermediate value for updating |x0..y2| */
10614 pointer q; /* original |link(p)| */
10617 if ( rise>0 ) ww=link(w); /* a pointer to $w\k$ */
10618 else ww=knil(w); /* a pointer to $w_{k-1}$ */
10619 @<Compute test coefficients |(t0,t1,t2)|
10620 for $d(t)$ versus $d_k$ or $d_{k-1}$@>;
10621 t=mp_crossing_point(mp, t0,t1,t2);
10622 if ( t>=fraction_one ) {
10623 if ( turn_amt>0 ) t=fraction_one; else return;
10625 @<Split the cubic at $t$,
10626 and split off another cubic if the derivative crosses back@>;
10631 @ We want $B(\\{t0},\\{t1},\\{t2};t)$ to be the dot product of $d(t)$ with a
10632 $-90^\circ$ rotation of the vector from |w| to |ww|. This makes the resulting
10633 function cross from positive to negative when $d_{k-1}\preceq d(t)\preceq d_k$
10636 @<Compute test coefficients |(t0,t1,t2)| for $d(t)$ versus...@>=
10637 du=x_coord(ww)-x_coord(w); dv=y_coord(ww)-y_coord(w);
10638 if ( abs(du)>=abs(dv) ) {
10639 s=mp_make_fraction(mp, dv,du);
10640 t0=mp_take_fraction(mp, x0,s)-y0;
10641 t1=mp_take_fraction(mp, x1,s)-y1;
10642 t2=mp_take_fraction(mp, x2,s)-y2;
10643 if ( du<0 ) { negate(t0); negate(t1); negate(t2); }
10645 s=mp_make_fraction(mp, du,dv);
10646 t0=x0-mp_take_fraction(mp, y0,s);
10647 t1=x1-mp_take_fraction(mp, y1,s);
10648 t2=x2-mp_take_fraction(mp, y2,s);
10649 if ( dv<0 ) { negate(t0); negate(t1); negate(t2); }
10651 if ( t0<0 ) t0=0 /* should be positive without rounding error */
10653 @ The curve has crossed $d_k$ or $d_{k-1}$; its initial segment satisfies
10654 $(*)$, and it might cross again, yielding another solution of $(*)$.
10656 @<Split the cubic at $t$, and split off another...@>=
10658 mp_split_cubic(mp, p,t); p=link(p); info(p)=zero_off+rise;
10660 v=t_of_the_way(x0,x1); x1=t_of_the_way(x1,x2);
10661 x0=t_of_the_way(v,x1);
10662 v=t_of_the_way(y0,y1); y1=t_of_the_way(y1,y2);
10663 y0=t_of_the_way(v,y1);
10664 if ( turn_amt<0 ) {
10665 t1=t_of_the_way(t1,t2);
10666 if ( t1>0 ) t1=0; /* without rounding error, |t1| would be |<=0| */
10667 t=mp_crossing_point(mp, 0,-t1,-t2);
10668 if ( t>fraction_one ) t=fraction_one;
10670 if ( (t==fraction_one)&&(link(p)!=q) ) {
10671 info(link(p))=info(link(p))-rise;
10673 mp_split_cubic(mp, p,t); info(link(p))=zero_off-rise;
10674 v=t_of_the_way(x1,x2); x1=t_of_the_way(x0,x1);
10675 x2=t_of_the_way(x1,v);
10676 v=t_of_the_way(y1,y2); y1=t_of_the_way(y0,y1);
10677 y2=t_of_the_way(y1,v);
10682 @ Now we must consider the general problem of |offset_prep|, when
10683 nothing is known about a given cubic. We start by finding its
10684 direction in the vicinity of |t=0|.
10686 If $z'(t)=0$, the given cubic is numerically unstable but |offset_prep|
10687 has not yet introduced any more numerical errors. Thus we can compute
10688 the true initial direction for the given cubic, even if it is almost
10691 @<Find the initial direction |(dx,dy)|@>=
10693 if ( dx==0 && dy==0 ) {
10695 if ( dx==0 && dy==0 ) {
10699 if ( p==c ) { dx0=dx; dy0=dy; }
10701 @ @<Find the final direction |(dxin,dyin)|@>=
10703 if ( dxin==0 && dyin==0 ) {
10705 if ( dxin==0 && dyin==0 ) {
10710 @ The next step is to bracket the initial direction between consecutive
10711 edges of the pen polygon. We must be careful to turn clockwise only if
10712 this makes the turn less than $180^\circ$. (A $180^\circ$ turn must be
10713 counter-clockwise in order to make \&{doublepath} envelopes come out
10714 @:double_path_}{\&{doublepath} primitive@>
10715 right.) This code depends on |w0| being the offset for |(dxin,dyin)|.
10717 @<Update |info(p)| and find the offset $w_k$ such that...@>=
10718 turn_amt=mp_get_turn_amt(mp,w0,dx,dy,(mp_ab_vs_cd(mp, dy,dxin,dx,dyin)>=0));
10719 w=mp_pen_walk(mp, w0, turn_amt);
10721 info(p)=info(p)+turn_amt
10723 @ Decide how many pen offsets to go away from |w| in order to find the offset
10724 for |(dx,dy)|, going counterclockwise if |ccw| is |true|. This assumes that
10725 |w| is the offset for some direction $(x',y')$ from which the angle to |(dx,dy)|
10726 in the sense determined by |ccw| is less than or equal to $180^\circ$.
10728 If the pen polygon has only two edges, they could both be parallel
10729 to |(dx,dy)|. In this case, we must be careful to stop after crossing the first
10730 such edge in order to avoid an infinite loop.
10732 @<Declare subroutines needed by |offset_prep|@>=
10733 integer mp_get_turn_amt (MP mp,pointer w, scaled dx,
10734 scaled dy, boolean ccw) {
10735 pointer ww; /* a neighbor of knot~|w| */
10736 integer s; /* turn amount so far */
10737 integer t; /* |ab_vs_cd| result */
10742 t=mp_ab_vs_cd(mp, dy,(x_coord(ww)-x_coord(w)),
10743 dx,(y_coord(ww)-y_coord(w)));
10750 while ( mp_ab_vs_cd(mp, dy,(x_coord(w)-x_coord(ww)),
10751 dx,(y_coord(w)-y_coord(ww))) < 0) {
10759 @ When we're all done, the final offset is |w0| and the final curve direction
10760 is |(dxin,dyin)|. With this knowledge of the incoming direction at |c|, we
10761 can correct |info(c)| which was erroneously based on an incoming offset
10764 @d fix_by(A) info(c)=info(c)+(A)
10766 @<Fix the offset change in |info(c)| and set |c| to the return value of...@>=
10767 mp->spec_offset=info(c)-zero_off;
10768 if ( link(c)==c ) {
10769 info(c)=zero_off+n;
10772 while ( w0!=h ) { fix_by(1); w0=link(w0); };
10773 while ( info(c)<=zero_off-n ) fix_by(n);
10774 while ( info(c)>zero_off ) fix_by(-n);
10775 if ( (info(c)!=zero_off)&&(mp_ab_vs_cd(mp, dy0,dxin,dx0,dyin)>=0) ) fix_by(n);
10779 @ Finally we want to reduce the general problem to situations that
10780 |fin_offset_prep| can handle. We split the cubic into at most three parts
10781 with respect to $d_{k-1}$, and apply |fin_offset_prep| to each part.
10783 @<Complete the offset splitting process@>=
10785 @<Compute test coeff...@>;
10786 @<Find the first |t| where $d(t)$ crosses $d_{k-1}$ or set
10787 |t:=fraction_one+1|@>;
10788 if ( t>fraction_one ) {
10789 mp_fin_offset_prep(mp, p,w,x0,x1,x2,y0,y1,y2,1,turn_amt);
10791 mp_split_cubic(mp, p,t); r=link(p);
10792 x1a=t_of_the_way(x0,x1); x1=t_of_the_way(x1,x2);
10793 x2a=t_of_the_way(x1a,x1);
10794 y1a=t_of_the_way(y0,y1); y1=t_of_the_way(y1,y2);
10795 y2a=t_of_the_way(y1a,y1);
10796 mp_fin_offset_prep(mp, p,w,x0,x1a,x2a,y0,y1a,y2a,1,0); x0=x2a; y0=y2a;
10797 info(r)=zero_off-1;
10798 if ( turn_amt>=0 ) {
10799 t1=t_of_the_way(t1,t2);
10801 t=mp_crossing_point(mp, 0,-t1,-t2);
10802 if ( t>fraction_one ) t=fraction_one;
10803 @<Split off another rising cubic for |fin_offset_prep|@>;
10804 mp_fin_offset_prep(mp, r,ww,x0,x1,x2,y0,y1,y2,-1,0);
10806 mp_fin_offset_prep(mp, r,ww,x0,x1,x2,y0,y1,y2,-1,(-1-turn_amt));
10810 @ @<Split off another rising cubic for |fin_offset_prep|@>=
10811 mp_split_cubic(mp, r,t); info(link(r))=zero_off+1;
10812 x1a=t_of_the_way(x1,x2); x1=t_of_the_way(x0,x1);
10813 x0a=t_of_the_way(x1,x1a);
10814 y1a=t_of_the_way(y1,y2); y1=t_of_the_way(y0,y1);
10815 y0a=t_of_the_way(y1,y1a);
10816 mp_fin_offset_prep(mp, link(r),w,x0a,x1a,x2,y0a,y1a,y2,1,turn_amt);
10819 @ At this point, the direction of the incoming pen edge is |(-du,-dv)|.
10820 When the component of $d(t)$ perpendicular to |(-du,-dv)| crosses zero, we
10821 need to decide whether the directions are parallel or antiparallel. We
10822 can test this by finding the dot product of $d(t)$ and |(-du,-dv)|, but this
10823 should be avoided when the value of |turn_amt| already determines the
10824 answer. If |t2<0|, there is one crossing and it is antiparallel only if
10825 |turn_amt>=0|. If |turn_amt<0|, there should always be at least one
10826 crossing and the first crossing cannot be antiparallel.
10828 @<Find the first |t| where $d(t)$ crosses $d_{k-1}$ or set...@>=
10829 t=mp_crossing_point(mp, t0,t1,t2);
10830 if ( turn_amt>=0 ) {
10834 u0=t_of_the_way(x0,x1);
10835 u1=t_of_the_way(x1,x2);
10836 ss=mp_take_fraction(mp, -du,t_of_the_way(u0,u1));
10837 v0=t_of_the_way(y0,y1);
10838 v1=t_of_the_way(y1,y2);
10839 ss=ss+mp_take_fraction(mp, -dv,t_of_the_way(v0,v1));
10840 if ( ss<0 ) t=fraction_one+1;
10842 } else if ( t>fraction_one ) {
10846 @ @<Other local variables for |offset_prep|@>=
10847 integer u0,u1,v0,v1; /* intermediate values for $d(t)$ calculation */
10848 integer ss = 0; /* the part of the dot product computed so far */
10849 int d_sign; /* sign of overall change in direction for this cubic */
10851 @ If the cubic almost has a cusp, it is a numerically ill-conditioned
10852 problem to decide which way it loops around but that's OK as long we're
10853 consistent. To make \&{doublepath} envelopes work properly, reversing
10854 the path should always change the sign of |turn_amt|.
10856 @<Decide on the net change in pen offsets and set |turn_amt|@>=
10857 d_sign=mp_ab_vs_cd(mp, dx,dyin, dxin,dy);
10859 @<Check rotation direction based on node position@>
10863 if ( dy>0 ) d_sign=1; else d_sign=-1;
10865 if ( dx>0 ) d_sign=1; else d_sign=-1;
10868 @<Make |ss| negative if and only if the total change in direction is
10869 more than $180^\circ$@>;
10870 turn_amt=mp_get_turn_amt(mp, w, dxin, dyin, (d_sign>0));
10871 if ( ss<0 ) turn_amt=turn_amt-d_sign*n
10873 @ We check rotation direction by looking at the vector connecting the current
10874 node with the next. If its angle with incoming and outgoing tangents has the
10875 same sign, we pick this as |d_sign|, since it means we have a flex, not a cusp.
10876 Otherwise we proceed to the cusp code.
10878 @<Check rotation direction based on node position@>=
10879 u0=x_coord(q)-x_coord(p);
10880 u1=y_coord(q)-y_coord(p);
10881 d_sign = half(mp_ab_vs_cd(mp, dx, u1, u0, dy)+
10882 mp_ab_vs_cd(mp, u0, dyin, dxin, u1));
10884 @ In order to be invariant under path reversal, the result of this computation
10885 should not change when |x0|, |y0|, $\ldots$ are all negated and |(x0,y0)| is
10886 then swapped with |(x2,y2)|. We make use of the identities
10887 |take_fraction(-a,-b)=take_fraction(a,b)| and
10888 |t_of_the_way(-a,-b)=-(t_of_the_way(a,b))|.
10890 @<Make |ss| negative if and only if the total change in direction is...@>=
10891 t0=half(mp_take_fraction(mp, x0,y2))-half(mp_take_fraction(mp, x2,y0));
10892 t1=half(mp_take_fraction(mp, x1,(y0+y2)))-half(mp_take_fraction(mp, y1,(x0+x2)));
10893 if ( t0==0 ) t0=d_sign; /* path reversal always negates |d_sign| */
10895 t=mp_crossing_point(mp, t0,t1,-t0);
10896 u0=t_of_the_way(x0,x1);
10897 u1=t_of_the_way(x1,x2);
10898 v0=t_of_the_way(y0,y1);
10899 v1=t_of_the_way(y1,y2);
10901 t=mp_crossing_point(mp, -t0,t1,t0);
10902 u0=t_of_the_way(x2,x1);
10903 u1=t_of_the_way(x1,x0);
10904 v0=t_of_the_way(y2,y1);
10905 v1=t_of_the_way(y1,y0);
10907 ss=mp_take_fraction(mp, (x0+x2),t_of_the_way(u0,u1))+
10908 mp_take_fraction(mp, (y0+y2),t_of_the_way(v0,v1))
10910 @ Here's a routine that prints an envelope spec in symbolic form. It assumes
10911 that the |cur_pen| has not been walked around to the first offset.
10914 void mp_print_spec (MP mp,pointer cur_spec, pointer cur_pen, char *s) {
10915 pointer p,q; /* list traversal */
10916 pointer w; /* the current pen offset */
10917 mp_print_diagnostic(mp, "Envelope spec",s,true);
10918 p=cur_spec; w=mp_pen_walk(mp, cur_pen,mp->spec_offset);
10920 mp_print_two(mp, x_coord(cur_spec),y_coord(cur_spec));
10921 mp_print(mp, " % beginning with offset ");
10922 mp_print_two(mp, x_coord(w),y_coord(w));
10926 @<Print the cubic between |p| and |q|@>;
10928 if ((p==cur_spec) || (info(p)!=zero_off))
10931 if ( info(p)!=zero_off ) {
10932 @<Update |w| as indicated by |info(p)| and print an explanation@>;
10934 } while (p!=cur_spec);
10935 mp_print_nl(mp, " & cycle");
10936 mp_end_diagnostic(mp, true);
10939 @ @<Update |w| as indicated by |info(p)| and print an explanation@>=
10941 w=mp_pen_walk(mp, w, (info(p)-zero_off));
10942 mp_print(mp, " % ");
10943 if ( info(p)>zero_off ) mp_print(mp, "counter");
10944 mp_print(mp, "clockwise to offset ");
10945 mp_print_two(mp, x_coord(w),y_coord(w));
10948 @ @<Print the cubic between |p| and |q|@>=
10950 mp_print_nl(mp, " ..controls ");
10951 mp_print_two(mp, right_x(p),right_y(p));
10952 mp_print(mp, " and ");
10953 mp_print_two(mp, left_x(q),left_y(q));
10954 mp_print_nl(mp, " ..");
10955 mp_print_two(mp, x_coord(q),y_coord(q));
10958 @ Once we have an envelope spec, the remaining task to construct the actual
10959 envelope by offsetting each cubic as determined by the |info| fields in
10960 the knots. First we use |offset_prep| to convert the |c| into an envelope
10961 spec. Then we add the offsets so that |c| becomes a cyclic path that represents
10964 The |ljoin| and |miterlim| parameters control the treatment of points where the
10965 pen offset changes, and |lcap| controls the endpoints of a \&{doublepath}.
10966 The endpoints are easily located because |c| is given in undoubled form
10967 and then doubled in this procedure. We use |spec_p1| and |spec_p2| to keep
10968 track of the endpoints and treat them like very sharp corners.
10969 Butt end caps are treated like beveled joins; round end caps are treated like
10970 round joins; and square end caps are achieved by setting |join_type:=3|.
10972 None of these parameters apply to inside joins where the convolution tracing
10973 has retrograde lines. In such cases we use a simple connect-the-endpoints
10974 approach that is achieved by setting |join_type:=2|.
10976 @c @<Declare a function called |insert_knot|@>;
10977 pointer mp_make_envelope (MP mp,pointer c, pointer h, small_number ljoin,
10978 small_number lcap, scaled miterlim) {
10979 pointer p,q,r,q0; /* for manipulating the path */
10980 int join_type=0; /* codes |0..3| for mitered, round, beveled, or square */
10981 pointer w,w0; /* the pen knot for the current offset */
10982 scaled qx,qy; /* unshifted coordinates of |q| */
10983 halfword k,k0; /* controls pen edge insertion */
10984 @<Other local variables for |make_envelope|@>;
10985 dxin=0; dyin=0; dxout=0; dyout=0;
10986 mp->spec_p1=null; mp->spec_p2=null;
10987 @<If endpoint, double the path |c|, and set |spec_p1| and |spec_p2|@>;
10988 @<Use |offset_prep| to compute the envelope spec then walk |h| around to
10989 the initial offset@>;
10994 qx=x_coord(q); qy=y_coord(q);
10997 if ( k!=zero_off ) {
10998 @<Set |join_type| to indicate how to handle offset changes at~|q|@>;
11000 @<Add offset |w| to the cubic from |p| to |q|@>;
11001 while ( k!=zero_off ) {
11002 @<Step |w| and move |k| one step closer to |zero_off|@>;
11003 if ( (join_type==1)||(k==zero_off) )
11004 q=mp_insert_knot(mp, q,qx+x_coord(w),qy+y_coord(w));
11006 if ( q!=link(p) ) {
11007 @<Set |p=link(p)| and add knots between |p| and |q| as
11008 required by |join_type|@>;
11015 @ @<Use |offset_prep| to compute the envelope spec then walk |h| around to...@>=
11016 c=mp_offset_prep(mp, c,h);
11017 if ( mp->internal[mp_tracing_specs]>0 )
11018 mp_print_spec(mp, c,h,"");
11019 h=mp_pen_walk(mp, h,mp->spec_offset)
11021 @ Mitered and squared-off joins depend on path directions that are difficult to
11022 compute for degenerate cubics. The envelope spec computed by |offset_prep| can
11023 have degenerate cubics only if the entire cycle collapses to a single
11024 degenerate cubic. Setting |join_type:=2| in this case makes the computed
11025 envelope degenerate as well.
11027 @<Set |join_type| to indicate how to handle offset changes at~|q|@>=
11028 if ( k<zero_off ) {
11031 if ( (q!=mp->spec_p1)&&(q!=mp->spec_p2) ) join_type=ljoin;
11032 else if ( lcap==2 ) join_type=3;
11033 else join_type=2-lcap;
11034 if ( (join_type==0)||(join_type==3) ) {
11035 @<Set the incoming and outgoing directions at |q|; in case of
11036 degeneracy set |join_type:=2|@>;
11037 if ( join_type==0 ) {
11038 @<If |miterlim| is less than the secant of half the angle at |q|
11039 then set |join_type:=2|@>;
11044 @ @<If |miterlim| is less than the secant of half the angle at |q|...@>=
11046 tmp=mp_take_fraction(mp, miterlim,fraction_half+
11047 half(mp_take_fraction(mp, dxin,dxout)+mp_take_fraction(mp, dyin,dyout)));
11049 if ( mp_take_scaled(mp, miterlim,tmp)<unity ) join_type=2;
11052 @ @<Other local variables for |make_envelope|@>=
11053 fraction dxin,dyin,dxout,dyout; /* directions at |q| when square or mitered */
11054 scaled tmp; /* a temporary value */
11056 @ The coordinates of |p| have already been shifted unless |p| is the first
11057 knot in which case they get shifted at the very end.
11059 @<Add offset |w| to the cubic from |p| to |q|@>=
11060 right_x(p)=right_x(p)+x_coord(w);
11061 right_y(p)=right_y(p)+y_coord(w);
11062 left_x(q)=left_x(q)+x_coord(w);
11063 left_y(q)=left_y(q)+y_coord(w);
11064 x_coord(q)=x_coord(q)+x_coord(w);
11065 y_coord(q)=y_coord(q)+y_coord(w);
11066 left_type(q)=mp_explicit;
11067 right_type(q)=mp_explicit
11069 @ @<Step |w| and move |k| one step closer to |zero_off|@>=
11070 if ( k>zero_off ){ w=link(w); decr(k); }
11071 else { w=knil(w); incr(k); }
11073 @ The cubic from |q| to the new knot at |(x,y)| becomes a line segment and
11074 the |right_x| and |right_y| fields of |r| are set from |q|. This is done in
11075 case the cubic containing these control points is ``yet to be examined.''
11077 @<Declare a function called |insert_knot|@>=
11078 pointer mp_insert_knot (MP mp,pointer q, scaled x, scaled y) {
11079 /* returns the inserted knot */
11080 pointer r; /* the new knot */
11081 r=mp_get_node(mp, knot_node_size);
11082 link(r)=link(q); link(q)=r;
11083 right_x(r)=right_x(q);
11084 right_y(r)=right_y(q);
11087 right_x(q)=x_coord(q);
11088 right_y(q)=y_coord(q);
11089 left_x(r)=x_coord(r);
11090 left_y(r)=y_coord(r);
11091 left_type(r)=mp_explicit;
11092 right_type(r)=mp_explicit;
11093 originator(r)=mp_program_code;
11097 @ After setting |p:=link(p)|, either |join_type=1| or |q=link(p)|.
11099 @<Set |p=link(p)| and add knots between |p| and |q| as...@>=
11102 if ( (join_type==0)||(join_type==3) ) {
11103 if ( join_type==0 ) {
11104 @<Insert a new knot |r| between |p| and |q| as required for a mitered join@>
11106 @<Make |r| the last of two knots inserted between |p| and |q| to form a
11110 right_x(r)=x_coord(r);
11111 right_y(r)=y_coord(r);
11116 @ For very small angles, adding a knot is unnecessary and would cause numerical
11117 problems, so we just set |r:=null| in that case.
11119 @<Insert a new knot |r| between |p| and |q| as required for a mitered join@>=
11121 det=mp_take_fraction(mp, dyout,dxin)-mp_take_fraction(mp, dxout,dyin);
11122 if ( abs(det)<26844 ) {
11123 r=null; /* sine $<10^{-4}$ */
11125 tmp=mp_take_fraction(mp, x_coord(q)-x_coord(p),dyout)-
11126 mp_take_fraction(mp, y_coord(q)-y_coord(p),dxout);
11127 tmp=mp_make_fraction(mp, tmp,det);
11128 r=mp_insert_knot(mp, p,x_coord(p)+mp_take_fraction(mp, tmp,dxin),
11129 y_coord(p)+mp_take_fraction(mp, tmp,dyin));
11133 @ @<Other local variables for |make_envelope|@>=
11134 fraction det; /* a determinant used for mitered join calculations */
11136 @ @<Make |r| the last of two knots inserted between |p| and |q| to form a...@>=
11138 ht_x=y_coord(w)-y_coord(w0);
11139 ht_y=x_coord(w0)-x_coord(w);
11140 while ( (abs(ht_x)<fraction_half)&&(abs(ht_y)<fraction_half) ) {
11141 ht_x+=ht_x; ht_y+=ht_y;
11143 @<Scan the pen polygon between |w0| and |w| and make |max_ht| the range dot
11144 product with |(ht_x,ht_y)|@>;
11145 tmp=mp_make_fraction(mp, max_ht,mp_take_fraction(mp, dxin,ht_x)+
11146 mp_take_fraction(mp, dyin,ht_y));
11147 r=mp_insert_knot(mp, p,x_coord(p)+mp_take_fraction(mp, tmp,dxin),
11148 y_coord(p)+mp_take_fraction(mp, tmp,dyin));
11149 tmp=mp_make_fraction(mp, max_ht,mp_take_fraction(mp, dxout,ht_x)+
11150 mp_take_fraction(mp, dyout,ht_y));
11151 r=mp_insert_knot(mp, r,x_coord(q)+mp_take_fraction(mp, tmp,dxout),
11152 y_coord(q)+mp_take_fraction(mp, tmp,dyout));
11155 @ @<Other local variables for |make_envelope|@>=
11156 fraction ht_x,ht_y; /* perpendicular to the segment from |p| to |q| */
11157 scaled max_ht; /* maximum height of the pen polygon above the |w0|-|w| line */
11158 halfword kk; /* keeps track of the pen vertices being scanned */
11159 pointer ww; /* the pen vertex being tested */
11161 @ The dot product of the vector from |w0| to |ww| with |(ht_x,ht_y)| ranges
11162 from zero to |max_ht|.
11164 @<Scan the pen polygon between |w0| and |w| and make |max_ht| the range...@>=
11169 @<Step |ww| and move |kk| one step closer to |k0|@>;
11170 if ( kk==k0 ) break;
11171 tmp=mp_take_fraction(mp, (x_coord(ww)-x_coord(w0)),ht_x)+
11172 mp_take_fraction(mp, (y_coord(ww)-y_coord(w0)),ht_y);
11173 if ( tmp>max_ht ) max_ht=tmp;
11177 @ @<Step |ww| and move |kk| one step closer to |k0|@>=
11178 if ( kk>k0 ) { ww=link(ww); decr(kk); }
11179 else { ww=knil(ww); incr(kk); }
11181 @ @<If endpoint, double the path |c|, and set |spec_p1| and |spec_p2|@>=
11182 if ( left_type(c)==mp_endpoint ) {
11183 mp->spec_p1=mp_htap_ypoc(mp, c);
11184 mp->spec_p2=mp->path_tail;
11185 originator(mp->spec_p1)=mp_program_code;
11186 link(mp->spec_p2)=link(mp->spec_p1);
11187 link(mp->spec_p1)=c;
11188 mp_remove_cubic(mp, mp->spec_p1);
11190 if ( c!=link(c) ) {
11191 originator(mp->spec_p2)=mp_program_code;
11192 mp_remove_cubic(mp, mp->spec_p2);
11194 @<Make |c| look like a cycle of length one@>;
11198 @ @<Make |c| look like a cycle of length one@>=
11200 left_type(c)=mp_explicit; right_type(c)=mp_explicit;
11201 left_x(c)=x_coord(c); left_y(c)=y_coord(c);
11202 right_x(c)=x_coord(c); right_y(c)=y_coord(c);
11205 @ In degenerate situations we might have to look at the knot preceding~|q|.
11206 That knot is |p| but if |p<>c|, its coordinates have already been offset by |w|.
11208 @<Set the incoming and outgoing directions at |q|; in case of...@>=
11209 dxin=x_coord(q)-left_x(q);
11210 dyin=y_coord(q)-left_y(q);
11211 if ( (dxin==0)&&(dyin==0) ) {
11212 dxin=x_coord(q)-right_x(p);
11213 dyin=y_coord(q)-right_y(p);
11214 if ( (dxin==0)&&(dyin==0) ) {
11215 dxin=x_coord(q)-x_coord(p);
11216 dyin=y_coord(q)-y_coord(p);
11217 if ( p!=c ) { /* the coordinates of |p| have been offset by |w| */
11218 dxin=dxin+x_coord(w);
11219 dyin=dyin+y_coord(w);
11223 tmp=mp_pyth_add(mp, dxin,dyin);
11227 dxin=mp_make_fraction(mp, dxin,tmp);
11228 dyin=mp_make_fraction(mp, dyin,tmp);
11229 @<Set the outgoing direction at |q|@>;
11232 @ If |q=c| then the coordinates of |r| and the control points between |q|
11233 and~|r| have already been offset by |h|.
11235 @<Set the outgoing direction at |q|@>=
11236 dxout=right_x(q)-x_coord(q);
11237 dyout=right_y(q)-y_coord(q);
11238 if ( (dxout==0)&&(dyout==0) ) {
11240 dxout=left_x(r)-x_coord(q);
11241 dyout=left_y(r)-y_coord(q);
11242 if ( (dxout==0)&&(dyout==0) ) {
11243 dxout=x_coord(r)-x_coord(q);
11244 dyout=y_coord(r)-y_coord(q);
11248 dxout=dxout-x_coord(h);
11249 dyout=dyout-y_coord(h);
11251 tmp=mp_pyth_add(mp, dxout,dyout);
11252 if ( tmp==0 ) mp_confusion(mp, "degenerate spec");
11253 @:this can't happen degerate spec}{\quad degenerate spec@>
11254 dxout=mp_make_fraction(mp, dxout,tmp);
11255 dyout=mp_make_fraction(mp, dyout,tmp)
11257 @* \[23] Direction and intersection times.
11258 A path of length $n$ is defined parametrically by functions $x(t)$ and
11259 $y(t)$, for |0<=t<=n|; we can regard $t$ as the ``time'' at which the path
11260 reaches the point $\bigl(x(t),y(t)\bigr)$. In this section of the program
11261 we shall consider operations that determine special times associated with
11262 given paths: the first time that a path travels in a given direction, and
11263 a pair of times at which two paths cross each other.
11265 @ Let's start with the easier task. The function |find_direction_time| is
11266 given a direction |(x,y)| and a path starting at~|h|. If the path never
11267 travels in direction |(x,y)|, the direction time will be~|-1|; otherwise
11268 it will be nonnegative.
11270 Certain anomalous cases can arise: If |(x,y)=(0,0)|, so that the given
11271 direction is undefined, the direction time will be~0. If $\bigl(x'(t),
11272 y'(t)\bigr)=(0,0)$, so that the path direction is undefined, it will be
11273 assumed to match any given direction at time~|t|.
11275 The routine solves this problem in nondegenerate cases by rotating the path
11276 and the given direction so that |(x,y)=(1,0)|; i.e., the main task will be
11277 to find when a given path first travels ``due east.''
11280 scaled mp_find_direction_time (MP mp,scaled x, scaled y, pointer h) {
11281 scaled max; /* $\max\bigl(\vert x\vert,\vert y\vert\bigr)$ */
11282 pointer p,q; /* for list traversal */
11283 scaled n; /* the direction time at knot |p| */
11284 scaled tt; /* the direction time within a cubic */
11285 @<Other local variables for |find_direction_time|@>;
11286 @<Normalize the given direction for better accuracy;
11287 but |return| with zero result if it's zero@>;
11290 if ( right_type(p)==mp_endpoint ) break;
11292 @<Rotate the cubic between |p| and |q|; then
11293 |goto found| if the rotated cubic travels due east at some time |tt|;
11294 but |break| if an entire cyclic path has been traversed@>;
11302 @ @<Normalize the given direction for better accuracy...@>=
11303 if ( abs(x)<abs(y) ) {
11304 x=mp_make_fraction(mp, x,abs(y));
11305 if ( y>0 ) y=fraction_one; else y=-fraction_one;
11306 } else if ( x==0 ) {
11309 y=mp_make_fraction(mp, y,abs(x));
11310 if ( x>0 ) x=fraction_one; else x=-fraction_one;
11313 @ Since we're interested in the tangent directions, we work with the
11314 derivative $${\textstyle1\over3}B'(x_0,x_1,x_2,x_3;t)=
11315 B(x_1-x_0,x_2-x_1,x_3-x_2;t)$$ instead of
11316 $B(x_0,x_1,x_2,x_3;t)$ itself. The derived coefficients are also scaled up
11317 in order to achieve better accuracy.
11319 The given path may turn abruptly at a knot, and it might pass the critical
11320 tangent direction at such a time. Therefore we remember the direction |phi|
11321 in which the previous rotated cubic was traveling. (The value of |phi| will be
11322 undefined on the first cubic, i.e., when |n=0|.)
11324 @<Rotate the cubic between |p| and |q|; then...@>=
11326 @<Set local variables |x1,x2,x3| and |y1,y2,y3| to multiples of the control
11327 points of the rotated derivatives@>;
11328 if ( y1==0 ) if ( x1>=0 ) goto FOUND;
11330 @<Exit to |found| if an eastward direction occurs at knot |p|@>;
11333 if ( (x3!=0)||(y3!=0) ) phi=mp_n_arg(mp, x3,y3);
11334 @<Exit to |found| if the curve whose derivatives are specified by
11335 |x1,x2,x3,y1,y2,y3| travels eastward at some time~|tt|@>
11337 @ @<Other local variables for |find_direction_time|@>=
11338 scaled x1,x2,x3,y1,y2,y3; /* multiples of rotated derivatives */
11339 angle theta,phi; /* angles of exit and entry at a knot */
11340 fraction t; /* temp storage */
11342 @ @<Set local variables |x1,x2,x3| and |y1,y2,y3| to multiples...@>=
11343 x1=right_x(p)-x_coord(p); x2=left_x(q)-right_x(p);
11344 x3=x_coord(q)-left_x(q);
11345 y1=right_y(p)-y_coord(p); y2=left_y(q)-right_y(p);
11346 y3=y_coord(q)-left_y(q);
11348 if ( abs(x2)>max ) max=abs(x2);
11349 if ( abs(x3)>max ) max=abs(x3);
11350 if ( abs(y1)>max ) max=abs(y1);
11351 if ( abs(y2)>max ) max=abs(y2);
11352 if ( abs(y3)>max ) max=abs(y3);
11353 if ( max==0 ) goto FOUND;
11354 while ( max<fraction_half ){
11355 max+=max; x1+=x1; x2+=x2; x3+=x3;
11356 y1+=y1; y2+=y2; y3+=y3;
11358 t=x1; x1=mp_take_fraction(mp, x1,x)+mp_take_fraction(mp, y1,y);
11359 y1=mp_take_fraction(mp, y1,x)-mp_take_fraction(mp, t,y);
11360 t=x2; x2=mp_take_fraction(mp, x2,x)+mp_take_fraction(mp, y2,y);
11361 y2=mp_take_fraction(mp, y2,x)-mp_take_fraction(mp, t,y);
11362 t=x3; x3=mp_take_fraction(mp, x3,x)+mp_take_fraction(mp, y3,y);
11363 y3=mp_take_fraction(mp, y3,x)-mp_take_fraction(mp, t,y)
11365 @ @<Exit to |found| if an eastward direction occurs at knot |p|@>=
11366 theta=mp_n_arg(mp, x1,y1);
11367 if ( theta>=0 ) if ( phi<=0 ) if ( phi>=theta-one_eighty_deg ) goto FOUND;
11368 if ( theta<=0 ) if ( phi>=0 ) if ( phi<=theta+one_eighty_deg ) goto FOUND
11370 @ In this step we want to use the |crossing_point| routine to find the
11371 roots of the quadratic equation $B(y_1,y_2,y_3;t)=0$.
11372 Several complications arise: If the quadratic equation has a double root,
11373 the curve never crosses zero, and |crossing_point| will find nothing;
11374 this case occurs iff $y_1y_3=y_2^2$ and $y_1y_2<0$. If the quadratic
11375 equation has simple roots, or only one root, we may have to negate it
11376 so that $B(y_1,y_2,y_3;t)$ crosses from positive to negative at its first root.
11377 And finally, we need to do special things if $B(y_1,y_2,y_3;t)$ is
11380 @ @<Exit to |found| if the curve whose derivatives are specified by...@>=
11381 if ( x1<0 ) if ( x2<0 ) if ( x3<0 ) goto DONE;
11382 if ( mp_ab_vs_cd(mp, y1,y3,y2,y2)==0 ) {
11383 @<Handle the test for eastward directions when $y_1y_3=y_2^2$;
11384 either |goto found| or |goto done|@>;
11387 if ( y1<0 ) { y1=-y1; y2=-y2; y3=-y3; }
11388 else if ( y2>0 ){ y2=-y2; y3=-y3; };
11390 @<Check the places where $B(y_1,y_2,y_3;t)=0$ to see if
11391 $B(x_1,x_2,x_3;t)\ge0$@>;
11394 @ The quadratic polynomial $B(y_1,y_2,y_3;t)$ begins |>=0| and has at most
11395 two roots, because we know that it isn't identically zero.
11397 It must be admitted that the |crossing_point| routine is not perfectly accurate;
11398 rounding errors might cause it to find a root when $y_1y_3>y_2^2$, or to
11399 miss the roots when $y_1y_3<y_2^2$. The rotation process is itself
11400 subject to rounding errors. Yet this code optimistically tries to
11401 do the right thing.
11403 @d we_found_it { tt=(t+04000) / 010000; goto FOUND; }
11405 @<Check the places where $B(y_1,y_2,y_3;t)=0$...@>=
11406 t=mp_crossing_point(mp, y1,y2,y3);
11407 if ( t>fraction_one ) goto DONE;
11408 y2=t_of_the_way(y2,y3);
11409 x1=t_of_the_way(x1,x2);
11410 x2=t_of_the_way(x2,x3);
11411 x1=t_of_the_way(x1,x2);
11412 if ( x1>=0 ) we_found_it;
11414 tt=t; t=mp_crossing_point(mp, 0,-y2,-y3);
11415 if ( t>fraction_one ) goto DONE;
11416 x1=t_of_the_way(x1,x2);
11417 x2=t_of_the_way(x2,x3);
11418 if ( t_of_the_way(x1,x2)>=0 ) {
11419 t=t_of_the_way(tt,fraction_one); we_found_it;
11422 @ @<Handle the test for eastward directions when $y_1y_3=y_2^2$;
11423 either |goto found| or |goto done|@>=
11425 if ( mp_ab_vs_cd(mp, y1,y2,0,0)<0 ) {
11426 t=mp_make_fraction(mp, y1,y1-y2);
11427 x1=t_of_the_way(x1,x2);
11428 x2=t_of_the_way(x2,x3);
11429 if ( t_of_the_way(x1,x2)>=0 ) we_found_it;
11430 } else if ( y3==0 ) {
11432 @<Exit to |found| if the derivative $B(x_1,x_2,x_3;t)$ becomes |>=0|@>;
11433 } else if ( x3>=0 ) {
11434 tt=unity; goto FOUND;
11440 @ At this point we know that the derivative of |y(t)| is identically zero,
11441 and that |x1<0|; but either |x2>=0| or |x3>=0|, so there's some hope of
11444 @<Exit to |found| if the derivative $B(x_1,x_2,x_3;t)$ becomes |>=0|...@>=
11446 t=mp_crossing_point(mp, -x1,-x2,-x3);
11447 if ( t<=fraction_one ) we_found_it;
11448 if ( mp_ab_vs_cd(mp, x1,x3,x2,x2)<=0 ) {
11449 t=mp_make_fraction(mp, x1,x1-x2); we_found_it;
11453 @ The intersection of two cubics can be found by an interesting variant
11454 of the general bisection scheme described in the introduction to
11456 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)$,
11457 we wish to find a pair of times $(t_1,t_2)$ such that $w(t_1)=z(t_2)$,
11458 if an intersection exists. First we find the smallest rectangle that
11459 encloses the points $\{w_0,w_1,w_2,w_3\}$ and check that it overlaps
11460 the smallest rectangle that encloses
11461 $\{z_0,z_1,z_2,z_3\}$; if not, the cubics certainly don't intersect.
11462 But if the rectangles do overlap, we bisect the intervals, getting
11463 new cubics $w'$ and~$w''$, $z'$~and~$z''$; the intersection routine first
11464 tries for an intersection between $w'$ and~$z'$, then (if unsuccessful)
11465 between $w'$ and~$z''$, then (if still unsuccessful) between $w''$ and~$z'$,
11466 finally (if thrice unsuccessful) between $w''$ and~$z''$. After $l$~successful
11467 levels of bisection we will have determined the intersection times $t_1$
11468 and~$t_2$ to $l$~bits of accuracy.
11470 \def\submin{_{\rm min}} \def\submax{_{\rm max}}
11471 As before, it is better to work with the numbers $W_k=2^l(w_k-w_{k-1})$
11472 and $Z_k=2^l(z_k-z_{k-1})$ rather than the coefficients $w_k$ and $z_k$
11473 themselves. We also need one other quantity, $\Delta=2^l(w_0-z_0)$,
11474 to determine when the enclosing rectangles overlap. Here's why:
11475 The $x$~coordinates of~$w(t)$ are between $u\submin$ and $u\submax$,
11476 and the $x$~coordinates of~$z(t)$ are between $x\submin$ and $x\submax$,
11477 if we write $w_k=(u_k,v_k)$ and $z_k=(x_k,y_k)$ and $u\submin=
11478 \min(u_0,u_1,u_2,u_3)$, etc. These intervals of $x$~coordinates
11479 overlap if and only if $u\submin\L x\submax$ and
11480 $x\submin\L u\submax$. Letting
11481 $$U\submin=\min(0,U_1,U_1+U_2,U_1+U_2+U_3),\;
11482 U\submax=\max(0,U_1,U_1+U_2,U_1+U_2+U_3),$$
11483 we have $u\submin=2^lu_0+U\submin$, etc.; the condition for overlap
11485 $$X\submin-U\submax\L 2^l(u_0-x_0)\L X\submax-U\submin.$$
11486 Thus we want to maintain the quantity $2^l(u_0-x_0)$; similarly,
11487 the quantity $2^l(v_0-y_0)$ accounts for the $y$~coordinates. The
11488 coordinates of $\Delta=2^l(w_0-z_0)$ must stay bounded as $l$ increases,
11489 because of the overlap condition; i.e., we know that $X\submin$,
11490 $X\submax$, and their relatives are bounded, hence $X\submax-
11491 U\submin$ and $X\submin-U\submax$ are bounded.
11493 @ Incidentally, if the given cubics intersect more than once, the process
11494 just sketched will not necessarily find the lexicographically smallest pair
11495 $(t_1,t_2)$. The solution actually obtained will be smallest in ``shuffled
11496 order''; i.e., if $t_1=(.a_1a_2\ldots a_{16})_2$ and
11497 $t_2=(.b_1b_2\ldots b_{16})_2$, then we will minimize
11498 $a_1b_1a_2b_2\ldots a_{16}b_{16}$, not
11499 $a_1a_2\ldots a_{16}b_1b_2\ldots b_{16}$.
11500 Shuffled order agrees with lexicographic order if all pairs of solutions
11501 $(t_1,t_2)$ and $(t_1',t_2')$ have the property that $t_1<t_1'$ iff
11502 $t_2<t_2'$; but in general, lexicographic order can be quite different,
11503 and the bisection algorithm would be substantially less efficient if it were
11504 constrained by lexicographic order.
11506 For example, suppose that an overlap has been found for $l=3$ and
11507 $(t_1,t_2)= (.101,.011)$ in binary, but that no overlap is produced by
11508 either of the alternatives $(.1010,.0110)$, $(.1010,.0111)$ at level~4.
11509 Then there is probably an intersection in one of the subintervals
11510 $(.1011,.011x)$; but lexicographic order would require us to explore
11511 $(.1010,.1xxx)$ and $(.1011,.00xx)$ and $(.1011,.010x)$ first. We wouldn't
11512 want to store all of the subdivision data for the second path, so the
11513 subdivisions would have to be regenerated many times. Such inefficiencies
11514 would be associated with every `1' in the binary representation of~$t_1$.
11516 @ The subdivision process introduces rounding errors, hence we need to
11517 make a more liberal test for overlap. It is not hard to show that the
11518 computed values of $U_i$ differ from the truth by at most~$l$, on
11519 level~$l$, hence $U\submin$ and $U\submax$ will be at most $3l$ in error.
11520 If $\beta$ is an upper bound on the absolute error in the computed
11521 components of $\Delta=(|delx|,|dely|)$ on level~$l$, we will replace
11522 the test `$X\submin-U\submax\L|delx|$' by the more liberal test
11523 `$X\submin-U\submax\L|delx|+|tol|$', where $|tol|=6l+\beta$.
11525 More accuracy is obtained if we try the algorithm first with |tol=0|;
11526 the more liberal tolerance is used only if an exact approach fails.
11527 It is convenient to do this double-take by letting `3' in the preceding
11528 paragraph be a parameter, which is first 0, then 3.
11531 unsigned int tol_step; /* either 0 or 3, usually */
11533 @ We shall use an explicit stack to implement the recursive bisection
11534 method described above. The |bisect_stack| array will contain numerous 5-word
11535 packets like $(U_1,U_2,U_3,U\submin,U\submax)$, as well as 20-word packets
11536 comprising the 5-word packets for $U$, $V$, $X$, and~$Y$.
11538 The following macros define the allocation of stack positions to
11539 the quantities needed for bisection-intersection.
11541 @d stack_1(A) mp->bisect_stack[(A)] /* $U_1$, $V_1$, $X_1$, or $Y_1$ */
11542 @d stack_2(A) mp->bisect_stack[(A)+1] /* $U_2$, $V_2$, $X_2$, or $Y_2$ */
11543 @d stack_3(A) mp->bisect_stack[(A)+2] /* $U_3$, $V_3$, $X_3$, or $Y_3$ */
11544 @d stack_min(A) mp->bisect_stack[(A)+3]
11545 /* $U\submin$, $V\submin$, $X\submin$, or $Y\submin$ */
11546 @d stack_max(A) mp->bisect_stack[(A)+4]
11547 /* $U\submax$, $V\submax$, $X\submax$, or $Y\submax$ */
11548 @d int_packets 20 /* number of words to represent $U_k$, $V_k$, $X_k$, and $Y_k$ */
11550 @d u_packet(A) ((A)-5)
11551 @d v_packet(A) ((A)-10)
11552 @d x_packet(A) ((A)-15)
11553 @d y_packet(A) ((A)-20)
11554 @d l_packets (mp->bisect_ptr-int_packets)
11555 @d r_packets mp->bisect_ptr
11556 @d ul_packet u_packet(l_packets) /* base of $U'_k$ variables */
11557 @d vl_packet v_packet(l_packets) /* base of $V'_k$ variables */
11558 @d xl_packet x_packet(l_packets) /* base of $X'_k$ variables */
11559 @d yl_packet y_packet(l_packets) /* base of $Y'_k$ variables */
11560 @d ur_packet u_packet(r_packets) /* base of $U''_k$ variables */
11561 @d vr_packet v_packet(r_packets) /* base of $V''_k$ variables */
11562 @d xr_packet x_packet(r_packets) /* base of $X''_k$ variables */
11563 @d yr_packet y_packet(r_packets) /* base of $Y''_k$ variables */
11565 @d u1l stack_1(ul_packet) /* $U'_1$ */
11566 @d u2l stack_2(ul_packet) /* $U'_2$ */
11567 @d u3l stack_3(ul_packet) /* $U'_3$ */
11568 @d v1l stack_1(vl_packet) /* $V'_1$ */
11569 @d v2l stack_2(vl_packet) /* $V'_2$ */
11570 @d v3l stack_3(vl_packet) /* $V'_3$ */
11571 @d x1l stack_1(xl_packet) /* $X'_1$ */
11572 @d x2l stack_2(xl_packet) /* $X'_2$ */
11573 @d x3l stack_3(xl_packet) /* $X'_3$ */
11574 @d y1l stack_1(yl_packet) /* $Y'_1$ */
11575 @d y2l stack_2(yl_packet) /* $Y'_2$ */
11576 @d y3l stack_3(yl_packet) /* $Y'_3$ */
11577 @d u1r stack_1(ur_packet) /* $U''_1$ */
11578 @d u2r stack_2(ur_packet) /* $U''_2$ */
11579 @d u3r stack_3(ur_packet) /* $U''_3$ */
11580 @d v1r stack_1(vr_packet) /* $V''_1$ */
11581 @d v2r stack_2(vr_packet) /* $V''_2$ */
11582 @d v3r stack_3(vr_packet) /* $V''_3$ */
11583 @d x1r stack_1(xr_packet) /* $X''_1$ */
11584 @d x2r stack_2(xr_packet) /* $X''_2$ */
11585 @d x3r stack_3(xr_packet) /* $X''_3$ */
11586 @d y1r stack_1(yr_packet) /* $Y''_1$ */
11587 @d y2r stack_2(yr_packet) /* $Y''_2$ */
11588 @d y3r stack_3(yr_packet) /* $Y''_3$ */
11590 @d stack_dx mp->bisect_stack[mp->bisect_ptr] /* stacked value of |delx| */
11591 @d stack_dy mp->bisect_stack[mp->bisect_ptr+1] /* stacked value of |dely| */
11592 @d stack_tol mp->bisect_stack[mp->bisect_ptr+2] /* stacked value of |tol| */
11593 @d stack_uv mp->bisect_stack[mp->bisect_ptr+3] /* stacked value of |uv| */
11594 @d stack_xy mp->bisect_stack[mp->bisect_ptr+4] /* stacked value of |xy| */
11595 @d int_increment (int_packets+int_packets+5) /* number of stack words per level */
11598 integer *bisect_stack;
11599 unsigned int bisect_ptr;
11601 @ @<Allocate or initialize ...@>=
11602 mp->bisect_stack = xmalloc((bistack_size+1),sizeof(integer));
11604 @ @<Dealloc variables@>=
11605 xfree(mp->bisect_stack);
11607 @ @<Check the ``constant''...@>=
11608 if ( int_packets+17*int_increment>bistack_size ) mp->bad=19;
11610 @ Computation of the min and max is a tedious but fairly fast sequence of
11611 instructions; exactly four comparisons are made in each branch.
11614 if ( stack_1((A))<0 ) {
11615 if ( stack_3((A))>=0 ) {
11616 if ( stack_2((A))<0 ) stack_min((A))=stack_1((A))+stack_2((A));
11617 else stack_min((A))=stack_1((A));
11618 stack_max((A))=stack_1((A))+stack_2((A))+stack_3((A));
11619 if ( stack_max((A))<0 ) stack_max((A))=0;
11621 stack_min((A))=stack_1((A))+stack_2((A))+stack_3((A));
11622 if ( stack_min((A))>stack_1((A)) ) stack_min((A))=stack_1((A));
11623 stack_max((A))=stack_1((A))+stack_2((A));
11624 if ( stack_max((A))<0 ) stack_max((A))=0;
11626 } else if ( stack_3((A))<=0 ) {
11627 if ( stack_2((A))>0 ) stack_max((A))=stack_1((A))+stack_2((A));
11628 else stack_max((A))=stack_1((A));
11629 stack_min((A))=stack_1((A))+stack_2((A))+stack_3((A));
11630 if ( stack_min((A))>0 ) stack_min((A))=0;
11632 stack_max((A))=stack_1((A))+stack_2((A))+stack_3((A));
11633 if ( stack_max((A))<stack_1((A)) ) stack_max((A))=stack_1((A));
11634 stack_min((A))=stack_1((A))+stack_2((A));
11635 if ( stack_min((A))>0 ) stack_min((A))=0;
11638 @ It's convenient to keep the current values of $l$, $t_1$, and $t_2$ in
11639 the integer form $2^l+2^lt_1$ and $2^l+2^lt_2$. The |cubic_intersection|
11640 routine uses global variables |cur_t| and |cur_tt| for this purpose;
11641 after successful completion, |cur_t| and |cur_tt| will contain |unity|
11642 plus the |scaled| values of $t_1$ and~$t_2$.
11644 The values of |cur_t| and |cur_tt| will be set to zero if |cubic_intersection|
11645 finds no intersection. The routine gives up and gives an approximate answer
11646 if it has backtracked
11647 more than 5000 times (otherwise there are cases where several minutes
11648 of fruitless computation would be possible).
11650 @d max_patience 5000
11653 integer cur_t;integer cur_tt; /* controls and results of |cubic_intersection| */
11654 integer time_to_go; /* this many backtracks before giving up */
11655 integer max_t; /* maximum of $2^{l+1}$ so far achieved */
11657 @ The given cubics $B(w_0,w_1,w_2,w_3;t)$ and
11658 $B(z_0,z_1,z_2,z_3;t)$ are specified in adjacent knot nodes |(p,link(p))|
11659 and |(pp,link(pp))|, respectively.
11661 @c void mp_cubic_intersection (MP mp,pointer p, pointer pp) {
11662 pointer q,qq; /* |link(p)|, |link(pp)| */
11663 mp->time_to_go=max_patience; mp->max_t=2;
11664 @<Initialize for intersections at level zero@>;
11667 if ( mp->delx-mp->tol<=stack_max(x_packet(mp->xy))-stack_min(u_packet(mp->uv)))
11668 if ( mp->delx+mp->tol>=stack_min(x_packet(mp->xy))-stack_max(u_packet(mp->uv)))
11669 if ( mp->dely-mp->tol<=stack_max(y_packet(mp->xy))-stack_min(v_packet(mp->uv)))
11670 if ( mp->dely+mp->tol>=stack_min(y_packet(mp->xy))-stack_max(v_packet(mp->uv)))
11672 if ( mp->cur_t>=mp->max_t ){
11673 if ( mp->max_t==two ) { /* we've done 17 bisections */
11674 mp->cur_t=halfp(mp->cur_t+1); mp->cur_tt=halfp(mp->cur_tt+1); return;
11676 mp->max_t+=mp->max_t; mp->appr_t=mp->cur_t; mp->appr_tt=mp->cur_tt;
11678 @<Subdivide for a new level of intersection@>;
11681 if ( mp->time_to_go>0 ) {
11682 decr(mp->time_to_go);
11684 while ( mp->appr_t<unity ) {
11685 mp->appr_t+=mp->appr_t; mp->appr_tt+=mp->appr_tt;
11687 mp->cur_t=mp->appr_t; mp->cur_tt=mp->appr_tt; return;
11689 @<Advance to the next pair |(cur_t,cur_tt)|@>;
11693 @ The following variables are global, although they are used only by
11694 |cubic_intersection|, because it is necessary on some machines to
11695 split |cubic_intersection| up into two procedures.
11698 integer delx;integer dely; /* the components of $\Delta=2^l(w_0-z_0)$ */
11699 integer tol; /* bound on the uncertainly in the overlap test */
11701 unsigned int xy; /* pointers to the current packets of interest */
11702 integer three_l; /* |tol_step| times the bisection level */
11703 integer appr_t;integer appr_tt; /* best approximations known to the answers */
11705 @ We shall assume that the coordinates are sufficiently non-extreme that
11706 integer overflow will not occur.
11708 @<Initialize for intersections at level zero@>=
11709 q=link(p); qq=link(pp); mp->bisect_ptr=int_packets;
11710 u1r=right_x(p)-x_coord(p); u2r=left_x(q)-right_x(p);
11711 u3r=x_coord(q)-left_x(q); set_min_max(ur_packet);
11712 v1r=right_y(p)-y_coord(p); v2r=left_y(q)-right_y(p);
11713 v3r=y_coord(q)-left_y(q); set_min_max(vr_packet);
11714 x1r=right_x(pp)-x_coord(pp); x2r=left_x(qq)-right_x(pp);
11715 x3r=x_coord(qq)-left_x(qq); set_min_max(xr_packet);
11716 y1r=right_y(pp)-y_coord(pp); y2r=left_y(qq)-right_y(pp);
11717 y3r=y_coord(qq)-left_y(qq); set_min_max(yr_packet);
11718 mp->delx=x_coord(p)-x_coord(pp); mp->dely=y_coord(p)-y_coord(pp);
11719 mp->tol=0; mp->uv=r_packets; mp->xy=r_packets;
11720 mp->three_l=0; mp->cur_t=1; mp->cur_tt=1
11722 @ @<Subdivide for a new level of intersection@>=
11723 stack_dx=mp->delx; stack_dy=mp->dely; stack_tol=mp->tol;
11724 stack_uv=mp->uv; stack_xy=mp->xy;
11725 mp->bisect_ptr=mp->bisect_ptr+int_increment;
11726 mp->cur_t+=mp->cur_t; mp->cur_tt+=mp->cur_tt;
11727 u1l=stack_1(u_packet(mp->uv)); u3r=stack_3(u_packet(mp->uv));
11728 u2l=half(u1l+stack_2(u_packet(mp->uv)));
11729 u2r=half(u3r+stack_2(u_packet(mp->uv)));
11730 u3l=half(u2l+u2r); u1r=u3l;
11731 set_min_max(ul_packet); set_min_max(ur_packet);
11732 v1l=stack_1(v_packet(mp->uv)); v3r=stack_3(v_packet(mp->uv));
11733 v2l=half(v1l+stack_2(v_packet(mp->uv)));
11734 v2r=half(v3r+stack_2(v_packet(mp->uv)));
11735 v3l=half(v2l+v2r); v1r=v3l;
11736 set_min_max(vl_packet); set_min_max(vr_packet);
11737 x1l=stack_1(x_packet(mp->xy)); x3r=stack_3(x_packet(mp->xy));
11738 x2l=half(x1l+stack_2(x_packet(mp->xy)));
11739 x2r=half(x3r+stack_2(x_packet(mp->xy)));
11740 x3l=half(x2l+x2r); x1r=x3l;
11741 set_min_max(xl_packet); set_min_max(xr_packet);
11742 y1l=stack_1(y_packet(mp->xy)); y3r=stack_3(y_packet(mp->xy));
11743 y2l=half(y1l+stack_2(y_packet(mp->xy)));
11744 y2r=half(y3r+stack_2(y_packet(mp->xy)));
11745 y3l=half(y2l+y2r); y1r=y3l;
11746 set_min_max(yl_packet); set_min_max(yr_packet);
11747 mp->uv=l_packets; mp->xy=l_packets;
11748 mp->delx+=mp->delx; mp->dely+=mp->dely;
11749 mp->tol=mp->tol-mp->three_l+mp->tol_step;
11750 mp->tol+=mp->tol; mp->three_l=mp->three_l+mp->tol_step
11752 @ @<Advance to the next pair |(cur_t,cur_tt)|@>=
11754 if ( odd(mp->cur_tt) ) {
11755 if ( odd(mp->cur_t) ) {
11756 @<Descend to the previous level and |goto not_found|@>;
11759 mp->delx=mp->delx+stack_1(u_packet(mp->uv))+stack_2(u_packet(mp->uv))
11760 +stack_3(u_packet(mp->uv));
11761 mp->dely=mp->dely+stack_1(v_packet(mp->uv))+stack_2(v_packet(mp->uv))
11762 +stack_3(v_packet(mp->uv));
11763 mp->uv=mp->uv+int_packets; /* switch from |l_packet| to |r_packet| */
11764 decr(mp->cur_tt); mp->xy=mp->xy-int_packets;
11765 /* switch from |r_packet| to |l_packet| */
11766 mp->delx=mp->delx+stack_1(x_packet(mp->xy))+stack_2(x_packet(mp->xy))
11767 +stack_3(x_packet(mp->xy));
11768 mp->dely=mp->dely+stack_1(y_packet(mp->xy))+stack_2(y_packet(mp->xy))
11769 +stack_3(y_packet(mp->xy));
11772 incr(mp->cur_tt); mp->tol=mp->tol+mp->three_l;
11773 mp->delx=mp->delx-stack_1(x_packet(mp->xy))-stack_2(x_packet(mp->xy))
11774 -stack_3(x_packet(mp->xy));
11775 mp->dely=mp->dely-stack_1(y_packet(mp->xy))-stack_2(y_packet(mp->xy))
11776 -stack_3(y_packet(mp->xy));
11777 mp->xy=mp->xy+int_packets; /* switch from |l_packet| to |r_packet| */
11780 @ @<Descend to the previous level...@>=
11782 mp->cur_t=halfp(mp->cur_t); mp->cur_tt=halfp(mp->cur_tt);
11783 if ( mp->cur_t==0 ) return;
11784 mp->bisect_ptr=mp->bisect_ptr-int_increment;
11785 mp->three_l=mp->three_l-mp->tol_step;
11786 mp->delx=stack_dx; mp->dely=stack_dy; mp->tol=stack_tol;
11787 mp->uv=stack_uv; mp->xy=stack_xy;
11791 @ The |path_intersection| procedure is much simpler.
11792 It invokes |cubic_intersection| in lexicographic order until finding a
11793 pair of cubics that intersect. The final intersection times are placed in
11794 |cur_t| and~|cur_tt|.
11796 @c void mp_path_intersection (MP mp,pointer h, pointer hh) {
11797 pointer p,pp; /* link registers that traverse the given paths */
11798 integer n,nn; /* integer parts of intersection times, minus |unity| */
11799 @<Change one-point paths into dead cycles@>;
11804 if ( right_type(p)!=mp_endpoint ) {
11807 if ( right_type(pp)!=mp_endpoint ) {
11808 mp_cubic_intersection(mp, p,pp);
11809 if ( mp->cur_t>0 ) {
11810 mp->cur_t=mp->cur_t+n; mp->cur_tt=mp->cur_tt+nn;
11814 nn=nn+unity; pp=link(pp);
11817 n=n+unity; p=link(p);
11819 mp->tol_step=mp->tol_step+3;
11820 } while (mp->tol_step<=3);
11821 mp->cur_t=-unity; mp->cur_tt=-unity;
11824 @ @<Change one-point paths...@>=
11825 if ( right_type(h)==mp_endpoint ) {
11826 right_x(h)=x_coord(h); left_x(h)=x_coord(h);
11827 right_y(h)=y_coord(h); left_y(h)=y_coord(h); right_type(h)=mp_explicit;
11829 if ( right_type(hh)==mp_endpoint ) {
11830 right_x(hh)=x_coord(hh); left_x(hh)=x_coord(hh);
11831 right_y(hh)=y_coord(hh); left_y(hh)=y_coord(hh); right_type(hh)=mp_explicit;
11834 @* \[24] Dynamic linear equations.
11835 \MP\ users define variables implicitly by stating equations that should be
11836 satisfied; the computer is supposed to be smart enough to solve those equations.
11837 And indeed, the computer tries valiantly to do so, by distinguishing five
11838 different types of numeric values:
11841 |type(p)=mp_known| is the nice case, when |value(p)| is the |scaled| value
11842 of the variable whose address is~|p|.
11845 |type(p)=mp_dependent| means that |value(p)| is not present, but |dep_list(p)|
11846 points to a {\sl dependency list\/} that expresses the value of variable~|p|
11847 as a |scaled| number plus a sum of independent variables with |fraction|
11851 |type(p)=mp_independent| means that |value(p)=64s+m|, where |s>0| is a ``serial
11852 number'' reflecting the time this variable was first used in an equation;
11853 also |0<=m<64|, and each dependent variable
11854 that refers to this one is actually referring to the future value of
11855 this variable times~$2^m$. (Usually |m=0|, but higher degrees of
11856 scaling are sometimes needed to keep the coefficients in dependency lists
11857 from getting too large. The value of~|m| will always be even.)
11860 |type(p)=mp_numeric_type| means that variable |p| hasn't appeared in an
11861 equation before, but it has been explicitly declared to be numeric.
11864 |type(p)=undefined| means that variable |p| hasn't appeared before.
11866 \smallskip\noindent
11867 We have actually discussed these five types in the reverse order of their
11868 history during a computation: Once |known|, a variable never again
11869 becomes |dependent|; once |dependent|, it almost never again becomes
11870 |mp_independent|; once |mp_independent|, it never again becomes |mp_numeric_type|;
11871 and once |mp_numeric_type|, it never again becomes |undefined| (except
11872 of course when the user specifically decides to scrap the old value
11873 and start again). A backward step may, however, take place: Sometimes
11874 a |dependent| variable becomes |mp_independent| again, when one of the
11875 independent variables it depends on is reverting to |undefined|.
11878 The next patch detects overflow of independent-variable serial
11879 numbers. Diagnosed and patched by Thorsten Dahlheimer.
11881 @d s_scale 64 /* the serial numbers are multiplied by this factor */
11882 @d max_indep_vars 0177777777 /* $2^{25}-1$ */
11883 @d max_serial_no 017777777700 /* |max_indep_vars*s_scale| */
11884 @d new_indep(A) /* create a new independent variable */
11885 { if ( mp->serial_no==max_serial_no )
11886 mp_fatal_error(mp, "variable instance identifiers exhausted");
11887 type((A))=mp_independent; mp->serial_no=mp->serial_no+s_scale;
11888 value((A))=mp->serial_no;
11892 integer serial_no; /* the most recent serial number, times |s_scale| */
11894 @ @<Make variable |q+s| newly independent@>=new_indep(q+s)
11896 @ But how are dependency lists represented? It's simple: The linear combination
11897 $\alpha_1v_1+\cdots+\alpha_kv_k+\beta$ appears in |k+1| value nodes. If
11898 |q=dep_list(p)| points to this list, and if |k>0|, then |value(q)=
11899 @t$\alpha_1$@>| (which is a |fraction|); |info(q)| points to the location
11900 of $\alpha_1$; and |link(p)| points to the dependency list
11901 $\alpha_2v_2+\cdots+\alpha_kv_k+\beta$. On the other hand if |k=0|,
11902 then |value(q)=@t$\beta$@>| (which is |scaled|) and |info(q)=null|.
11903 The independent variables $v_1$, \dots,~$v_k$ have been sorted so that
11904 they appear in decreasing order of their |value| fields (i.e., of
11905 their serial numbers). \ (It is convenient to use decreasing order,
11906 since |value(null)=0|. If the independent variables were not sorted by
11907 serial number but by some other criterion, such as their location in |mem|,
11908 the equation-solving mechanism would be too system-dependent, because
11909 the ordering can affect the computed results.)
11911 The |link| field in the node that contains the constant term $\beta$ is
11912 called the {\sl final link\/} of the dependency list. \MP\ maintains
11913 a doubly-linked master list of all dependency lists, in terms of a permanently
11915 in |mem| called |dep_head|. If there are no dependencies, we have
11916 |link(dep_head)=dep_head| and |prev_dep(dep_head)=dep_head|;
11917 otherwise |link(dep_head)| points to the first dependent variable, say~|p|,
11918 and |prev_dep(p)=dep_head|. We have |type(p)=mp_dependent|, and |dep_list(p)|
11919 points to its dependency list. If the final link of that dependency list
11920 occurs in location~|q|, then |link(q)| points to the next dependent
11921 variable (say~|r|); and we have |prev_dep(r)=q|, etc.
11923 @d dep_list(A) link(value_loc((A)))
11924 /* half of the |value| field in a |dependent| variable */
11925 @d prev_dep(A) info(value_loc((A)))
11926 /* the other half; makes a doubly linked list */
11927 @d dep_node_size 2 /* the number of words per dependency node */
11929 @<Initialize table entries...@>= mp->serial_no=0;
11930 link(dep_head)=dep_head; prev_dep(dep_head)=dep_head;
11931 info(dep_head)=null; dep_list(dep_head)=null;
11933 @ Actually the description above contains a little white lie. There's
11934 another kind of variable called |mp_proto_dependent|, which is
11935 just like a |dependent| one except that the $\alpha$ coefficients
11936 in its dependency list are |scaled| instead of being fractions.
11937 Proto-dependency lists are mixed with dependency lists in the
11938 nodes reachable from |dep_head|.
11940 @ Here is a procedure that prints a dependency list in symbolic form.
11941 The second parameter should be either |dependent| or |mp_proto_dependent|,
11942 to indicate the scaling of the coefficients.
11944 @<Declare subroutines for printing expressions@>=
11945 void mp_print_dependency (MP mp,pointer p, small_number t) {
11946 integer v; /* a coefficient */
11947 pointer pp,q; /* for list manipulation */
11950 v=abs(value(p)); q=info(p);
11951 if ( q==null ) { /* the constant term */
11952 if ( (v!=0)||(p==pp) ) {
11953 if ( value(p)>0 ) if ( p!=pp ) mp_print_char(mp, '+');
11954 mp_print_scaled(mp, value(p));
11958 @<Print the coefficient, unless it's $\pm1.0$@>;
11959 if ( type(q)!=mp_independent ) mp_confusion(mp, "dep");
11960 @:this can't happen dep}{\quad dep@>
11961 mp_print_variable_name(mp, q); v=value(q) % s_scale;
11962 while ( v>0 ) { mp_print(mp, "*4"); v=v-2; }
11967 @ @<Print the coefficient, unless it's $\pm1.0$@>=
11968 if ( value(p)<0 ) mp_print_char(mp, '-');
11969 else if ( p!=pp ) mp_print_char(mp, '+');
11970 if ( t==mp_dependent ) v=mp_round_fraction(mp, v);
11971 if ( v!=unity ) mp_print_scaled(mp, v)
11973 @ The maximum absolute value of a coefficient in a given dependency list
11974 is returned by the following simple function.
11976 @c fraction mp_max_coef (MP mp,pointer p) {
11977 fraction x; /* the maximum so far */
11979 while ( info(p)!=null ) {
11980 if ( abs(value(p))>x ) x=abs(value(p));
11986 @ One of the main operations needed on dependency lists is to add a multiple
11987 of one list to the other; we call this |p_plus_fq|, where |p| and~|q| point
11988 to dependency lists and |f| is a fraction.
11990 If the coefficient of any independent variable becomes |coef_bound| or
11991 more, in absolute value, this procedure changes the type of that variable
11992 to `|independent_needing_fix|', and sets the global variable |fix_needed|
11993 to~|true|. The value of $|coef_bound|=\mu$ is chosen so that
11994 $\mu^2+\mu<8$; this means that the numbers we deal with won't
11995 get too large. (Instead of the ``optimum'' $\mu=(\sqrt{33}-1)/2\approx
11996 2.3723$, the safer value 7/3 is taken as the threshold.)
11998 The changes mentioned in the preceding paragraph are actually done only if
11999 the global variable |watch_coefs| is |true|. But it usually is; in fact,
12000 it is |false| only when \MP\ is making a dependency list that will soon
12001 be equated to zero.
12003 Several procedures that act on dependency lists, including |p_plus_fq|,
12004 set the global variable |dep_final| to the final (constant term) node of
12005 the dependency list that they produce.
12007 @d coef_bound 04525252525 /* |fraction| approximation to 7/3 */
12008 @d independent_needing_fix 0
12011 boolean fix_needed; /* does at least one |independent| variable need scaling? */
12012 boolean watch_coefs; /* should we scale coefficients that exceed |coef_bound|? */
12013 pointer dep_final; /* location of the constant term and final link */
12016 mp->fix_needed=false; mp->watch_coefs=true;
12018 @ The |p_plus_fq| procedure has a fourth parameter, |t|, that should be
12019 set to |mp_proto_dependent| if |p| is a proto-dependency list. In this
12020 case |f| will be |scaled|, not a |fraction|. Similarly, the fifth parameter~|tt|
12021 should be |mp_proto_dependent| if |q| is a proto-dependency list.
12023 List |q| is unchanged by the operation; but list |p| is totally destroyed.
12025 The final link of the dependency list or proto-dependency list returned
12026 by |p_plus_fq| is the same as the original final link of~|p|. Indeed, the
12027 constant term of the result will be located in the same |mem| location
12028 as the original constant term of~|p|.
12030 Coefficients of the result are assumed to be zero if they are less than
12031 a certain threshold. This compensates for inevitable rounding errors,
12032 and tends to make more variables `|known|'. The threshold is approximately
12033 $10^{-5}$ in the case of normal dependency lists, $10^{-4}$ for
12034 proto-dependencies.
12036 @d fraction_threshold 2685 /* a |fraction| coefficient less than this is zeroed */
12037 @d half_fraction_threshold 1342 /* half of |fraction_threshold| */
12038 @d scaled_threshold 8 /* a |scaled| coefficient less than this is zeroed */
12039 @d half_scaled_threshold 4 /* half of |scaled_threshold| */
12041 @<Declare basic dependency-list subroutines@>=
12042 pointer mp_p_plus_fq ( MP mp, pointer p, integer f,
12043 pointer q, small_number t, small_number tt) ;
12046 pointer mp_p_plus_fq ( MP mp, pointer p, integer f,
12047 pointer q, small_number t, small_number tt) {
12048 pointer pp,qq; /* |info(p)| and |info(q)|, respectively */
12049 pointer r,s; /* for list manipulation */
12050 integer mp_threshold; /* defines a neighborhood of zero */
12051 integer v; /* temporary register */
12052 if ( t==mp_dependent ) mp_threshold=fraction_threshold;
12053 else mp_threshold=scaled_threshold;
12054 r=temp_head; pp=info(p); qq=info(q);
12060 @<Contribute a term from |p|, plus |f| times the
12061 corresponding term from |q|@>
12063 } else if ( value(pp)<value(qq) ) {
12064 @<Contribute a term from |q|, multiplied by~|f|@>
12066 link(r)=p; r=p; p=link(p); pp=info(p);
12069 if ( t==mp_dependent )
12070 value(p)=mp_slow_add(mp, value(p),mp_take_fraction(mp, value(q),f));
12072 value(p)=mp_slow_add(mp, value(p),mp_take_scaled(mp, value(q),f));
12073 link(r)=p; mp->dep_final=p;
12074 return link(temp_head);
12077 @ @<Contribute a term from |p|, plus |f|...@>=
12079 if ( tt==mp_dependent ) v=value(p)+mp_take_fraction(mp, f,value(q));
12080 else v=value(p)+mp_take_scaled(mp, f,value(q));
12081 value(p)=v; s=p; p=link(p);
12082 if ( abs(v)<mp_threshold ) {
12083 mp_free_node(mp, s,dep_node_size);
12085 if ( (abs(v)>=coef_bound) && mp->watch_coefs ) {
12086 type(qq)=independent_needing_fix; mp->fix_needed=true;
12090 pp=info(p); q=link(q); qq=info(q);
12093 @ @<Contribute a term from |q|, multiplied by~|f|@>=
12095 if ( tt==mp_dependent ) v=mp_take_fraction(mp, f,value(q));
12096 else v=mp_take_scaled(mp, f,value(q));
12097 if ( abs(v)>halfp(mp_threshold) ) {
12098 s=mp_get_node(mp, dep_node_size); info(s)=qq; value(s)=v;
12099 if ( (abs(v)>=coef_bound) && mp->watch_coefs ) {
12100 type(qq)=independent_needing_fix; mp->fix_needed=true;
12104 q=link(q); qq=info(q);
12107 @ It is convenient to have another subroutine for the special case
12108 of |p_plus_fq| when |f=1.0|. In this routine lists |p| and |q| are
12109 both of the same type~|t| (either |dependent| or |mp_proto_dependent|).
12111 @c pointer mp_p_plus_q (MP mp,pointer p, pointer q, small_number t) {
12112 pointer pp,qq; /* |info(p)| and |info(q)|, respectively */
12113 pointer r,s; /* for list manipulation */
12114 integer mp_threshold; /* defines a neighborhood of zero */
12115 integer v; /* temporary register */
12116 if ( t==mp_dependent ) mp_threshold=fraction_threshold;
12117 else mp_threshold=scaled_threshold;
12118 r=temp_head; pp=info(p); qq=info(q);
12124 @<Contribute a term from |p|, plus the
12125 corresponding term from |q|@>
12127 } else if ( value(pp)<value(qq) ) {
12128 s=mp_get_node(mp, dep_node_size); info(s)=qq; value(s)=value(q);
12129 q=link(q); qq=info(q); link(r)=s; r=s;
12131 link(r)=p; r=p; p=link(p); pp=info(p);
12134 value(p)=mp_slow_add(mp, value(p),value(q));
12135 link(r)=p; mp->dep_final=p;
12136 return link(temp_head);
12139 @ @<Contribute a term from |p|, plus the...@>=
12141 v=value(p)+value(q);
12142 value(p)=v; s=p; p=link(p); pp=info(p);
12143 if ( abs(v)<mp_threshold ) {
12144 mp_free_node(mp, s,dep_node_size);
12146 if ( (abs(v)>=coef_bound ) && mp->watch_coefs ) {
12147 type(qq)=independent_needing_fix; mp->fix_needed=true;
12151 q=link(q); qq=info(q);
12154 @ A somewhat simpler routine will multiply a dependency list
12155 by a given constant~|v|. The constant is either a |fraction| less than
12156 |fraction_one|, or it is |scaled|. In the latter case we might be forced to
12157 convert a dependency list to a proto-dependency list.
12158 Parameters |t0| and |t1| are the list types before and after;
12159 they should agree unless |t0=mp_dependent| and |t1=mp_proto_dependent|
12160 and |v_is_scaled=true|.
12162 @c pointer mp_p_times_v (MP mp,pointer p, integer v, small_number t0,
12163 small_number t1, boolean v_is_scaled) {
12164 pointer r,s; /* for list manipulation */
12165 integer w; /* tentative coefficient */
12166 integer mp_threshold;
12167 boolean scaling_down;
12168 if ( t0!=t1 ) scaling_down=true; else scaling_down=! v_is_scaled;
12169 if ( t1==mp_dependent ) mp_threshold=half_fraction_threshold;
12170 else mp_threshold=half_scaled_threshold;
12172 while ( info(p)!=null ) {
12173 if ( scaling_down ) w=mp_take_fraction(mp, v,value(p));
12174 else w=mp_take_scaled(mp, v,value(p));
12175 if ( abs(w)<=mp_threshold ) {
12176 s=link(p); mp_free_node(mp, p,dep_node_size); p=s;
12178 if ( abs(w)>=coef_bound ) {
12179 mp->fix_needed=true; type(info(p))=independent_needing_fix;
12181 link(r)=p; r=p; value(p)=w; p=link(p);
12185 if ( v_is_scaled ) value(p)=mp_take_scaled(mp, value(p),v);
12186 else value(p)=mp_take_fraction(mp, value(p),v);
12187 return link(temp_head);
12190 @ Similarly, we sometimes need to divide a dependency list
12191 by a given |scaled| constant.
12193 @<Declare basic dependency-list subroutines@>=
12194 pointer mp_p_over_v (MP mp,pointer p, scaled v, small_number
12195 t0, small_number t1) ;
12198 pointer mp_p_over_v (MP mp,pointer p, scaled v, small_number
12199 t0, small_number t1) {
12200 pointer r,s; /* for list manipulation */
12201 integer w; /* tentative coefficient */
12202 integer mp_threshold;
12203 boolean scaling_down;
12204 if ( t0!=t1 ) scaling_down=true; else scaling_down=false;
12205 if ( t1==mp_dependent ) mp_threshold=half_fraction_threshold;
12206 else mp_threshold=half_scaled_threshold;
12208 while ( info( p)!=null ) {
12209 if ( scaling_down ) {
12210 if ( abs(v)<02000000 ) w=mp_make_scaled(mp, value(p),v*010000);
12211 else w=mp_make_scaled(mp, mp_round_fraction(mp, value(p)),v);
12213 w=mp_make_scaled(mp, value(p),v);
12215 if ( abs(w)<=mp_threshold ) {
12216 s=link(p); mp_free_node(mp, p,dep_node_size); p=s;
12218 if ( abs(w)>=coef_bound ) {
12219 mp->fix_needed=true; type(info(p))=independent_needing_fix;
12221 link(r)=p; r=p; value(p)=w; p=link(p);
12224 link(r)=p; value(p)=mp_make_scaled(mp, value(p),v);
12225 return link(temp_head);
12228 @ Here's another utility routine for dependency lists. When an independent
12229 variable becomes dependent, we want to remove it from all existing
12230 dependencies. The |p_with_x_becoming_q| function computes the
12231 dependency list of~|p| after variable~|x| has been replaced by~|q|.
12233 This procedure has basically the same calling conventions as |p_plus_fq|:
12234 List~|q| is unchanged; list~|p| is destroyed; the constant node and the
12235 final link are inherited from~|p|; and the fourth parameter tells whether
12236 or not |p| is |mp_proto_dependent|. However, the global variable |dep_final|
12237 is not altered if |x| does not occur in list~|p|.
12239 @c pointer mp_p_with_x_becoming_q (MP mp,pointer p,
12240 pointer x, pointer q, small_number t) {
12241 pointer r,s; /* for list manipulation */
12242 integer v; /* coefficient of |x| */
12243 integer sx; /* serial number of |x| */
12244 s=p; r=temp_head; sx=value(x);
12245 while ( value(info(s))>sx ) { r=s; s=link(s); };
12246 if ( info(s)!=x ) {
12249 link(temp_head)=p; link(r)=link(s); v=value(s);
12250 mp_free_node(mp, s,dep_node_size);
12251 return mp_p_plus_fq(mp, link(temp_head),v,q,t,mp_dependent);
12255 @ Here's a simple procedure that reports an error when a variable
12256 has just received a known value that's out of the required range.
12258 @<Declare basic dependency-list subroutines@>=
12259 void mp_val_too_big (MP mp,scaled x) ;
12261 @ @c void mp_val_too_big (MP mp,scaled x) {
12262 if ( mp->internal[mp_warning_check]>0 ) {
12263 print_err("Value is too large ("); mp_print_scaled(mp, x); mp_print_char(mp, ')');
12264 @.Value is too large@>
12265 help4("The equation I just processed has given some variable")
12266 ("a value of 4096 or more. Continue and I'll try to cope")
12267 ("with that big value; but it might be dangerous.")
12268 ("(Set warningcheck:=0 to suppress this message.)");
12273 @ When a dependent variable becomes known, the following routine
12274 removes its dependency list. Here |p| points to the variable, and
12275 |q| points to the dependency list (which is one node long).
12277 @<Declare basic dependency-list subroutines@>=
12278 void mp_make_known (MP mp,pointer p, pointer q) ;
12280 @ @c void mp_make_known (MP mp,pointer p, pointer q) {
12281 int t; /* the previous type */
12282 prev_dep(link(q))=prev_dep(p);
12283 link(prev_dep(p))=link(q); t=type(p);
12284 type(p)=mp_known; value(p)=value(q); mp_free_node(mp, q,dep_node_size);
12285 if ( abs(value(p))>=fraction_one ) mp_val_too_big(mp, value(p));
12286 if (( mp->internal[mp_tracing_equations]>0) && mp_interesting(mp, p) ) {
12287 mp_begin_diagnostic(mp); mp_print_nl(mp, "#### ");
12288 @:]]]\#\#\#\#_}{\.{\#\#\#\#}@>
12289 mp_print_variable_name(mp, p);
12290 mp_print_char(mp, '='); mp_print_scaled(mp, value(p));
12291 mp_end_diagnostic(mp, false);
12293 if (( mp->cur_exp==p ) && mp->cur_type==t ) {
12294 mp->cur_type=mp_known; mp->cur_exp=value(p);
12295 mp_free_node(mp, p,value_node_size);
12299 @ The |fix_dependencies| routine is called into action when |fix_needed|
12300 has been triggered. The program keeps a list~|s| of independent variables
12301 whose coefficients must be divided by~4.
12303 In unusual cases, this fixup process might reduce one or more coefficients
12304 to zero, so that a variable will become known more or less by default.
12306 @<Declare basic dependency-list subroutines@>=
12307 void mp_fix_dependencies (MP mp);
12309 @ @c void mp_fix_dependencies (MP mp) {
12310 pointer p,q,r,s,t; /* list manipulation registers */
12311 pointer x; /* an independent variable */
12312 r=link(dep_head); s=null;
12313 while ( r!=dep_head ){
12315 @<Run through the dependency list for variable |t|, fixing
12316 all nodes, and ending with final link~|q|@>;
12318 if ( q==dep_list(t) ) mp_make_known(mp, t,q);
12320 while ( s!=null ) {
12321 p=link(s); x=info(s); free_avail(s); s=p;
12322 type(x)=mp_independent; value(x)=value(x)+2;
12324 mp->fix_needed=false;
12327 @ @d independent_being_fixed 1 /* this variable already appears in |s| */
12329 @<Run through the dependency list for variable |t|...@>=
12330 r=value_loc(t); /* |link(r)=dep_list(t)| */
12332 q=link(r); x=info(q);
12333 if ( x==null ) break;
12334 if ( type(x)<=independent_being_fixed ) {
12335 if ( type(x)<independent_being_fixed ) {
12336 p=mp_get_avail(mp); link(p)=s; s=p;
12337 info(s)=x; type(x)=independent_being_fixed;
12339 value(q)=value(q) / 4;
12340 if ( value(q)==0 ) {
12341 link(r)=link(q); mp_free_node(mp, q,dep_node_size); q=r;
12348 @ The |new_dep| routine installs a dependency list~|p| into the value node~|q|,
12349 linking it into the list of all known dependencies. We assume that
12350 |dep_final| points to the final node of list~|p|.
12352 @c void mp_new_dep (MP mp,pointer q, pointer p) {
12353 pointer r; /* what used to be the first dependency */
12354 dep_list(q)=p; prev_dep(q)=dep_head;
12355 r=link(dep_head); link(mp->dep_final)=r; prev_dep(r)=mp->dep_final;
12359 @ Here is one of the ways a dependency list gets started.
12360 The |const_dependency| routine produces a list that has nothing but
12363 @c pointer mp_const_dependency (MP mp, scaled v) {
12364 mp->dep_final=mp_get_node(mp, dep_node_size);
12365 value(mp->dep_final)=v; info(mp->dep_final)=null;
12366 return mp->dep_final;
12369 @ And here's a more interesting way to start a dependency list from scratch:
12370 The parameter to |single_dependency| is the location of an
12371 independent variable~|x|, and the result is the simple dependency list
12374 In the unlikely event that the given independent variable has been doubled so
12375 often that we can't refer to it with a nonzero coefficient,
12376 |single_dependency| returns the simple list `0'. This case can be
12377 recognized by testing that the returned list pointer is equal to
12380 @c pointer mp_single_dependency (MP mp,pointer p) {
12381 pointer q; /* the new dependency list */
12382 integer m; /* the number of doublings */
12383 m=value(p) % s_scale;
12385 return mp_const_dependency(mp, 0);
12387 q=mp_get_node(mp, dep_node_size);
12388 value(q)=two_to_the(28-m); info(q)=p;
12389 link(q)=mp_const_dependency(mp, 0);
12394 @ We sometimes need to make an exact copy of a dependency list.
12396 @c pointer mp_copy_dep_list (MP mp,pointer p) {
12397 pointer q; /* the new dependency list */
12398 q=mp_get_node(mp, dep_node_size); mp->dep_final=q;
12400 info(mp->dep_final)=info(p); value(mp->dep_final)=value(p);
12401 if ( info(mp->dep_final)==null ) break;
12402 link(mp->dep_final)=mp_get_node(mp, dep_node_size);
12403 mp->dep_final=link(mp->dep_final); p=link(p);
12408 @ But how do variables normally become known? Ah, now we get to the heart of the
12409 equation-solving mechanism. The |linear_eq| procedure is given a |dependent|
12410 or |mp_proto_dependent| list,~|p|, in which at least one independent variable
12411 appears. It equates this list to zero, by choosing an independent variable
12412 with the largest coefficient and making it dependent on the others. The
12413 newly dependent variable is eliminated from all current dependencies,
12414 thereby possibly making other dependent variables known.
12416 The given list |p| is, of course, totally destroyed by all this processing.
12418 @c void mp_linear_eq (MP mp, pointer p, small_number t) {
12419 pointer q,r,s; /* for link manipulation */
12420 pointer x; /* the variable that loses its independence */
12421 integer n; /* the number of times |x| had been halved */
12422 integer v; /* the coefficient of |x| in list |p| */
12423 pointer prev_r; /* lags one step behind |r| */
12424 pointer final_node; /* the constant term of the new dependency list */
12425 integer w; /* a tentative coefficient */
12426 @<Find a node |q| in list |p| whose coefficient |v| is largest@>;
12427 x=info(q); n=value(x) % s_scale;
12428 @<Divide list |p| by |-v|, removing node |q|@>;
12429 if ( mp->internal[mp_tracing_equations]>0 ) {
12430 @<Display the new dependency@>;
12432 @<Simplify all existing dependencies by substituting for |x|@>;
12433 @<Change variable |x| from |independent| to |dependent| or |known|@>;
12434 if ( mp->fix_needed ) mp_fix_dependencies(mp);
12437 @ @<Find a node |q| in list |p| whose coefficient |v| is largest@>=
12438 q=p; r=link(p); v=value(q);
12439 while ( info(r)!=null ) {
12440 if ( abs(value(r))>abs(v) ) { q=r; v=value(r); };
12444 @ Here we want to change the coefficients from |scaled| to |fraction|,
12445 except in the constant term. In the common case of a trivial equation
12446 like `\.{x=3.14}', we will have |v=-fraction_one|, |q=p|, and |t=mp_dependent|.
12448 @<Divide list |p| by |-v|, removing node |q|@>=
12449 s=temp_head; link(s)=p; r=p;
12452 link(s)=link(r); mp_free_node(mp, r,dep_node_size);
12454 w=mp_make_fraction(mp, value(r),v);
12455 if ( abs(w)<=half_fraction_threshold ) {
12456 link(s)=link(r); mp_free_node(mp, r,dep_node_size);
12462 } while (info(r)!=null);
12463 if ( t==mp_proto_dependent ) {
12464 value(r)=-mp_make_scaled(mp, value(r),v);
12465 } else if ( v!=-fraction_one ) {
12466 value(r)=-mp_make_fraction(mp, value(r),v);
12468 final_node=r; p=link(temp_head)
12470 @ @<Display the new dependency@>=
12471 if ( mp_interesting(mp, x) ) {
12472 mp_begin_diagnostic(mp); mp_print_nl(mp, "## ");
12473 mp_print_variable_name(mp, x);
12474 @:]]]\#\#_}{\.{\#\#}@>
12476 while ( w>0 ) { mp_print(mp, "*4"); w=w-2; };
12477 mp_print_char(mp, '='); mp_print_dependency(mp, p,mp_dependent);
12478 mp_end_diagnostic(mp, false);
12481 @ @<Simplify all existing dependencies by substituting for |x|@>=
12482 prev_r=dep_head; r=link(dep_head);
12483 while ( r!=dep_head ) {
12484 s=dep_list(r); q=mp_p_with_x_becoming_q(mp, s,x,p,type(r));
12485 if ( info(q)==null ) {
12486 mp_make_known(mp, r,q);
12489 do { q=link(q); } while (info(q)!=null);
12495 @ @<Change variable |x| from |independent| to |dependent| or |known|@>=
12496 if ( n>0 ) @<Divide list |p| by $2^n$@>;
12497 if ( info(p)==null ) {
12500 if ( abs(value(x))>=fraction_one ) mp_val_too_big(mp, value(x));
12501 mp_free_node(mp, p,dep_node_size);
12502 if ( mp->cur_exp==x ) if ( mp->cur_type==mp_independent ) {
12503 mp->cur_exp=value(x); mp->cur_type=mp_known;
12504 mp_free_node(mp, x,value_node_size);
12507 type(x)=mp_dependent; mp->dep_final=final_node; mp_new_dep(mp, x,p);
12508 if ( mp->cur_exp==x ) if ( mp->cur_type==mp_independent ) mp->cur_type=mp_dependent;
12511 @ @<Divide list |p| by $2^n$@>=
12513 s=temp_head; link(temp_head)=p; r=p;
12516 else w=value(r) / two_to_the(n);
12517 if ( (abs(w)<=half_fraction_threshold)&&(info(r)!=null) ) {
12519 mp_free_node(mp, r,dep_node_size);
12524 } while (info(s)!=null);
12528 @ The |check_mem| procedure, which is used only when \MP\ is being
12529 debugged, makes sure that the current dependency lists are well formed.
12531 @<Check the list of linear dependencies@>=
12532 q=dep_head; p=link(q);
12533 while ( p!=dep_head ) {
12534 if ( prev_dep(p)!=q ) {
12535 mp_print_nl(mp, "Bad PREVDEP at "); mp_print_int(mp, p);
12540 r=info(p); q=p; p=link(q);
12541 if ( r==null ) break;
12542 if ( value(info(p))>=value(r) ) {
12543 mp_print_nl(mp, "Out of order at "); mp_print_int(mp, p);
12544 @.Out of order...@>
12549 @* \[25] Dynamic nonlinear equations.
12550 Variables of numeric type are maintained by the general scheme of
12551 independent, dependent, and known values that we have just studied;
12552 and the components of pair and transform variables are handled in the
12553 same way. But \MP\ also has five other types of values: \&{boolean},
12554 \&{string}, \&{pen}, \&{path}, and \&{picture}; what about them?
12556 Equations are allowed between nonlinear quantities, but only in a
12557 simple form. Two variables that haven't yet been assigned values are
12558 either equal to each other, or they're not.
12560 Before a boolean variable has received a value, its type is |mp_unknown_boolean|;
12561 similarly, there are variables whose type is |mp_unknown_string|, |mp_unknown_pen|,
12562 |mp_unknown_path|, and |mp_unknown_picture|. In such cases the value is either
12563 |null| (which means that no other variables are equivalent to this one), or
12564 it points to another variable of the same undefined type. The pointers in the
12565 latter case form a cycle of nodes, which we shall call a ``ring.''
12566 Rings of undefined variables may include capsules, which arise as
12567 intermediate results within expressions or as \&{expr} parameters to macros.
12569 When one member of a ring receives a value, the same value is given to
12570 all the other members. In the case of paths and pictures, this implies
12571 making separate copies of a potentially large data structure; users should
12572 restrain their enthusiasm for such generality, unless they have lots and
12573 lots of memory space.
12575 @ The following procedure is called when a capsule node is being
12576 added to a ring (e.g., when an unknown variable is mentioned in an expression).
12578 @c pointer mp_new_ring_entry (MP mp,pointer p) {
12579 pointer q; /* the new capsule node */
12580 q=mp_get_node(mp, value_node_size); name_type(q)=mp_capsule;
12582 if ( value(p)==null ) value(q)=p; else value(q)=value(p);
12587 @ Conversely, we might delete a capsule or a variable before it becomes known.
12588 The following procedure simply detaches a quantity from its ring,
12589 without recycling the storage.
12591 @<Declare the recycling subroutines@>=
12592 void mp_ring_delete (MP mp,pointer p) {
12595 if ( q!=null ) if ( q!=p ){
12596 while ( value(q)!=p ) q=value(q);
12601 @ Eventually there might be an equation that assigns values to all of the
12602 variables in a ring. The |nonlinear_eq| subroutine does the necessary
12603 propagation of values.
12605 If the parameter |flush_p| is |true|, node |p| itself needn't receive a
12606 value, it will soon be recycled.
12608 @c void mp_nonlinear_eq (MP mp,integer v, pointer p, boolean flush_p) {
12609 small_number t; /* the type of ring |p| */
12610 pointer q,r; /* link manipulation registers */
12611 t=type(p)-unknown_tag; q=value(p);
12612 if ( flush_p ) type(p)=mp_vacuous; else p=q;
12614 r=value(q); type(q)=t;
12616 case mp_boolean_type: value(q)=v; break;
12617 case mp_string_type: value(q)=v; add_str_ref(v); break;
12618 case mp_pen_type: value(q)=copy_pen(v); break;
12619 case mp_path_type: value(q)=mp_copy_path(mp, v); break;
12620 case mp_picture_type: value(q)=v; add_edge_ref(v); break;
12621 } /* there ain't no more cases */
12626 @ If two members of rings are equated, and if they have the same type,
12627 the |ring_merge| procedure is called on to make them equivalent.
12629 @c void mp_ring_merge (MP mp,pointer p, pointer q) {
12630 pointer r; /* traverses one list */
12634 @<Exclaim about a redundant equation@>;
12639 r=value(p); value(p)=value(q); value(q)=r;
12642 @ @<Exclaim about a redundant equation@>=
12644 print_err("Redundant equation");
12645 @.Redundant equation@>
12646 help2("I already knew that this equation was true.")
12647 ("But perhaps no harm has been done; let's continue.");
12648 mp_put_get_error(mp);
12651 @* \[26] Introduction to the syntactic routines.
12652 Let's pause a moment now and try to look at the Big Picture.
12653 The \MP\ program consists of three main parts: syntactic routines,
12654 semantic routines, and output routines. The chief purpose of the
12655 syntactic routines is to deliver the user's input to the semantic routines,
12656 while parsing expressions and locating operators and operands. The
12657 semantic routines act as an interpreter responding to these operators,
12658 which may be regarded as commands. And the output routines are
12659 periodically called on to produce compact font descriptions that can be
12660 used for typesetting or for making interim proof drawings. We have
12661 discussed the basic data structures and many of the details of semantic
12662 operations, so we are good and ready to plunge into the part of \MP\ that
12663 actually controls the activities.
12665 Our current goal is to come to grips with the |get_next| procedure,
12666 which is the keystone of \MP's input mechanism. Each call of |get_next|
12667 sets the value of three variables |cur_cmd|, |cur_mod|, and |cur_sym|,
12668 representing the next input token.
12669 $$\vbox{\halign{#\hfil\cr
12670 \hbox{|cur_cmd| denotes a command code from the long list of codes
12672 \hbox{|cur_mod| denotes a modifier of the command code;}\cr
12673 \hbox{|cur_sym| is the hash address of the symbolic token that was
12675 \hbox{\qquad or zero in the case of a numeric or string
12676 or capsule token.}\cr}}$$
12677 Underlying this external behavior of |get_next| is all the machinery
12678 necessary to convert from character files to tokens. At a given time we
12679 may be only partially finished with the reading of several files (for
12680 which \&{input} was specified), and partially finished with the expansion
12681 of some user-defined macros and/or some macro parameters, and partially
12682 finished reading some text that the user has inserted online,
12683 and so on. When reading a character file, the characters must be
12684 converted to tokens; comments and blank spaces must
12685 be removed, numeric and string tokens must be evaluated.
12687 To handle these situations, which might all be present simultaneously,
12688 \MP\ uses various stacks that hold information about the incomplete
12689 activities, and there is a finite state control for each level of the
12690 input mechanism. These stacks record the current state of an implicitly
12691 recursive process, but the |get_next| procedure is not recursive.
12694 eight_bits cur_cmd; /* current command set by |get_next| */
12695 integer cur_mod; /* operand of current command */
12696 halfword cur_sym; /* hash address of current symbol */
12698 @ The |print_cmd_mod| routine prints a symbolic interpretation of a
12699 command code and its modifier.
12700 It consists of a rather tedious sequence of print
12701 commands, and most of it is essentially an inverse to the |primitive|
12702 routine that enters a \MP\ primitive into |hash| and |eqtb|. Therefore almost
12703 all of this procedure appears elsewhere in the program, together with the
12704 corresponding |primitive| calls.
12706 @<Declare the procedure called |print_cmd_mod|@>=
12707 void mp_print_cmd_mod (MP mp,integer c, integer m) {
12709 @<Cases of |print_cmd_mod| for symbolic printing of primitives@>
12710 default: mp_print(mp, "[unknown command code!]"); break;
12714 @ Here is a procedure that displays a given command in braces, in the
12715 user's transcript file.
12717 @d show_cur_cmd_mod mp_show_cmd_mod(mp, mp->cur_cmd,mp->cur_mod)
12720 void mp_show_cmd_mod (MP mp,integer c, integer m) {
12721 mp_begin_diagnostic(mp); mp_print_nl(mp, "{");
12722 mp_print_cmd_mod(mp, c,m); mp_print_char(mp, '}');
12723 mp_end_diagnostic(mp, false);
12726 @* \[27] Input stacks and states.
12727 The state of \MP's input mechanism appears in the input stack, whose
12728 entries are records with five fields, called |index|, |start|, |loc|,
12729 |limit|, and |name|. The top element of this stack is maintained in a
12730 global variable for which no subscripting needs to be done; the other
12731 elements of the stack appear in an array. Hence the stack is declared thus:
12735 quarterword index_field;
12736 halfword start_field, loc_field, limit_field, name_field;
12740 in_state_record *input_stack;
12741 integer input_ptr; /* first unused location of |input_stack| */
12742 integer max_in_stack; /* largest value of |input_ptr| when pushing */
12743 in_state_record cur_input; /* the ``top'' input state */
12744 int stack_size; /* maximum number of simultaneous input sources */
12746 @ @<Allocate or initialize ...@>=
12747 mp->stack_size = 300;
12748 mp->input_stack = xmalloc((mp->stack_size+1),sizeof(in_state_record));
12750 @ @<Dealloc variables@>=
12751 xfree(mp->input_stack);
12753 @ We've already defined the special variable |loc==cur_input.loc_field|
12754 in our discussion of basic input-output routines. The other components of
12755 |cur_input| are defined in the same way:
12757 @d index mp->cur_input.index_field /* reference for buffer information */
12758 @d start mp->cur_input.start_field /* starting position in |buffer| */
12759 @d limit mp->cur_input.limit_field /* end of current line in |buffer| */
12760 @d name mp->cur_input.name_field /* name of the current file */
12762 @ Let's look more closely now at the five control variables
12763 (|index|,~|start|,~|loc|,~|limit|,~|name|),
12764 assuming that \MP\ is reading a line of characters that have been input
12765 from some file or from the user's terminal. There is an array called
12766 |buffer| that acts as a stack of all lines of characters that are
12767 currently being read from files, including all lines on subsidiary
12768 levels of the input stack that are not yet completed. \MP\ will return to
12769 the other lines when it is finished with the present input file.
12771 (Incidentally, on a machine with byte-oriented addressing, it would be
12772 appropriate to combine |buffer| with the |str_pool| array,
12773 letting the buffer entries grow downward from the top of the string pool
12774 and checking that these two tables don't bump into each other.)
12776 The line we are currently working on begins in position |start| of the
12777 buffer; the next character we are about to read is |buffer[loc]|; and
12778 |limit| is the location of the last character present. We always have
12779 |loc<=limit|. For convenience, |buffer[limit]| has been set to |"%"|, so
12780 that the end of a line is easily sensed.
12782 The |name| variable is a string number that designates the name of
12783 the current file, if we are reading an ordinary text file. Special codes
12784 |is_term..max_spec_src| indicate other sources of input text.
12786 @d is_term 0 /* |name| value when reading from the terminal for normal input */
12787 @d is_read 1 /* |name| value when executing a \&{readstring} or \&{readfrom} */
12788 @d is_scantok 2 /* |name| value when reading text generated by \&{scantokens} */
12789 @d max_spec_src is_scantok
12791 @ Additional information about the current line is available via the
12792 |index| variable, which counts how many lines of characters are present
12793 in the buffer below the current level. We have |index=0| when reading
12794 from the terminal and prompting the user for each line; then if the user types,
12795 e.g., `\.{input figs}', we will have |index=1| while reading
12796 the file \.{figs.mp}. However, it does not follow that |index| is the
12797 same as the input stack pointer, since many of the levels on the input
12798 stack may come from token lists and some |index| values may correspond
12799 to \.{MPX} files that are not currently on the stack.
12801 The global variable |in_open| is equal to the highest |index| value counting
12802 \.{MPX} files but excluding token-list input levels. Thus, the number of
12803 partially read lines in the buffer is |in_open+1| and we have |in_open>=index|
12804 when we are not reading a token list.
12806 If we are not currently reading from the terminal,
12807 we are reading from the file variable |input_file[index]|. We use
12808 the notation |terminal_input| as a convenient abbreviation for |name=is_term|,
12809 and |cur_file| as an abbreviation for |input_file[index]|.
12811 When \MP\ is not reading from the terminal, the global variable |line| contains
12812 the line number in the current file, for use in error messages. More precisely,
12813 |line| is a macro for |line_stack[index]| and the |line_stack| array gives
12814 the line number for each file in the |input_file| array.
12816 When an \.{MPX} file is opened the file name is stored in the |mpx_name|
12817 array so that the name doesn't get lost when the file is temporarily removed
12818 from the input stack.
12819 Thus when |input_file[k]| is an \.{MPX} file, its name is |mpx_name[k]|
12820 and it contains translated \TeX\ pictures for |input_file[k-1]|.
12821 Since this is not an \.{MPX} file, we have
12822 $$ \hbox{|mpx_name[k-1]<=absent|}. $$
12823 This |name| field is set to |finished| when |input_file[k]| is completely
12826 If more information about the input state is needed, it can be
12827 included in small arrays like those shown here. For example,
12828 the current page or segment number in the input file might be put
12829 into a variable |page|, that is really a macro for the current entry
12830 in `\ignorespaces|page_stack:array[0..max_in_open] of integer|\unskip'
12831 by analogy with |line_stack|.
12832 @^system dependencies@>
12834 @d terminal_input (name==is_term) /* are we reading from the terminal? */
12835 @d cur_file mp->input_file[index] /* the current |void *| variable */
12836 @d line mp->line_stack[index] /* current line number in the current source file */
12837 @d in_name mp->iname_stack[index] /* a string used to construct \.{MPX} file names */
12838 @d in_area mp->iarea_stack[index] /* another string for naming \.{MPX} files */
12839 @d absent 1 /* |name_field| value for unused |mpx_in_stack| entries */
12840 @d mpx_reading (mp->mpx_name[index]>absent)
12841 /* when reading a file, is it an \.{MPX} file? */
12843 /* |name_field| value when the corresponding \.{MPX} file is finished */
12846 integer in_open; /* the number of lines in the buffer, less one */
12847 unsigned int open_parens; /* the number of open text files */
12848 void * *input_file ;
12849 integer *line_stack ; /* the line number for each file */
12850 char * *iname_stack; /* used for naming \.{MPX} files */
12851 char * *iarea_stack; /* used for naming \.{MPX} files */
12852 halfword*mpx_name ;
12854 @ @<Allocate or ...@>=
12855 mp->input_file = xmalloc((mp->max_in_open+1),sizeof(void *));
12856 mp->line_stack = xmalloc((mp->max_in_open+1),sizeof(integer));
12857 mp->iname_stack = xmalloc((mp->max_in_open+1),sizeof(char *));
12858 mp->iarea_stack = xmalloc((mp->max_in_open+1),sizeof(char *));
12859 mp->mpx_name = xmalloc((mp->max_in_open+1),sizeof(halfword));
12862 for (k=0;k<=mp->max_in_open;k++) {
12863 mp->iname_stack[k] =NULL;
12864 mp->iarea_stack[k] =NULL;
12868 @ @<Dealloc variables@>=
12871 for (l=0;l<=mp->max_in_open;l++) {
12872 xfree(mp->iname_stack[l]);
12873 xfree(mp->iarea_stack[l]);
12876 xfree(mp->input_file);
12877 xfree(mp->line_stack);
12878 xfree(mp->iname_stack);
12879 xfree(mp->iarea_stack);
12880 xfree(mp->mpx_name);
12883 @ However, all this discussion about input state really applies only to the
12884 case that we are inputting from a file. There is another important case,
12885 namely when we are currently getting input from a token list. In this case
12886 |index>max_in_open|, and the conventions about the other state variables
12889 \yskip\hang|loc| is a pointer to the current node in the token list, i.e.,
12890 the node that will be read next. If |loc=null|, the token list has been
12893 \yskip\hang|start| points to the first node of the token list; this node
12894 may or may not contain a reference count, depending on the type of token
12897 \yskip\hang|token_type|, which takes the place of |index| in the
12898 discussion above, is a code number that explains what kind of token list
12901 \yskip\hang|name| points to the |eqtb| address of the control sequence
12902 being expanded, if the current token list is a macro not defined by
12903 \&{vardef}. Macros defined by \&{vardef} have |name=null|; their name
12904 can be deduced by looking at their first two parameters.
12906 \yskip\hang|param_start|, which takes the place of |limit|, tells where
12907 the parameters of the current macro or loop text begin in the |param_stack|.
12909 \yskip\noindent The |token_type| can take several values, depending on
12910 where the current token list came from:
12913 \indent|forever_text|, if the token list being scanned is the body of
12914 a \&{forever} loop;
12916 \indent|loop_text|, if the token list being scanned is the body of
12917 a \&{for} or \&{forsuffixes} loop;
12919 \indent|parameter|, if a \&{text} or \&{suffix} parameter is being scanned;
12921 \indent|backed_up|, if the token list being scanned has been inserted as
12922 `to be read again'.
12924 \indent|inserted|, if the token list being scanned has been inserted as
12925 part of error recovery;
12927 \indent|macro|, if the expansion of a user-defined symbolic token is being
12931 The token list begins with a reference count if and only if |token_type=
12933 @^reference counts@>
12935 @d token_type index /* type of current token list */
12936 @d token_state (index>(int)mp->max_in_open) /* are we scanning a token list? */
12937 @d file_state (index<=(int)mp->max_in_open) /* are we scanning a file line? */
12938 @d param_start limit /* base of macro parameters in |param_stack| */
12939 @d forever_text (mp->max_in_open+1) /* |token_type| code for loop texts */
12940 @d loop_text (mp->max_in_open+2) /* |token_type| code for loop texts */
12941 @d parameter (mp->max_in_open+3) /* |token_type| code for parameter texts */
12942 @d backed_up (mp->max_in_open+4) /* |token_type| code for texts to be reread */
12943 @d inserted (mp->max_in_open+5) /* |token_type| code for inserted texts */
12944 @d macro (mp->max_in_open+6) /* |token_type| code for macro replacement texts */
12946 @ The |param_stack| is an auxiliary array used to hold pointers to the token
12947 lists for parameters at the current level and subsidiary levels of input.
12948 This stack grows at a different rate from the others.
12951 pointer *param_stack; /* token list pointers for parameters */
12952 integer param_ptr; /* first unused entry in |param_stack| */
12953 integer max_param_stack; /* largest value of |param_ptr| */
12955 @ @<Allocate or initialize ...@>=
12956 mp->param_stack = xmalloc((mp->param_size+1),sizeof(pointer));
12958 @ @<Dealloc variables@>=
12959 xfree(mp->param_stack);
12961 @ Notice that the |line| isn't valid when |token_state| is true because it
12962 depends on |index|. If we really need to know the line number for the
12963 topmost file in the index stack we use the following function. If a page
12964 number or other information is needed, this routine should be modified to
12965 compute it as well.
12966 @^system dependencies@>
12968 @<Declare a function called |true_line|@>=
12969 integer mp_true_line (MP mp) {
12970 int k; /* an index into the input stack */
12971 if ( file_state && (name>max_spec_src) ) {
12976 ((mp->input_stack[(k-1)].index_field>mp->max_in_open)||
12977 (mp->input_stack[(k-1)].name_field<=max_spec_src))) {
12980 return (k>0 ? mp->line_stack[(k-1)] : 0 );
12985 @ Thus, the ``current input state'' can be very complicated indeed; there
12986 can be many levels and each level can arise in a variety of ways. The
12987 |show_context| procedure, which is used by \MP's error-reporting routine to
12988 print out the current input state on all levels down to the most recent
12989 line of characters from an input file, illustrates most of these conventions.
12990 The global variable |file_ptr| contains the lowest level that was
12991 displayed by this procedure.
12994 integer file_ptr; /* shallowest level shown by |show_context| */
12996 @ The status at each level is indicated by printing two lines, where the first
12997 line indicates what was read so far and the second line shows what remains
12998 to be read. The context is cropped, if necessary, so that the first line
12999 contains at most |half_error_line| characters, and the second contains
13000 at most |error_line|. Non-current input levels whose |token_type| is
13001 `|backed_up|' are shown only if they have not been fully read.
13003 @c void mp_show_context (MP mp) { /* prints where the scanner is */
13004 int old_setting; /* saved |selector| setting */
13005 @<Local variables for formatting calculations@>
13006 mp->file_ptr=mp->input_ptr; mp->input_stack[mp->file_ptr]=mp->cur_input;
13007 /* store current state */
13009 mp->cur_input=mp->input_stack[mp->file_ptr]; /* enter into the context */
13010 @<Display the current context@>;
13012 if ( (name>max_spec_src) || (mp->file_ptr==0) ) break;
13013 decr(mp->file_ptr);
13015 mp->cur_input=mp->input_stack[mp->input_ptr]; /* restore original state */
13018 @ @<Display the current context@>=
13019 if ( (mp->file_ptr==mp->input_ptr) || file_state ||
13020 (token_type!=backed_up) || (loc!=null) ) {
13021 /* we omit backed-up token lists that have already been read */
13022 mp->tally=0; /* get ready to count characters */
13023 old_setting=mp->selector;
13024 if ( file_state ) {
13025 @<Print location of current line@>;
13026 @<Pseudoprint the line@>;
13028 @<Print type of token list@>;
13029 @<Pseudoprint the token list@>;
13031 mp->selector=old_setting; /* stop pseudoprinting */
13032 @<Print two lines using the tricky pseudoprinted information@>;
13035 @ This routine should be changed, if necessary, to give the best possible
13036 indication of where the current line resides in the input file.
13037 For example, on some systems it is best to print both a page and line number.
13038 @^system dependencies@>
13040 @<Print location of current line@>=
13041 if ( name>max_spec_src ) {
13042 mp_print_nl(mp, "l."); mp_print_int(mp, mp_true_line(mp));
13043 } else if ( terminal_input ) {
13044 if ( mp->file_ptr==0 ) mp_print_nl(mp, "<*>");
13045 else mp_print_nl(mp, "<insert>");
13046 } else if ( name==is_scantok ) {
13047 mp_print_nl(mp, "<scantokens>");
13049 mp_print_nl(mp, "<read>");
13051 mp_print_char(mp, ' ')
13053 @ Can't use case statement here because the |token_type| is not
13054 a constant expression.
13056 @<Print type of token list@>=
13058 if(token_type==forever_text) {
13059 mp_print_nl(mp, "<forever> ");
13060 } else if (token_type==loop_text) {
13061 @<Print the current loop value@>;
13062 } else if (token_type==parameter) {
13063 mp_print_nl(mp, "<argument> ");
13064 } else if (token_type==backed_up) {
13065 if ( loc==null ) mp_print_nl(mp, "<recently read> ");
13066 else mp_print_nl(mp, "<to be read again> ");
13067 } else if (token_type==inserted) {
13068 mp_print_nl(mp, "<inserted text> ");
13069 } else if (token_type==macro) {
13071 if ( name!=null ) mp_print_text(name);
13072 else @<Print the name of a \&{vardef}'d macro@>;
13073 mp_print(mp, "->");
13075 mp_print_nl(mp, "?");/* this should never happen */
13080 @ The parameter that corresponds to a loop text is either a token list
13081 (in the case of \&{forsuffixes}) or a ``capsule'' (in the case of \&{for}).
13082 We'll discuss capsules later; for now, all we need to know is that
13083 the |link| field in a capsule parameter is |void| and that
13084 |print_exp(p,0)| displays the value of capsule~|p| in abbreviated form.
13086 @<Print the current loop value@>=
13087 { mp_print_nl(mp, "<for("); p=mp->param_stack[param_start];
13089 if ( link(p)==mp_void ) mp_print_exp(mp, p,0); /* we're in a \&{for} loop */
13090 else mp_show_token_list(mp, p,null,20,mp->tally);
13092 mp_print(mp, ")> ");
13095 @ The first two parameters of a macro defined by \&{vardef} will be token
13096 lists representing the macro's prefix and ``at point.'' By putting these
13097 together, we get the macro's full name.
13099 @<Print the name of a \&{vardef}'d macro@>=
13100 { p=mp->param_stack[param_start];
13102 mp_show_token_list(mp, mp->param_stack[param_start+1],null,20,mp->tally);
13105 while ( link(q)!=null ) q=link(q);
13106 link(q)=mp->param_stack[param_start+1];
13107 mp_show_token_list(mp, p,null,20,mp->tally);
13112 @ Now it is necessary to explain a little trick. We don't want to store a long
13113 string that corresponds to a token list, because that string might take up
13114 lots of memory; and we are printing during a time when an error message is
13115 being given, so we dare not do anything that might overflow one of \MP's
13116 tables. So `pseudoprinting' is the answer: We enter a mode of printing
13117 that stores characters into a buffer of length |error_line|, where character
13118 $k+1$ is placed into \hbox{|trick_buf[k mod error_line]|} if
13119 |k<trick_count|, otherwise character |k| is dropped. Initially we set
13120 |tally:=0| and |trick_count:=1000000|; then when we reach the
13121 point where transition from line 1 to line 2 should occur, we
13122 set |first_count:=tally| and |trick_count:=@tmax@>(error_line,
13123 tally+1+error_line-half_error_line)|. At the end of the
13124 pseudoprinting, the values of |first_count|, |tally|, and
13125 |trick_count| give us all the information we need to print the two lines,
13126 and all of the necessary text is in |trick_buf|.
13128 Namely, let |l| be the length of the descriptive information that appears
13129 on the first line. The length of the context information gathered for that
13130 line is |k=first_count|, and the length of the context information
13131 gathered for line~2 is $m=\min(|tally|, |trick_count|)-k$. If |l+k<=h|,
13132 where |h=half_error_line|, we print |trick_buf[0..k-1]| after the
13133 descriptive information on line~1, and set |n:=l+k|; here |n| is the
13134 length of line~1. If $l+k>h$, some cropping is necessary, so we set |n:=h|
13135 and print `\.{...}' followed by
13136 $$\hbox{|trick_buf[(l+k-h+3)..k-1]|,}$$
13137 where subscripts of |trick_buf| are circular modulo |error_line|. The
13138 second line consists of |n|~spaces followed by |trick_buf[k..(k+m-1)]|,
13139 unless |n+m>error_line|; in the latter case, further cropping is done.
13140 This is easier to program than to explain.
13142 @<Local variables for formatting...@>=
13143 int i; /* index into |buffer| */
13144 integer l; /* length of descriptive information on line 1 */
13145 integer m; /* context information gathered for line 2 */
13146 int n; /* length of line 1 */
13147 integer p; /* starting or ending place in |trick_buf| */
13148 integer q; /* temporary index */
13150 @ The following code tells the print routines to gather
13151 the desired information.
13153 @d begin_pseudoprint {
13154 l=mp->tally; mp->tally=0; mp->selector=pseudo;
13155 mp->trick_count=1000000;
13157 @d set_trick_count {
13158 mp->first_count=mp->tally;
13159 mp->trick_count=mp->tally+1+mp->error_line-mp->half_error_line;
13160 if ( mp->trick_count<mp->error_line ) mp->trick_count=mp->error_line;
13163 @ And the following code uses the information after it has been gathered.
13165 @<Print two lines using the tricky pseudoprinted information@>=
13166 if ( mp->trick_count==1000000 ) set_trick_count;
13167 /* |set_trick_count| must be performed */
13168 if ( mp->tally<mp->trick_count ) m=mp->tally-mp->first_count;
13169 else m=mp->trick_count-mp->first_count; /* context on line 2 */
13170 if ( l+mp->first_count<=mp->half_error_line ) {
13171 p=0; n=l+mp->first_count;
13173 mp_print(mp, "..."); p=l+mp->first_count-mp->half_error_line+3;
13174 n=mp->half_error_line;
13176 for (q=p;q<=mp->first_count-1;q++) {
13177 mp_print_char(mp, mp->trick_buf[q % mp->error_line]);
13180 for (q=1;q<=n;q++) {
13181 mp_print_char(mp, ' '); /* print |n| spaces to begin line~2 */
13183 if ( m+n<=mp->error_line ) p=mp->first_count+m;
13184 else p=mp->first_count+(mp->error_line-n-3);
13185 for (q=mp->first_count;q<=p-1;q++) {
13186 mp_print_char(mp, mp->trick_buf[q % mp->error_line]);
13188 if ( m+n>mp->error_line ) mp_print(mp, "...")
13190 @ But the trick is distracting us from our current goal, which is to
13191 understand the input state. So let's concentrate on the data structures that
13192 are being pseudoprinted as we finish up the |show_context| procedure.
13194 @<Pseudoprint the line@>=
13197 for (i=start;i<=limit-1;i++) {
13198 if ( i==loc ) set_trick_count;
13199 mp_print_str(mp, mp->buffer[i]);
13203 @ @<Pseudoprint the token list@>=
13205 if ( token_type!=macro ) mp_show_token_list(mp, start,loc,100000,0);
13206 else mp_show_macro(mp, start,loc,100000)
13208 @ Here is the missing piece of |show_token_list| that is activated when the
13209 token beginning line~2 is about to be shown:
13211 @<Do magic computation@>=set_trick_count
13213 @* \[28] Maintaining the input stacks.
13214 The following subroutines change the input status in commonly needed ways.
13216 First comes |push_input|, which stores the current state and creates a
13217 new level (having, initially, the same properties as the old).
13219 @d push_input { /* enter a new input level, save the old */
13220 if ( mp->input_ptr>mp->max_in_stack ) {
13221 mp->max_in_stack=mp->input_ptr;
13222 if ( mp->input_ptr==mp->stack_size ) {
13223 int l = (mp->stack_size+(mp->stack_size>>2));
13224 XREALLOC(mp->input_stack, l, in_state_record);
13225 mp->stack_size = l;
13228 mp->input_stack[mp->input_ptr]=mp->cur_input; /* stack the record */
13229 incr(mp->input_ptr);
13232 @ And of course what goes up must come down.
13234 @d pop_input { /* leave an input level, re-enter the old */
13235 decr(mp->input_ptr); mp->cur_input=mp->input_stack[mp->input_ptr];
13238 @ Here is a procedure that starts a new level of token-list input, given
13239 a token list |p| and its type |t|. If |t=macro|, the calling routine should
13240 set |name|, reset~|loc|, and increase the macro's reference count.
13242 @d back_list(A) mp_begin_token_list(mp, (A),backed_up) /* backs up a simple token list */
13244 @c void mp_begin_token_list (MP mp,pointer p, quarterword t) {
13245 push_input; start=p; token_type=t;
13246 param_start=mp->param_ptr; loc=p;
13249 @ When a token list has been fully scanned, the following computations
13250 should be done as we leave that level of input.
13253 @c void mp_end_token_list (MP mp) { /* leave a token-list input level */
13254 pointer p; /* temporary register */
13255 if ( token_type>=backed_up ) { /* token list to be deleted */
13256 if ( token_type<=inserted ) {
13257 mp_flush_token_list(mp, start); goto DONE;
13259 mp_delete_mac_ref(mp, start); /* update reference count */
13262 while ( mp->param_ptr>param_start ) { /* parameters must be flushed */
13263 decr(mp->param_ptr);
13264 p=mp->param_stack[mp->param_ptr];
13266 if ( link(p)==mp_void ) { /* it's an \&{expr} parameter */
13267 mp_recycle_value(mp, p); mp_free_node(mp, p,value_node_size);
13269 mp_flush_token_list(mp, p); /* it's a \&{suffix} or \&{text} parameter */
13274 pop_input; check_interrupt;
13277 @ The contents of |cur_cmd,cur_mod,cur_sym| are placed into an equivalent
13278 token by the |cur_tok| routine.
13281 @c @<Declare the procedure called |make_exp_copy|@>;
13282 pointer mp_cur_tok (MP mp) {
13283 pointer p; /* a new token node */
13284 small_number save_type; /* |cur_type| to be restored */
13285 integer save_exp; /* |cur_exp| to be restored */
13286 if ( mp->cur_sym==0 ) {
13287 if ( mp->cur_cmd==capsule_token ) {
13288 save_type=mp->cur_type; save_exp=mp->cur_exp;
13289 mp_make_exp_copy(mp, mp->cur_mod); p=mp_stash_cur_exp(mp); link(p)=null;
13290 mp->cur_type=save_type; mp->cur_exp=save_exp;
13292 p=mp_get_node(mp, token_node_size);
13293 value(p)=mp->cur_mod; name_type(p)=mp_token;
13294 if ( mp->cur_cmd==numeric_token ) type(p)=mp_known;
13295 else type(p)=mp_string_type;
13298 fast_get_avail(p); info(p)=mp->cur_sym;
13303 @ Sometimes \MP\ has read too far and wants to ``unscan'' what it has
13304 seen. The |back_input| procedure takes care of this by putting the token
13305 just scanned back into the input stream, ready to be read again.
13306 If |cur_sym<>0|, the values of |cur_cmd| and |cur_mod| are irrelevant.
13309 void mp_back_input (MP mp);
13311 @ @c void mp_back_input (MP mp) {/* undoes one token of input */
13312 pointer p; /* a token list of length one */
13314 while ( token_state &&(loc==null) )
13315 mp_end_token_list(mp); /* conserve stack space */
13319 @ The |back_error| routine is used when we want to restore or replace an
13320 offending token just before issuing an error message. We disable interrupts
13321 during the call of |back_input| so that the help message won't be lost.
13324 void mp_error (MP mp);
13325 void mp_back_error (MP mp);
13327 @ @c void mp_back_error (MP mp) { /* back up one token and call |error| */
13328 mp->OK_to_interrupt=false;
13330 mp->OK_to_interrupt=true; mp_error(mp);
13332 void mp_ins_error (MP mp) { /* back up one inserted token and call |error| */
13333 mp->OK_to_interrupt=false;
13334 mp_back_input(mp); token_type=inserted;
13335 mp->OK_to_interrupt=true; mp_error(mp);
13338 @ The |begin_file_reading| procedure starts a new level of input for lines
13339 of characters to be read from a file, or as an insertion from the
13340 terminal. It does not take care of opening the file, nor does it set |loc|
13341 or |limit| or |line|.
13342 @^system dependencies@>
13344 @c void mp_begin_file_reading (MP mp) {
13345 if ( mp->in_open==mp->max_in_open )
13346 mp_overflow(mp, "text input levels",mp->max_in_open);
13347 @:MetaPost capacity exceeded text input levels}{\quad text input levels@>
13348 if ( mp->first==mp->buf_size )
13349 mp_reallocate_buffer(mp,(mp->buf_size+(mp->buf_size>>2)));
13350 incr(mp->in_open); push_input; index=mp->in_open;
13351 mp->mpx_name[index]=absent;
13353 name=is_term; /* |terminal_input| is now |true| */
13356 @ Conversely, the variables must be downdated when such a level of input
13357 is finished. Any associated \.{MPX} file must also be closed and popped
13358 off the file stack.
13360 @c void mp_end_file_reading (MP mp) {
13361 if ( mp->in_open>index ) {
13362 if ( (mp->mpx_name[mp->in_open]==absent)||(name<=max_spec_src) ) {
13363 mp_confusion(mp, "endinput");
13364 @:this can't happen endinput}{\quad endinput@>
13366 (mp->close_file)(mp,mp->input_file[mp->in_open]); /* close an \.{MPX} file */
13367 delete_str_ref(mp->mpx_name[mp->in_open]);
13372 if ( index!=mp->in_open ) mp_confusion(mp, "endinput");
13373 if ( name>max_spec_src ) {
13374 (mp->close_file)(mp,cur_file);
13375 delete_str_ref(name);
13379 pop_input; decr(mp->in_open);
13382 @ Here is a function that tries to resume input from an \.{MPX} file already
13383 associated with the current input file. It returns |false| if this doesn't
13386 @c boolean mp_begin_mpx_reading (MP mp) {
13387 if ( mp->in_open!=index+1 ) {
13390 if ( mp->mpx_name[mp->in_open]<=absent ) mp_confusion(mp, "mpx");
13391 @:this can't happen mpx}{\quad mpx@>
13392 if ( mp->first==mp->buf_size )
13393 mp_reallocate_buffer(mp,(mp->buf_size+(mp->buf_size>>2)));
13394 push_input; index=mp->in_open;
13396 name=mp->mpx_name[mp->in_open]; add_str_ref(name);
13397 @<Put an empty line in the input buffer@>;
13402 @ This procedure temporarily stops reading an \.{MPX} file.
13404 @c void mp_end_mpx_reading (MP mp) {
13405 if ( mp->in_open!=index ) mp_confusion(mp, "mpx");
13406 @:this can't happen mpx}{\quad mpx@>
13408 @<Complain that we are not at the end of a line in the \.{MPX} file@>;
13414 @ Here we enforce a restriction that simplifies the input stacks considerably.
13415 This should not inconvenience the user because \.{MPX} files are generated
13416 by an auxiliary program called \.{DVItoMP}.
13418 @ @<Complain that we are not at the end of a line in the \.{MPX} file@>=
13420 print_err("`mpxbreak' must be at the end of a line");
13421 help4("This file contains picture expressions for btex...etex")
13422 ("blocks. Such files are normally generated automatically")
13423 ("but this one seems to be messed up. I'm going to ignore")
13424 ("the rest of this line.");
13428 @ In order to keep the stack from overflowing during a long sequence of
13429 inserted `\.{show}' commands, the following routine removes completed
13430 error-inserted lines from memory.
13432 @c void mp_clear_for_error_prompt (MP mp) {
13433 while ( file_state && terminal_input &&
13434 (mp->input_ptr>0)&&(loc==limit) ) mp_end_file_reading(mp);
13435 mp_print_ln(mp); clear_terminal;
13438 @ To get \MP's whole input mechanism going, we perform the following
13441 @<Initialize the input routines@>=
13442 { mp->input_ptr=0; mp->max_in_stack=0;
13443 mp->in_open=0; mp->open_parens=0; mp->max_buf_stack=0;
13444 mp->param_ptr=0; mp->max_param_stack=0;
13446 start=1; index=0; line=0; name=is_term;
13447 mp->mpx_name[0]=absent;
13448 mp->force_eof=false;
13449 if ( ! mp_init_terminal(mp) ) mp_jump_out(mp);
13450 limit=mp->last; mp->first=mp->last+1;
13451 /* |init_terminal| has set |loc| and |last| */
13454 @* \[29] Getting the next token.
13455 The heart of \MP's input mechanism is the |get_next| procedure, which
13456 we shall develop in the next few sections of the program. Perhaps we
13457 shouldn't actually call it the ``heart,'' however; it really acts as \MP's
13458 eyes and mouth, reading the source files and gobbling them up. And it also
13459 helps \MP\ to regurgitate stored token lists that are to be processed again.
13461 The main duty of |get_next| is to input one token and to set |cur_cmd|
13462 and |cur_mod| to that token's command code and modifier. Furthermore, if
13463 the input token is a symbolic token, that token's |hash| address
13464 is stored in |cur_sym|; otherwise |cur_sym| is set to zero.
13466 Underlying this simple description is a certain amount of complexity
13467 because of all the cases that need to be handled.
13468 However, the inner loop of |get_next| is reasonably short and fast.
13470 @ Before getting into |get_next|, we need to consider a mechanism by which
13471 \MP\ helps keep errors from propagating too far. Whenever the program goes
13472 into a mode where it keeps calling |get_next| repeatedly until a certain
13473 condition is met, it sets |scanner_status| to some value other than |normal|.
13474 Then if an input file ends, or if an `\&{outer}' symbol appears,
13475 an appropriate error recovery will be possible.
13477 The global variable |warning_info| helps in this error recovery by providing
13478 additional information. For example, |warning_info| might indicate the
13479 name of a macro whose replacement text is being scanned.
13481 @d normal 0 /* |scanner_status| at ``quiet times'' */
13482 @d skipping 1 /* |scanner_status| when false conditional text is being skipped */
13483 @d flushing 2 /* |scanner_status| when junk after a statement is being ignored */
13484 @d absorbing 3 /* |scanner_status| when a \&{text} parameter is being scanned */
13485 @d var_defining 4 /* |scanner_status| when a \&{vardef} is being scanned */
13486 @d op_defining 5 /* |scanner_status| when a macro \&{def} is being scanned */
13487 @d loop_defining 6 /* |scanner_status| when a \&{for} loop is being scanned */
13488 @d tex_flushing 7 /* |scanner_status| when skipping \TeX\ material */
13491 integer scanner_status; /* are we scanning at high speed? */
13492 integer warning_info; /* if so, what else do we need to know,
13493 in case an error occurs? */
13495 @ @<Initialize the input routines@>=
13496 mp->scanner_status=normal;
13498 @ The following subroutine
13499 is called when an `\&{outer}' symbolic token has been scanned or
13500 when the end of a file has been reached. These two cases are distinguished
13501 by |cur_sym|, which is zero at the end of a file.
13503 @c boolean mp_check_outer_validity (MP mp) {
13504 pointer p; /* points to inserted token list */
13505 if ( mp->scanner_status==normal ) {
13507 } else if ( mp->scanner_status==tex_flushing ) {
13508 @<Check if the file has ended while flushing \TeX\ material and set the
13509 result value for |check_outer_validity|@>;
13511 mp->deletions_allowed=false;
13512 @<Back up an outer symbolic token so that it can be reread@>;
13513 if ( mp->scanner_status>skipping ) {
13514 @<Tell the user what has run away and try to recover@>;
13516 print_err("Incomplete if; all text was ignored after line ");
13517 @.Incomplete if...@>
13518 mp_print_int(mp, mp->warning_info);
13519 help3("A forbidden `outer' token occurred in skipped text.")
13520 ("This kind of error happens when you say `if...' and forget")
13521 ("the matching `fi'. I've inserted a `fi'; this might work.");
13522 if ( mp->cur_sym==0 )
13523 mp->help_line[2]="The file ended while I was skipping conditional text.";
13524 mp->cur_sym=frozen_fi; mp_ins_error(mp);
13526 mp->deletions_allowed=true;
13531 @ @<Check if the file has ended while flushing \TeX\ material and set...@>=
13532 if ( mp->cur_sym!=0 ) {
13535 mp->deletions_allowed=false;
13536 print_err("TeX mode didn't end; all text was ignored after line ");
13537 mp_print_int(mp, mp->warning_info);
13538 help2("The file ended while I was looking for the `etex' to")
13539 ("finish this TeX material. I've inserted `etex' now.");
13540 mp->cur_sym = frozen_etex;
13542 mp->deletions_allowed=true;
13546 @ @<Back up an outer symbolic token so that it can be reread@>=
13547 if ( mp->cur_sym!=0 ) {
13548 p=mp_get_avail(mp); info(p)=mp->cur_sym;
13549 back_list(p); /* prepare to read the symbolic token again */
13552 @ @<Tell the user what has run away...@>=
13554 mp_runaway(mp); /* print the definition-so-far */
13555 if ( mp->cur_sym==0 ) {
13556 print_err("File ended");
13557 @.File ended while scanning...@>
13559 print_err("Forbidden token found");
13560 @.Forbidden token found...@>
13562 mp_print(mp, " while scanning ");
13563 help4("I suspect you have forgotten an `enddef',")
13564 ("causing me to read past where you wanted me to stop.")
13565 ("I'll try to recover; but if the error is serious,")
13566 ("you'd better type `E' or `X' now and fix your file.");
13567 switch (mp->scanner_status) {
13568 @<Complete the error message,
13569 and set |cur_sym| to a token that might help recover from the error@>
13570 } /* there are no other cases */
13574 @ As we consider various kinds of errors, it is also appropriate to
13575 change the first line of the help message just given; |help_line[3]|
13576 points to the string that might be changed.
13578 @<Complete the error message,...@>=
13580 mp_print(mp, "to the end of the statement");
13581 mp->help_line[3]="A previous error seems to have propagated,";
13582 mp->cur_sym=frozen_semicolon;
13585 mp_print(mp, "a text argument");
13586 mp->help_line[3]="It seems that a right delimiter was left out,";
13587 if ( mp->warning_info==0 ) {
13588 mp->cur_sym=frozen_end_group;
13590 mp->cur_sym=frozen_right_delimiter;
13591 equiv(frozen_right_delimiter)=mp->warning_info;
13596 mp_print(mp, "the definition of ");
13597 if ( mp->scanner_status==op_defining )
13598 mp_print_text(mp->warning_info);
13600 mp_print_variable_name(mp, mp->warning_info);
13601 mp->cur_sym=frozen_end_def;
13603 case loop_defining:
13604 mp_print(mp, "the text of a ");
13605 mp_print_text(mp->warning_info);
13606 mp_print(mp, " loop");
13607 mp->help_line[3]="I suspect you have forgotten an `endfor',";
13608 mp->cur_sym=frozen_end_for;
13611 @ The |runaway| procedure displays the first part of the text that occurred
13612 when \MP\ began its special |scanner_status|, if that text has been saved.
13614 @<Declare the procedure called |runaway|@>=
13615 void mp_runaway (MP mp) {
13616 if ( mp->scanner_status>flushing ) {
13617 mp_print_nl(mp, "Runaway ");
13618 switch (mp->scanner_status) {
13619 case absorbing: mp_print(mp, "text?"); break;
13621 case op_defining: mp_print(mp,"definition?"); break;
13622 case loop_defining: mp_print(mp, "loop?"); break;
13623 } /* there are no other cases */
13625 mp_show_token_list(mp, link(hold_head),null,mp->error_line-10,0);
13629 @ We need to mention a procedure that may be called by |get_next|.
13632 void mp_firm_up_the_line (MP mp);
13634 @ And now we're ready to take the plunge into |get_next| itself.
13635 Note that the behavior depends on the |scanner_status| because percent signs
13636 and double quotes need to be passed over when skipping TeX material.
13639 void mp_get_next (MP mp) {
13640 /* sets |cur_cmd|, |cur_mod|, |cur_sym| to next token */
13642 /*restart*/ /* go here to get the next input token */
13643 /*exit*/ /* go here when the next input token has been got */
13644 /*|common_ending|*/ /* go here to finish getting a symbolic token */
13645 /*found*/ /* go here when the end of a symbolic token has been found */
13646 /*switch*/ /* go here to branch on the class of an input character */
13647 /*|start_numeric_token|,|start_decimal_token|,|fin_numeric_token|,|done|*/
13648 /* go here at crucial stages when scanning a number */
13649 int k; /* an index into |buffer| */
13650 ASCII_code c; /* the current character in the buffer */
13651 ASCII_code class; /* its class number */
13652 integer n,f; /* registers for decimal-to-binary conversion */
13655 if ( file_state ) {
13656 @<Input from external file; |goto restart| if no input found,
13657 or |return| if a non-symbolic token is found@>;
13659 @<Input from token list; |goto restart| if end of list or
13660 if a parameter needs to be expanded,
13661 or |return| if a non-symbolic token is found@>;
13664 @<Finish getting the symbolic token in |cur_sym|;
13665 |goto restart| if it is illegal@>;
13668 @ When a symbolic token is declared to be `\&{outer}', its command code
13669 is increased by |outer_tag|.
13672 @<Finish getting the symbolic token in |cur_sym|...@>=
13673 mp->cur_cmd=eq_type(mp->cur_sym); mp->cur_mod=equiv(mp->cur_sym);
13674 if ( mp->cur_cmd>=outer_tag ) {
13675 if ( mp_check_outer_validity(mp) )
13676 mp->cur_cmd=mp->cur_cmd-outer_tag;
13681 @ A percent sign appears in |buffer[limit]|; this makes it unnecessary
13682 to have a special test for end-of-line.
13685 @<Input from external file;...@>=
13688 c=mp->buffer[loc]; incr(loc); class=mp->char_class[c];
13690 case digit_class: goto START_NUMERIC_TOKEN; break;
13692 class=mp->char_class[mp->buffer[loc]];
13693 if ( class>period_class ) {
13695 } else if ( class<period_class ) { /* |class=digit_class| */
13696 n=0; goto START_DECIMAL_TOKEN;
13700 case space_class: goto SWITCH; break;
13701 case percent_class:
13702 if ( mp->scanner_status==tex_flushing ) {
13703 if ( loc<limit ) goto SWITCH;
13705 @<Move to next line of file, or |goto restart| if there is no next line@>;
13710 if ( mp->scanner_status==tex_flushing ) goto SWITCH;
13711 else @<Get a string token and |return|@>;
13713 case isolated_classes:
13714 k=loc-1; goto FOUND; break;
13715 case invalid_class:
13716 if ( mp->scanner_status==tex_flushing ) goto SWITCH;
13717 else @<Decry the invalid character and |goto restart|@>;
13719 default: break; /* letters, etc. */
13722 while ( mp->char_class[mp->buffer[loc]]==class ) incr(loc);
13724 START_NUMERIC_TOKEN:
13725 @<Get the integer part |n| of a numeric token;
13726 set |f:=0| and |goto fin_numeric_token| if there is no decimal point@>;
13727 START_DECIMAL_TOKEN:
13728 @<Get the fraction part |f| of a numeric token@>;
13730 @<Pack the numeric and fraction parts of a numeric token
13733 mp->cur_sym=mp_id_lookup(mp, k,loc-k);
13736 @ We go to |restart| instead of to |SWITCH|, because |state| might equal
13737 |token_list| after the error has been dealt with
13738 (cf.\ |clear_for_error_prompt|).
13740 @<Decry the invalid...@>=
13742 print_err("Text line contains an invalid character");
13743 @.Text line contains...@>
13744 help2("A funny symbol that I can\'t read has just been input.")
13745 ("Continue, and I'll forget that it ever happened.");
13746 mp->deletions_allowed=false; mp_error(mp); mp->deletions_allowed=true;
13750 @ @<Get a string token and |return|@>=
13752 if ( mp->buffer[loc]=='"' ) {
13753 mp->cur_mod=rts("");
13755 k=loc; mp->buffer[limit+1]='"';
13758 } while (mp->buffer[loc]!='"');
13760 @<Decry the missing string delimiter and |goto restart|@>;
13763 mp->cur_mod=mp->buffer[k];
13767 append_char(mp->buffer[k]); incr(k);
13769 mp->cur_mod=mp_make_string(mp);
13772 incr(loc); mp->cur_cmd=string_token;
13776 @ We go to |restart| after this error message, not to |SWITCH|,
13777 because the |clear_for_error_prompt| routine might have reinstated
13778 |token_state| after |error| has finished.
13780 @<Decry the missing string delimiter and |goto restart|@>=
13782 loc=limit; /* the next character to be read on this line will be |"%"| */
13783 print_err("Incomplete string token has been flushed");
13784 @.Incomplete string token...@>
13785 help3("Strings should finish on the same line as they began.")
13786 ("I've deleted the partial string; you might want to")
13787 ("insert another by typing, e.g., `I\"new string\"'.");
13788 mp->deletions_allowed=false; mp_error(mp);
13789 mp->deletions_allowed=true;
13793 @ @<Get the integer part |n| of a numeric token...@>=
13795 while ( mp->char_class[mp->buffer[loc]]==digit_class ) {
13796 if ( n<32768 ) n=10*n+mp->buffer[loc]-'0';
13799 if ( mp->buffer[loc]=='.' )
13800 if ( mp->char_class[mp->buffer[loc+1]]==digit_class )
13803 goto FIN_NUMERIC_TOKEN;
13806 @ @<Get the fraction part |f| of a numeric token@>=
13809 if ( k<17 ) { /* digits for |k>=17| cannot affect the result */
13810 mp->dig[k]=mp->buffer[loc]-'0'; incr(k);
13813 } while (mp->char_class[mp->buffer[loc]]==digit_class);
13814 f=mp_round_decimals(mp, k);
13819 @ @<Pack the numeric and fraction parts of a numeric token and |return|@>=
13821 @<Set |cur_mod:=n*unity+f| and check if it is uncomfortably large@>;
13822 } else if ( mp->scanner_status!=tex_flushing ) {
13823 print_err("Enormous number has been reduced");
13824 @.Enormous number...@>
13825 help2("I can\'t handle numbers bigger than 32767.99998;")
13826 ("so I've changed your constant to that maximum amount.");
13827 mp->deletions_allowed=false; mp_error(mp); mp->deletions_allowed=true;
13828 mp->cur_mod=el_gordo;
13830 mp->cur_cmd=numeric_token; return
13832 @ @<Set |cur_mod:=n*unity+f| and check if it is uncomfortably large@>=
13834 mp->cur_mod=n*unity+f;
13835 if ( mp->cur_mod>=fraction_one ) {
13836 if ( (mp->internal[mp_warning_check]>0) &&
13837 (mp->scanner_status!=tex_flushing) ) {
13838 print_err("Number is too large (");
13839 mp_print_scaled(mp, mp->cur_mod);
13840 mp_print_char(mp, ')');
13841 help3("It is at least 4096. Continue and I'll try to cope")
13842 ("with that big value; but it might be dangerous.")
13843 ("(Set warningcheck:=0 to suppress this message.)");
13849 @ Let's consider now what happens when |get_next| is looking at a token list.
13852 @<Input from token list;...@>=
13853 if ( loc>=mp->hi_mem_min ) { /* one-word token */
13854 mp->cur_sym=info(loc); loc=link(loc); /* move to next */
13855 if ( mp->cur_sym>=expr_base ) {
13856 if ( mp->cur_sym>=suffix_base ) {
13857 @<Insert a suffix or text parameter and |goto restart|@>;
13859 mp->cur_cmd=capsule_token;
13860 mp->cur_mod=mp->param_stack[param_start+mp->cur_sym-(expr_base)];
13861 mp->cur_sym=0; return;
13864 } else if ( loc>null ) {
13865 @<Get a stored numeric or string or capsule token and |return|@>
13866 } else { /* we are done with this token list */
13867 mp_end_token_list(mp); goto RESTART; /* resume previous level */
13870 @ @<Insert a suffix or text parameter...@>=
13872 if ( mp->cur_sym>=text_base ) mp->cur_sym=mp->cur_sym-mp->param_size;
13873 /* |param_size=text_base-suffix_base| */
13874 mp_begin_token_list(mp,
13875 mp->param_stack[param_start+mp->cur_sym-(suffix_base)],
13880 @ @<Get a stored numeric or string or capsule token...@>=
13882 if ( name_type(loc)==mp_token ) {
13883 mp->cur_mod=value(loc);
13884 if ( type(loc)==mp_known ) {
13885 mp->cur_cmd=numeric_token;
13887 mp->cur_cmd=string_token; add_str_ref(mp->cur_mod);
13890 mp->cur_mod=loc; mp->cur_cmd=capsule_token;
13892 loc=link(loc); return;
13895 @ All of the easy branches of |get_next| have now been taken care of.
13896 There is one more branch.
13898 @<Move to next line of file, or |goto restart|...@>=
13899 if ( name>max_spec_src ) {
13900 @<Read next line of file into |buffer|, or
13901 |goto restart| if the file has ended@>;
13903 if ( mp->input_ptr>0 ) {
13904 /* text was inserted during error recovery or by \&{scantokens} */
13905 mp_end_file_reading(mp); goto RESTART; /* resume previous level */
13907 if ( mp->selector<log_only || mp->selector>=write_file) mp_open_log_file(mp);
13908 if ( mp->interaction>mp_nonstop_mode ) {
13909 if ( limit==start ) /* previous line was empty */
13910 mp_print_nl(mp, "(Please type a command or say `end')");
13912 mp_print_ln(mp); mp->first=start;
13913 prompt_input("*"); /* input on-line into |buffer| */
13915 limit=mp->last; mp->buffer[limit]='%';
13916 mp->first=limit+1; loc=start;
13918 mp_fatal_error(mp, "*** (job aborted, no legal end found)");
13920 /* nonstop mode, which is intended for overnight batch processing,
13921 never waits for on-line input */
13925 @ The global variable |force_eof| is normally |false|; it is set |true|
13926 by an \&{endinput} command.
13929 boolean force_eof; /* should the next \&{input} be aborted early? */
13931 @ We must decrement |loc| in order to leave the buffer in a valid state
13932 when an error condition causes us to |goto restart| without calling
13933 |end_file_reading|.
13935 @<Read next line of file into |buffer|, or
13936 |goto restart| if the file has ended@>=
13938 incr(line); mp->first=start;
13939 if ( ! mp->force_eof ) {
13940 if ( mp_input_ln(mp, cur_file ) ) /* not end of file */
13941 mp_firm_up_the_line(mp); /* this sets |limit| */
13943 mp->force_eof=true;
13945 if ( mp->force_eof ) {
13946 mp->force_eof=false;
13948 if ( mpx_reading ) {
13949 @<Complain that the \.{MPX} file ended unexpectly; then set
13950 |cur_sym:=frozen_mpx_break| and |goto comon_ending|@>;
13952 mp_print_char(mp, ')'); decr(mp->open_parens);
13953 update_terminal; /* show user that file has been read */
13954 mp_end_file_reading(mp); /* resume previous level */
13955 if ( mp_check_outer_validity(mp) ) goto RESTART;
13959 mp->buffer[limit]='%'; mp->first=limit+1; loc=start; /* ready to read */
13962 @ We should never actually come to the end of an \.{MPX} file because such
13963 files should have an \&{mpxbreak} after the translation of the last
13964 \&{btex}$\,\ldots\,$\&{etex} block.
13966 @<Complain that the \.{MPX} file ended unexpectly; then set...@>=
13968 mp->mpx_name[index]=finished;
13969 print_err("mpx file ended unexpectedly");
13970 help4("The file had too few picture expressions for btex...etex")
13971 ("blocks. Such files are normally generated automatically")
13972 ("but this one got messed up. You might want to insert a")
13973 ("picture expression now.");
13974 mp->deletions_allowed=false; mp_error(mp); mp->deletions_allowed=true;
13975 mp->cur_sym=frozen_mpx_break; goto COMMON_ENDING;
13978 @ Sometimes we want to make it look as though we have just read a blank line
13979 without really doing so.
13981 @<Put an empty line in the input buffer@>=
13982 mp->last=mp->first; limit=mp->last; /* simulate |input_ln| and |firm_up_the_line| */
13983 mp->buffer[limit]='%'; mp->first=limit+1; loc=start
13985 @ If the user has set the |mp_pausing| parameter to some positive value,
13986 and if nonstop mode has not been selected, each line of input is displayed
13987 on the terminal and the transcript file, followed by `\.{=>}'.
13988 \MP\ waits for a response. If the response is null (i.e., if nothing is
13989 typed except perhaps a few blank spaces), the original
13990 line is accepted as it stands; otherwise the line typed is
13991 used instead of the line in the file.
13993 @c void mp_firm_up_the_line (MP mp) {
13994 size_t k; /* an index into |buffer| */
13996 if ( mp->internal[mp_pausing]>0) if ( mp->interaction>mp_nonstop_mode ) {
13997 wake_up_terminal; mp_print_ln(mp);
13998 if ( start<limit ) {
13999 for (k=(size_t)start;k<=(size_t)(limit-1);k++) {
14000 mp_print_str(mp, mp->buffer[k]);
14003 mp->first=limit; prompt_input("=>"); /* wait for user response */
14005 if ( mp->last>mp->first ) {
14006 for (k=mp->first;k<=mp->last-1;k++) { /* move line down in buffer */
14007 mp->buffer[k+start-mp->first]=mp->buffer[k];
14009 limit=start+mp->last-mp->first;
14014 @* \[30] Dealing with \TeX\ material.
14015 The \&{btex}$\,\ldots\,$\&{etex} and \&{verbatimtex}$\,\ldots\,$\&{etex}
14016 features need to be implemented at a low level in the scanning process
14017 so that \MP\ can stay in synch with the a preprocessor that treats
14018 blocks of \TeX\ material as they occur in the input file without trying
14019 to expand \MP\ macros. Thus we need a special version of |get_next|
14020 that does not expand macros and such but does handle \&{btex},
14021 \&{verbatimtex}, etc.
14023 The special version of |get_next| is called |get_t_next|. It works by flushing
14024 \&{btex}$\,\ldots\,$\&{etex} and \&{verbatimtex}\allowbreak
14025 $\,\ldots\,$\&{etex} blocks, switching to the \.{MPX} file when it sees
14026 \&{btex}, and switching back when it sees \&{mpxbreak}.
14032 mp_primitive(mp, "btex",start_tex,btex_code);
14033 @:btex_}{\&{btex} primitive@>
14034 mp_primitive(mp, "verbatimtex",start_tex,verbatim_code);
14035 @:verbatimtex_}{\&{verbatimtex} primitive@>
14036 mp_primitive(mp, "etex",etex_marker,0); mp->eqtb[frozen_etex]=mp->eqtb[mp->cur_sym];
14037 @:etex_}{\&{etex} primitive@>
14038 mp_primitive(mp, "mpxbreak",mpx_break,0); mp->eqtb[frozen_mpx_break]=mp->eqtb[mp->cur_sym];
14039 @:mpx_break_}{\&{mpxbreak} primitive@>
14041 @ @<Cases of |print_cmd...@>=
14042 case start_tex: if ( m==btex_code ) mp_print(mp, "btex");
14043 else mp_print(mp, "verbatimtex"); break;
14044 case etex_marker: mp_print(mp, "etex"); break;
14045 case mpx_break: mp_print(mp, "mpxbreak"); break;
14047 @ Actually, |get_t_next| is a macro that avoids procedure overhead except
14048 in the unusual case where \&{btex}, \&{verbatimtex}, \&{etex}, or \&{mpxbreak}
14051 @d get_t_next {mp_get_next(mp); if ( mp->cur_cmd<=max_pre_command ) mp_t_next(mp); }
14054 void mp_start_mpx_input (MP mp);
14057 void mp_t_next (MP mp) {
14058 int old_status; /* saves the |scanner_status| */
14059 integer old_info; /* saves the |warning_info| */
14060 while ( mp->cur_cmd<=max_pre_command ) {
14061 if ( mp->cur_cmd==mpx_break ) {
14062 if ( ! file_state || (mp->mpx_name[index]==absent) ) {
14063 @<Complain about a misplaced \&{mpxbreak}@>;
14065 mp_end_mpx_reading(mp);
14068 } else if ( mp->cur_cmd==start_tex ) {
14069 if ( token_state || (name<=max_spec_src) ) {
14070 @<Complain that we are not reading a file@>;
14071 } else if ( mpx_reading ) {
14072 @<Complain that \.{MPX} files cannot contain \TeX\ material@>;
14073 } else if ( (mp->cur_mod!=verbatim_code)&&
14074 (mp->mpx_name[index]!=finished) ) {
14075 if ( ! mp_begin_mpx_reading(mp) ) mp_start_mpx_input(mp);
14080 @<Complain about a misplaced \&{etex}@>;
14082 goto COMMON_ENDING;
14084 @<Flush the \TeX\ material@>;
14090 @ We could be in the middle of an operation such as skipping false conditional
14091 text when \TeX\ material is encountered, so we must be careful to save the
14094 @<Flush the \TeX\ material@>=
14095 old_status=mp->scanner_status;
14096 old_info=mp->warning_info;
14097 mp->scanner_status=tex_flushing;
14098 mp->warning_info=line;
14099 do { mp_get_next(mp); } while (mp->cur_cmd!=etex_marker);
14100 mp->scanner_status=old_status;
14101 mp->warning_info=old_info
14103 @ @<Complain that \.{MPX} files cannot contain \TeX\ material@>=
14104 { print_err("An mpx file cannot contain btex or verbatimtex blocks");
14105 help4("This file contains picture expressions for btex...etex")
14106 ("blocks. Such files are normally generated automatically")
14107 ("but this one seems to be messed up. I'll just keep going")
14108 ("and hope for the best.");
14112 @ @<Complain that we are not reading a file@>=
14113 { print_err("You can only use `btex' or `verbatimtex' in a file");
14114 help3("I'll have to ignore this preprocessor command because it")
14115 ("only works when there is a file to preprocess. You might")
14116 ("want to delete everything up to the next `etex`.");
14120 @ @<Complain about a misplaced \&{mpxbreak}@>=
14121 { print_err("Misplaced mpxbreak");
14122 help2("I'll ignore this preprocessor command because it")
14123 ("doesn't belong here");
14127 @ @<Complain about a misplaced \&{etex}@>=
14128 { print_err("Extra etex will be ignored");
14129 help1("There is no btex or verbatimtex for this to match");
14133 @* \[31] Scanning macro definitions.
14134 \MP\ has a variety of ways to tuck tokens away into token lists for later
14135 use: Macros can be defined with \&{def}, \&{vardef}, \&{primarydef}, etc.;
14136 repeatable code can be defined with \&{for}, \&{forever}, \&{forsuffixes}.
14137 All such operations are handled by the routines in this part of the program.
14139 The modifier part of each command code is zero for the ``ending delimiters''
14140 like \&{enddef} and \&{endfor}.
14142 @d start_def 1 /* command modifier for \&{def} */
14143 @d var_def 2 /* command modifier for \&{vardef} */
14144 @d end_def 0 /* command modifier for \&{enddef} */
14145 @d start_forever 1 /* command modifier for \&{forever} */
14146 @d end_for 0 /* command modifier for \&{endfor} */
14149 mp_primitive(mp, "def",macro_def,start_def);
14150 @:def_}{\&{def} primitive@>
14151 mp_primitive(mp, "vardef",macro_def,var_def);
14152 @:var_def_}{\&{vardef} primitive@>
14153 mp_primitive(mp, "primarydef",macro_def,secondary_primary_macro);
14154 @:primary_def_}{\&{primarydef} primitive@>
14155 mp_primitive(mp, "secondarydef",macro_def,tertiary_secondary_macro);
14156 @:secondary_def_}{\&{secondarydef} primitive@>
14157 mp_primitive(mp, "tertiarydef",macro_def,expression_tertiary_macro);
14158 @:tertiary_def_}{\&{tertiarydef} primitive@>
14159 mp_primitive(mp, "enddef",macro_def,end_def); mp->eqtb[frozen_end_def]=mp->eqtb[mp->cur_sym];
14160 @:end_def_}{\&{enddef} primitive@>
14162 mp_primitive(mp, "for",iteration,expr_base);
14163 @:for_}{\&{for} primitive@>
14164 mp_primitive(mp, "forsuffixes",iteration,suffix_base);
14165 @:for_suffixes_}{\&{forsuffixes} primitive@>
14166 mp_primitive(mp, "forever",iteration,start_forever);
14167 @:forever_}{\&{forever} primitive@>
14168 mp_primitive(mp, "endfor",iteration,end_for); mp->eqtb[frozen_end_for]=mp->eqtb[mp->cur_sym];
14169 @:end_for_}{\&{endfor} primitive@>
14171 @ @<Cases of |print_cmd...@>=
14173 if ( m<=var_def ) {
14174 if ( m==start_def ) mp_print(mp, "def");
14175 else if ( m<start_def ) mp_print(mp, "enddef");
14176 else mp_print(mp, "vardef");
14177 } else if ( m==secondary_primary_macro ) {
14178 mp_print(mp, "primarydef");
14179 } else if ( m==tertiary_secondary_macro ) {
14180 mp_print(mp, "secondarydef");
14182 mp_print(mp, "tertiarydef");
14186 if ( m<=start_forever ) {
14187 if ( m==start_forever ) mp_print(mp, "forever");
14188 else mp_print(mp, "endfor");
14189 } else if ( m==expr_base ) {
14190 mp_print(mp, "for");
14192 mp_print(mp, "forsuffixes");
14196 @ Different macro-absorbing operations have different syntaxes, but they
14197 also have a lot in common. There is a list of special symbols that are to
14198 be replaced by parameter tokens; there is a special command code that
14199 ends the definition; the quotation conventions are identical. Therefore
14200 it makes sense to have most of the work done by a single subroutine. That
14201 subroutine is called |scan_toks|.
14203 The first parameter to |scan_toks| is the command code that will
14204 terminate scanning (either |macro_def|, |loop_repeat|, or |iteration|).
14206 The second parameter, |subst_list|, points to a (possibly empty) list
14207 of two-word nodes whose |info| and |value| fields specify symbol tokens
14208 before and after replacement. The list will be returned to free storage
14211 The third parameter is simply appended to the token list that is built.
14212 And the final parameter tells how many of the special operations
14213 \.{\#\AT!}, \.{\AT!}, and \.{\AT!\#} are to be replaced by suffix parameters.
14214 When such parameters are present, they are called \.{(SUFFIX0)},
14215 \.{(SUFFIX1)}, and \.{(SUFFIX2)}.
14217 @c pointer mp_scan_toks (MP mp,command_code terminator, pointer
14218 subst_list, pointer tail_end, small_number suffix_count) {
14219 pointer p; /* tail of the token list being built */
14220 pointer q; /* temporary for link management */
14221 integer balance; /* left delimiters minus right delimiters */
14222 p=hold_head; balance=1; link(hold_head)=null;
14225 if ( mp->cur_sym>0 ) {
14226 @<Substitute for |cur_sym|, if it's on the |subst_list|@>;
14227 if ( mp->cur_cmd==terminator ) {
14228 @<Adjust the balance; |break| if it's zero@>;
14229 } else if ( mp->cur_cmd==macro_special ) {
14230 @<Handle quoted symbols, \.{\#\AT!}, \.{\AT!}, or \.{\AT!\#}@>;
14233 link(p)=mp_cur_tok(mp); p=link(p);
14235 link(p)=tail_end; mp_flush_node_list(mp, subst_list);
14236 return link(hold_head);
14239 @ @<Substitute for |cur_sym|...@>=
14242 while ( q!=null ) {
14243 if ( info(q)==mp->cur_sym ) {
14244 mp->cur_sym=value(q); mp->cur_cmd=relax; break;
14250 @ @<Adjust the balance; |break| if it's zero@>=
14251 if ( mp->cur_mod>0 ) {
14259 @ Four commands are intended to be used only within macro texts: \&{quote},
14260 \.{\#\AT!}, \.{\AT!}, and \.{\AT!\#}. They are variants of a single command
14261 code called |macro_special|.
14263 @d quote 0 /* |macro_special| modifier for \&{quote} */
14264 @d macro_prefix 1 /* |macro_special| modifier for \.{\#\AT!} */
14265 @d macro_at 2 /* |macro_special| modifier for \.{\AT!} */
14266 @d macro_suffix 3 /* |macro_special| modifier for \.{\AT!\#} */
14269 mp_primitive(mp, "quote",macro_special,quote);
14270 @:quote_}{\&{quote} primitive@>
14271 mp_primitive(mp, "#@@",macro_special,macro_prefix);
14272 @:]]]\#\AT!_}{\.{\#\AT!} primitive@>
14273 mp_primitive(mp, "@@",macro_special,macro_at);
14274 @:]]]\AT!_}{\.{\AT!} primitive@>
14275 mp_primitive(mp, "@@#",macro_special,macro_suffix);
14276 @:]]]\AT!\#_}{\.{\AT!\#} primitive@>
14278 @ @<Cases of |print_cmd...@>=
14279 case macro_special:
14281 case macro_prefix: mp_print(mp, "#@@"); break;
14282 case macro_at: mp_print_char(mp, '@@'); break;
14283 case macro_suffix: mp_print(mp, "@@#"); break;
14284 default: mp_print(mp, "quote"); break;
14288 @ @<Handle quoted...@>=
14290 if ( mp->cur_mod==quote ) { get_t_next; }
14291 else if ( mp->cur_mod<=suffix_count )
14292 mp->cur_sym=suffix_base-1+mp->cur_mod;
14295 @ Here is a routine that's used whenever a token will be redefined. If
14296 the user's token is unredefinable, the `|frozen_inaccessible|' token is
14297 substituted; the latter is redefinable but essentially impossible to use,
14298 hence \MP's tables won't get fouled up.
14300 @c void mp_get_symbol (MP mp) { /* sets |cur_sym| to a safe symbol */
14303 if ( (mp->cur_sym==0)||(mp->cur_sym>frozen_inaccessible) ) {
14304 print_err("Missing symbolic token inserted");
14305 @.Missing symbolic token...@>
14306 help3("Sorry: You can\'t redefine a number, string, or expr.")
14307 ("I've inserted an inaccessible symbol so that your")
14308 ("definition will be completed without mixing me up too badly.");
14309 if ( mp->cur_sym>0 )
14310 mp->help_line[2]="Sorry: You can\'t redefine my error-recovery tokens.";
14311 else if ( mp->cur_cmd==string_token )
14312 delete_str_ref(mp->cur_mod);
14313 mp->cur_sym=frozen_inaccessible; mp_ins_error(mp); goto RESTART;
14317 @ Before we actually redefine a symbolic token, we need to clear away its
14318 former value, if it was a variable. The following stronger version of
14319 |get_symbol| does that.
14321 @c void mp_get_clear_symbol (MP mp) {
14322 mp_get_symbol(mp); mp_clear_symbol(mp, mp->cur_sym,false);
14325 @ Here's another little subroutine; it checks that an equals sign
14326 or assignment sign comes along at the proper place in a macro definition.
14328 @c void mp_check_equals (MP mp) {
14329 if ( mp->cur_cmd!=equals ) if ( mp->cur_cmd!=assignment ) {
14330 mp_missing_err(mp, "=");
14332 help5("The next thing in this `def' should have been `=',")
14333 ("because I've already looked at the definition heading.")
14334 ("But don't worry; I'll pretend that an equals sign")
14335 ("was present. Everything from here to `enddef'")
14336 ("will be the replacement text of this macro.");
14341 @ A \&{primarydef}, \&{secondarydef}, or \&{tertiarydef} is rather easily
14342 handled now that we have |scan_toks|. In this case there are
14343 two parameters, which will be \.{EXPR0} and \.{EXPR1} (i.e.,
14344 |expr_base| and |expr_base+1|).
14346 @c void mp_make_op_def (MP mp) {
14347 command_code m; /* the type of definition */
14348 pointer p,q,r; /* for list manipulation */
14350 mp_get_symbol(mp); q=mp_get_node(mp, token_node_size);
14351 info(q)=mp->cur_sym; value(q)=expr_base;
14352 mp_get_clear_symbol(mp); mp->warning_info=mp->cur_sym;
14353 mp_get_symbol(mp); p=mp_get_node(mp, token_node_size);
14354 info(p)=mp->cur_sym; value(p)=expr_base+1; link(p)=q;
14355 get_t_next; mp_check_equals(mp);
14356 mp->scanner_status=op_defining; q=mp_get_avail(mp); ref_count(q)=null;
14357 r=mp_get_avail(mp); link(q)=r; info(r)=general_macro;
14358 link(r)=mp_scan_toks(mp, macro_def,p,null,0);
14359 mp->scanner_status=normal; eq_type(mp->warning_info)=m;
14360 equiv(mp->warning_info)=q; mp_get_x_next(mp);
14363 @ Parameters to macros are introduced by the keywords \&{expr},
14364 \&{suffix}, \&{text}, \&{primary}, \&{secondary}, and \&{tertiary}.
14367 mp_primitive(mp, "expr",param_type,expr_base);
14368 @:expr_}{\&{expr} primitive@>
14369 mp_primitive(mp, "suffix",param_type,suffix_base);
14370 @:suffix_}{\&{suffix} primitive@>
14371 mp_primitive(mp, "text",param_type,text_base);
14372 @:text_}{\&{text} primitive@>
14373 mp_primitive(mp, "primary",param_type,primary_macro);
14374 @:primary_}{\&{primary} primitive@>
14375 mp_primitive(mp, "secondary",param_type,secondary_macro);
14376 @:secondary_}{\&{secondary} primitive@>
14377 mp_primitive(mp, "tertiary",param_type,tertiary_macro);
14378 @:tertiary_}{\&{tertiary} primitive@>
14380 @ @<Cases of |print_cmd...@>=
14382 if ( m>=expr_base ) {
14383 if ( m==expr_base ) mp_print(mp, "expr");
14384 else if ( m==suffix_base ) mp_print(mp, "suffix");
14385 else mp_print(mp, "text");
14386 } else if ( m<secondary_macro ) {
14387 mp_print(mp, "primary");
14388 } else if ( m==secondary_macro ) {
14389 mp_print(mp, "secondary");
14391 mp_print(mp, "tertiary");
14395 @ Let's turn next to the more complex processing associated with \&{def}
14396 and \&{vardef}. When the following procedure is called, |cur_mod|
14397 should be either |start_def| or |var_def|.
14399 @c @<Declare the procedure called |check_delimiter|@>;
14400 @<Declare the function called |scan_declared_variable|@>;
14401 void mp_scan_def (MP mp) {
14402 int m; /* the type of definition */
14403 int n; /* the number of special suffix parameters */
14404 int k; /* the total number of parameters */
14405 int c; /* the kind of macro we're defining */
14406 pointer r; /* parameter-substitution list */
14407 pointer q; /* tail of the macro token list */
14408 pointer p; /* temporary storage */
14409 halfword base; /* |expr_base|, |suffix_base|, or |text_base| */
14410 pointer l_delim,r_delim; /* matching delimiters */
14411 m=mp->cur_mod; c=general_macro; link(hold_head)=null;
14412 q=mp_get_avail(mp); ref_count(q)=null; r=null;
14413 @<Scan the token or variable to be defined;
14414 set |n|, |scanner_status|, and |warning_info|@>;
14416 if ( mp->cur_cmd==left_delimiter ) {
14417 @<Absorb delimited parameters, putting them into lists |q| and |r|@>;
14419 if ( mp->cur_cmd==param_type ) {
14420 @<Absorb undelimited parameters, putting them into list |r|@>;
14422 mp_check_equals(mp);
14423 p=mp_get_avail(mp); info(p)=c; link(q)=p;
14424 @<Attach the replacement text to the tail of node |p|@>;
14425 mp->scanner_status=normal; mp_get_x_next(mp);
14428 @ We don't put `|frozen_end_group|' into the replacement text of
14429 a \&{vardef}, because the user may want to redefine `\.{endgroup}'.
14431 @<Attach the replacement text to the tail of node |p|@>=
14432 if ( m==start_def ) {
14433 link(p)=mp_scan_toks(mp, macro_def,r,null,n);
14435 q=mp_get_avail(mp); info(q)=mp->bg_loc; link(p)=q;
14436 p=mp_get_avail(mp); info(p)=mp->eg_loc;
14437 link(q)=mp_scan_toks(mp, macro_def,r,p,n);
14439 if ( mp->warning_info==bad_vardef )
14440 mp_flush_token_list(mp, value(bad_vardef))
14444 int eg_loc; /* hash addresses of `\.{begingroup}' and `\.{endgroup}' */
14446 @ @<Scan the token or variable to be defined;...@>=
14447 if ( m==start_def ) {
14448 mp_get_clear_symbol(mp); mp->warning_info=mp->cur_sym; get_t_next;
14449 mp->scanner_status=op_defining; n=0;
14450 eq_type(mp->warning_info)=defined_macro; equiv(mp->warning_info)=q;
14452 p=mp_scan_declared_variable(mp);
14453 mp_flush_variable(mp, equiv(info(p)),link(p),true);
14454 mp->warning_info=mp_find_variable(mp, p); mp_flush_list(mp, p);
14455 if ( mp->warning_info==null ) @<Change to `\.{a bad variable}'@>;
14456 mp->scanner_status=var_defining; n=2;
14457 if ( mp->cur_cmd==macro_special ) if ( mp->cur_mod==macro_suffix ) {/* \.{\AT!\#} */
14460 type(mp->warning_info)=mp_unsuffixed_macro-2+n; value(mp->warning_info)=q;
14461 } /* |mp_suffixed_macro=mp_unsuffixed_macro+1| */
14463 @ @<Change to `\.{a bad variable}'@>=
14465 print_err("This variable already starts with a macro");
14466 @.This variable already...@>
14467 help2("After `vardef a' you can\'t say `vardef a.b'.")
14468 ("So I'll have to discard this definition.");
14469 mp_error(mp); mp->warning_info=bad_vardef;
14472 @ @<Initialize table entries...@>=
14473 name_type(bad_vardef)=mp_root; link(bad_vardef)=frozen_bad_vardef;
14474 equiv(frozen_bad_vardef)=bad_vardef; eq_type(frozen_bad_vardef)=tag_token;
14476 @ @<Absorb delimited parameters, putting them into lists |q| and |r|@>=
14478 l_delim=mp->cur_sym; r_delim=mp->cur_mod; get_t_next;
14479 if ( (mp->cur_cmd==param_type)&&(mp->cur_mod>=expr_base) ) {
14482 print_err("Missing parameter type; `expr' will be assumed");
14483 @.Missing parameter type@>
14484 help1("You should've had `expr' or `suffix' or `text' here.");
14485 mp_back_error(mp); base=expr_base;
14487 @<Absorb parameter tokens for type |base|@>;
14488 mp_check_delimiter(mp, l_delim,r_delim);
14490 } while (mp->cur_cmd==left_delimiter)
14492 @ @<Absorb parameter tokens for type |base|@>=
14494 link(q)=mp_get_avail(mp); q=link(q); info(q)=base+k;
14495 mp_get_symbol(mp); p=mp_get_node(mp, token_node_size);
14496 value(p)=base+k; info(p)=mp->cur_sym;
14497 if ( k==mp->param_size ) mp_overflow(mp, "parameter stack size",mp->param_size);
14498 @:MetaPost capacity exceeded parameter stack size}{\quad parameter stack size@>
14499 incr(k); link(p)=r; r=p; get_t_next;
14500 } while (mp->cur_cmd==comma)
14502 @ @<Absorb undelimited parameters, putting them into list |r|@>=
14504 p=mp_get_node(mp, token_node_size);
14505 if ( mp->cur_mod<expr_base ) {
14506 c=mp->cur_mod; value(p)=expr_base+k;
14508 value(p)=mp->cur_mod+k;
14509 if ( mp->cur_mod==expr_base ) c=expr_macro;
14510 else if ( mp->cur_mod==suffix_base ) c=suffix_macro;
14513 if ( k==mp->param_size ) mp_overflow(mp, "parameter stack size",mp->param_size);
14514 incr(k); mp_get_symbol(mp); info(p)=mp->cur_sym; link(p)=r; r=p; get_t_next;
14515 if ( c==expr_macro ) if ( mp->cur_cmd==of_token ) {
14516 c=of_macro; p=mp_get_node(mp, token_node_size);
14517 if ( k==mp->param_size ) mp_overflow(mp, "parameter stack size",mp->param_size);
14518 value(p)=expr_base+k; mp_get_symbol(mp); info(p)=mp->cur_sym;
14519 link(p)=r; r=p; get_t_next;
14523 @* \[32] Expanding the next token.
14524 Only a few command codes |<min_command| can possibly be returned by
14525 |get_t_next|; in increasing order, they are
14526 |if_test|, |fi_or_else|, |input|, |iteration|, |repeat_loop|,
14527 |exit_test|, |relax|, |scan_tokens|, |expand_after|, and |defined_macro|.
14529 \MP\ usually gets the next token of input by saying |get_x_next|. This is
14530 like |get_t_next| except that it keeps getting more tokens until
14531 finding |cur_cmd>=min_command|. In other words, |get_x_next| expands
14532 macros and removes conditionals or iterations or input instructions that
14535 It follows that |get_x_next| might invoke itself recursively. In fact,
14536 there is massive recursion, since macro expansion can involve the
14537 scanning of arbitrarily complex expressions, which in turn involve
14538 macro expansion and conditionals, etc.
14541 Therefore it's necessary to declare a whole bunch of |forward|
14542 procedures at this point, and to insert some other procedures
14543 that will be invoked by |get_x_next|.
14546 void mp_scan_primary (MP mp);
14547 void mp_scan_secondary (MP mp);
14548 void mp_scan_tertiary (MP mp);
14549 void mp_scan_expression (MP mp);
14550 void mp_scan_suffix (MP mp);
14551 @<Declare the procedure called |macro_call|@>;
14552 void mp_get_boolean (MP mp);
14553 void mp_pass_text (MP mp);
14554 void mp_conditional (MP mp);
14555 void mp_start_input (MP mp);
14556 void mp_begin_iteration (MP mp);
14557 void mp_resume_iteration (MP mp);
14558 void mp_stop_iteration (MP mp);
14560 @ An auxiliary subroutine called |expand| is used by |get_x_next|
14561 when it has to do exotic expansion commands.
14563 @c void mp_expand (MP mp) {
14564 pointer p; /* for list manipulation */
14565 size_t k; /* something that we hope is |<=buf_size| */
14566 pool_pointer j; /* index into |str_pool| */
14567 if ( mp->internal[mp_tracing_commands]>unity )
14568 if ( mp->cur_cmd!=defined_macro )
14570 switch (mp->cur_cmd) {
14572 mp_conditional(mp); /* this procedure is discussed in Part 36 below */
14575 @<Terminate the current conditional and skip to \&{fi}@>;
14578 @<Initiate or terminate input from a file@>;
14581 if ( mp->cur_mod==end_for ) {
14582 @<Scold the user for having an extra \&{endfor}@>;
14584 mp_begin_iteration(mp); /* this procedure is discussed in Part 37 below */
14591 @<Exit a loop if the proper time has come@>;
14596 @<Expand the token after the next token@>;
14599 @<Put a string into the input buffer@>;
14601 case defined_macro:
14602 mp_macro_call(mp, mp->cur_mod,null,mp->cur_sym);
14604 }; /* there are no other cases */
14607 @ @<Scold the user...@>=
14609 print_err("Extra `endfor'");
14611 help2("I'm not currently working on a for loop,")
14612 ("so I had better not try to end anything.");
14616 @ The processing of \&{input} involves the |start_input| subroutine,
14617 which will be declared later; the processing of \&{endinput} is trivial.
14620 mp_primitive(mp, "input",input,0);
14621 @:input_}{\&{input} primitive@>
14622 mp_primitive(mp, "endinput",input,1);
14623 @:end_input_}{\&{endinput} primitive@>
14625 @ @<Cases of |print_cmd_mod|...@>=
14627 if ( m==0 ) mp_print(mp, "input");
14628 else mp_print(mp, "endinput");
14631 @ @<Initiate or terminate input...@>=
14632 if ( mp->cur_mod>0 ) mp->force_eof=true;
14633 else mp_start_input(mp)
14635 @ We'll discuss the complicated parts of loop operations later. For now
14636 it suffices to know that there's a global variable called |loop_ptr|
14637 that will be |null| if no loop is in progress.
14640 { while ( token_state &&(loc==null) )
14641 mp_end_token_list(mp); /* conserve stack space */
14642 if ( mp->loop_ptr==null ) {
14643 print_err("Lost loop");
14645 help2("I'm confused; after exiting from a loop, I still seem")
14646 ("to want to repeat it. I'll try to forget the problem.");
14649 mp_resume_iteration(mp); /* this procedure is in Part 37 below */
14653 @ @<Exit a loop if the proper time has come@>=
14654 { mp_get_boolean(mp);
14655 if ( mp->internal[mp_tracing_commands]>unity )
14656 mp_show_cmd_mod(mp, nullary,mp->cur_exp);
14657 if ( mp->cur_exp==true_code ) {
14658 if ( mp->loop_ptr==null ) {
14659 print_err("No loop is in progress");
14660 @.No loop is in progress@>
14661 help1("Why say `exitif' when there's nothing to exit from?");
14662 if ( mp->cur_cmd==semicolon ) mp_error(mp); else mp_back_error(mp);
14664 @<Exit prematurely from an iteration@>;
14666 } else if ( mp->cur_cmd!=semicolon ) {
14667 mp_missing_err(mp, ";");
14669 help2("After `exitif <boolean exp>' I expect to see a semicolon.")
14670 ("I shall pretend that one was there."); mp_back_error(mp);
14674 @ Here we use the fact that |forever_text| is the only |token_type| that
14675 is less than |loop_text|.
14677 @<Exit prematurely...@>=
14680 if ( file_state ) {
14681 mp_end_file_reading(mp);
14683 if ( token_type<=loop_text ) p=start;
14684 mp_end_token_list(mp);
14687 if ( p!=info(mp->loop_ptr) ) mp_fatal_error(mp, "*** (loop confusion)");
14689 mp_stop_iteration(mp); /* this procedure is in Part 34 below */
14692 @ @<Expand the token after the next token@>=
14694 p=mp_cur_tok(mp); get_t_next;
14695 if ( mp->cur_cmd<min_command ) mp_expand(mp);
14696 else mp_back_input(mp);
14700 @ @<Put a string into the input buffer@>=
14701 { mp_get_x_next(mp); mp_scan_primary(mp);
14702 if ( mp->cur_type!=mp_string_type ) {
14703 mp_disp_err(mp, null,"Not a string");
14705 help2("I'm going to flush this expression, since")
14706 ("scantokens should be followed by a known string.");
14707 mp_put_get_flush_error(mp, 0);
14710 if ( length(mp->cur_exp)>0 )
14711 @<Pretend we're reading a new one-line file@>;
14715 @ @<Pretend we're reading a new one-line file@>=
14716 { mp_begin_file_reading(mp); name=is_scantok;
14717 k=mp->first+length(mp->cur_exp);
14718 if ( k>=mp->max_buf_stack ) {
14719 while ( k>=mp->buf_size ) {
14720 mp_reallocate_buffer(mp,(mp->buf_size+(mp->buf_size>>2)));
14722 mp->max_buf_stack=k+1;
14724 j=mp->str_start[mp->cur_exp]; limit=k;
14725 while ( mp->first<(size_t)limit ) {
14726 mp->buffer[mp->first]=mp->str_pool[j]; incr(j); incr(mp->first);
14728 mp->buffer[limit]='%'; mp->first=limit+1; loc=start;
14729 mp_flush_cur_exp(mp, 0);
14732 @ Here finally is |get_x_next|.
14734 The expression scanning routines to be considered later
14735 communicate via the global quantities |cur_type| and |cur_exp|;
14736 we must be very careful to save and restore these quantities while
14737 macros are being expanded.
14741 void mp_get_x_next (MP mp);
14743 @ @c void mp_get_x_next (MP mp) {
14744 pointer save_exp; /* a capsule to save |cur_type| and |cur_exp| */
14746 if ( mp->cur_cmd<min_command ) {
14747 save_exp=mp_stash_cur_exp(mp);
14749 if ( mp->cur_cmd==defined_macro )
14750 mp_macro_call(mp, mp->cur_mod,null,mp->cur_sym);
14754 } while (mp->cur_cmd<min_command);
14755 mp_unstash_cur_exp(mp, save_exp); /* that restores |cur_type| and |cur_exp| */
14759 @ Now let's consider the |macro_call| procedure, which is used to start up
14760 all user-defined macros. Since the arguments to a macro might be expressions,
14761 |macro_call| is recursive.
14764 The first parameter to |macro_call| points to the reference count of the
14765 token list that defines the macro. The second parameter contains any
14766 arguments that have already been parsed (see below). The third parameter
14767 points to the symbolic token that names the macro. If the third parameter
14768 is |null|, the macro was defined by \&{vardef}, so its name can be
14769 reconstructed from the prefix and ``at'' arguments found within the
14772 What is this second parameter? It's simply a linked list of one-word items,
14773 whose |info| fields point to the arguments. In other words, if |arg_list=null|,
14774 no arguments have been scanned yet; otherwise |info(arg_list)| points to
14775 the first scanned argument, and |link(arg_list)| points to the list of
14776 further arguments (if any).
14778 Arguments of type \&{expr} are so-called capsules, which we will
14779 discuss later when we concentrate on expressions; they can be
14780 recognized easily because their |link| field is |void|. Arguments of type
14781 \&{suffix} and \&{text} are token lists without reference counts.
14783 @ After argument scanning is complete, the arguments are moved to the
14784 |param_stack|. (They can't be put on that stack any sooner, because
14785 the stack is growing and shrinking in unpredictable ways as more arguments
14786 are being acquired.) Then the macro body is fed to the scanner; i.e.,
14787 the replacement text of the macro is placed at the top of the \MP's
14788 input stack, so that |get_t_next| will proceed to read it next.
14790 @<Declare the procedure called |macro_call|@>=
14791 @<Declare the procedure called |print_macro_name|@>;
14792 @<Declare the procedure called |print_arg|@>;
14793 @<Declare the procedure called |scan_text_arg|@>;
14794 void mp_macro_call (MP mp,pointer def_ref, pointer arg_list,
14795 pointer macro_name) ;
14798 void mp_macro_call (MP mp,pointer def_ref, pointer arg_list,
14799 pointer macro_name) {
14800 /* invokes a user-defined control sequence */
14801 pointer r; /* current node in the macro's token list */
14802 pointer p,q; /* for list manipulation */
14803 integer n; /* the number of arguments */
14804 pointer tail = 0; /* tail of the argument list */
14805 pointer l_delim=0,r_delim=0; /* a delimiter pair */
14806 r=link(def_ref); add_mac_ref(def_ref);
14807 if ( arg_list==null ) {
14810 @<Determine the number |n| of arguments already supplied,
14811 and set |tail| to the tail of |arg_list|@>;
14813 if ( mp->internal[mp_tracing_macros]>0 ) {
14814 @<Show the text of the macro being expanded, and the existing arguments@>;
14816 @<Scan the remaining arguments, if any; set |r| to the first token
14817 of the replacement text@>;
14818 @<Feed the arguments and replacement text to the scanner@>;
14821 @ @<Show the text of the macro...@>=
14822 mp_begin_diagnostic(mp); mp_print_ln(mp);
14823 mp_print_macro_name(mp, arg_list,macro_name);
14824 if ( n==3 ) mp_print(mp, "@@#"); /* indicate a suffixed macro */
14825 mp_show_macro(mp, def_ref,null,100000);
14826 if ( arg_list!=null ) {
14830 mp_print_arg(mp, q,n,0);
14831 incr(n); p=link(p);
14834 mp_end_diagnostic(mp, false)
14837 @ @<Declare the procedure called |print_macro_name|@>=
14838 void mp_print_macro_name (MP mp,pointer a, pointer n);
14841 void mp_print_macro_name (MP mp,pointer a, pointer n) {
14842 pointer p,q; /* they traverse the first part of |a| */
14848 mp_print_text(info(info(link(a))));
14851 while ( link(q)!=null ) q=link(q);
14852 link(q)=info(link(a));
14853 mp_show_token_list(mp, p,null,1000,0);
14859 @ @<Declare the procedure called |print_arg|@>=
14860 void mp_print_arg (MP mp,pointer q, integer n, pointer b) ;
14863 void mp_print_arg (MP mp,pointer q, integer n, pointer b) {
14864 if ( link(q)==mp_void ) mp_print_nl(mp, "(EXPR");
14865 else if ( (b<text_base)&&(b!=text_macro) ) mp_print_nl(mp, "(SUFFIX");
14866 else mp_print_nl(mp, "(TEXT");
14867 mp_print_int(mp, n); mp_print(mp, ")<-");
14868 if ( link(q)==mp_void ) mp_print_exp(mp, q,1);
14869 else mp_show_token_list(mp, q,null,1000,0);
14872 @ @<Determine the number |n| of arguments already supplied...@>=
14874 n=1; tail=arg_list;
14875 while ( link(tail)!=null ) {
14876 incr(n); tail=link(tail);
14880 @ @<Scan the remaining arguments, if any; set |r|...@>=
14881 mp->cur_cmd=comma+1; /* anything |<>comma| will do */
14882 while ( info(r)>=expr_base ) {
14883 @<Scan the delimited argument represented by |info(r)|@>;
14886 if ( mp->cur_cmd==comma ) {
14887 print_err("Too many arguments to ");
14888 @.Too many arguments...@>
14889 mp_print_macro_name(mp, arg_list,macro_name); mp_print_char(mp, ';');
14890 mp_print_nl(mp, " Missing `"); mp_print_text(r_delim);
14892 mp_print(mp, "' has been inserted");
14893 help3("I'm going to assume that the comma I just read was a")
14894 ("right delimiter, and then I'll begin expanding the macro.")
14895 ("You might want to delete some tokens before continuing.");
14898 if ( info(r)!=general_macro ) {
14899 @<Scan undelimited argument(s)@>;
14903 @ At this point, the reader will find it advisable to review the explanation
14904 of token list format that was presented earlier, paying special attention to
14905 the conventions that apply only at the beginning of a macro's token list.
14907 On the other hand, the reader will have to take the expression-parsing
14908 aspects of the following program on faith; we will explain |cur_type|
14909 and |cur_exp| later. (Several things in this program depend on each other,
14910 and it's necessary to jump into the circle somewhere.)
14912 @<Scan the delimited argument represented by |info(r)|@>=
14913 if ( mp->cur_cmd!=comma ) {
14915 if ( mp->cur_cmd!=left_delimiter ) {
14916 print_err("Missing argument to ");
14917 @.Missing argument...@>
14918 mp_print_macro_name(mp, arg_list,macro_name);
14919 help3("That macro has more parameters than you thought.")
14920 ("I'll continue by pretending that each missing argument")
14921 ("is either zero or null.");
14922 if ( info(r)>=suffix_base ) {
14923 mp->cur_exp=null; mp->cur_type=mp_token_list;
14925 mp->cur_exp=0; mp->cur_type=mp_known;
14927 mp_back_error(mp); mp->cur_cmd=right_delimiter;
14930 l_delim=mp->cur_sym; r_delim=mp->cur_mod;
14932 @<Scan the argument represented by |info(r)|@>;
14933 if ( mp->cur_cmd!=comma )
14934 @<Check that the proper right delimiter was present@>;
14936 @<Append the current expression to |arg_list|@>
14938 @ @<Check that the proper right delim...@>=
14939 if ( (mp->cur_cmd!=right_delimiter)||(mp->cur_mod!=l_delim) ) {
14940 if ( info(link(r))>=expr_base ) {
14941 mp_missing_err(mp, ",");
14943 help3("I've finished reading a macro argument and am about to")
14944 ("read another; the arguments weren't delimited correctly.")
14945 ("You might want to delete some tokens before continuing.");
14946 mp_back_error(mp); mp->cur_cmd=comma;
14948 mp_missing_err(mp, str(text(r_delim)));
14950 help2("I've gotten to the end of the macro parameter list.")
14951 ("You might want to delete some tokens before continuing.");
14956 @ A \&{suffix} or \&{text} parameter will be have been scanned as
14957 a token list pointed to by |cur_exp|, in which case we will have
14958 |cur_type=token_list|.
14960 @<Append the current expression to |arg_list|@>=
14962 p=mp_get_avail(mp);
14963 if ( mp->cur_type==mp_token_list ) info(p)=mp->cur_exp;
14964 else info(p)=mp_stash_cur_exp(mp);
14965 if ( mp->internal[mp_tracing_macros]>0 ) {
14966 mp_begin_diagnostic(mp); mp_print_arg(mp, info(p),n,info(r));
14967 mp_end_diagnostic(mp, false);
14969 if ( arg_list==null ) arg_list=p;
14974 @ @<Scan the argument represented by |info(r)|@>=
14975 if ( info(r)>=text_base ) {
14976 mp_scan_text_arg(mp, l_delim,r_delim);
14979 if ( info(r)>=suffix_base ) mp_scan_suffix(mp);
14980 else mp_scan_expression(mp);
14983 @ The parameters to |scan_text_arg| are either a pair of delimiters
14984 or zero; the latter case is for undelimited text arguments, which
14985 end with the first semicolon or \&{endgroup} or \&{end} that is not
14986 contained in a group.
14988 @<Declare the procedure called |scan_text_arg|@>=
14989 void mp_scan_text_arg (MP mp,pointer l_delim, pointer r_delim) ;
14992 void mp_scan_text_arg (MP mp,pointer l_delim, pointer r_delim) {
14993 integer balance; /* excess of |l_delim| over |r_delim| */
14994 pointer p; /* list tail */
14995 mp->warning_info=l_delim; mp->scanner_status=absorbing;
14996 p=hold_head; balance=1; link(hold_head)=null;
14999 if ( l_delim==0 ) {
15000 @<Adjust the balance for an undelimited argument; |break| if done@>;
15002 @<Adjust the balance for a delimited argument; |break| if done@>;
15004 link(p)=mp_cur_tok(mp); p=link(p);
15006 mp->cur_exp=link(hold_head); mp->cur_type=mp_token_list;
15007 mp->scanner_status=normal;
15010 @ @<Adjust the balance for a delimited argument...@>=
15011 if ( mp->cur_cmd==right_delimiter ) {
15012 if ( mp->cur_mod==l_delim ) {
15014 if ( balance==0 ) break;
15016 } else if ( mp->cur_cmd==left_delimiter ) {
15017 if ( mp->cur_mod==r_delim ) incr(balance);
15020 @ @<Adjust the balance for an undelimited...@>=
15021 if ( end_of_statement ) { /* |cur_cmd=semicolon|, |end_group|, or |stop| */
15022 if ( balance==1 ) { break; }
15023 else { if ( mp->cur_cmd==end_group ) decr(balance); }
15024 } else if ( mp->cur_cmd==begin_group ) {
15028 @ @<Scan undelimited argument(s)@>=
15030 if ( info(r)<text_macro ) {
15032 if ( info(r)!=suffix_macro ) {
15033 if ( (mp->cur_cmd==equals)||(mp->cur_cmd==assignment) ) mp_get_x_next(mp);
15037 case primary_macro:mp_scan_primary(mp); break;
15038 case secondary_macro:mp_scan_secondary(mp); break;
15039 case tertiary_macro:mp_scan_tertiary(mp); break;
15040 case expr_macro:mp_scan_expression(mp); break;
15042 @<Scan an expression followed by `\&{of} $\langle$primary$\rangle$'@>;
15045 @<Scan a suffix with optional delimiters@>;
15047 case text_macro:mp_scan_text_arg(mp, 0,0); break;
15048 } /* there are no other cases */
15050 @<Append the current expression to |arg_list|@>;
15053 @ @<Scan an expression followed by `\&{of} $\langle$primary$\rangle$'@>=
15055 mp_scan_expression(mp); p=mp_get_avail(mp); info(p)=mp_stash_cur_exp(mp);
15056 if ( mp->internal[mp_tracing_macros]>0 ) {
15057 mp_begin_diagnostic(mp); mp_print_arg(mp, info(p),n,0);
15058 mp_end_diagnostic(mp, false);
15060 if ( arg_list==null ) arg_list=p; else link(tail)=p;
15062 if ( mp->cur_cmd!=of_token ) {
15063 mp_missing_err(mp, "of"); mp_print(mp, " for ");
15065 mp_print_macro_name(mp, arg_list,macro_name);
15066 help1("I've got the first argument; will look now for the other.");
15069 mp_get_x_next(mp); mp_scan_primary(mp);
15072 @ @<Scan a suffix with optional delimiters@>=
15074 if ( mp->cur_cmd!=left_delimiter ) {
15077 l_delim=mp->cur_sym; r_delim=mp->cur_mod; mp_get_x_next(mp);
15079 mp_scan_suffix(mp);
15080 if ( l_delim!=null ) {
15081 if ((mp->cur_cmd!=right_delimiter)||(mp->cur_mod!=l_delim) ) {
15082 mp_missing_err(mp, str(text(r_delim)));
15084 help2("I've gotten to the end of the macro parameter list.")
15085 ("You might want to delete some tokens before continuing.");
15092 @ Before we put a new token list on the input stack, it is wise to clean off
15093 all token lists that have recently been depleted. Then a user macro that ends
15094 with a call to itself will not require unbounded stack space.
15096 @<Feed the arguments and replacement text to the scanner@>=
15097 while ( token_state &&(loc==null) ) mp_end_token_list(mp); /* conserve stack space */
15098 if ( mp->param_ptr+n>mp->max_param_stack ) {
15099 mp->max_param_stack=mp->param_ptr+n;
15100 if ( mp->max_param_stack>mp->param_size )
15101 mp_overflow(mp, "parameter stack size",mp->param_size);
15102 @:MetaPost capacity exceeded parameter stack size}{\quad parameter stack size@>
15104 mp_begin_token_list(mp, def_ref,macro); name=macro_name; loc=r;
15108 mp->param_stack[mp->param_ptr]=info(p); incr(mp->param_ptr); p=link(p);
15110 mp_flush_list(mp, arg_list);
15113 @ It's sometimes necessary to put a single argument onto |param_stack|.
15114 The |stack_argument| subroutine does this.
15116 @c void mp_stack_argument (MP mp,pointer p) {
15117 if ( mp->param_ptr==mp->max_param_stack ) {
15118 incr(mp->max_param_stack);
15119 if ( mp->max_param_stack>mp->param_size )
15120 mp_overflow(mp, "parameter stack size",mp->param_size);
15121 @:MetaPost capacity exceeded parameter stack size}{\quad parameter stack size@>
15123 mp->param_stack[mp->param_ptr]=p; incr(mp->param_ptr);
15126 @* \[33] Conditional processing.
15127 Let's consider now the way \&{if} commands are handled.
15129 Conditions can be inside conditions, and this nesting has a stack
15130 that is independent of other stacks.
15131 Four global variables represent the top of the condition stack:
15132 |cond_ptr| points to pushed-down entries, if~any; |cur_if| tells whether
15133 we are processing \&{if} or \&{elseif}; |if_limit| specifies
15134 the largest code of a |fi_or_else| command that is syntactically legal;
15135 and |if_line| is the line number at which the current conditional began.
15137 If no conditions are currently in progress, the condition stack has the
15138 special state |cond_ptr=null|, |if_limit=normal|, |cur_if=0|, |if_line=0|.
15139 Otherwise |cond_ptr| points to a two-word node; the |type|, |name_type|, and
15140 |link| fields of the first word contain |if_limit|, |cur_if|, and
15141 |cond_ptr| at the next level, and the second word contains the
15142 corresponding |if_line|.
15144 @d if_node_size 2 /* number of words in stack entry for conditionals */
15145 @d if_line_field(A) mp->mem[(A)+1].cint
15146 @d if_code 1 /* code for \&{if} being evaluated */
15147 @d fi_code 2 /* code for \&{fi} */
15148 @d else_code 3 /* code for \&{else} */
15149 @d else_if_code 4 /* code for \&{elseif} */
15152 pointer cond_ptr; /* top of the condition stack */
15153 integer if_limit; /* upper bound on |fi_or_else| codes */
15154 small_number cur_if; /* type of conditional being worked on */
15155 integer if_line; /* line where that conditional began */
15158 mp->cond_ptr=null; mp->if_limit=normal; mp->cur_if=0; mp->if_line=0;
15161 mp_primitive(mp, "if",if_test,if_code);
15162 @:if_}{\&{if} primitive@>
15163 mp_primitive(mp, "fi",fi_or_else,fi_code); mp->eqtb[frozen_fi]=mp->eqtb[mp->cur_sym];
15164 @:fi_}{\&{fi} primitive@>
15165 mp_primitive(mp, "else",fi_or_else,else_code);
15166 @:else_}{\&{else} primitive@>
15167 mp_primitive(mp, "elseif",fi_or_else,else_if_code);
15168 @:else_if_}{\&{elseif} primitive@>
15170 @ @<Cases of |print_cmd_mod|...@>=
15174 case if_code:mp_print(mp, "if"); break;
15175 case fi_code:mp_print(mp, "fi"); break;
15176 case else_code:mp_print(mp, "else"); break;
15177 default: mp_print(mp, "elseif"); break;
15181 @ Here is a procedure that ignores text until coming to an \&{elseif},
15182 \&{else}, or \&{fi} at level zero of $\&{if}\ldots\&{fi}$
15183 nesting. After it has acted, |cur_mod| will indicate the token that
15186 \MP's smallest two command codes are |if_test| and |fi_or_else|; this
15187 makes the skipping process a bit simpler.
15190 void mp_pass_text (MP mp) {
15192 mp->scanner_status=skipping;
15193 mp->warning_info=mp_true_line(mp);
15196 if ( mp->cur_cmd<=fi_or_else ) {
15197 if ( mp->cur_cmd<fi_or_else ) {
15201 if ( mp->cur_mod==fi_code ) decr(l);
15204 @<Decrease the string reference count,
15205 if the current token is a string@>;
15208 mp->scanner_status=normal;
15211 @ @<Decrease the string reference count...@>=
15212 if ( mp->cur_cmd==string_token ) { delete_str_ref(mp->cur_mod); }
15214 @ When we begin to process a new \&{if}, we set |if_limit:=if_code|; then
15215 if \&{elseif} or \&{else} or \&{fi} occurs before the current \&{if}
15216 condition has been evaluated, a colon will be inserted.
15217 A construction like `\.{if fi}' would otherwise get \MP\ confused.
15219 @<Push the condition stack@>=
15220 { p=mp_get_node(mp, if_node_size); link(p)=mp->cond_ptr; type(p)=mp->if_limit;
15221 name_type(p)=mp->cur_if; if_line_field(p)=mp->if_line;
15222 mp->cond_ptr=p; mp->if_limit=if_code; mp->if_line=mp_true_line(mp);
15223 mp->cur_if=if_code;
15226 @ @<Pop the condition stack@>=
15227 { p=mp->cond_ptr; mp->if_line=if_line_field(p);
15228 mp->cur_if=name_type(p); mp->if_limit=type(p); mp->cond_ptr=link(p);
15229 mp_free_node(mp, p,if_node_size);
15232 @ Here's a procedure that changes the |if_limit| code corresponding to
15233 a given value of |cond_ptr|.
15235 @c void mp_change_if_limit (MP mp,small_number l, pointer p) {
15237 if ( p==mp->cond_ptr ) {
15238 mp->if_limit=l; /* that's the easy case */
15242 if ( q==null ) mp_confusion(mp, "if");
15243 @:this can't happen if}{\quad if@>
15244 if ( link(q)==p ) {
15252 @ The user is supposed to put colons into the proper parts of conditional
15253 statements. Therefore, \MP\ has to check for their presence.
15256 void mp_check_colon (MP mp) {
15257 if ( mp->cur_cmd!=colon ) {
15258 mp_missing_err(mp, ":");
15260 help2("There should've been a colon after the condition.")
15261 ("I shall pretend that one was there.");;
15266 @ A condition is started when the |get_x_next| procedure encounters
15267 an |if_test| command; in that case |get_x_next| calls |conditional|,
15268 which is a recursive procedure.
15271 @c void mp_conditional (MP mp) {
15272 pointer save_cond_ptr; /* |cond_ptr| corresponding to this conditional */
15273 int new_if_limit; /* future value of |if_limit| */
15274 pointer p; /* temporary register */
15275 @<Push the condition stack@>;
15276 save_cond_ptr=mp->cond_ptr;
15278 mp_get_boolean(mp); new_if_limit=else_if_code;
15279 if ( mp->internal[mp_tracing_commands]>unity ) {
15280 @<Display the boolean value of |cur_exp|@>;
15283 mp_check_colon(mp);
15284 if ( mp->cur_exp==true_code ) {
15285 mp_change_if_limit(mp, new_if_limit,save_cond_ptr);
15286 return; /* wait for \&{elseif}, \&{else}, or \&{fi} */
15288 @<Skip to \&{elseif} or \&{else} or \&{fi}, then |goto done|@>;
15290 mp->cur_if=mp->cur_mod; mp->if_line=mp_true_line(mp);
15291 if ( mp->cur_mod==fi_code ) {
15292 @<Pop the condition stack@>
15293 } else if ( mp->cur_mod==else_if_code ) {
15296 mp->cur_exp=true_code; new_if_limit=fi_code; mp_get_x_next(mp);
15301 @ In a construction like `\&{if} \&{if} \&{true}: $0=1$: \\{foo}
15302 \&{else}: \\{bar} \&{fi}', the first \&{else}
15303 that we come to after learning that the \&{if} is false is not the
15304 \&{else} we're looking for. Hence the following curious logic is needed.
15306 @<Skip to \&{elseif}...@>=
15309 if ( mp->cond_ptr==save_cond_ptr ) goto DONE;
15310 else if ( mp->cur_mod==fi_code ) @<Pop the condition stack@>;
15314 @ @<Display the boolean value...@>=
15315 { mp_begin_diagnostic(mp);
15316 if ( mp->cur_exp==true_code ) mp_print(mp, "{true}");
15317 else mp_print(mp, "{false}");
15318 mp_end_diagnostic(mp, false);
15321 @ The processing of conditionals is complete except for the following
15322 code, which is actually part of |get_x_next|. It comes into play when
15323 \&{elseif}, \&{else}, or \&{fi} is scanned.
15325 @<Terminate the current conditional and skip to \&{fi}@>=
15326 if ( mp->cur_mod>mp->if_limit ) {
15327 if ( mp->if_limit==if_code ) { /* condition not yet evaluated */
15328 mp_missing_err(mp, ":");
15330 mp_back_input(mp); mp->cur_sym=frozen_colon; mp_ins_error(mp);
15332 print_err("Extra "); mp_print_cmd_mod(mp, fi_or_else,mp->cur_mod);
15336 help1("I'm ignoring this; it doesn't match any if.");
15340 while ( mp->cur_mod!=fi_code ) mp_pass_text(mp); /* skip to \&{fi} */
15341 @<Pop the condition stack@>;
15344 @* \[34] Iterations.
15345 To bring our treatment of |get_x_next| to a close, we need to consider what
15346 \MP\ does when it sees \&{for}, \&{forsuffixes}, and \&{forever}.
15348 There's a global variable |loop_ptr| that keeps track of the \&{for} loops
15349 that are currently active. If |loop_ptr=null|, no loops are in progress;
15350 otherwise |info(loop_ptr)| points to the iterative text of the current
15351 (innermost) loop, and |link(loop_ptr)| points to the data for any other
15352 loops that enclose the current one.
15354 A loop-control node also has two other fields, called |loop_type| and
15355 |loop_list|, whose contents depend on the type of loop:
15357 \yskip\indent|loop_type(loop_ptr)=null| means that |loop_list(loop_ptr)|
15358 points to a list of one-word nodes whose |info| fields point to the
15359 remaining argument values of a suffix list and expression list.
15361 \yskip\indent|loop_type(loop_ptr)=mp_void| means that the current loop is
15364 \yskip\indent|loop_type(loop_ptr)=progression_flag| means that
15365 |p=loop_list(loop_ptr)| points to a ``progression node'' and |value(p)|,
15366 |step_size(p)|, and |final_value(p)| contain the data for an arithmetic
15369 \yskip\indent|loop_type(loop_ptr)=p>mp_void| means that |p| points to an edge
15370 header and |loop_list(loop_ptr)| points into the graphical object list for
15373 \yskip\noindent In the case of a progression node, the first word is not used
15374 because the link field of words in the dynamic memory area cannot be arbitrary.
15376 @d loop_list_loc(A) ((A)+1) /* where the |loop_list| field resides */
15377 @d loop_type(A) info(loop_list_loc((A))) /* the type of \&{for} loop */
15378 @d loop_list(A) link(loop_list_loc((A))) /* the remaining list elements */
15379 @d loop_node_size 2 /* the number of words in a loop control node */
15380 @d progression_node_size 4 /* the number of words in a progression node */
15381 @d step_size(A) mp->mem[(A)+2].sc /* the step size in an arithmetic progression */
15382 @d final_value(A) mp->mem[(A)+3].sc /* the final value in an arithmetic progression */
15383 @d progression_flag (null+2)
15384 /* |loop_type| value when |loop_list| points to a progression node */
15387 pointer loop_ptr; /* top of the loop-control-node stack */
15392 @ If the expressions that define an arithmetic progression in
15393 a \&{for} loop don't have known numeric values, the |bad_for|
15394 subroutine screams at the user.
15396 @c void mp_bad_for (MP mp, char * s) {
15397 mp_disp_err(mp, null,"Improper "); /* show the bad expression above the message */
15398 @.Improper...replaced by 0@>
15399 mp_print(mp, s); mp_print(mp, " has been replaced by 0");
15400 help4("When you say `for x=a step b until c',")
15401 ("the initial value `a' and the step size `b'")
15402 ("and the final value `c' must have known numeric values.")
15403 ("I'm zeroing this one. Proceed, with fingers crossed.");
15404 mp_put_get_flush_error(mp, 0);
15407 @ Here's what \MP\ does when \&{for}, \&{forsuffixes}, or \&{forever}
15408 has just been scanned. (This code requires slight familiarity with
15409 expression-parsing routines that we have not yet discussed; but it seems
15410 to belong in the present part of the program, even though the original author
15411 didn't write it until later. The reader may wish to come back to it.)
15413 @c void mp_begin_iteration (MP mp) {
15414 halfword m; /* |expr_base| (\&{for}) or |suffix_base| (\&{forsuffixes}) */
15415 halfword n; /* hash address of the current symbol */
15416 pointer s; /* the new loop-control node */
15417 pointer p; /* substitution list for |scan_toks| */
15418 pointer q; /* link manipulation register */
15419 pointer pp; /* a new progression node */
15420 m=mp->cur_mod; n=mp->cur_sym; s=mp_get_node(mp, loop_node_size);
15421 if ( m==start_forever ){
15422 loop_type(s)=mp_void; p=null; mp_get_x_next(mp);
15424 mp_get_symbol(mp); p=mp_get_node(mp, token_node_size);
15425 info(p)=mp->cur_sym; value(p)=m;
15427 if ( mp->cur_cmd==within_token ) {
15428 @<Set up a picture iteration@>;
15430 @<Check for the |"="| or |":="| in a loop header@>;
15431 @<Scan the values to be used in the loop@>;
15434 @<Check for the presence of a colon@>;
15435 @<Scan the loop text and put it on the loop control stack@>;
15436 mp_resume_iteration(mp);
15439 @ @<Check for the |"="| or |":="| in a loop header@>=
15440 if ( (mp->cur_cmd!=equals)&&(mp->cur_cmd!=assignment) ) {
15441 mp_missing_err(mp, "=");
15443 help3("The next thing in this loop should have been `=' or `:='.")
15444 ("But don't worry; I'll pretend that an equals sign")
15445 ("was present, and I'll look for the values next.");
15449 @ @<Check for the presence of a colon@>=
15450 if ( mp->cur_cmd!=colon ) {
15451 mp_missing_err(mp, ":");
15453 help3("The next thing in this loop should have been a `:'.")
15454 ("So I'll pretend that a colon was present;")
15455 ("everything from here to `endfor' will be iterated.");
15459 @ We append a special |frozen_repeat_loop| token in place of the
15460 `\&{endfor}' at the end of the loop. This will come through \MP's scanner
15461 at the proper time to cause the loop to be repeated.
15463 (If the user tries some shenanigan like `\&{for} $\ldots$ \&{let} \&{endfor}',
15464 he will be foiled by the |get_symbol| routine, which keeps frozen
15465 tokens unchanged. Furthermore the |frozen_repeat_loop| is an \&{outer}
15466 token, so it won't be lost accidentally.)
15468 @ @<Scan the loop text...@>=
15469 q=mp_get_avail(mp); info(q)=frozen_repeat_loop;
15470 mp->scanner_status=loop_defining; mp->warning_info=n;
15471 info(s)=mp_scan_toks(mp, iteration,p,q,0); mp->scanner_status=normal;
15472 link(s)=mp->loop_ptr; mp->loop_ptr=s
15474 @ @<Initialize table...@>=
15475 eq_type(frozen_repeat_loop)=repeat_loop+outer_tag;
15476 text(frozen_repeat_loop)=intern(" ENDFOR");
15478 @ The loop text is inserted into \MP's scanning apparatus by the
15479 |resume_iteration| routine.
15481 @c void mp_resume_iteration (MP mp) {
15482 pointer p,q; /* link registers */
15483 p=loop_type(mp->loop_ptr);
15484 if ( p==progression_flag ) {
15485 p=loop_list(mp->loop_ptr); /* now |p| points to a progression node */
15486 mp->cur_exp=value(p);
15487 if ( @<The arithmetic progression has ended@> ) {
15488 mp_stop_iteration(mp);
15491 mp->cur_type=mp_known; q=mp_stash_cur_exp(mp); /* make |q| an \&{expr} argument */
15492 value(p)=mp->cur_exp+step_size(p); /* set |value(p)| for the next iteration */
15493 } else if ( p==null ) {
15494 p=loop_list(mp->loop_ptr);
15496 mp_stop_iteration(mp);
15499 loop_list(mp->loop_ptr)=link(p); q=info(p); free_avail(p);
15500 } else if ( p==mp_void ) {
15501 mp_begin_token_list(mp, info(mp->loop_ptr),forever_text); return;
15503 @<Make |q| a capsule containing the next picture component from
15504 |loop_list(loop_ptr)| or |goto not_found|@>;
15506 mp_begin_token_list(mp, info(mp->loop_ptr),loop_text);
15507 mp_stack_argument(mp, q);
15508 if ( mp->internal[mp_tracing_commands]>unity ) {
15509 @<Trace the start of a loop@>;
15513 mp_stop_iteration(mp);
15516 @ @<The arithmetic progression has ended@>=
15517 ((step_size(p)>0)&&(mp->cur_exp>final_value(p)))||
15518 ((step_size(p)<0)&&(mp->cur_exp<final_value(p)))
15520 @ @<Trace the start of a loop@>=
15522 mp_begin_diagnostic(mp); mp_print_nl(mp, "{loop value=");
15524 if ( (q!=null)&&(link(q)==mp_void) ) mp_print_exp(mp, q,1);
15525 else mp_show_token_list(mp, q,null,50,0);
15526 mp_print_char(mp, '}'); mp_end_diagnostic(mp, false);
15529 @ @<Make |q| a capsule containing the next picture component from...@>=
15530 { q=loop_list(mp->loop_ptr);
15531 if ( q==null ) goto NOT_FOUND;
15532 skip_component(q) goto NOT_FOUND;
15533 mp->cur_exp=mp_copy_objects(mp, loop_list(mp->loop_ptr),q);
15534 mp_init_bbox(mp, mp->cur_exp);
15535 mp->cur_type=mp_picture_type;
15536 loop_list(mp->loop_ptr)=q;
15537 q=mp_stash_cur_exp(mp);
15540 @ A level of loop control disappears when |resume_iteration| has decided
15541 not to resume, or when an \&{exitif} construction has removed the loop text
15542 from the input stack.
15544 @c void mp_stop_iteration (MP mp) {
15545 pointer p,q; /* the usual */
15546 p=loop_type(mp->loop_ptr);
15547 if ( p==progression_flag ) {
15548 mp_free_node(mp, loop_list(mp->loop_ptr),progression_node_size);
15549 } else if ( p==null ){
15550 q=loop_list(mp->loop_ptr);
15551 while ( q!=null ) {
15554 if ( link(p)==mp_void ) { /* it's an \&{expr} parameter */
15555 mp_recycle_value(mp, p); mp_free_node(mp, p,value_node_size);
15557 mp_flush_token_list(mp, p); /* it's a \&{suffix} or \&{text} parameter */
15560 p=q; q=link(q); free_avail(p);
15562 } else if ( p>progression_flag ) {
15563 delete_edge_ref(p);
15565 p=mp->loop_ptr; mp->loop_ptr=link(p); mp_flush_token_list(mp, info(p));
15566 mp_free_node(mp, p,loop_node_size);
15569 @ Now that we know all about loop control, we can finish up
15570 the missing portion of |begin_iteration| and we'll be done.
15572 The following code is performed after the `\.=' has been scanned in
15573 a \&{for} construction (if |m=expr_base|) or a \&{forsuffixes} construction
15574 (if |m=suffix_base|).
15576 @<Scan the values to be used in the loop@>=
15577 loop_type(s)=null; q=loop_list_loc(s); link(q)=null; /* |link(q)=loop_list(s)| */
15580 if ( m!=expr_base ) {
15581 mp_scan_suffix(mp);
15583 if ( mp->cur_cmd>=colon ) if ( mp->cur_cmd<=comma )
15585 mp_scan_expression(mp);
15586 if ( mp->cur_cmd==step_token ) if ( q==loop_list_loc(s) ) {
15587 @<Prepare for step-until construction and |break|@>;
15589 mp->cur_exp=mp_stash_cur_exp(mp);
15591 link(q)=mp_get_avail(mp); q=link(q);
15592 info(q)=mp->cur_exp; mp->cur_type=mp_vacuous;
15595 } while (mp->cur_cmd==comma)
15597 @ @<Prepare for step-until construction and |break|@>=
15599 if ( mp->cur_type!=mp_known ) mp_bad_for(mp, "initial value");
15600 pp=mp_get_node(mp, progression_node_size); value(pp)=mp->cur_exp;
15601 mp_get_x_next(mp); mp_scan_expression(mp);
15602 if ( mp->cur_type!=mp_known ) mp_bad_for(mp, "step size");
15603 step_size(pp)=mp->cur_exp;
15604 if ( mp->cur_cmd!=until_token ) {
15605 mp_missing_err(mp, "until");
15606 @.Missing `until'@>
15607 help2("I assume you meant to say `until' after `step'.")
15608 ("So I'll look for the final value and colon next.");
15611 mp_get_x_next(mp); mp_scan_expression(mp);
15612 if ( mp->cur_type!=mp_known ) mp_bad_for(mp, "final value");
15613 final_value(pp)=mp->cur_exp; loop_list(s)=pp;
15614 loop_type(s)=progression_flag;
15618 @ The last case is when we have just seen ``\&{within}'', and we need to
15619 parse a picture expression and prepare to iterate over it.
15621 @<Set up a picture iteration@>=
15622 { mp_get_x_next(mp);
15623 mp_scan_expression(mp);
15624 @<Make sure the current expression is a known picture@>;
15625 loop_type(s)=mp->cur_exp; mp->cur_type=mp_vacuous;
15626 q=link(dummy_loc(mp->cur_exp));
15628 if ( is_start_or_stop(q) )
15629 if ( mp_skip_1component(mp, q)==null ) q=link(q);
15633 @ @<Make sure the current expression is a known picture@>=
15634 if ( mp->cur_type!=mp_picture_type ) {
15635 mp_disp_err(mp, null,"Improper iteration spec has been replaced by nullpicture");
15636 help1("When you say `for x in p', p must be a known picture.");
15637 mp_put_get_flush_error(mp, mp_get_node(mp, edge_header_size));
15638 mp_init_edges(mp, mp->cur_exp); mp->cur_type=mp_picture_type;
15641 @* \[35] File names.
15642 It's time now to fret about file names. Besides the fact that different
15643 operating systems treat files in different ways, we must cope with the
15644 fact that completely different naming conventions are used by different
15645 groups of people. The following programs show what is required for one
15646 particular operating system; similar routines for other systems are not
15647 difficult to devise.
15648 @^system dependencies@>
15650 \MP\ assumes that a file name has three parts: the name proper; its
15651 ``extension''; and a ``file area'' where it is found in an external file
15652 system. The extension of an input file is assumed to be
15653 `\.{.mp}' unless otherwise specified; it is `\.{.log}' on the
15654 transcript file that records each run of \MP; it is `\.{.tfm}' on the font
15655 metric files that describe characters in any fonts created by \MP; it is
15656 `\.{.ps}' or `.{\it nnn}' for some number {\it nnn} on the \ps\ output files;
15657 and it is `\.{.mem}' on the mem files written by \.{INIMP} to initialize \MP.
15658 The file area can be arbitrary on input files, but files are usually
15659 output to the user's current area. If an input file cannot be
15660 found on the specified area, \MP\ will look for it on a special system
15661 area; this special area is intended for commonly used input files.
15663 Simple uses of \MP\ refer only to file names that have no explicit
15664 extension or area. For example, a person usually says `\.{input} \.{cmr10}'
15665 instead of `\.{input} \.{cmr10.new}'. Simple file
15666 names are best, because they make the \MP\ source files portable;
15667 whenever a file name consists entirely of letters and digits, it should be
15668 treated in the same way by all implementations of \MP. However, users
15669 need the ability to refer to other files in their environment, especially
15670 when responding to error messages concerning unopenable files; therefore
15671 we want to let them use the syntax that appears in their favorite
15674 @ \MP\ uses the same conventions that have proved to be satisfactory for
15675 \TeX\ and \MF. In order to isolate the system-dependent aspects of file names,
15676 @^system dependencies@>
15677 the system-independent parts of \MP\ are expressed in terms
15678 of three system-dependent
15679 procedures called |begin_name|, |more_name|, and |end_name|. In
15680 essence, if the user-specified characters of the file name are $c_1\ldots c_n$,
15681 the system-independent driver program does the operations
15682 $$|begin_name|;\,|more_name|(c_1);\,\ldots\,;|more_name|(c_n);
15684 These three procedures communicate with each other via global variables.
15685 Afterwards the file name will appear in the string pool as three strings
15686 called |cur_name|\penalty10000\hskip-.05em,
15687 |cur_area|, and |cur_ext|; the latter two are null (i.e.,
15688 |""|), unless they were explicitly specified by the user.
15690 Actually the situation is slightly more complicated, because \MP\ needs
15691 to know when the file name ends. The |more_name| routine is a function
15692 (with side effects) that returns |true| on the calls |more_name|$(c_1)$,
15693 \dots, |more_name|$(c_{n-1})$. The final call |more_name|$(c_n)$
15694 returns |false|; or, it returns |true| and $c_n$ is the last character
15695 on the current input line. In other words,
15696 |more_name| is supposed to return |true| unless it is sure that the
15697 file name has been completely scanned; and |end_name| is supposed to be able
15698 to finish the assembly of |cur_name|, |cur_area|, and |cur_ext| regardless of
15699 whether $|more_name|(c_n)$ returned |true| or |false|.
15702 char * cur_name; /* name of file just scanned */
15703 char * cur_area; /* file area just scanned, or \.{""} */
15704 char * cur_ext; /* file extension just scanned, or \.{""} */
15706 @ It is easier to maintain reference counts if we assign initial values.
15709 mp->cur_name=xstrdup("");
15710 mp->cur_area=xstrdup("");
15711 mp->cur_ext=xstrdup("");
15713 @ @<Dealloc variables@>=
15714 xfree(mp->cur_area);
15715 xfree(mp->cur_name);
15716 xfree(mp->cur_ext);
15718 @ The file names we shall deal with for illustrative purposes have the
15719 following structure: If the name contains `\.>' or `\.:', the file area
15720 consists of all characters up to and including the final such character;
15721 otherwise the file area is null. If the remaining file name contains
15722 `\..', the file extension consists of all such characters from the first
15723 remaining `\..' to the end, otherwise the file extension is null.
15724 @^system dependencies@>
15726 We can scan such file names easily by using two global variables that keep track
15727 of the occurrences of area and extension delimiters. Note that these variables
15728 cannot be of type |pool_pointer| because a string pool compaction could occur
15729 while scanning a file name.
15732 integer area_delimiter;
15733 /* most recent `\.>' or `\.:' relative to |str_start[str_ptr]| */
15734 integer ext_delimiter; /* the relevant `\..', if any */
15736 @ Here now is the first of the system-dependent routines for file name scanning.
15737 @^system dependencies@>
15739 @<Declare subroutines for parsing file names@>=
15740 void mp_begin_name (MP mp) {
15741 xfree(mp->cur_name);
15742 xfree(mp->cur_area);
15743 xfree(mp->cur_ext);
15744 mp->area_delimiter=-1;
15745 mp->ext_delimiter=-1;
15748 @ And here's the second.
15749 @^system dependencies@>
15751 @<Declare subroutines for parsing file names@>=
15752 boolean mp_more_name (MP mp, ASCII_code c) {
15756 if ( (c=='>')||(c==':') ) {
15757 mp->area_delimiter=mp->pool_ptr;
15758 mp->ext_delimiter=-1;
15759 } else if ( (c=='.')&&(mp->ext_delimiter<0) ) {
15760 mp->ext_delimiter=mp->pool_ptr;
15762 str_room(1); append_char(c); /* contribute |c| to the current string */
15768 @^system dependencies@>
15770 @d copy_pool_segment(A,B,C) {
15771 A = xmalloc(C+1,sizeof(char));
15772 strncpy(A,(char *)(mp->str_pool+B),C);
15775 @<Declare subroutines for parsing file names@>=
15776 void mp_end_name (MP mp) {
15777 pool_pointer s; /* length of area, name, and extension */
15780 s = mp->str_start[mp->str_ptr];
15781 if ( mp->area_delimiter<0 ) {
15782 mp->cur_area=xstrdup("");
15784 len = mp->area_delimiter-s;
15785 copy_pool_segment(mp->cur_area,s,len);
15788 if ( mp->ext_delimiter<0 ) {
15789 mp->cur_ext=xstrdup("");
15790 len = mp->pool_ptr-s;
15792 copy_pool_segment(mp->cur_ext,mp->ext_delimiter,(mp->pool_ptr-mp->ext_delimiter));
15793 len = mp->ext_delimiter-s;
15795 copy_pool_segment(mp->cur_name,s,len);
15796 mp->pool_ptr=s; /* don't need this partial string */
15799 @ Conversely, here is a routine that takes three strings and prints a file
15800 name that might have produced them. (The routine is system dependent, because
15801 some operating systems put the file area last instead of first.)
15802 @^system dependencies@>
15804 @<Basic printing...@>=
15805 void mp_print_file_name (MP mp, char * n, char * a, char * e) {
15806 mp_print(mp, a); mp_print(mp, n); mp_print(mp, e);
15809 @ Another system-dependent routine is needed to convert three internal
15811 to the |name_of_file| value that is used to open files. The present code
15812 allows both lowercase and uppercase letters in the file name.
15813 @^system dependencies@>
15815 @d append_to_name(A) { c=(A);
15816 if ( k<file_name_size ) {
15817 mp->name_of_file[k]=xchr(c);
15822 @<Declare subroutines for parsing file names@>=
15823 void mp_pack_file_name (MP mp, char *n, char *a, char *e) {
15824 integer k; /* number of positions filled in |name_of_file| */
15825 ASCII_code c; /* character being packed */
15826 char *j; /* a character index */
15830 for (j=a;*j;j++) { append_to_name(*j); }
15832 for (j=n;*j;j++) { append_to_name(*j); }
15834 for (j=e;*j;j++) { append_to_name(*j); }
15836 mp->name_of_file[k]=0;
15840 @ @<Internal library declarations@>=
15841 void mp_pack_file_name (MP mp, char *n, char *a, char *e) ;
15843 @ A messier routine is also needed, since mem file names must be scanned
15844 before \MP's string mechanism has been initialized. We shall use the
15845 global variable |MP_mem_default| to supply the text for default system areas
15846 and extensions related to mem files.
15847 @^system dependencies@>
15849 @d mem_default_length 9 /* length of the |MP_mem_default| string */
15850 @d mem_ext_length 4 /* length of its `\.{.mem}' part */
15851 @d mem_extension ".mem" /* the extension, as a \.{WEB} constant */
15854 char *MP_mem_default;
15856 @ @<Option variables@>=
15857 char *mem_name; /* for commandline */
15859 @ @<Allocate or initialize ...@>=
15860 mp->MP_mem_default = xstrdup("plain.mem");
15861 mp->mem_name = xstrdup(opt->mem_name);
15863 @^system dependencies@>
15865 @ @<Dealloc variables@>=
15866 xfree(mp->MP_mem_default);
15867 xfree(mp->mem_name);
15869 @ @<Check the ``constant'' values for consistency@>=
15870 if ( mem_default_length>file_name_size ) mp->bad=20;
15872 @ Here is the messy routine that was just mentioned. It sets |name_of_file|
15873 from the first |n| characters of |MP_mem_default|, followed by
15874 |buffer[a..b-1]|, followed by the last |mem_ext_length| characters of
15877 We dare not give error messages here, since \MP\ calls this routine before
15878 the |error| routine is ready to roll. Instead, we simply drop excess characters,
15879 since the error will be detected in another way when a strange file name
15881 @^system dependencies@>
15883 @c void mp_pack_buffered_name (MP mp,small_number n, integer a,
15885 integer k; /* number of positions filled in |name_of_file| */
15886 ASCII_code c; /* character being packed */
15887 integer j; /* index into |buffer| or |MP_mem_default| */
15888 if ( n+b-a+1+mem_ext_length>file_name_size )
15889 b=a+file_name_size-n-1-mem_ext_length;
15891 for (j=0;j<n;j++) {
15892 append_to_name(xord((int)mp->MP_mem_default[j]));
15894 for (j=a;j<b;j++) {
15895 append_to_name(mp->buffer[j]);
15897 for (j=mem_default_length-mem_ext_length;
15898 j<mem_default_length;j++) {
15899 append_to_name(xord((int)mp->MP_mem_default[j]));
15901 mp->name_of_file[k]=0;
15905 @ Here is the only place we use |pack_buffered_name|. This part of the program
15906 becomes active when a ``virgin'' \MP\ is trying to get going, just after
15907 the preliminary initialization, or when the user is substituting another
15908 mem file by typing `\.\&' after the initial `\.{**}' prompt. The buffer
15909 contains the first line of input in |buffer[loc..(last-1)]|, where
15910 |loc<last| and |buffer[loc]<>" "|.
15913 boolean mp_open_mem_file (MP mp) ;
15916 boolean mp_open_mem_file (MP mp) {
15917 int j; /* the first space after the file name */
15918 if (mp->mem_name!=NULL) {
15919 mp->mem_file = (mp->open_file)(mp,mp->mem_name, "rb", mp_filetype_memfile);
15920 if ( mp->mem_file ) return true;
15923 if ( mp->buffer[loc]=='&' ) {
15924 incr(loc); j=loc; mp->buffer[mp->last]=' ';
15925 while ( mp->buffer[j]!=' ' ) incr(j);
15926 mp_pack_buffered_name(mp, 0,loc,j); /* try first without the system file area */
15927 if ( mp_w_open_in(mp, &mp->mem_file) ) goto FOUND;
15929 wterm_ln("Sorry, I can\'t find that mem file; will try PLAIN.");
15930 @.Sorry, I can't find...@>
15933 /* now pull out all the stops: try for the system \.{plain} file */
15934 mp_pack_buffered_name(mp, mem_default_length-mem_ext_length,0,0);
15935 if ( ! mp_w_open_in(mp, &mp->mem_file) ) {
15937 wterm_ln("I can\'t find the PLAIN mem file!\n");
15938 @.I can't find PLAIN...@>
15943 loc=j; return true;
15946 @ Operating systems often make it possible to determine the exact name (and
15947 possible version number) of a file that has been opened. The following routine,
15948 which simply makes a \MP\ string from the value of |name_of_file|, should
15949 ideally be changed to deduce the full name of file~|f|, which is the file
15950 most recently opened, if it is possible to do this.
15951 @^system dependencies@>
15954 #define mp_a_make_name_string(A,B) mp_make_name_string(A)
15955 #define mp_b_make_name_string(A,B) mp_make_name_string(A)
15956 #define mp_w_make_name_string(A,B) mp_make_name_string(A)
15959 str_number mp_make_name_string (MP mp) {
15960 int k; /* index into |name_of_file| */
15961 str_room(mp->name_length);
15962 for (k=0;k<mp->name_length;k++) {
15963 append_char(xord((int)mp->name_of_file[k]));
15965 return mp_make_string(mp);
15968 @ Now let's consider the ``driver''
15969 routines by which \MP\ deals with file names
15970 in a system-independent manner. First comes a procedure that looks for a
15971 file name in the input by taking the information from the input buffer.
15972 (We can't use |get_next|, because the conversion to tokens would
15973 destroy necessary information.)
15975 This procedure doesn't allow semicolons or percent signs to be part of
15976 file names, because of other conventions of \MP.
15977 {\sl The {\logos METAFONT\/}book} doesn't
15978 use semicolons or percents immediately after file names, but some users
15979 no doubt will find it natural to do so; therefore system-dependent
15980 changes to allow such characters in file names should probably
15981 be made with reluctance, and only when an entire file name that
15982 includes special characters is ``quoted'' somehow.
15983 @^system dependencies@>
15985 @c void mp_scan_file_name (MP mp) {
15987 while ( mp->buffer[loc]==' ' ) incr(loc);
15989 if ( (mp->buffer[loc]==';')||(mp->buffer[loc]=='%') ) break;
15990 if ( ! mp_more_name(mp, mp->buffer[loc]) ) break;
15996 @ Here is another version that takes its input from a string.
15998 @<Declare subroutines for parsing file names@>=
15999 void mp_str_scan_file (MP mp, str_number s) {
16000 pool_pointer p,q; /* current position and stopping point */
16002 p=mp->str_start[s]; q=str_stop(s);
16004 if ( ! mp_more_name(mp, mp->str_pool[p]) ) break;
16010 @ And one that reads from a |char*|.
16012 @<Declare subroutines for parsing file names@>=
16013 void mp_ptr_scan_file (MP mp, char *s) {
16014 char *p, *q; /* current position and stopping point */
16016 p=s; q=p+strlen(s);
16018 if ( ! mp_more_name(mp, *p)) break;
16025 @ The global variable |job_name| contains the file name that was first
16026 \&{input} by the user. This name is extended by `\.{.log}' and `\.{ps}' and
16027 `\.{.mem}' and `\.{.tfm}' in order to make the names of \MP's output files.
16030 boolean log_opened; /* has the transcript file been opened? */
16031 char *log_name; /* full name of the log file */
16033 @ @<Option variables@>=
16034 char *job_name; /* principal file name */
16036 @ Initially |job_name=NULL|; it becomes nonzero as soon as the true name is known.
16037 We have |job_name=NULL| if and only if the `\.{log}' file has not been opened,
16038 except of course for a short time just after |job_name| has become nonzero.
16040 @<Allocate or ...@>=
16041 mp->job_name=opt->job_name;
16042 mp->log_opened=false;
16044 @ @<Dealloc variables@>=
16045 xfree(mp->job_name);
16047 @ Here is a routine that manufactures the output file names, assuming that
16048 |job_name<>0|. It ignores and changes the current settings of |cur_area|
16051 @d pack_cur_name mp_pack_file_name(mp, mp->cur_name,mp->cur_area,mp->cur_ext)
16054 void mp_pack_job_name (MP mp, char *s) ;
16056 @ @c void mp_pack_job_name (MP mp, char *s) { /* |s = ".log"|, |".mem"|, |".ps"|, or .\\{nnn} */
16057 xfree(mp->cur_name); mp->cur_name=xstrdup(mp->job_name);
16058 xfree(mp->cur_area); mp->cur_area=xstrdup("");
16059 xfree(mp->cur_ext); mp->cur_ext=xstrdup(s);
16063 @ If some trouble arises when \MP\ tries to open a file, the following
16064 routine calls upon the user to supply another file name. Parameter~|s|
16065 is used in the error message to identify the type of file; parameter~|e|
16066 is the default extension if none is given. Upon exit from the routine,
16067 variables |cur_name|, |cur_area|, |cur_ext|, and |name_of_file| are
16068 ready for another attempt at file opening.
16071 void mp_prompt_file_name (MP mp,char * s, char * e) ;
16073 @ @c void mp_prompt_file_name (MP mp,char * s, char * e) {
16074 size_t k; /* index into |buffer| */
16075 char * saved_cur_name;
16076 if ( mp->interaction==mp_scroll_mode )
16078 if (strcmp(s,"input file name")==0) {
16079 print_err("I can\'t find file `");
16080 @.I can't find file x@>
16082 print_err("I can\'t write on file `");
16084 @.I can't write on file x@>
16085 mp_print_file_name(mp, mp->cur_name,mp->cur_area,mp->cur_ext);
16086 mp_print(mp, "'.");
16087 if (strcmp(e,"")==0)
16088 mp_show_context(mp);
16089 mp_print_nl(mp, "Please type another "); mp_print(mp, s);
16091 if ( mp->interaction<mp_scroll_mode )
16092 mp_fatal_error(mp, "*** (job aborted, file error in nonstop mode)");
16093 @.job aborted, file error...@>
16094 saved_cur_name = xstrdup(mp->cur_name);
16095 clear_terminal; prompt_input(": "); @<Scan file name in the buffer@>;
16096 if (strcmp(mp->cur_ext,"")==0)
16098 if (strlen(mp->cur_name)==0) {
16099 mp->cur_name=saved_cur_name;
16101 xfree(saved_cur_name);
16106 @ @<Scan file name in the buffer@>=
16108 mp_begin_name(mp); k=mp->first;
16109 while ( (mp->buffer[k]==' ')&&(k<mp->last) ) incr(k);
16111 if ( k==mp->last ) break;
16112 if ( ! mp_more_name(mp, mp->buffer[k]) ) break;
16118 @ The |open_log_file| routine is used to open the transcript file and to help
16119 it catch up to what has previously been printed on the terminal.
16121 @c void mp_open_log_file (MP mp) {
16122 int old_setting; /* previous |selector| setting */
16123 int k; /* index into |months| and |buffer| */
16124 int l; /* end of first input line */
16125 integer m; /* the current month */
16126 char *months="JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
16127 /* abbreviations of month names */
16128 old_setting=mp->selector;
16129 if ( mp->job_name==NULL ) {
16130 mp->job_name=xstrdup("mpout");
16132 mp_pack_job_name(mp,".log");
16133 while ( ! mp_a_open_out(mp, &mp->log_file, mp_filetype_log) ) {
16134 @<Try to get a different log file name@>;
16136 mp->log_name=xstrdup(mp->name_of_file);
16137 mp->selector=log_only; mp->log_opened=true;
16138 @<Print the banner line, including the date and time@>;
16139 mp->input_stack[mp->input_ptr]=mp->cur_input;
16140 /* make sure bottom level is in memory */
16142 if (!mp->noninteractive) {
16143 mp_print_nl(mp, "**");
16144 l=mp->input_stack[0].limit_field-1; /* last position of first line */
16145 for (k=0;k<=l;k++) mp_print_str(mp, mp->buffer[k]);
16146 mp_print_ln(mp); /* now the transcript file contains the first line of input */
16148 mp->selector=old_setting+2; /* |log_only| or |term_and_log| */
16151 @ @<Dealloc variables@>=
16152 xfree(mp->log_name);
16154 @ Sometimes |open_log_file| is called at awkward moments when \MP\ is
16155 unable to print error messages or even to |show_context|.
16156 The |prompt_file_name| routine can result in a |fatal_error|, but the |error|
16157 routine will not be invoked because |log_opened| will be false.
16159 The normal idea of |mp_batch_mode| is that nothing at all should be written
16160 on the terminal. However, in the unusual case that
16161 no log file could be opened, we make an exception and allow
16162 an explanatory message to be seen.
16164 Incidentally, the program always refers to the log file as a `\.{transcript
16165 file}', because some systems cannot use the extension `\.{.log}' for
16168 @<Try to get a different log file name@>=
16170 mp->selector=term_only;
16171 mp_prompt_file_name(mp, "transcript file name",".log");
16174 @ @<Print the banner...@>=
16177 mp_print(mp, mp->mem_ident); mp_print(mp, " ");
16178 mp_print_int(mp, mp_round_unscaled(mp, mp->internal[mp_day]));
16179 mp_print_char(mp, ' ');
16180 m=mp_round_unscaled(mp, mp->internal[mp_month]);
16181 for (k=3*m-3;k<3*m;k++) { wlog_chr(months[k]); }
16182 mp_print_char(mp, ' ');
16183 mp_print_int(mp, mp_round_unscaled(mp, mp->internal[mp_year]));
16184 mp_print_char(mp, ' ');
16185 m=mp_round_unscaled(mp, mp->internal[mp_time]);
16186 mp_print_dd(mp, m / 60); mp_print_char(mp, ':'); mp_print_dd(mp, m % 60);
16189 @ The |try_extension| function tries to open an input file determined by
16190 |cur_name|, |cur_area|, and the argument |ext|. It returns |false| if it
16191 can't find the file in |cur_area| or the appropriate system area.
16193 @c boolean mp_try_extension (MP mp,char *ext) {
16194 mp_pack_file_name(mp, mp->cur_name,mp->cur_area, ext);
16195 in_name=xstrdup(mp->cur_name);
16196 in_area=xstrdup(mp->cur_area);
16197 if ( mp_a_open_in(mp, &cur_file, mp_filetype_program) ) {
16200 mp_pack_file_name(mp, mp->cur_name,NULL,ext);
16201 return mp_a_open_in(mp, &cur_file, mp_filetype_program);
16206 @ Let's turn now to the procedure that is used to initiate file reading
16207 when an `\.{input}' command is being processed.
16209 @c void mp_start_input (MP mp) { /* \MP\ will \.{input} something */
16210 char *fname = NULL;
16211 @<Put the desired file name in |(cur_name,cur_ext,cur_area)|@>;
16213 mp_begin_file_reading(mp); /* set up |cur_file| and new level of input */
16214 if ( strlen(mp->cur_ext)==0 ) {
16215 if ( mp_try_extension(mp, ".mp") ) break;
16216 else if ( mp_try_extension(mp, "") ) break;
16217 else if ( mp_try_extension(mp, ".mf") ) break;
16218 /* |else do_nothing; | */
16219 } else if ( mp_try_extension(mp, mp->cur_ext) ) {
16222 mp_end_file_reading(mp); /* remove the level that didn't work */
16223 mp_prompt_file_name(mp, "input file name","");
16225 name=mp_a_make_name_string(mp, cur_file);
16226 fname = xstrdup(mp->name_of_file);
16227 if ( mp->job_name==NULL ) {
16228 mp->job_name=xstrdup(mp->cur_name);
16229 mp_open_log_file(mp);
16230 } /* |open_log_file| doesn't |show_context|, so |limit|
16231 and |loc| needn't be set to meaningful values yet */
16232 if ( ((int)mp->term_offset+(int)strlen(fname)) > (mp->max_print_line-2)) mp_print_ln(mp);
16233 else if ( (mp->term_offset>0)||(mp->file_offset>0) ) mp_print_char(mp, ' ');
16234 mp_print_char(mp, '('); incr(mp->open_parens); mp_print(mp, fname);
16237 @<Flush |name| and replace it with |cur_name| if it won't be needed@>;
16238 @<Read the first line of the new file@>;
16241 @ This code should be omitted if |a_make_name_string| returns something other
16242 than just a copy of its argument and the full file name is needed for opening
16243 \.{MPX} files or implementing the switch-to-editor option.
16244 @^system dependencies@>
16246 @<Flush |name| and replace it with |cur_name| if it won't be needed@>=
16247 mp_flush_string(mp, name); name=rts(mp->cur_name); xfree(mp->cur_name)
16249 @ If the file is empty, it is considered to contain a single blank line,
16250 so there is no need to test the return value.
16252 @<Read the first line...@>=
16255 (void)mp_input_ln(mp, cur_file );
16256 mp_firm_up_the_line(mp);
16257 mp->buffer[limit]='%'; mp->first=limit+1; loc=start;
16260 @ @<Put the desired file name in |(cur_name,cur_ext,cur_area)|@>=
16261 while ( token_state &&(loc==null) ) mp_end_token_list(mp);
16262 if ( token_state ) {
16263 print_err("File names can't appear within macros");
16264 @.File names can't...@>
16265 help3("Sorry...I've converted what follows to tokens,")
16266 ("possibly garbaging the name you gave.")
16267 ("Please delete the tokens and insert the name again.");
16270 if ( file_state ) {
16271 mp_scan_file_name(mp);
16273 xfree(mp->cur_name); mp->cur_name=xstrdup("");
16274 xfree(mp->cur_ext); mp->cur_ext =xstrdup("");
16275 xfree(mp->cur_area); mp->cur_area=xstrdup("");
16278 @ The following simple routine starts reading the \.{MPX} file associated
16279 with the current input file.
16281 @c void mp_start_mpx_input (MP mp) {
16282 char *origname = NULL; /* a copy of nameoffile */
16283 mp_pack_file_name(mp, in_name, in_area, ".mpx");
16284 @<Try to make sure |name_of_file| refers to a valid \.{MPX} file and
16285 |goto not_found| if there is a problem@>;
16286 mp_begin_file_reading(mp);
16287 if ( ! mp_a_open_in(mp, &cur_file, mp_filetype_program) ) {
16288 mp_end_file_reading(mp);
16291 name=mp_a_make_name_string(mp, cur_file);
16292 mp->mpx_name[index]=name; add_str_ref(name);
16293 @<Read the first line of the new file@>;
16296 @<Explain that the \.{MPX} file can't be read and |succumb|@>;
16300 @ This should ideally be changed to do whatever is necessary to create the
16301 \.{MPX} file given by |name_of_file| if it does not exist or if it is out
16302 of date. This requires invoking \.{MPtoTeX} on the |origname| and passing
16303 the results through \TeX\ and \.{DVItoMP}. (It is possible to use a
16304 completely different typesetting program if suitable postprocessor is
16305 available to perform the function of \.{DVItoMP}.)
16306 @^system dependencies@>
16308 @ @<Exported types@>=
16309 typedef int (*mp_run_make_mpx_command)(MP mp, char *origname, char *mtxname);
16311 @ @<Option variables@>=
16312 mp_run_make_mpx_command run_make_mpx;
16314 @ @<Allocate or initialize ...@>=
16315 set_callback_option(run_make_mpx);
16317 @ @<Internal library declarations@>=
16318 int mp_run_make_mpx (MP mp, char *origname, char *mtxname);
16320 @ The default does nothing.
16322 int mp_run_make_mpx (MP mp, char *origname, char *mtxname) {
16323 if (mp && origname && mtxname) /* for -W */
16328 @ @<Try to make sure |name_of_file| refers to a valid \.{MPX} file and
16329 |goto not_found| if there is a problem@>=
16330 origname = mp_xstrdup(mp,mp->name_of_file);
16331 *(origname+strlen(origname)-1)=0; /* drop the x */
16332 if (!(mp->run_make_mpx)(mp, origname, mp->name_of_file))
16335 @ @<Explain that the \.{MPX} file can't be read and |succumb|@>=
16336 if ( mp->interaction==mp_error_stop_mode ) wake_up_terminal;
16337 mp_print_nl(mp, ">> ");
16338 mp_print(mp, origname);
16339 mp_print_nl(mp, ">> ");
16340 mp_print(mp, mp->name_of_file);
16341 mp_print_nl(mp, "! Unable to make mpx file");
16342 help4("The two files given above are one of your source files")
16343 ("and an auxiliary file I need to read to find out what your")
16344 ("btex..etex blocks mean. If you don't know why I had trouble,")
16345 ("try running it manually through MPtoTeX, TeX, and DVItoMP");
16348 @ The last file-opening commands are for files accessed via the \&{readfrom}
16349 @:read_from_}{\&{readfrom} primitive@>
16350 operator and the \&{write} command. Such files are stored in separate arrays.
16351 @:write_}{\&{write} primitive@>
16353 @<Types in the outer block@>=
16354 typedef unsigned int readf_index; /* |0..max_read_files| */
16355 typedef unsigned int write_index; /* |0..max_write_files| */
16358 readf_index max_read_files; /* maximum number of simultaneously open \&{readfrom} files */
16359 void ** rd_file; /* \&{readfrom} files */
16360 char ** rd_fname; /* corresponding file name or 0 if file not open */
16361 readf_index read_files; /* number of valid entries in the above arrays */
16362 write_index max_write_files; /* maximum number of simultaneously open \&{write} */
16363 void ** wr_file; /* \&{write} files */
16364 char ** wr_fname; /* corresponding file name or 0 if file not open */
16365 write_index write_files; /* number of valid entries in the above arrays */
16367 @ @<Allocate or initialize ...@>=
16368 mp->max_read_files=8;
16369 mp->rd_file = xmalloc((mp->max_read_files+1),sizeof(void *));
16370 mp->rd_fname = xmalloc((mp->max_read_files+1),sizeof(char *));
16371 memset(mp->rd_fname, 0, sizeof(char *)*(mp->max_read_files+1));
16373 mp->max_write_files=8;
16374 mp->wr_file = xmalloc((mp->max_write_files+1),sizeof(void *));
16375 mp->wr_fname = xmalloc((mp->max_write_files+1),sizeof(char *));
16376 memset(mp->wr_fname, 0, sizeof(char *)*(mp->max_write_files+1));
16380 @ This routine starts reading the file named by string~|s| without setting
16381 |loc|, |limit|, or |name|. It returns |false| if the file is empty or cannot
16382 be opened. Otherwise it updates |rd_file[n]| and |rd_fname[n]|.
16384 @c boolean mp_start_read_input (MP mp,char *s, readf_index n) {
16385 mp_ptr_scan_file(mp, s);
16387 mp_begin_file_reading(mp);
16388 if ( ! mp_a_open_in(mp, &mp->rd_file[n], (mp_filetype_text+n)) )
16390 if ( ! mp_input_ln(mp, mp->rd_file[n] ) ) {
16391 (mp->close_file)(mp,mp->rd_file[n]);
16394 mp->rd_fname[n]=xstrdup(mp->name_of_file);
16397 mp_end_file_reading(mp);
16401 @ Open |wr_file[n]| using file name~|s| and update |wr_fname[n]|.
16404 void mp_open_write_file (MP mp, char *s, readf_index n) ;
16406 @ @c void mp_open_write_file (MP mp,char *s, readf_index n) {
16407 mp_ptr_scan_file(mp, s);
16409 while ( ! mp_a_open_out(mp, &mp->wr_file[n], (mp_filetype_text+n)) )
16410 mp_prompt_file_name(mp, "file name for write output","");
16411 mp->wr_fname[n]=xstrdup(mp->name_of_file);
16415 @* \[36] Introduction to the parsing routines.
16416 We come now to the central nervous system that sparks many of \MP's activities.
16417 By evaluating expressions, from their primary constituents to ever larger
16418 subexpressions, \MP\ builds the structures that ultimately define complete
16419 pictures or fonts of type.
16421 Four mutually recursive subroutines are involved in this process: We call them
16422 $$\hbox{|scan_primary|, |scan_secondary|, |scan_tertiary|,
16423 and |scan_expression|.}$$
16425 Each of them is parameterless and begins with the first token to be scanned
16426 already represented in |cur_cmd|, |cur_mod|, and |cur_sym|. After execution,
16427 the value of the primary or secondary or tertiary or expression that was
16428 found will appear in the global variables |cur_type| and |cur_exp|. The
16429 token following the expression will be represented in |cur_cmd|, |cur_mod|,
16432 Technically speaking, the parsing algorithms are ``LL(1),'' more or less;
16433 backup mechanisms have been added in order to provide reasonable error
16437 small_number cur_type; /* the type of the expression just found */
16438 integer cur_exp; /* the value of the expression just found */
16443 @ Many different kinds of expressions are possible, so it is wise to have
16444 precise descriptions of what |cur_type| and |cur_exp| mean in all cases:
16447 |cur_type=mp_vacuous| means that this expression didn't turn out to have a
16448 value at all, because it arose from a \&{begingroup}$\,\ldots\,$\&{endgroup}
16449 construction in which there was no expression before the \&{endgroup}.
16450 In this case |cur_exp| has some irrelevant value.
16453 |cur_type=mp_boolean_type| means that |cur_exp| is either |true_code|
16457 |cur_type=mp_unknown_boolean| means that |cur_exp| points to a capsule
16458 node that is in the ring of variables equivalent
16459 to at least one undefined boolean variable.
16462 |cur_type=mp_string_type| means that |cur_exp| is a string number (i.e., an
16463 integer in the range |0<=cur_exp<str_ptr|). That string's reference count
16464 includes this particular reference.
16467 |cur_type=mp_unknown_string| means that |cur_exp| points to a capsule
16468 node that is in the ring of variables equivalent
16469 to at least one undefined string variable.
16472 |cur_type=mp_pen_type| means that |cur_exp| points to a node in a pen. Nobody
16473 else points to any of the nodes in this pen. The pen may be polygonal or
16477 |cur_type=mp_unknown_pen| means that |cur_exp| points to a capsule
16478 node that is in the ring of variables equivalent
16479 to at least one undefined pen variable.
16482 |cur_type=mp_path_type| means that |cur_exp| points to a the first node of
16483 a path; nobody else points to this particular path. The control points of
16484 the path will have been chosen.
16487 |cur_type=mp_unknown_path| means that |cur_exp| points to a capsule
16488 node that is in the ring of variables equivalent
16489 to at least one undefined path variable.
16492 |cur_type=mp_picture_type| means that |cur_exp| points to an edge header node.
16493 There may be other pointers to this particular set of edges. The header node
16494 contains a reference count that includes this particular reference.
16497 |cur_type=mp_unknown_picture| means that |cur_exp| points to a capsule
16498 node that is in the ring of variables equivalent
16499 to at least one undefined picture variable.
16502 |cur_type=mp_transform_type| means that |cur_exp| points to a |mp_transform_type|
16503 capsule node. The |value| part of this capsule
16504 points to a transform node that contains six numeric values,
16505 each of which is |independent|, |dependent|, |mp_proto_dependent|, or |known|.
16508 |cur_type=mp_color_type| means that |cur_exp| points to a |color_type|
16509 capsule node. The |value| part of this capsule
16510 points to a color node that contains three numeric values,
16511 each of which is |independent|, |dependent|, |mp_proto_dependent|, or |known|.
16514 |cur_type=mp_cmykcolor_type| means that |cur_exp| points to a |mp_cmykcolor_type|
16515 capsule node. The |value| part of this capsule
16516 points to a color node that contains four numeric values,
16517 each of which is |independent|, |dependent|, |mp_proto_dependent|, or |known|.
16520 |cur_type=mp_pair_type| means that |cur_exp| points to a capsule
16521 node whose type is |mp_pair_type|. The |value| part of this capsule
16522 points to a pair node that contains two numeric values,
16523 each of which is |independent|, |dependent|, |mp_proto_dependent|, or |known|.
16526 |cur_type=mp_known| means that |cur_exp| is a |scaled| value.
16529 |cur_type=mp_dependent| means that |cur_exp| points to a capsule node whose type
16530 is |dependent|. The |dep_list| field in this capsule points to the associated
16534 |cur_type=mp_proto_dependent| means that |cur_exp| points to a |mp_proto_dependent|
16535 capsule node. The |dep_list| field in this capsule
16536 points to the associated dependency list.
16539 |cur_type=independent| means that |cur_exp| points to a capsule node
16540 whose type is |independent|. This somewhat unusual case can arise, for
16541 example, in the expression
16542 `$x+\&{begingroup}\penalty0\,\&{string}\,x; 0\,\&{endgroup}$'.
16545 |cur_type=mp_token_list| means that |cur_exp| points to a linked list of
16546 tokens. This case arises only on the left-hand side of an assignment
16547 (`\.{:=}') operation, under very special circumstances.
16549 \smallskip\noindent
16550 The possible settings of |cur_type| have been listed here in increasing
16551 numerical order. Notice that |cur_type| will never be |mp_numeric_type| or
16552 |suffixed_macro| or |mp_unsuffixed_macro|, although variables of those types
16553 are allowed. Conversely, \MP\ has no variables of type |mp_vacuous| or
16556 @ Capsules are two-word nodes that have a similar meaning
16557 to |cur_type| and |cur_exp|. Such nodes have |name_type=capsule|
16558 and |link<=mp_void|; and their |type| field is one of the possibilities for
16559 |cur_type| listed above.
16561 The |value| field of a capsule is, in most cases, the value that
16562 corresponds to its |type|, as |cur_exp| corresponds to |cur_type|.
16563 However, when |cur_exp| would point to a capsule,
16564 no extra layer of indirection is present; the |value|
16565 field is what would have been called |value(cur_exp)| if it had not been
16566 encapsulated. Furthermore, if the type is |dependent| or
16567 |mp_proto_dependent|, the |value| field of a capsule is replaced by
16568 |dep_list| and |prev_dep| fields, since dependency lists in capsules are
16569 always part of the general |dep_list| structure.
16571 The |get_x_next| routine is careful not to change the values of |cur_type|
16572 and |cur_exp| when it gets an expanded token. However, |get_x_next| might
16573 call a macro, which might parse an expression, which might execute lots of
16574 commands in a group; hence it's possible that |cur_type| might change
16575 from, say, |mp_unknown_boolean| to |mp_boolean_type|, or from |dependent| to
16576 |known| or |independent|, during the time |get_x_next| is called. The
16577 programs below are careful to stash sensitive intermediate results in
16578 capsules, so that \MP's generality doesn't cause trouble.
16580 Here's a procedure that illustrates these conventions. It takes
16581 the contents of $(|cur_type|\kern-.3pt,|cur_exp|\kern-.3pt)$
16582 and stashes them away in a
16583 capsule. It is not used when |cur_type=mp_token_list|.
16584 After the operation, |cur_type=mp_vacuous|; hence there is no need to
16585 copy path lists or to update reference counts, etc.
16587 The special link |mp_void| is put on the capsule returned by
16588 |stash_cur_exp|, because this procedure is used to store macro parameters
16589 that must be easily distinguishable from token lists.
16591 @<Declare the stashing/unstashing routines@>=
16592 pointer mp_stash_cur_exp (MP mp) {
16593 pointer p; /* the capsule that will be returned */
16594 switch (mp->cur_type) {
16595 case unknown_types:
16596 case mp_transform_type:
16597 case mp_color_type:
16600 case mp_proto_dependent:
16601 case mp_independent:
16602 case mp_cmykcolor_type:
16606 p=mp_get_node(mp, value_node_size); name_type(p)=mp_capsule;
16607 type(p)=mp->cur_type; value(p)=mp->cur_exp;
16610 mp->cur_type=mp_vacuous; link(p)=mp_void;
16614 @ The inverse of |stash_cur_exp| is the following procedure, which
16615 deletes an unnecessary capsule and puts its contents into |cur_type|
16618 The program steps of \MP\ can be divided into two categories: those in
16619 which |cur_type| and |cur_exp| are ``alive'' and those in which they are
16620 ``dead,'' in the sense that |cur_type| and |cur_exp| contain relevant
16621 information or not. It's important not to ignore them when they're alive,
16622 and it's important not to pay attention to them when they're dead.
16624 There's also an intermediate category: If |cur_type=mp_vacuous|, then
16625 |cur_exp| is irrelevant, hence we can proceed without caring if |cur_type|
16626 and |cur_exp| are alive or dead. In such cases we say that |cur_type|
16627 and |cur_exp| are {\sl dormant}. It is permissible to call |get_x_next|
16628 only when they are alive or dormant.
16630 The \\{stash} procedure above assumes that |cur_type| and |cur_exp|
16631 are alive or dormant. The \\{unstash} procedure assumes that they are
16632 dead or dormant; it resuscitates them.
16634 @<Declare the stashing/unstashing...@>=
16635 void mp_unstash_cur_exp (MP mp,pointer p) ;
16638 void mp_unstash_cur_exp (MP mp,pointer p) {
16639 mp->cur_type=type(p);
16640 switch (mp->cur_type) {
16641 case unknown_types:
16642 case mp_transform_type:
16643 case mp_color_type:
16646 case mp_proto_dependent:
16647 case mp_independent:
16648 case mp_cmykcolor_type:
16652 mp->cur_exp=value(p);
16653 mp_free_node(mp, p,value_node_size);
16658 @ The following procedure prints the values of expressions in an
16659 abbreviated format. If its first parameter |p| is null, the value of
16660 |(cur_type,cur_exp)| is displayed; otherwise |p| should be a capsule
16661 containing the desired value. The second parameter controls the amount of
16662 output. If it is~0, dependency lists will be abbreviated to
16663 `\.{linearform}' unless they consist of a single term. If it is greater
16664 than~1, complicated structures (pens, pictures, and paths) will be displayed
16667 @<Declare subroutines for printing expressions@>=
16668 @<Declare the procedure called |print_dp|@>;
16669 @<Declare the stashing/unstashing routines@>;
16670 void mp_print_exp (MP mp,pointer p, small_number verbosity) {
16671 boolean restore_cur_exp; /* should |cur_exp| be restored? */
16672 small_number t; /* the type of the expression */
16673 pointer q; /* a big node being displayed */
16674 integer v=0; /* the value of the expression */
16676 restore_cur_exp=false;
16678 p=mp_stash_cur_exp(mp); restore_cur_exp=true;
16681 if ( t<mp_dependent ) v=value(p); else if ( t<mp_independent ) v=dep_list(p);
16682 @<Print an abbreviated value of |v| with format depending on |t|@>;
16683 if ( restore_cur_exp ) mp_unstash_cur_exp(mp, p);
16686 @ @<Print an abbreviated value of |v| with format depending on |t|@>=
16688 case mp_vacuous:mp_print(mp, "mp_vacuous"); break;
16689 case mp_boolean_type:
16690 if ( v==true_code ) mp_print(mp, "true"); else mp_print(mp, "false");
16692 case unknown_types: case mp_numeric_type:
16693 @<Display a variable that's been declared but not defined@>;
16695 case mp_string_type:
16696 mp_print_char(mp, '"'); mp_print_str(mp, v); mp_print_char(mp, '"');
16698 case mp_pen_type: case mp_path_type: case mp_picture_type:
16699 @<Display a complex type@>;
16701 case mp_transform_type: case mp_color_type: case mp_pair_type: case mp_cmykcolor_type:
16702 if ( v==null ) mp_print_type(mp, t);
16703 else @<Display a big node@>;
16705 case mp_known:mp_print_scaled(mp, v); break;
16706 case mp_dependent: case mp_proto_dependent:
16707 mp_print_dp(mp, t,v,verbosity);
16709 case mp_independent:mp_print_variable_name(mp, p); break;
16710 default: mp_confusion(mp, "exp"); break;
16711 @:this can't happen exp}{\quad exp@>
16714 @ @<Display a big node@>=
16716 mp_print_char(mp, '('); q=v+mp->big_node_size[t];
16718 if ( type(v)==mp_known ) mp_print_scaled(mp, value(v));
16719 else if ( type(v)==mp_independent ) mp_print_variable_name(mp, v);
16720 else mp_print_dp(mp, type(v),dep_list(v),verbosity);
16722 if ( v!=q ) mp_print_char(mp, ',');
16724 mp_print_char(mp, ')');
16727 @ Values of type \&{picture}, \&{path}, and \&{pen} are displayed verbosely
16728 in the log file only, unless the user has given a positive value to
16731 @<Display a complex type@>=
16732 if ( verbosity<=1 ) {
16733 mp_print_type(mp, t);
16735 if ( mp->selector==term_and_log )
16736 if ( mp->internal[mp_tracing_online]<=0 ) {
16737 mp->selector=term_only;
16738 mp_print_type(mp, t); mp_print(mp, " (see the transcript file)");
16739 mp->selector=term_and_log;
16742 case mp_pen_type:mp_print_pen(mp, v,"",false); break;
16743 case mp_path_type:mp_print_path(mp, v,"",false); break;
16744 case mp_picture_type:mp_print_edges(mp, v,"",false); break;
16745 } /* there are no other cases */
16748 @ @<Declare the procedure called |print_dp|@>=
16749 void mp_print_dp (MP mp,small_number t, pointer p,
16750 small_number verbosity) {
16751 pointer q; /* the node following |p| */
16753 if ( (info(q)==null) || (verbosity>0) ) mp_print_dependency(mp, p,t);
16754 else mp_print(mp, "linearform");
16757 @ The displayed name of a variable in a ring will not be a capsule unless
16758 the ring consists entirely of capsules.
16760 @<Display a variable that's been declared but not defined@>=
16761 { mp_print_type(mp, t);
16763 { mp_print_char(mp, ' ');
16764 while ( (name_type(v)==mp_capsule) && (v!=p) ) v=value(v);
16765 mp_print_variable_name(mp, v);
16769 @ When errors are detected during parsing, it is often helpful to
16770 display an expression just above the error message, using |exp_err|
16771 or |disp_err| instead of |print_err|.
16773 @d exp_err(A) mp_disp_err(mp, null,(A)) /* displays the current expression */
16775 @<Declare subroutines for printing expressions@>=
16776 void mp_disp_err (MP mp,pointer p, char *s) {
16777 if ( mp->interaction==mp_error_stop_mode ) wake_up_terminal;
16778 mp_print_nl(mp, ">> ");
16780 mp_print_exp(mp, p,1); /* ``medium verbose'' printing of the expression */
16782 mp_print_nl(mp, "! "); mp_print(mp, s);
16787 @ If |cur_type| and |cur_exp| contain relevant information that should
16788 be recycled, we will use the following procedure, which changes |cur_type|
16789 to |known| and stores a given value in |cur_exp|. We can think of |cur_type|
16790 and |cur_exp| as either alive or dormant after this has been done,
16791 because |cur_exp| will not contain a pointer value.
16793 @ @c void mp_flush_cur_exp (MP mp,scaled v) {
16794 switch (mp->cur_type) {
16795 case unknown_types: case mp_transform_type: case mp_color_type: case mp_pair_type:
16796 case mp_dependent: case mp_proto_dependent: case mp_independent: case mp_cmykcolor_type:
16797 mp_recycle_value(mp, mp->cur_exp);
16798 mp_free_node(mp, mp->cur_exp,value_node_size);
16800 case mp_string_type:
16801 delete_str_ref(mp->cur_exp); break;
16802 case mp_pen_type: case mp_path_type:
16803 mp_toss_knot_list(mp, mp->cur_exp); break;
16804 case mp_picture_type:
16805 delete_edge_ref(mp->cur_exp); break;
16809 mp->cur_type=mp_known; mp->cur_exp=v;
16812 @ There's a much more general procedure that is capable of releasing
16813 the storage associated with any two-word value packet.
16815 @<Declare the recycling subroutines@>=
16816 void mp_recycle_value (MP mp,pointer p) ;
16818 @ @c void mp_recycle_value (MP mp,pointer p) {
16819 small_number t; /* a type code */
16820 integer vv; /* another value */
16821 pointer q,r,s,pp; /* link manipulation registers */
16822 integer v=0; /* a value */
16824 if ( t<mp_dependent ) v=value(p);
16826 case undefined: case mp_vacuous: case mp_boolean_type: case mp_known:
16827 case mp_numeric_type:
16829 case unknown_types:
16830 mp_ring_delete(mp, p); break;
16831 case mp_string_type:
16832 delete_str_ref(v); break;
16833 case mp_path_type: case mp_pen_type:
16834 mp_toss_knot_list(mp, v); break;
16835 case mp_picture_type:
16836 delete_edge_ref(v); break;
16837 case mp_cmykcolor_type: case mp_pair_type: case mp_color_type:
16838 case mp_transform_type:
16839 @<Recycle a big node@>; break;
16840 case mp_dependent: case mp_proto_dependent:
16841 @<Recycle a dependency list@>; break;
16842 case mp_independent:
16843 @<Recycle an independent variable@>; break;
16844 case mp_token_list: case mp_structured:
16845 mp_confusion(mp, "recycle"); break;
16846 @:this can't happen recycle}{\quad recycle@>
16847 case mp_unsuffixed_macro: case mp_suffixed_macro:
16848 mp_delete_mac_ref(mp, value(p)); break;
16849 } /* there are no other cases */
16853 @ @<Recycle a big node@>=
16855 q=v+mp->big_node_size[t];
16857 q=q-2; mp_recycle_value(mp, q);
16859 mp_free_node(mp, v,mp->big_node_size[t]);
16862 @ @<Recycle a dependency list@>=
16865 while ( info(q)!=null ) q=link(q);
16866 link(prev_dep(p))=link(q);
16867 prev_dep(link(q))=prev_dep(p);
16868 link(q)=null; mp_flush_node_list(mp, dep_list(p));
16871 @ When an independent variable disappears, it simply fades away, unless
16872 something depends on it. In the latter case, a dependent variable whose
16873 coefficient of dependence is maximal will take its place.
16874 The relevant algorithm is due to Ignacio~A. Zabala, who implemented it
16875 as part of his Ph.D. thesis (Stanford University, December 1982).
16876 @^Zabala Salelles, Ignacio Andres@>
16878 For example, suppose that variable $x$ is being recycled, and that the
16879 only variables depending on~$x$ are $y=2x+a$ and $z=x+b$. In this case
16880 we want to make $y$ independent and $z=.5y-.5a+b$; no other variables
16881 will depend on~$y$. If $\\{tracingequations}>0$ in this situation,
16882 we will print `\.{\#\#\# -2x=-y+a}'.
16884 There's a slight complication, however: An independent variable $x$
16885 can occur both in dependency lists and in proto-dependency lists.
16886 This makes it necessary to be careful when deciding which coefficient
16889 Furthermore, this complication is not so slight when
16890 a proto-dependent variable is chosen to become independent. For example,
16891 suppose that $y=2x+100a$ is proto-dependent while $z=x+b$ is dependent;
16892 then we must change $z=.5y-50a+b$ to a proto-dependency, because of the
16893 large coefficient `50'.
16895 In order to deal with these complications without wasting too much time,
16896 we shall link together the occurrences of~$x$ among all the linear
16897 dependencies, maintaining separate lists for the dependent and
16898 proto-dependent cases.
16900 @<Recycle an independent variable@>=
16902 mp->max_c[mp_dependent]=0; mp->max_c[mp_proto_dependent]=0;
16903 mp->max_link[mp_dependent]=null; mp->max_link[mp_proto_dependent]=null;
16905 while ( q!=dep_head ) {
16906 s=value_loc(q); /* now |link(s)=dep_list(q)| */
16909 if ( info(r)==null ) break;;
16910 if ( info(r)!=p ) {
16913 t=type(q); link(s)=link(r); info(r)=q;
16914 if ( abs(value(r))>mp->max_c[t] ) {
16915 @<Record a new maximum coefficient of type |t|@>;
16917 link(r)=mp->max_link[t]; mp->max_link[t]=r;
16923 if ( (mp->max_c[mp_dependent]>0)||(mp->max_c[mp_proto_dependent]>0) ) {
16924 @<Choose a dependent variable to take the place of the disappearing
16925 independent variable, and change all remaining dependencies
16930 @ The code for independency removal makes use of three two-word arrays.
16933 integer max_c[mp_proto_dependent+1]; /* max coefficient magnitude */
16934 pointer max_ptr[mp_proto_dependent+1]; /* where |p| occurs with |max_c| */
16935 pointer max_link[mp_proto_dependent+1]; /* other occurrences of |p| */
16937 @ @<Record a new maximum coefficient...@>=
16939 if ( mp->max_c[t]>0 ) {
16940 link(mp->max_ptr[t])=mp->max_link[t]; mp->max_link[t]=mp->max_ptr[t];
16942 mp->max_c[t]=abs(value(r)); mp->max_ptr[t]=r;
16945 @ @<Choose a dependent...@>=
16947 if ( (mp->max_c[mp_dependent] / 010000 >= mp->max_c[mp_proto_dependent]) )
16950 t=mp_proto_dependent;
16951 @<Determine the dependency list |s| to substitute for the independent
16953 t=mp_dependent+mp_proto_dependent-t; /* complement |t| */
16954 if ( mp->max_c[t]>0 ) { /* we need to pick up an unchosen dependency */
16955 link(mp->max_ptr[t])=mp->max_link[t]; mp->max_link[t]=mp->max_ptr[t];
16957 if ( t!=mp_dependent ) { @<Substitute new dependencies in place of |p|@>; }
16958 else { @<Substitute new proto-dependencies in place of |p|@>;}
16959 mp_flush_node_list(mp, s);
16960 if ( mp->fix_needed ) mp_fix_dependencies(mp);
16964 @ Let |s=max_ptr[t]|. At this point we have $|value|(s)=\pm|max_c|[t]$,
16965 and |info(s)| points to the dependent variable~|pp| of type~|t| from
16966 whose dependency list we have removed node~|s|. We must reinsert
16967 node~|s| into the dependency list, with coefficient $-1.0$, and with
16968 |pp| as the new independent variable. Since |pp| will have a larger serial
16969 number than any other variable, we can put node |s| at the head of the
16972 @<Determine the dep...@>=
16973 s=mp->max_ptr[t]; pp=info(s); v=value(s);
16974 if ( t==mp_dependent ) value(s)=-fraction_one; else value(s)=-unity;
16975 r=dep_list(pp); link(s)=r;
16976 while ( info(r)!=null ) r=link(r);
16977 q=link(r); link(r)=null;
16978 prev_dep(q)=prev_dep(pp); link(prev_dep(pp))=q;
16980 if ( mp->cur_exp==pp ) if ( mp->cur_type==t ) mp->cur_type=mp_independent;
16981 if ( mp->internal[mp_tracing_equations]>0 ) {
16982 @<Show the transformed dependency@>;
16985 @ Now $(-v)$ times the formerly independent variable~|p| is being replaced
16986 by the dependency list~|s|.
16988 @<Show the transformed...@>=
16989 if ( mp_interesting(mp, p) ) {
16990 mp_begin_diagnostic(mp); mp_print_nl(mp, "### ");
16991 @:]]]\#\#\#_}{\.{\#\#\#}@>
16992 if ( v>0 ) mp_print_char(mp, '-');
16993 if ( t==mp_dependent ) vv=mp_round_fraction(mp, mp->max_c[mp_dependent]);
16994 else vv=mp->max_c[mp_proto_dependent];
16995 if ( vv!=unity ) mp_print_scaled(mp, vv);
16996 mp_print_variable_name(mp, p);
16997 while ( value(p) % s_scale>0 ) {
16998 mp_print(mp, "*4"); value(p)=value(p)-2;
17000 if ( t==mp_dependent ) mp_print_char(mp, '='); else mp_print(mp, " = ");
17001 mp_print_dependency(mp, s,t);
17002 mp_end_diagnostic(mp, false);
17005 @ Finally, there are dependent and proto-dependent variables whose
17006 dependency lists must be brought up to date.
17008 @<Substitute new dependencies...@>=
17009 for (t=mp_dependent;t<=mp_proto_dependent;t++){
17011 while ( r!=null ) {
17013 dep_list(q)=mp_p_plus_fq(mp, dep_list(q),
17014 mp_make_fraction(mp, value(r),-v),s,t,mp_dependent);
17015 if ( dep_list(q)==mp->dep_final ) mp_make_known(mp, q,mp->dep_final);
17016 q=r; r=link(r); mp_free_node(mp, q,dep_node_size);
17020 @ @<Substitute new proto...@>=
17021 for (t=mp_dependent;t<=mp_proto_dependent;t++) {
17023 while ( r!=null ) {
17025 if ( t==mp_dependent ) { /* for safety's sake, we change |q| to |mp_proto_dependent| */
17026 if ( mp->cur_exp==q ) if ( mp->cur_type==mp_dependent )
17027 mp->cur_type=mp_proto_dependent;
17028 dep_list(q)=mp_p_over_v(mp, dep_list(q),unity,mp_dependent,mp_proto_dependent);
17029 type(q)=mp_proto_dependent; value(r)=mp_round_fraction(mp, value(r));
17031 dep_list(q)=mp_p_plus_fq(mp, dep_list(q),
17032 mp_make_scaled(mp, value(r),-v),s,mp_proto_dependent,mp_proto_dependent);
17033 if ( dep_list(q)==mp->dep_final ) mp_make_known(mp, q,mp->dep_final);
17034 q=r; r=link(r); mp_free_node(mp, q,dep_node_size);
17038 @ Here are some routines that provide handy combinations of actions
17039 that are often needed during error recovery. For example,
17040 `|flush_error|' flushes the current expression, replaces it by
17041 a given value, and calls |error|.
17043 Errors often are detected after an extra token has already been scanned.
17044 The `\\{put\_get}' routines put that token back before calling |error|;
17045 then they get it back again. (Or perhaps they get another token, if
17046 the user has changed things.)
17049 void mp_flush_error (MP mp,scaled v);
17050 void mp_put_get_error (MP mp);
17051 void mp_put_get_flush_error (MP mp,scaled v) ;
17054 void mp_flush_error (MP mp,scaled v) {
17055 mp_error(mp); mp_flush_cur_exp(mp, v);
17057 void mp_put_get_error (MP mp) {
17058 mp_back_error(mp); mp_get_x_next(mp);
17060 void mp_put_get_flush_error (MP mp,scaled v) {
17061 mp_put_get_error(mp);
17062 mp_flush_cur_exp(mp, v);
17065 @ A global variable |var_flag| is set to a special command code
17066 just before \MP\ calls |scan_expression|, if the expression should be
17067 treated as a variable when this command code immediately follows. For
17068 example, |var_flag| is set to |assignment| at the beginning of a
17069 statement, because we want to know the {\sl location\/} of a variable at
17070 the left of `\.{:=}', not the {\sl value\/} of that variable.
17072 The |scan_expression| subroutine calls |scan_tertiary|,
17073 which calls |scan_secondary|, which calls |scan_primary|, which sets
17074 |var_flag:=0|. In this way each of the scanning routines ``knows''
17075 when it has been called with a special |var_flag|, but |var_flag| is
17078 A variable preceding a command that equals |var_flag| is converted to a
17079 token list rather than a value. Furthermore, an `\.{=}' sign following an
17080 expression with |var_flag=assignment| is not considered to be a relation
17081 that produces boolean expressions.
17085 int var_flag; /* command that wants a variable */
17090 @* \[37] Parsing primary expressions.
17091 The first parsing routine, |scan_primary|, is also the most complicated one,
17092 since it involves so many different cases. But each case---with one
17093 exception---is fairly simple by itself.
17095 When |scan_primary| begins, the first token of the primary to be scanned
17096 should already appear in |cur_cmd|, |cur_mod|, and |cur_sym|. The values
17097 of |cur_type| and |cur_exp| should be either dead or dormant, as explained
17098 earlier. If |cur_cmd| is not between |min_primary_command| and
17099 |max_primary_command|, inclusive, a syntax error will be signaled.
17101 @<Declare the basic parsing subroutines@>=
17102 void mp_scan_primary (MP mp) {
17103 pointer p,q,r; /* for list manipulation */
17104 quarterword c; /* a primitive operation code */
17105 int my_var_flag; /* initial value of |my_var_flag| */
17106 pointer l_delim,r_delim; /* hash addresses of a delimiter pair */
17107 @<Other local variables for |scan_primary|@>;
17108 my_var_flag=mp->var_flag; mp->var_flag=0;
17111 @<Supply diagnostic information, if requested@>;
17112 switch (mp->cur_cmd) {
17113 case left_delimiter:
17114 @<Scan a delimited primary@>; break;
17116 @<Scan a grouped primary@>; break;
17118 @<Scan a string constant@>; break;
17119 case numeric_token:
17120 @<Scan a primary that starts with a numeric token@>; break;
17122 @<Scan a nullary operation@>; break;
17123 case unary: case type_name: case cycle: case plus_or_minus:
17124 @<Scan a unary operation@>; break;
17125 case primary_binary:
17126 @<Scan a binary operation with `\&{of}' between its operands@>; break;
17128 @<Convert a suffix to a string@>; break;
17129 case internal_quantity:
17130 @<Scan an internal numeric quantity@>; break;
17131 case capsule_token:
17132 mp_make_exp_copy(mp, mp->cur_mod); break;
17134 @<Scan a variable primary; |goto restart| if it turns out to be a macro@>; break;
17136 mp_bad_exp(mp, "A primary"); goto RESTART; break;
17137 @.A primary expression...@>
17139 mp_get_x_next(mp); /* the routines |goto done| if they don't want this */
17141 if ( mp->cur_cmd==left_bracket ) {
17142 if ( mp->cur_type>=mp_known ) {
17143 @<Scan a mediation construction@>;
17150 @ Errors at the beginning of expressions are flagged by |bad_exp|.
17152 @c void mp_bad_exp (MP mp,char * s) {
17154 print_err(s); mp_print(mp, " expression can't begin with `");
17155 mp_print_cmd_mod(mp, mp->cur_cmd,mp->cur_mod);
17156 mp_print_char(mp, '\'');
17157 help4("I'm afraid I need some sort of value in order to continue,")
17158 ("so I've tentatively inserted `0'. You may want to")
17159 ("delete this zero and insert something else;")
17160 ("see Chapter 27 of The METAFONTbook for an example.");
17161 @:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
17162 mp_back_input(mp); mp->cur_sym=0; mp->cur_cmd=numeric_token;
17163 mp->cur_mod=0; mp_ins_error(mp);
17164 save_flag=mp->var_flag; mp->var_flag=0; mp_get_x_next(mp);
17165 mp->var_flag=save_flag;
17168 @ @<Supply diagnostic information, if requested@>=
17170 if ( mp->panicking ) mp_check_mem(mp, false);
17172 if ( mp->interrupt!=0 ) if ( mp->OK_to_interrupt ) {
17173 mp_back_input(mp); check_interrupt; mp_get_x_next(mp);
17176 @ @<Scan a delimited primary@>=
17178 l_delim=mp->cur_sym; r_delim=mp->cur_mod;
17179 mp_get_x_next(mp); mp_scan_expression(mp);
17180 if ( (mp->cur_cmd==comma) && (mp->cur_type>=mp_known) ) {
17181 @<Scan the rest of a delimited set of numerics@>;
17183 mp_check_delimiter(mp, l_delim,r_delim);
17187 @ The |stash_in| subroutine puts the current (numeric) expression into a field
17188 within a ``big node.''
17190 @c void mp_stash_in (MP mp,pointer p) {
17191 pointer q; /* temporary register */
17192 type(p)=mp->cur_type;
17193 if ( mp->cur_type==mp_known ) {
17194 value(p)=mp->cur_exp;
17196 if ( mp->cur_type==mp_independent ) {
17197 @<Stash an independent |cur_exp| into a big node@>;
17199 mp->mem[value_loc(p)]=mp->mem[value_loc(mp->cur_exp)];
17200 /* |dep_list(p):=dep_list(cur_exp)| and |prev_dep(p):=prev_dep(cur_exp)| */
17201 link(prev_dep(p))=p;
17203 mp_free_node(mp, mp->cur_exp,value_node_size);
17205 mp->cur_type=mp_vacuous;
17208 @ In rare cases the current expression can become |independent|. There
17209 may be many dependency lists pointing to such an independent capsule,
17210 so we can't simply move it into place within a big node. Instead,
17211 we copy it, then recycle it.
17213 @ @<Stash an independent |cur_exp|...@>=
17215 q=mp_single_dependency(mp, mp->cur_exp);
17216 if ( q==mp->dep_final ){
17217 type(p)=mp_known; value(p)=0; mp_free_node(mp, q,dep_node_size);
17219 type(p)=mp_dependent; mp_new_dep(mp, p,q);
17221 mp_recycle_value(mp, mp->cur_exp);
17224 @ This code uses the fact that |red_part_loc| and |green_part_loc|
17225 are synonymous with |x_part_loc| and |y_part_loc|.
17227 @<Scan the rest of a delimited set of numerics@>=
17229 p=mp_stash_cur_exp(mp);
17230 mp_get_x_next(mp); mp_scan_expression(mp);
17231 @<Make sure the second part of a pair or color has a numeric type@>;
17232 q=mp_get_node(mp, value_node_size); name_type(q)=mp_capsule;
17233 if ( mp->cur_cmd==comma ) type(q)=mp_color_type;
17234 else type(q)=mp_pair_type;
17235 mp_init_big_node(mp, q); r=value(q);
17236 mp_stash_in(mp, y_part_loc(r));
17237 mp_unstash_cur_exp(mp, p);
17238 mp_stash_in(mp, x_part_loc(r));
17239 if ( mp->cur_cmd==comma ) {
17240 @<Scan the last of a triplet of numerics@>;
17242 if ( mp->cur_cmd==comma ) {
17243 type(q)=mp_cmykcolor_type;
17244 mp_init_big_node(mp, q); t=value(q);
17245 mp->mem[cyan_part_loc(t)]=mp->mem[red_part_loc(r)];
17246 value(cyan_part_loc(t))=value(red_part_loc(r));
17247 mp->mem[magenta_part_loc(t)]=mp->mem[green_part_loc(r)];
17248 value(magenta_part_loc(t))=value(green_part_loc(r));
17249 mp->mem[yellow_part_loc(t)]=mp->mem[blue_part_loc(r)];
17250 value(yellow_part_loc(t))=value(blue_part_loc(r));
17251 mp_recycle_value(mp, r);
17253 @<Scan the last of a quartet of numerics@>;
17255 mp_check_delimiter(mp, l_delim,r_delim);
17256 mp->cur_type=type(q);
17260 @ @<Make sure the second part of a pair or color has a numeric type@>=
17261 if ( mp->cur_type<mp_known ) {
17262 exp_err("Nonnumeric ypart has been replaced by 0");
17263 @.Nonnumeric...replaced by 0@>
17264 help4("I've started to scan a pair `(a,b)' or a color `(a,b,c)';")
17265 ("but after finding a nice `a' I found a `b' that isn't")
17266 ("of numeric type. So I've changed that part to zero.")
17267 ("(The b that I didn't like appears above the error message.)");
17268 mp_put_get_flush_error(mp, 0);
17271 @ @<Scan the last of a triplet of numerics@>=
17273 mp_get_x_next(mp); mp_scan_expression(mp);
17274 if ( mp->cur_type<mp_known ) {
17275 exp_err("Nonnumeric third part has been replaced by 0");
17276 @.Nonnumeric...replaced by 0@>
17277 help3("I've just scanned a color `(a,b,c)' or cmykcolor(a,b,c,d); but the `c'")
17278 ("isn't of numeric type. So I've changed that part to zero.")
17279 ("(The c that I didn't like appears above the error message.)");
17280 mp_put_get_flush_error(mp, 0);
17282 mp_stash_in(mp, blue_part_loc(r));
17285 @ @<Scan the last of a quartet of numerics@>=
17287 mp_get_x_next(mp); mp_scan_expression(mp);
17288 if ( mp->cur_type<mp_known ) {
17289 exp_err("Nonnumeric blackpart has been replaced by 0");
17290 @.Nonnumeric...replaced by 0@>
17291 help3("I've just scanned a cmykcolor `(c,m,y,k)'; but the `k' isn't")
17292 ("of numeric type. So I've changed that part to zero.")
17293 ("(The k that I didn't like appears above the error message.)");
17294 mp_put_get_flush_error(mp, 0);
17296 mp_stash_in(mp, black_part_loc(r));
17299 @ The local variable |group_line| keeps track of the line
17300 where a \&{begingroup} command occurred; this will be useful
17301 in an error message if the group doesn't actually end.
17303 @<Other local variables for |scan_primary|@>=
17304 integer group_line; /* where a group began */
17306 @ @<Scan a grouped primary@>=
17308 group_line=mp_true_line(mp);
17309 if ( mp->internal[mp_tracing_commands]>0 ) show_cur_cmd_mod;
17310 save_boundary_item(p);
17312 mp_do_statement(mp); /* ends with |cur_cmd>=semicolon| */
17313 } while (! (mp->cur_cmd!=semicolon));
17314 if ( mp->cur_cmd!=end_group ) {
17315 print_err("A group begun on line ");
17316 @.A group...never ended@>
17317 mp_print_int(mp, group_line);
17318 mp_print(mp, " never ended");
17319 help2("I saw a `begingroup' back there that hasn't been matched")
17320 ("by `endgroup'. So I've inserted `endgroup' now.");
17321 mp_back_error(mp); mp->cur_cmd=end_group;
17324 /* this might change |cur_type|, if independent variables are recycled */
17325 if ( mp->internal[mp_tracing_commands]>0 ) show_cur_cmd_mod;
17328 @ @<Scan a string constant@>=
17330 mp->cur_type=mp_string_type; mp->cur_exp=mp->cur_mod;
17333 @ Later we'll come to procedures that perform actual operations like
17334 addition, square root, and so on; our purpose now is to do the parsing.
17335 But we might as well mention those future procedures now, so that the
17336 suspense won't be too bad:
17339 |do_nullary(c)| does primitive operations that have no operands (e.g.,
17340 `\&{true}' or `\&{pencircle}');
17343 |do_unary(c)| applies a primitive operation to the current expression;
17346 |do_binary(p,c)| applies a primitive operation to the capsule~|p|
17347 and the current expression.
17349 @<Scan a nullary operation@>=mp_do_nullary(mp, mp->cur_mod)
17351 @ @<Scan a unary operation@>=
17353 c=mp->cur_mod; mp_get_x_next(mp); mp_scan_primary(mp);
17354 mp_do_unary(mp, c); goto DONE;
17357 @ A numeric token might be a primary by itself, or it might be the
17358 numerator of a fraction composed solely of numeric tokens, or it might
17359 multiply the primary that follows (provided that the primary doesn't begin
17360 with a plus sign or a minus sign). The code here uses the facts that
17361 |max_primary_command=plus_or_minus| and
17362 |max_primary_command-1=numeric_token|. If a fraction is found that is less
17363 than unity, we try to retain higher precision when we use it in scalar
17366 @<Other local variables for |scan_primary|@>=
17367 scaled num,denom; /* for primaries that are fractions, like `1/2' */
17369 @ @<Scan a primary that starts with a numeric token@>=
17371 mp->cur_exp=mp->cur_mod; mp->cur_type=mp_known; mp_get_x_next(mp);
17372 if ( mp->cur_cmd!=slash ) {
17376 if ( mp->cur_cmd!=numeric_token ) {
17378 mp->cur_cmd=slash; mp->cur_mod=over; mp->cur_sym=frozen_slash;
17381 num=mp->cur_exp; denom=mp->cur_mod;
17382 if ( denom==0 ) { @<Protest division by zero@>; }
17383 else { mp->cur_exp=mp_make_scaled(mp, num,denom); }
17384 check_arith; mp_get_x_next(mp);
17386 if ( mp->cur_cmd>=min_primary_command ) {
17387 if ( mp->cur_cmd<numeric_token ) { /* in particular, |cur_cmd<>plus_or_minus| */
17388 p=mp_stash_cur_exp(mp); mp_scan_primary(mp);
17389 if ( (abs(num)>=abs(denom))||(mp->cur_type<mp_color_type) ) {
17390 mp_do_binary(mp, p,times);
17392 mp_frac_mult(mp, num,denom);
17393 mp_free_node(mp, p,value_node_size);
17400 @ @<Protest division...@>=
17402 print_err("Division by zero");
17403 @.Division by zero@>
17404 help1("I'll pretend that you meant to divide by 1."); mp_error(mp);
17407 @ @<Scan a binary operation with `\&{of}' between its operands@>=
17409 c=mp->cur_mod; mp_get_x_next(mp); mp_scan_expression(mp);
17410 if ( mp->cur_cmd!=of_token ) {
17411 mp_missing_err(mp, "of"); mp_print(mp, " for ");
17412 mp_print_cmd_mod(mp, primary_binary,c);
17414 help1("I've got the first argument; will look now for the other.");
17417 p=mp_stash_cur_exp(mp); mp_get_x_next(mp); mp_scan_primary(mp);
17418 mp_do_binary(mp, p,c); goto DONE;
17421 @ @<Convert a suffix to a string@>=
17423 mp_get_x_next(mp); mp_scan_suffix(mp);
17424 mp->old_setting=mp->selector; mp->selector=new_string;
17425 mp_show_token_list(mp, mp->cur_exp,null,100000,0);
17426 mp_flush_token_list(mp, mp->cur_exp);
17427 mp->cur_exp=mp_make_string(mp); mp->selector=mp->old_setting;
17428 mp->cur_type=mp_string_type;
17432 @ If an internal quantity appears all by itself on the left of an
17433 assignment, we return a token list of length one, containing the address
17434 of the internal quantity plus |hash_end|. (This accords with the conventions
17435 of the save stack, as described earlier.)
17437 @<Scan an internal...@>=
17440 if ( my_var_flag==assignment ) {
17442 if ( mp->cur_cmd==assignment ) {
17443 mp->cur_exp=mp_get_avail(mp);
17444 info(mp->cur_exp)=q+hash_end; mp->cur_type=mp_token_list;
17449 mp->cur_type=mp_known; mp->cur_exp=mp->internal[q];
17452 @ The most difficult part of |scan_primary| has been saved for last, since
17453 it was necessary to build up some confidence first. We can now face the task
17454 of scanning a variable.
17456 As we scan a variable, we build a token list containing the relevant
17457 names and subscript values, simultaneously following along in the
17458 ``collective'' structure to see if we are actually dealing with a macro
17459 instead of a value.
17461 The local variables |pre_head| and |post_head| will point to the beginning
17462 of the prefix and suffix lists; |tail| will point to the end of the list
17463 that is currently growing.
17465 Another local variable, |tt|, contains partial information about the
17466 declared type of the variable-so-far. If |tt>=mp_unsuffixed_macro|, the
17467 relation |tt=type(q)| will always hold. If |tt=undefined|, the routine
17468 doesn't bother to update its information about type. And if
17469 |undefined<tt<mp_unsuffixed_macro|, the precise value of |tt| isn't critical.
17471 @ @<Other local variables for |scan_primary|@>=
17472 pointer pre_head,post_head,tail;
17473 /* prefix and suffix list variables */
17474 small_number tt; /* approximation to the type of the variable-so-far */
17475 pointer t; /* a token */
17476 pointer macro_ref = 0; /* reference count for a suffixed macro */
17478 @ @<Scan a variable primary...@>=
17480 fast_get_avail(pre_head); tail=pre_head; post_head=null; tt=mp_vacuous;
17482 t=mp_cur_tok(mp); link(tail)=t;
17483 if ( tt!=undefined ) {
17484 @<Find the approximate type |tt| and corresponding~|q|@>;
17485 if ( tt>=mp_unsuffixed_macro ) {
17486 @<Either begin an unsuffixed macro call or
17487 prepare for a suffixed one@>;
17490 mp_get_x_next(mp); tail=t;
17491 if ( mp->cur_cmd==left_bracket ) {
17492 @<Scan for a subscript; replace |cur_cmd| by |numeric_token| if found@>;
17494 if ( mp->cur_cmd>max_suffix_token ) break;
17495 if ( mp->cur_cmd<min_suffix_token ) break;
17496 } /* now |cur_cmd| is |internal_quantity|, |tag_token|, or |numeric_token| */
17497 @<Handle unusual cases that masquerade as variables, and |goto restart|
17498 or |goto done| if appropriate;
17499 otherwise make a copy of the variable and |goto done|@>;
17502 @ @<Either begin an unsuffixed macro call or...@>=
17505 if ( tt>mp_unsuffixed_macro ) { /* |tt=mp_suffixed_macro| */
17506 post_head=mp_get_avail(mp); tail=post_head; link(tail)=t;
17507 tt=undefined; macro_ref=value(q); add_mac_ref(macro_ref);
17509 @<Set up unsuffixed macro call and |goto restart|@>;
17513 @ @<Scan for a subscript; replace |cur_cmd| by |numeric_token| if found@>=
17515 mp_get_x_next(mp); mp_scan_expression(mp);
17516 if ( mp->cur_cmd!=right_bracket ) {
17517 @<Put the left bracket and the expression back to be rescanned@>;
17519 if ( mp->cur_type!=mp_known ) mp_bad_subscript(mp);
17520 mp->cur_cmd=numeric_token; mp->cur_mod=mp->cur_exp; mp->cur_sym=0;
17524 @ The left bracket that we thought was introducing a subscript might have
17525 actually been the left bracket in a mediation construction like `\.{x[a,b]}'.
17526 So we don't issue an error message at this point; but we do want to back up
17527 so as to avoid any embarrassment about our incorrect assumption.
17529 @<Put the left bracket and the expression back to be rescanned@>=
17531 mp_back_input(mp); /* that was the token following the current expression */
17532 mp_back_expr(mp); mp->cur_cmd=left_bracket;
17533 mp->cur_mod=0; mp->cur_sym=frozen_left_bracket;
17536 @ Here's a routine that puts the current expression back to be read again.
17538 @c void mp_back_expr (MP mp) {
17539 pointer p; /* capsule token */
17540 p=mp_stash_cur_exp(mp); link(p)=null; back_list(p);
17543 @ Unknown subscripts lead to the following error message.
17545 @c void mp_bad_subscript (MP mp) {
17546 exp_err("Improper subscript has been replaced by zero");
17547 @.Improper subscript...@>
17548 help3("A bracketed subscript must have a known numeric value;")
17549 ("unfortunately, what I found was the value that appears just")
17550 ("above this error message. So I'll try a zero subscript.");
17551 mp_flush_error(mp, 0);
17554 @ Every time we call |get_x_next|, there's a chance that the variable we've
17555 been looking at will disappear. Thus, we cannot safely keep |q| pointing
17556 into the variable structure; we need to start searching from the root each time.
17558 @<Find the approximate type |tt| and corresponding~|q|@>=
17561 p=link(pre_head); q=info(p); tt=undefined;
17562 if ( eq_type(q) % outer_tag==tag_token ) {
17564 if ( q==null ) goto DONE2;
17568 tt=type(q); goto DONE2;
17570 if ( type(q)!=mp_structured ) goto DONE2;
17571 q=link(attr_head(q)); /* the |collective_subscript| attribute */
17572 if ( p>=mp->hi_mem_min ) { /* it's not a subscript */
17573 do { q=link(q); } while (! (attr_loc(q)>=info(p)));
17574 if ( attr_loc(q)>info(p) ) goto DONE2;
17582 @ How do things stand now? Well, we have scanned an entire variable name,
17583 including possible subscripts and/or attributes; |cur_cmd|, |cur_mod|, and
17584 |cur_sym| represent the token that follows. If |post_head=null|, a
17585 token list for this variable name starts at |link(pre_head)|, with all
17586 subscripts evaluated. But if |post_head<>null|, the variable turned out
17587 to be a suffixed macro; |pre_head| is the head of the prefix list, while
17588 |post_head| is the head of a token list containing both `\.{\AT!}' and
17591 Our immediate problem is to see if this variable still exists. (Variable
17592 structures can change drastically whenever we call |get_x_next|; users
17593 aren't supposed to do this, but the fact that it is possible means that
17594 we must be cautious.)
17596 The following procedure prints an error message when a variable
17597 unexpectedly disappears. Its help message isn't quite right for
17598 our present purposes, but we'll be able to fix that up.
17601 void mp_obliterated (MP mp,pointer q) {
17602 print_err("Variable "); mp_show_token_list(mp, q,null,1000,0);
17603 mp_print(mp, " has been obliterated");
17604 @.Variable...obliterated@>
17605 help5("It seems you did a nasty thing---probably by accident,")
17606 ("but nevertheless you nearly hornswoggled me...")
17607 ("While I was evaluating the right-hand side of this")
17608 ("command, something happened, and the left-hand side")
17609 ("is no longer a variable! So I won't change anything.");
17612 @ If the variable does exist, we also need to check
17613 for a few other special cases before deciding that a plain old ordinary
17614 variable has, indeed, been scanned.
17616 @<Handle unusual cases that masquerade as variables...@>=
17617 if ( post_head!=null ) {
17618 @<Set up suffixed macro call and |goto restart|@>;
17620 q=link(pre_head); free_avail(pre_head);
17621 if ( mp->cur_cmd==my_var_flag ) {
17622 mp->cur_type=mp_token_list; mp->cur_exp=q; goto DONE;
17624 p=mp_find_variable(mp, q);
17626 mp_make_exp_copy(mp, p);
17628 mp_obliterated(mp, q);
17629 mp->help_line[2]="While I was evaluating the suffix of this variable,";
17630 mp->help_line[1]="something was redefined, and it's no longer a variable!";
17631 mp->help_line[0]="In order to get back on my feet, I've inserted `0' instead.";
17632 mp_put_get_flush_error(mp, 0);
17634 mp_flush_node_list(mp, q);
17637 @ The only complication associated with macro calling is that the prefix
17638 and ``at'' parameters must be packaged in an appropriate list of lists.
17640 @<Set up unsuffixed macro call and |goto restart|@>=
17642 p=mp_get_avail(mp); info(pre_head)=link(pre_head); link(pre_head)=p;
17643 info(p)=t; mp_macro_call(mp, value(q),pre_head,null);
17648 @ If the ``variable'' that turned out to be a suffixed macro no longer exists,
17649 we don't care, because we have reserved a pointer (|macro_ref|) to its
17652 @<Set up suffixed macro call and |goto restart|@>=
17654 mp_back_input(mp); p=mp_get_avail(mp); q=link(post_head);
17655 info(pre_head)=link(pre_head); link(pre_head)=post_head;
17656 info(post_head)=q; link(post_head)=p; info(p)=link(q); link(q)=null;
17657 mp_macro_call(mp, macro_ref,pre_head,null); decr(ref_count(macro_ref));
17658 mp_get_x_next(mp); goto RESTART;
17661 @ Our remaining job is simply to make a copy of the value that has been
17662 found. Some cases are harder than others, but complexity arises solely
17663 because of the multiplicity of possible cases.
17665 @<Declare the procedure called |make_exp_copy|@>=
17666 @<Declare subroutines needed by |make_exp_copy|@>;
17667 void mp_make_exp_copy (MP mp,pointer p) {
17668 pointer q,r,t; /* registers for list manipulation */
17670 mp->cur_type=type(p);
17671 switch (mp->cur_type) {
17672 case mp_vacuous: case mp_boolean_type: case mp_known:
17673 mp->cur_exp=value(p); break;
17674 case unknown_types:
17675 mp->cur_exp=mp_new_ring_entry(mp, p);
17677 case mp_string_type:
17678 mp->cur_exp=value(p); add_str_ref(mp->cur_exp);
17680 case mp_picture_type:
17681 mp->cur_exp=value(p);add_edge_ref(mp->cur_exp);
17684 mp->cur_exp=copy_pen(value(p));
17687 mp->cur_exp=mp_copy_path(mp, value(p));
17689 case mp_transform_type: case mp_color_type:
17690 case mp_cmykcolor_type: case mp_pair_type:
17691 @<Copy the big node |p|@>;
17693 case mp_dependent: case mp_proto_dependent:
17694 mp_encapsulate(mp, mp_copy_dep_list(mp, dep_list(p)));
17696 case mp_numeric_type:
17697 new_indep(p); goto RESTART;
17699 case mp_independent:
17700 q=mp_single_dependency(mp, p);
17701 if ( q==mp->dep_final ){
17702 mp->cur_type=mp_known; mp->cur_exp=0; mp_free_node(mp, q,value_node_size);
17704 mp->cur_type=mp_dependent; mp_encapsulate(mp, q);
17708 mp_confusion(mp, "copy");
17709 @:this can't happen copy}{\quad copy@>
17714 @ The |encapsulate| subroutine assumes that |dep_final| is the
17715 tail of dependency list~|p|.
17717 @<Declare subroutines needed by |make_exp_copy|@>=
17718 void mp_encapsulate (MP mp,pointer p) {
17719 mp->cur_exp=mp_get_node(mp, value_node_size); type(mp->cur_exp)=mp->cur_type;
17720 name_type(mp->cur_exp)=mp_capsule; mp_new_dep(mp, mp->cur_exp,p);
17723 @ The most tedious case arises when the user refers to a
17724 \&{pair}, \&{color}, or \&{transform} variable; we must copy several fields,
17725 each of which can be |independent|, |dependent|, |mp_proto_dependent|,
17728 @<Copy the big node |p|@>=
17730 if ( value(p)==null )
17731 mp_init_big_node(mp, p);
17732 t=mp_get_node(mp, value_node_size); name_type(t)=mp_capsule; type(t)=mp->cur_type;
17733 mp_init_big_node(mp, t);
17734 q=value(p)+mp->big_node_size[mp->cur_type];
17735 r=value(t)+mp->big_node_size[mp->cur_type];
17737 q=q-2; r=r-2; mp_install(mp, r,q);
17738 } while (q!=value(p));
17742 @ The |install| procedure copies a numeric field~|q| into field~|r| of
17743 a big node that will be part of a capsule.
17745 @<Declare subroutines needed by |make_exp_copy|@>=
17746 void mp_install (MP mp,pointer r, pointer q) {
17747 pointer p; /* temporary register */
17748 if ( type(q)==mp_known ){
17749 value(r)=value(q); type(r)=mp_known;
17750 } else if ( type(q)==mp_independent ) {
17751 p=mp_single_dependency(mp, q);
17752 if ( p==mp->dep_final ) {
17753 type(r)=mp_known; value(r)=0; mp_free_node(mp, p,value_node_size);
17755 type(r)=mp_dependent; mp_new_dep(mp, r,p);
17758 type(r)=type(q); mp_new_dep(mp, r,mp_copy_dep_list(mp, dep_list(q)));
17762 @ Expressions of the form `\.{a[b,c]}' are converted into
17763 `\.{b+a*(c-b)}', without checking the types of \.b~or~\.c,
17764 provided that \.a is numeric.
17766 @<Scan a mediation...@>=
17768 p=mp_stash_cur_exp(mp); mp_get_x_next(mp); mp_scan_expression(mp);
17769 if ( mp->cur_cmd!=comma ) {
17770 @<Put the left bracket and the expression back...@>;
17771 mp_unstash_cur_exp(mp, p);
17773 q=mp_stash_cur_exp(mp); mp_get_x_next(mp); mp_scan_expression(mp);
17774 if ( mp->cur_cmd!=right_bracket ) {
17775 mp_missing_err(mp, "]");
17777 help3("I've scanned an expression of the form `a[b,c',")
17778 ("so a right bracket should have come next.")
17779 ("I shall pretend that one was there.");
17782 r=mp_stash_cur_exp(mp); mp_make_exp_copy(mp, q);
17783 mp_do_binary(mp, r,minus); mp_do_binary(mp, p,times);
17784 mp_do_binary(mp, q,plus); mp_get_x_next(mp);
17788 @ Here is a comparatively simple routine that is used to scan the
17789 \&{suffix} parameters of a macro.
17791 @<Declare the basic parsing subroutines@>=
17792 void mp_scan_suffix (MP mp) {
17793 pointer h,t; /* head and tail of the list being built */
17794 pointer p; /* temporary register */
17795 h=mp_get_avail(mp); t=h;
17797 if ( mp->cur_cmd==left_bracket ) {
17798 @<Scan a bracketed subscript and set |cur_cmd:=numeric_token|@>;
17800 if ( mp->cur_cmd==numeric_token ) {
17801 p=mp_new_num_tok(mp, mp->cur_mod);
17802 } else if ((mp->cur_cmd==tag_token)||(mp->cur_cmd==internal_quantity) ) {
17803 p=mp_get_avail(mp); info(p)=mp->cur_sym;
17807 link(t)=p; t=p; mp_get_x_next(mp);
17809 mp->cur_exp=link(h); free_avail(h); mp->cur_type=mp_token_list;
17812 @ @<Scan a bracketed subscript and set |cur_cmd:=numeric_token|@>=
17814 mp_get_x_next(mp); mp_scan_expression(mp);
17815 if ( mp->cur_type!=mp_known ) mp_bad_subscript(mp);
17816 if ( mp->cur_cmd!=right_bracket ) {
17817 mp_missing_err(mp, "]");
17819 help3("I've seen a `[' and a subscript value, in a suffix,")
17820 ("so a right bracket should have come next.")
17821 ("I shall pretend that one was there.");
17824 mp->cur_cmd=numeric_token; mp->cur_mod=mp->cur_exp;
17827 @* \[38] Parsing secondary and higher expressions.
17829 After the intricacies of |scan_primary|\kern-1pt,
17830 the |scan_secondary| routine is
17831 refreshingly simple. It's not trivial, but the operations are relatively
17832 straightforward; the main difficulty is, again, that expressions and data
17833 structures might change drastically every time we call |get_x_next|, so a
17834 cautious approach is mandatory. For example, a macro defined by
17835 \&{primarydef} might have disappeared by the time its second argument has
17836 been scanned; we solve this by increasing the reference count of its token
17837 list, so that the macro can be called even after it has been clobbered.
17839 @<Declare the basic parsing subroutines@>=
17840 void mp_scan_secondary (MP mp) {
17841 pointer p; /* for list manipulation */
17842 halfword c,d; /* operation codes or modifiers */
17843 pointer mac_name; /* token defined with \&{primarydef} */
17845 if ((mp->cur_cmd<min_primary_command)||
17846 (mp->cur_cmd>max_primary_command) )
17847 mp_bad_exp(mp, "A secondary");
17848 @.A secondary expression...@>
17849 mp_scan_primary(mp);
17851 if ( mp->cur_cmd<=max_secondary_command )
17852 if ( mp->cur_cmd>=min_secondary_command ) {
17853 p=mp_stash_cur_exp(mp); c=mp->cur_mod; d=mp->cur_cmd;
17854 if ( d==secondary_primary_macro ) {
17855 mac_name=mp->cur_sym; add_mac_ref(c);
17857 mp_get_x_next(mp); mp_scan_primary(mp);
17858 if ( d!=secondary_primary_macro ) {
17859 mp_do_binary(mp, p,c);
17861 mp_back_input(mp); mp_binary_mac(mp, p,c,mac_name);
17862 decr(ref_count(c)); mp_get_x_next(mp);
17869 @ The following procedure calls a macro that has two parameters,
17872 @c void mp_binary_mac (MP mp,pointer p, pointer c, pointer n) {
17873 pointer q,r; /* nodes in the parameter list */
17874 q=mp_get_avail(mp); r=mp_get_avail(mp); link(q)=r;
17875 info(q)=p; info(r)=mp_stash_cur_exp(mp);
17876 mp_macro_call(mp, c,q,n);
17879 @ The next procedure, |scan_tertiary|, is pretty much the same deal.
17881 @<Declare the basic parsing subroutines@>=
17882 void mp_scan_tertiary (MP mp) {
17883 pointer p; /* for list manipulation */
17884 halfword c,d; /* operation codes or modifiers */
17885 pointer mac_name; /* token defined with \&{secondarydef} */
17887 if ((mp->cur_cmd<min_primary_command)||
17888 (mp->cur_cmd>max_primary_command) )
17889 mp_bad_exp(mp, "A tertiary");
17890 @.A tertiary expression...@>
17891 mp_scan_secondary(mp);
17893 if ( mp->cur_cmd<=max_tertiary_command ) {
17894 if ( mp->cur_cmd>=min_tertiary_command ) {
17895 p=mp_stash_cur_exp(mp); c=mp->cur_mod; d=mp->cur_cmd;
17896 if ( d==tertiary_secondary_macro ) {
17897 mac_name=mp->cur_sym; add_mac_ref(c);
17899 mp_get_x_next(mp); mp_scan_secondary(mp);
17900 if ( d!=tertiary_secondary_macro ) {
17901 mp_do_binary(mp, p,c);
17903 mp_back_input(mp); mp_binary_mac(mp, p,c,mac_name);
17904 decr(ref_count(c)); mp_get_x_next(mp);
17912 @ Finally we reach the deepest level in our quartet of parsing routines.
17913 This one is much like the others; but it has an extra complication from
17914 paths, which materialize here.
17916 @d continue_path 25 /* a label inside of |scan_expression| */
17917 @d finish_path 26 /* another */
17919 @<Declare the basic parsing subroutines@>=
17920 void mp_scan_expression (MP mp) {
17921 pointer p,q,r,pp,qq; /* for list manipulation */
17922 halfword c,d; /* operation codes or modifiers */
17923 int my_var_flag; /* initial value of |var_flag| */
17924 pointer mac_name; /* token defined with \&{tertiarydef} */
17925 boolean cycle_hit; /* did a path expression just end with `\&{cycle}'? */
17926 scaled x,y; /* explicit coordinates or tension at a path join */
17927 int t; /* knot type following a path join */
17929 my_var_flag=mp->var_flag; mac_name=null;
17931 if ((mp->cur_cmd<min_primary_command)||
17932 (mp->cur_cmd>max_primary_command) )
17933 mp_bad_exp(mp, "An");
17934 @.An expression...@>
17935 mp_scan_tertiary(mp);
17937 if ( mp->cur_cmd<=max_expression_command )
17938 if ( mp->cur_cmd>=min_expression_command ) {
17939 if ( (mp->cur_cmd!=equals)||(my_var_flag!=assignment) ) {
17940 p=mp_stash_cur_exp(mp); c=mp->cur_mod; d=mp->cur_cmd;
17941 if ( d==expression_tertiary_macro ) {
17942 mac_name=mp->cur_sym; add_mac_ref(c);
17944 if ( (d<ampersand)||((d==ampersand)&&
17945 ((type(p)==mp_pair_type)||(type(p)==mp_path_type))) ) {
17946 @<Scan a path construction operation;
17947 but |return| if |p| has the wrong type@>;
17949 mp_get_x_next(mp); mp_scan_tertiary(mp);
17950 if ( d!=expression_tertiary_macro ) {
17951 mp_do_binary(mp, p,c);
17953 mp_back_input(mp); mp_binary_mac(mp, p,c,mac_name);
17954 decr(ref_count(c)); mp_get_x_next(mp);
17963 @ The reader should review the data structure conventions for paths before
17964 hoping to understand the next part of this code.
17966 @<Scan a path construction operation...@>=
17969 @<Convert the left operand, |p|, into a partial path ending at~|q|;
17970 but |return| if |p| doesn't have a suitable type@>;
17972 @<Determine the path join parameters;
17973 but |goto finish_path| if there's only a direction specifier@>;
17974 if ( mp->cur_cmd==cycle ) {
17975 @<Get ready to close a cycle@>;
17977 mp_scan_tertiary(mp);
17978 @<Convert the right operand, |cur_exp|,
17979 into a partial path from |pp| to~|qq|@>;
17981 @<Join the partial paths and reset |p| and |q| to the head and tail
17983 if ( mp->cur_cmd>=min_expression_command )
17984 if ( mp->cur_cmd<=ampersand ) if ( ! cycle_hit ) goto CONTINUE_PATH;
17986 @<Choose control points for the path and put the result into |cur_exp|@>;
17989 @ @<Convert the left operand, |p|, into a partial path ending at~|q|...@>=
17991 mp_unstash_cur_exp(mp, p);
17992 if ( mp->cur_type==mp_pair_type ) p=mp_new_knot(mp);
17993 else if ( mp->cur_type==mp_path_type ) p=mp->cur_exp;
17996 while ( link(q)!=p ) q=link(q);
17997 if ( left_type(p)!=mp_endpoint ) { /* open up a cycle */
17998 r=mp_copy_knot(mp, p); link(q)=r; q=r;
18000 left_type(p)=mp_open; right_type(q)=mp_open;
18003 @ A pair of numeric values is changed into a knot node for a one-point path
18004 when \MP\ discovers that the pair is part of a path.
18006 @c@<Declare the procedure called |known_pair|@>;
18007 pointer mp_new_knot (MP mp) { /* convert a pair to a knot with two endpoints */
18008 pointer q; /* the new node */
18009 q=mp_get_node(mp, knot_node_size); left_type(q)=mp_endpoint;
18010 right_type(q)=mp_endpoint; originator(q)=mp_metapost_user; link(q)=q;
18011 mp_known_pair(mp); x_coord(q)=mp->cur_x; y_coord(q)=mp->cur_y;
18015 @ The |known_pair| subroutine sets |cur_x| and |cur_y| to the components
18016 of the current expression, assuming that the current expression is a
18017 pair of known numerics. Unknown components are zeroed, and the
18018 current expression is flushed.
18020 @<Declare the procedure called |known_pair|@>=
18021 void mp_known_pair (MP mp) {
18022 pointer p; /* the pair node */
18023 if ( mp->cur_type!=mp_pair_type ) {
18024 exp_err("Undefined coordinates have been replaced by (0,0)");
18025 @.Undefined coordinates...@>
18026 help5("I need x and y numbers for this part of the path.")
18027 ("The value I found (see above) was no good;")
18028 ("so I'll try to keep going by using zero instead.")
18029 ("(Chapter 27 of The METAFONTbook explains that")
18030 @:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
18031 ("you might want to type `I ??" "?' now.)");
18032 mp_put_get_flush_error(mp, 0); mp->cur_x=0; mp->cur_y=0;
18034 p=value(mp->cur_exp);
18035 @<Make sure that both |x| and |y| parts of |p| are known;
18036 copy them into |cur_x| and |cur_y|@>;
18037 mp_flush_cur_exp(mp, 0);
18041 @ @<Make sure that both |x| and |y| parts of |p| are known...@>=
18042 if ( type(x_part_loc(p))==mp_known ) {
18043 mp->cur_x=value(x_part_loc(p));
18045 mp_disp_err(mp, x_part_loc(p),
18046 "Undefined x coordinate has been replaced by 0");
18047 @.Undefined coordinates...@>
18048 help5("I need a `known' x value for this part of the path.")
18049 ("The value I found (see above) was no good;")
18050 ("so I'll try to keep going by using zero instead.")
18051 ("(Chapter 27 of The METAFONTbook explains that")
18052 @:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
18053 ("you might want to type `I ??" "?' now.)");
18054 mp_put_get_error(mp); mp_recycle_value(mp, x_part_loc(p)); mp->cur_x=0;
18056 if ( type(y_part_loc(p))==mp_known ) {
18057 mp->cur_y=value(y_part_loc(p));
18059 mp_disp_err(mp, y_part_loc(p),
18060 "Undefined y coordinate has been replaced by 0");
18061 help5("I need a `known' y value for this part of the path.")
18062 ("The value I found (see above) was no good;")
18063 ("so I'll try to keep going by using zero instead.")
18064 ("(Chapter 27 of The METAFONTbook explains that")
18065 ("you might want to type `I ??" "?' now.)");
18066 mp_put_get_error(mp); mp_recycle_value(mp, y_part_loc(p)); mp->cur_y=0;
18069 @ At this point |cur_cmd| is either |ampersand|, |left_brace|, or |path_join|.
18071 @<Determine the path join parameters...@>=
18072 if ( mp->cur_cmd==left_brace ) {
18073 @<Put the pre-join direction information into node |q|@>;
18076 if ( d==path_join ) {
18077 @<Determine the tension and/or control points@>;
18078 } else if ( d!=ampersand ) {
18082 if ( mp->cur_cmd==left_brace ) {
18083 @<Put the post-join direction information into |x| and |t|@>;
18084 } else if ( right_type(q)!=mp_explicit ) {
18088 @ The |scan_direction| subroutine looks at the directional information
18089 that is enclosed in braces, and also scans ahead to the following character.
18090 A type code is returned, either |open| (if the direction was $(0,0)$),
18091 or |curl| (if the direction was a curl of known value |cur_exp|), or
18092 |given| (if the direction is given by the |angle| value that now
18093 appears in |cur_exp|).
18095 There's nothing difficult about this subroutine, but the program is rather
18096 lengthy because a variety of potential errors need to be nipped in the bud.
18098 @c small_number mp_scan_direction (MP mp) {
18099 int t; /* the type of information found */
18100 scaled x; /* an |x| coordinate */
18102 if ( mp->cur_cmd==curl_command ) {
18103 @<Scan a curl specification@>;
18105 @<Scan a given direction@>;
18107 if ( mp->cur_cmd!=right_brace ) {
18108 mp_missing_err(mp, "}");
18109 @.Missing `\char`\}'@>
18110 help3("I've scanned a direction spec for part of a path,")
18111 ("so a right brace should have come next.")
18112 ("I shall pretend that one was there.");
18119 @ @<Scan a curl specification@>=
18120 { mp_get_x_next(mp); mp_scan_expression(mp);
18121 if ( (mp->cur_type!=mp_known)||(mp->cur_exp<0) ){
18122 exp_err("Improper curl has been replaced by 1");
18124 help1("A curl must be a known, nonnegative number.");
18125 mp_put_get_flush_error(mp, unity);
18130 @ @<Scan a given direction@>=
18131 { mp_scan_expression(mp);
18132 if ( mp->cur_type>mp_pair_type ) {
18133 @<Get given directions separated by commas@>;
18137 if ( (mp->cur_x==0)&&(mp->cur_y==0) ) t=mp_open;
18138 else { t=mp_given; mp->cur_exp=mp_n_arg(mp, mp->cur_x,mp->cur_y);}
18141 @ @<Get given directions separated by commas@>=
18143 if ( mp->cur_type!=mp_known ) {
18144 exp_err("Undefined x coordinate has been replaced by 0");
18145 @.Undefined coordinates...@>
18146 help5("I need a `known' x value for this part of the path.")
18147 ("The value I found (see above) was no good;")
18148 ("so I'll try to keep going by using zero instead.")
18149 ("(Chapter 27 of The METAFONTbook explains that")
18150 @:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
18151 ("you might want to type `I ??" "?' now.)");
18152 mp_put_get_flush_error(mp, 0);
18155 if ( mp->cur_cmd!=comma ) {
18156 mp_missing_err(mp, ",");
18158 help2("I've got the x coordinate of a path direction;")
18159 ("will look for the y coordinate next.");
18162 mp_get_x_next(mp); mp_scan_expression(mp);
18163 if ( mp->cur_type!=mp_known ) {
18164 exp_err("Undefined y coordinate has been replaced by 0");
18165 help5("I need a `known' y value for this part of the path.")
18166 ("The value I found (see above) was no good;")
18167 ("so I'll try to keep going by using zero instead.")
18168 ("(Chapter 27 of The METAFONTbook explains that")
18169 ("you might want to type `I ??" "?' now.)");
18170 mp_put_get_flush_error(mp, 0);
18172 mp->cur_y=mp->cur_exp; mp->cur_x=x;
18175 @ At this point |right_type(q)| is usually |open|, but it may have been
18176 set to some other value by a previous splicing operation. We must maintain
18177 the value of |right_type(q)| in unusual cases such as
18178 `\.{..z1\{z2\}\&\{z3\}z1\{0,0\}..}'.
18180 @<Put the pre-join...@>=
18182 t=mp_scan_direction(mp);
18183 if ( t!=mp_open ) {
18184 right_type(q)=t; right_given(q)=mp->cur_exp;
18185 if ( left_type(q)==mp_open ) {
18186 left_type(q)=t; left_given(q)=mp->cur_exp;
18187 } /* note that |left_given(q)=left_curl(q)| */
18191 @ Since |left_tension| and |left_y| share the same position in knot nodes,
18192 and since |left_given| is similarly equivalent to |left_x|, we use
18193 |x| and |y| to hold the given direction and tension information when
18194 there are no explicit control points.
18196 @<Put the post-join...@>=
18198 t=mp_scan_direction(mp);
18199 if ( right_type(q)!=mp_explicit ) x=mp->cur_exp;
18200 else t=mp_explicit; /* the direction information is superfluous */
18203 @ @<Determine the tension and/or...@>=
18206 if ( mp->cur_cmd==tension ) {
18207 @<Set explicit tensions@>;
18208 } else if ( mp->cur_cmd==controls ) {
18209 @<Set explicit control points@>;
18211 right_tension(q)=unity; y=unity; mp_back_input(mp); /* default tension */
18214 if ( mp->cur_cmd!=path_join ) {
18215 mp_missing_err(mp, "..");
18217 help1("A path join command should end with two dots.");
18224 @ @<Set explicit tensions@>=
18226 mp_get_x_next(mp); y=mp->cur_cmd;
18227 if ( mp->cur_cmd==at_least ) mp_get_x_next(mp);
18228 mp_scan_primary(mp);
18229 @<Make sure that the current expression is a valid tension setting@>;
18230 if ( y==at_least ) negate(mp->cur_exp);
18231 right_tension(q)=mp->cur_exp;
18232 if ( mp->cur_cmd==and_command ) {
18233 mp_get_x_next(mp); y=mp->cur_cmd;
18234 if ( mp->cur_cmd==at_least ) mp_get_x_next(mp);
18235 mp_scan_primary(mp);
18236 @<Make sure that the current expression is a valid tension setting@>;
18237 if ( y==at_least ) negate(mp->cur_exp);
18242 @ @d min_tension three_quarter_unit
18244 @<Make sure that the current expression is a valid tension setting@>=
18245 if ( (mp->cur_type!=mp_known)||(mp->cur_exp<min_tension) ) {
18246 exp_err("Improper tension has been set to 1");
18247 @.Improper tension@>
18248 help1("The expression above should have been a number >=3/4.");
18249 mp_put_get_flush_error(mp, unity);
18252 @ @<Set explicit control points@>=
18254 right_type(q)=mp_explicit; t=mp_explicit; mp_get_x_next(mp); mp_scan_primary(mp);
18255 mp_known_pair(mp); right_x(q)=mp->cur_x; right_y(q)=mp->cur_y;
18256 if ( mp->cur_cmd!=and_command ) {
18257 x=right_x(q); y=right_y(q);
18259 mp_get_x_next(mp); mp_scan_primary(mp);
18260 mp_known_pair(mp); x=mp->cur_x; y=mp->cur_y;
18264 @ @<Convert the right operand, |cur_exp|, into a partial path...@>=
18266 if ( mp->cur_type!=mp_path_type ) pp=mp_new_knot(mp);
18267 else pp=mp->cur_exp;
18269 while ( link(qq)!=pp ) qq=link(qq);
18270 if ( left_type(pp)!=mp_endpoint ) { /* open up a cycle */
18271 r=mp_copy_knot(mp, pp); link(qq)=r; qq=r;
18273 left_type(pp)=mp_open; right_type(qq)=mp_open;
18276 @ If a person tries to define an entire path by saying `\.{(x,y)\&cycle}',
18277 we silently change the specification to `\.{(x,y)..cycle}', since a cycle
18278 shouldn't have length zero.
18280 @<Get ready to close a cycle@>=
18282 cycle_hit=true; mp_get_x_next(mp); pp=p; qq=p;
18283 if ( d==ampersand ) if ( p==q ) {
18284 d=path_join; right_tension(q)=unity; y=unity;
18288 @ @<Join the partial paths and reset |p| and |q|...@>=
18290 if ( d==ampersand ) {
18291 if ( (x_coord(q)!=x_coord(pp))||(y_coord(q)!=y_coord(pp)) ) {
18292 print_err("Paths don't touch; `&' will be changed to `..'");
18293 @.Paths don't touch@>
18294 help3("When you join paths `p&q', the ending point of p")
18295 ("must be exactly equal to the starting point of q.")
18296 ("So I'm going to pretend that you said `p..q' instead.");
18297 mp_put_get_error(mp); d=path_join; right_tension(q)=unity; y=unity;
18300 @<Plug an opening in |right_type(pp)|, if possible@>;
18301 if ( d==ampersand ) {
18302 @<Splice independent paths together@>;
18304 @<Plug an opening in |right_type(q)|, if possible@>;
18305 link(q)=pp; left_y(pp)=y;
18306 if ( t!=mp_open ) { left_x(pp)=x; left_type(pp)=t; };
18311 @ @<Plug an opening in |right_type(q)|...@>=
18312 if ( right_type(q)==mp_open ) {
18313 if ( (left_type(q)==mp_curl)||(left_type(q)==mp_given) ) {
18314 right_type(q)=left_type(q); right_given(q)=left_given(q);
18318 @ @<Plug an opening in |right_type(pp)|...@>=
18319 if ( right_type(pp)==mp_open ) {
18320 if ( (t==mp_curl)||(t==mp_given) ) {
18321 right_type(pp)=t; right_given(pp)=x;
18325 @ @<Splice independent paths together@>=
18327 if ( left_type(q)==mp_open ) if ( right_type(q)==mp_open ) {
18328 left_type(q)=mp_curl; left_curl(q)=unity;
18330 if ( right_type(pp)==mp_open ) if ( t==mp_open ) {
18331 right_type(pp)=mp_curl; right_curl(pp)=unity;
18333 right_type(q)=right_type(pp); link(q)=link(pp);
18334 right_x(q)=right_x(pp); right_y(q)=right_y(pp);
18335 mp_free_node(mp, pp,knot_node_size);
18336 if ( qq==pp ) qq=q;
18339 @ @<Choose control points for the path...@>=
18341 if ( d==ampersand ) p=q;
18343 left_type(p)=mp_endpoint;
18344 if ( right_type(p)==mp_open ) {
18345 right_type(p)=mp_curl; right_curl(p)=unity;
18347 right_type(q)=mp_endpoint;
18348 if ( left_type(q)==mp_open ) {
18349 left_type(q)=mp_curl; left_curl(q)=unity;
18353 mp_make_choices(mp, p);
18354 mp->cur_type=mp_path_type; mp->cur_exp=p
18356 @ Finally, we sometimes need to scan an expression whose value is
18357 supposed to be either |true_code| or |false_code|.
18359 @<Declare the basic parsing subroutines@>=
18360 void mp_get_boolean (MP mp) {
18361 mp_get_x_next(mp); mp_scan_expression(mp);
18362 if ( mp->cur_type!=mp_boolean_type ) {
18363 exp_err("Undefined condition will be treated as `false'");
18364 @.Undefined condition...@>
18365 help2("The expression shown above should have had a definite")
18366 ("true-or-false value. I'm changing it to `false'.");
18367 mp_put_get_flush_error(mp, false_code); mp->cur_type=mp_boolean_type;
18371 @* \[39] Doing the operations.
18372 The purpose of parsing is primarily to permit people to avoid piles of
18373 parentheses. But the real work is done after the structure of an expression
18374 has been recognized; that's when new expressions are generated. We
18375 turn now to the guts of \MP, which handles individual operators that
18376 have come through the parsing mechanism.
18378 We'll start with the easy ones that take no operands, then work our way
18379 up to operators with one and ultimately two arguments. In other words,
18380 we will write the three procedures |do_nullary|, |do_unary|, and |do_binary|
18381 that are invoked periodically by the expression scanners.
18383 First let's make sure that all of the primitive operators are in the
18384 hash table. Although |scan_primary| and its relatives made use of the
18385 \\{cmd} code for these operators, the \\{do} routines base everything
18386 on the \\{mod} code. For example, |do_binary| doesn't care whether the
18387 operation it performs is a |primary_binary| or |secondary_binary|, etc.
18390 mp_primitive(mp, "true",nullary,true_code);
18391 @:true_}{\&{true} primitive@>
18392 mp_primitive(mp, "false",nullary,false_code);
18393 @:false_}{\&{false} primitive@>
18394 mp_primitive(mp, "nullpicture",nullary,null_picture_code);
18395 @:null_picture_}{\&{nullpicture} primitive@>
18396 mp_primitive(mp, "nullpen",nullary,null_pen_code);
18397 @:null_pen_}{\&{nullpen} primitive@>
18398 mp_primitive(mp, "jobname",nullary,job_name_op);
18399 @:job_name_}{\&{jobname} primitive@>
18400 mp_primitive(mp, "readstring",nullary,read_string_op);
18401 @:read_string_}{\&{readstring} primitive@>
18402 mp_primitive(mp, "pencircle",nullary,pen_circle);
18403 @:pen_circle_}{\&{pencircle} primitive@>
18404 mp_primitive(mp, "normaldeviate",nullary,normal_deviate);
18405 @:normal_deviate_}{\&{normaldeviate} primitive@>
18406 mp_primitive(mp, "readfrom",unary,read_from_op);
18407 @:read_from_}{\&{readfrom} primitive@>
18408 mp_primitive(mp, "closefrom",unary,close_from_op);
18409 @:close_from_}{\&{closefrom} primitive@>
18410 mp_primitive(mp, "odd",unary,odd_op);
18411 @:odd_}{\&{odd} primitive@>
18412 mp_primitive(mp, "known",unary,known_op);
18413 @:known_}{\&{known} primitive@>
18414 mp_primitive(mp, "unknown",unary,unknown_op);
18415 @:unknown_}{\&{unknown} primitive@>
18416 mp_primitive(mp, "not",unary,not_op);
18417 @:not_}{\&{not} primitive@>
18418 mp_primitive(mp, "decimal",unary,decimal);
18419 @:decimal_}{\&{decimal} primitive@>
18420 mp_primitive(mp, "reverse",unary,reverse);
18421 @:reverse_}{\&{reverse} primitive@>
18422 mp_primitive(mp, "makepath",unary,make_path_op);
18423 @:make_path_}{\&{makepath} primitive@>
18424 mp_primitive(mp, "makepen",unary,make_pen_op);
18425 @:make_pen_}{\&{makepen} primitive@>
18426 mp_primitive(mp, "oct",unary,oct_op);
18427 @:oct_}{\&{oct} primitive@>
18428 mp_primitive(mp, "hex",unary,hex_op);
18429 @:hex_}{\&{hex} primitive@>
18430 mp_primitive(mp, "ASCII",unary,ASCII_op);
18431 @:ASCII_}{\&{ASCII} primitive@>
18432 mp_primitive(mp, "char",unary,char_op);
18433 @:char_}{\&{char} primitive@>
18434 mp_primitive(mp, "length",unary,length_op);
18435 @:length_}{\&{length} primitive@>
18436 mp_primitive(mp, "turningnumber",unary,turning_op);
18437 @:turning_number_}{\&{turningnumber} primitive@>
18438 mp_primitive(mp, "xpart",unary,x_part);
18439 @:x_part_}{\&{xpart} primitive@>
18440 mp_primitive(mp, "ypart",unary,y_part);
18441 @:y_part_}{\&{ypart} primitive@>
18442 mp_primitive(mp, "xxpart",unary,xx_part);
18443 @:xx_part_}{\&{xxpart} primitive@>
18444 mp_primitive(mp, "xypart",unary,xy_part);
18445 @:xy_part_}{\&{xypart} primitive@>
18446 mp_primitive(mp, "yxpart",unary,yx_part);
18447 @:yx_part_}{\&{yxpart} primitive@>
18448 mp_primitive(mp, "yypart",unary,yy_part);
18449 @:yy_part_}{\&{yypart} primitive@>
18450 mp_primitive(mp, "redpart",unary,red_part);
18451 @:red_part_}{\&{redpart} primitive@>
18452 mp_primitive(mp, "greenpart",unary,green_part);
18453 @:green_part_}{\&{greenpart} primitive@>
18454 mp_primitive(mp, "bluepart",unary,blue_part);
18455 @:blue_part_}{\&{bluepart} primitive@>
18456 mp_primitive(mp, "cyanpart",unary,cyan_part);
18457 @:cyan_part_}{\&{cyanpart} primitive@>
18458 mp_primitive(mp, "magentapart",unary,magenta_part);
18459 @:magenta_part_}{\&{magentapart} primitive@>
18460 mp_primitive(mp, "yellowpart",unary,yellow_part);
18461 @:yellow_part_}{\&{yellowpart} primitive@>
18462 mp_primitive(mp, "blackpart",unary,black_part);
18463 @:black_part_}{\&{blackpart} primitive@>
18464 mp_primitive(mp, "greypart",unary,grey_part);
18465 @:grey_part_}{\&{greypart} primitive@>
18466 mp_primitive(mp, "colormodel",unary,color_model_part);
18467 @:color_model_part_}{\&{colormodel} primitive@>
18468 mp_primitive(mp, "fontpart",unary,font_part);
18469 @:font_part_}{\&{fontpart} primitive@>
18470 mp_primitive(mp, "textpart",unary,text_part);
18471 @:text_part_}{\&{textpart} primitive@>
18472 mp_primitive(mp, "pathpart",unary,path_part);
18473 @:path_part_}{\&{pathpart} primitive@>
18474 mp_primitive(mp, "penpart",unary,pen_part);
18475 @:pen_part_}{\&{penpart} primitive@>
18476 mp_primitive(mp, "dashpart",unary,dash_part);
18477 @:dash_part_}{\&{dashpart} primitive@>
18478 mp_primitive(mp, "sqrt",unary,sqrt_op);
18479 @:sqrt_}{\&{sqrt} primitive@>
18480 mp_primitive(mp, "mexp",unary,m_exp_op);
18481 @:m_exp_}{\&{mexp} primitive@>
18482 mp_primitive(mp, "mlog",unary,m_log_op);
18483 @:m_log_}{\&{mlog} primitive@>
18484 mp_primitive(mp, "sind",unary,sin_d_op);
18485 @:sin_d_}{\&{sind} primitive@>
18486 mp_primitive(mp, "cosd",unary,cos_d_op);
18487 @:cos_d_}{\&{cosd} primitive@>
18488 mp_primitive(mp, "floor",unary,floor_op);
18489 @:floor_}{\&{floor} primitive@>
18490 mp_primitive(mp, "uniformdeviate",unary,uniform_deviate);
18491 @:uniform_deviate_}{\&{uniformdeviate} primitive@>
18492 mp_primitive(mp, "charexists",unary,char_exists_op);
18493 @:char_exists_}{\&{charexists} primitive@>
18494 mp_primitive(mp, "fontsize",unary,font_size);
18495 @:font_size_}{\&{fontsize} primitive@>
18496 mp_primitive(mp, "llcorner",unary,ll_corner_op);
18497 @:ll_corner_}{\&{llcorner} primitive@>
18498 mp_primitive(mp, "lrcorner",unary,lr_corner_op);
18499 @:lr_corner_}{\&{lrcorner} primitive@>
18500 mp_primitive(mp, "ulcorner",unary,ul_corner_op);
18501 @:ul_corner_}{\&{ulcorner} primitive@>
18502 mp_primitive(mp, "urcorner",unary,ur_corner_op);
18503 @:ur_corner_}{\&{urcorner} primitive@>
18504 mp_primitive(mp, "arclength",unary,arc_length);
18505 @:arc_length_}{\&{arclength} primitive@>
18506 mp_primitive(mp, "angle",unary,angle_op);
18507 @:angle_}{\&{angle} primitive@>
18508 mp_primitive(mp, "cycle",cycle,cycle_op);
18509 @:cycle_}{\&{cycle} primitive@>
18510 mp_primitive(mp, "stroked",unary,stroked_op);
18511 @:stroked_}{\&{stroked} primitive@>
18512 mp_primitive(mp, "filled",unary,filled_op);
18513 @:filled_}{\&{filled} primitive@>
18514 mp_primitive(mp, "textual",unary,textual_op);
18515 @:textual_}{\&{textual} primitive@>
18516 mp_primitive(mp, "clipped",unary,clipped_op);
18517 @:clipped_}{\&{clipped} primitive@>
18518 mp_primitive(mp, "bounded",unary,bounded_op);
18519 @:bounded_}{\&{bounded} primitive@>
18520 mp_primitive(mp, "+",plus_or_minus,plus);
18521 @:+ }{\.{+} primitive@>
18522 mp_primitive(mp, "-",plus_or_minus,minus);
18523 @:- }{\.{-} primitive@>
18524 mp_primitive(mp, "*",secondary_binary,times);
18525 @:* }{\.{*} primitive@>
18526 mp_primitive(mp, "/",slash,over); mp->eqtb[frozen_slash]=mp->eqtb[mp->cur_sym];
18527 @:/ }{\.{/} primitive@>
18528 mp_primitive(mp, "++",tertiary_binary,pythag_add);
18529 @:++_}{\.{++} primitive@>
18530 mp_primitive(mp, "+-+",tertiary_binary,pythag_sub);
18531 @:+-+_}{\.{+-+} primitive@>
18532 mp_primitive(mp, "or",tertiary_binary,or_op);
18533 @:or_}{\&{or} primitive@>
18534 mp_primitive(mp, "and",and_command,and_op);
18535 @:and_}{\&{and} primitive@>
18536 mp_primitive(mp, "<",expression_binary,less_than);
18537 @:< }{\.{<} primitive@>
18538 mp_primitive(mp, "<=",expression_binary,less_or_equal);
18539 @:<=_}{\.{<=} primitive@>
18540 mp_primitive(mp, ">",expression_binary,greater_than);
18541 @:> }{\.{>} primitive@>
18542 mp_primitive(mp, ">=",expression_binary,greater_or_equal);
18543 @:>=_}{\.{>=} primitive@>
18544 mp_primitive(mp, "=",equals,equal_to);
18545 @:= }{\.{=} primitive@>
18546 mp_primitive(mp, "<>",expression_binary,unequal_to);
18547 @:<>_}{\.{<>} primitive@>
18548 mp_primitive(mp, "substring",primary_binary,substring_of);
18549 @:substring_}{\&{substring} primitive@>
18550 mp_primitive(mp, "subpath",primary_binary,subpath_of);
18551 @:subpath_}{\&{subpath} primitive@>
18552 mp_primitive(mp, "directiontime",primary_binary,direction_time_of);
18553 @:direction_time_}{\&{directiontime} primitive@>
18554 mp_primitive(mp, "point",primary_binary,point_of);
18555 @:point_}{\&{point} primitive@>
18556 mp_primitive(mp, "precontrol",primary_binary,precontrol_of);
18557 @:precontrol_}{\&{precontrol} primitive@>
18558 mp_primitive(mp, "postcontrol",primary_binary,postcontrol_of);
18559 @:postcontrol_}{\&{postcontrol} primitive@>
18560 mp_primitive(mp, "penoffset",primary_binary,pen_offset_of);
18561 @:pen_offset_}{\&{penoffset} primitive@>
18562 mp_primitive(mp, "arctime",primary_binary,arc_time_of);
18563 @:arc_time_of_}{\&{arctime} primitive@>
18564 mp_primitive(mp, "mpversion",nullary,mp_version);
18565 @:mp_verison_}{\&{mpversion} primitive@>
18566 mp_primitive(mp, "&",ampersand,concatenate);
18567 @:!!!}{\.{\&} primitive@>
18568 mp_primitive(mp, "rotated",secondary_binary,rotated_by);
18569 @:rotated_}{\&{rotated} primitive@>
18570 mp_primitive(mp, "slanted",secondary_binary,slanted_by);
18571 @:slanted_}{\&{slanted} primitive@>
18572 mp_primitive(mp, "scaled",secondary_binary,scaled_by);
18573 @:scaled_}{\&{scaled} primitive@>
18574 mp_primitive(mp, "shifted",secondary_binary,shifted_by);
18575 @:shifted_}{\&{shifted} primitive@>
18576 mp_primitive(mp, "transformed",secondary_binary,transformed_by);
18577 @:transformed_}{\&{transformed} primitive@>
18578 mp_primitive(mp, "xscaled",secondary_binary,x_scaled);
18579 @:x_scaled_}{\&{xscaled} primitive@>
18580 mp_primitive(mp, "yscaled",secondary_binary,y_scaled);
18581 @:y_scaled_}{\&{yscaled} primitive@>
18582 mp_primitive(mp, "zscaled",secondary_binary,z_scaled);
18583 @:z_scaled_}{\&{zscaled} primitive@>
18584 mp_primitive(mp, "infont",secondary_binary,in_font);
18585 @:in_font_}{\&{infont} primitive@>
18586 mp_primitive(mp, "intersectiontimes",tertiary_binary,intersect);
18587 @:intersection_times_}{\&{intersectiontimes} primitive@>
18588 mp_primitive(mp, "envelope",primary_binary,envelope_of);
18589 @:envelope_}{\&{envelope} primitive@>
18591 @ @<Cases of |print_cmd...@>=
18594 case primary_binary:
18595 case secondary_binary:
18596 case tertiary_binary:
18597 case expression_binary:
18599 case plus_or_minus:
18604 mp_print_op(mp, m);
18607 @ OK, let's look at the simplest \\{do} procedure first.
18609 @c @<Declare nullary action procedure@>;
18610 void mp_do_nullary (MP mp,quarterword c) {
18612 if ( mp->internal[mp_tracing_commands]>two )
18613 mp_show_cmd_mod(mp, nullary,c);
18615 case true_code: case false_code:
18616 mp->cur_type=mp_boolean_type; mp->cur_exp=c;
18618 case null_picture_code:
18619 mp->cur_type=mp_picture_type;
18620 mp->cur_exp=mp_get_node(mp, edge_header_size);
18621 mp_init_edges(mp, mp->cur_exp);
18623 case null_pen_code:
18624 mp->cur_type=mp_pen_type; mp->cur_exp=mp_get_pen_circle(mp, 0);
18626 case normal_deviate:
18627 mp->cur_type=mp_known; mp->cur_exp=mp_norm_rand(mp);
18630 mp->cur_type=mp_pen_type; mp->cur_exp=mp_get_pen_circle(mp, unity);
18633 if ( mp->job_name==NULL ) mp_open_log_file(mp);
18634 mp->cur_type=mp_string_type; mp->cur_exp=rts(mp->job_name);
18637 mp->cur_type=mp_string_type;
18638 mp->cur_exp=intern(metapost_version) ;
18640 case read_string_op:
18641 @<Read a string from the terminal@>;
18643 } /* there are no other cases */
18647 @ @<Read a string...@>=
18649 if ( mp->interaction<=mp_nonstop_mode )
18650 mp_fatal_error(mp, "*** (cannot readstring in nonstop modes)");
18651 mp_begin_file_reading(mp); name=is_read;
18652 limit=start; prompt_input("");
18653 mp_finish_read(mp);
18656 @ @<Declare nullary action procedure@>=
18657 void mp_finish_read (MP mp) { /* copy |buffer| line to |cur_exp| */
18659 str_room((int)mp->last-start);
18660 for (k=start;k<=mp->last-1;k++) {
18661 append_char(mp->buffer[k]);
18663 mp_end_file_reading(mp); mp->cur_type=mp_string_type;
18664 mp->cur_exp=mp_make_string(mp);
18667 @ Things get a bit more interesting when there's an operand. The
18668 operand to |do_unary| appears in |cur_type| and |cur_exp|.
18670 @c @<Declare unary action procedures@>;
18671 void mp_do_unary (MP mp,quarterword c) {
18672 pointer p,q,r; /* for list manipulation */
18673 integer x; /* a temporary register */
18675 if ( mp->internal[mp_tracing_commands]>two )
18676 @<Trace the current unary operation@>;
18679 if ( mp->cur_type<mp_color_type ) mp_bad_unary(mp, plus);
18682 @<Negate the current expression@>;
18684 @<Additional cases of unary operators@>;
18685 } /* there are no other cases */
18689 @ The |nice_pair| function returns |true| if both components of a pair
18692 @<Declare unary action procedures@>=
18693 boolean mp_nice_pair (MP mp,integer p, quarterword t) {
18694 if ( t==mp_pair_type ) {
18696 if ( type(x_part_loc(p))==mp_known )
18697 if ( type(y_part_loc(p))==mp_known )
18703 @ The |nice_color_or_pair| function is analogous except that it also accepts
18704 fully known colors.
18706 @<Declare unary action procedures@>=
18707 boolean mp_nice_color_or_pair (MP mp,integer p, quarterword t) {
18708 pointer q,r; /* for scanning the big node */
18709 if ( (t!=mp_pair_type)&&(t!=mp_color_type)&&(t!=mp_cmykcolor_type) ) {
18713 r=q+mp->big_node_size[type(p)];
18716 if ( type(r)!=mp_known )
18723 @ @<Declare unary action...@>=
18724 void mp_print_known_or_unknown_type (MP mp,small_number t, integer v) {
18725 mp_print_char(mp, '(');
18726 if ( t>mp_known ) mp_print(mp, "unknown numeric");
18727 else { if ( (t==mp_pair_type)||(t==mp_color_type)||(t==mp_cmykcolor_type) )
18728 if ( ! mp_nice_color_or_pair(mp, v,t) ) mp_print(mp, "unknown ");
18729 mp_print_type(mp, t);
18731 mp_print_char(mp, ')');
18734 @ @<Declare unary action...@>=
18735 void mp_bad_unary (MP mp,quarterword c) {
18736 exp_err("Not implemented: "); mp_print_op(mp, c);
18737 @.Not implemented...@>
18738 mp_print_known_or_unknown_type(mp, mp->cur_type,mp->cur_exp);
18739 help3("I'm afraid I don't know how to apply that operation to that")
18740 ("particular type. Continue, and I'll simply return the")
18741 ("argument (shown above) as the result of the operation.");
18742 mp_put_get_error(mp);
18745 @ @<Trace the current unary operation@>=
18747 mp_begin_diagnostic(mp); mp_print_nl(mp, "{");
18748 mp_print_op(mp, c); mp_print_char(mp, '(');
18749 mp_print_exp(mp, null,0); /* show the operand, but not verbosely */
18750 mp_print(mp, ")}"); mp_end_diagnostic(mp, false);
18753 @ Negation is easy except when the current expression
18754 is of type |independent|, or when it is a pair with one or more
18755 |independent| components.
18757 It is tempting to argue that the negative of an independent variable
18758 is an independent variable, hence we don't have to do anything when
18759 negating it. The fallacy is that other dependent variables pointing
18760 to the current expression must change the sign of their
18761 coefficients if we make no change to the current expression.
18763 Instead, we work around the problem by copying the current expression
18764 and recycling it afterwards (cf.~the |stash_in| routine).
18766 @<Negate the current expression@>=
18767 switch (mp->cur_type) {
18768 case mp_color_type:
18769 case mp_cmykcolor_type:
18771 case mp_independent:
18772 q=mp->cur_exp; mp_make_exp_copy(mp, q);
18773 if ( mp->cur_type==mp_dependent ) {
18774 mp_negate_dep_list(mp, dep_list(mp->cur_exp));
18775 } else if ( mp->cur_type<=mp_pair_type ) { /* |mp_color_type| or |mp_pair_type| */
18776 p=value(mp->cur_exp);
18777 r=p+mp->big_node_size[mp->cur_type];
18780 if ( type(r)==mp_known ) negate(value(r));
18781 else mp_negate_dep_list(mp, dep_list(r));
18783 } /* if |cur_type=mp_known| then |cur_exp=0| */
18784 mp_recycle_value(mp, q); mp_free_node(mp, q,value_node_size);
18787 case mp_proto_dependent:
18788 mp_negate_dep_list(mp, dep_list(mp->cur_exp));
18791 negate(mp->cur_exp);
18794 mp_bad_unary(mp, minus);
18798 @ @<Declare unary action...@>=
18799 void mp_negate_dep_list (MP mp,pointer p) {
18802 if ( info(p)==null ) return;
18807 @ @<Additional cases of unary operators@>=
18809 if ( mp->cur_type!=mp_boolean_type ) mp_bad_unary(mp, not_op);
18810 else mp->cur_exp=true_code+false_code-mp->cur_exp;
18813 @ @d three_sixty_units 23592960 /* that's |360*unity| */
18814 @d boolean_reset(A) if ( (A) ) mp->cur_exp=true_code; else mp->cur_exp=false_code
18816 @<Additional cases of unary operators@>=
18823 case uniform_deviate:
18825 case char_exists_op:
18826 if ( mp->cur_type!=mp_known ) {
18827 mp_bad_unary(mp, c);
18830 case sqrt_op:mp->cur_exp=mp_square_rt(mp, mp->cur_exp);break;
18831 case m_exp_op:mp->cur_exp=mp_m_exp(mp, mp->cur_exp);break;
18832 case m_log_op:mp->cur_exp=mp_m_log(mp, mp->cur_exp);break;
18835 mp_n_sin_cos(mp, (mp->cur_exp % three_sixty_units)*16);
18836 if ( c==sin_d_op ) mp->cur_exp=mp_round_fraction(mp, mp->n_sin);
18837 else mp->cur_exp=mp_round_fraction(mp, mp->n_cos);
18839 case floor_op:mp->cur_exp=mp_floor_scaled(mp, mp->cur_exp);break;
18840 case uniform_deviate:mp->cur_exp=mp_unif_rand(mp, mp->cur_exp);break;
18842 boolean_reset(odd(mp_round_unscaled(mp, mp->cur_exp)));
18843 mp->cur_type=mp_boolean_type;
18845 case char_exists_op:
18846 @<Determine if a character has been shipped out@>;
18848 } /* there are no other cases */
18852 @ @<Additional cases of unary operators@>=
18854 if ( mp_nice_pair(mp, mp->cur_exp,mp->cur_type) ) {
18855 p=value(mp->cur_exp);
18856 x=mp_n_arg(mp, value(x_part_loc(p)),value(y_part_loc(p)));
18857 if ( x>=0 ) mp_flush_cur_exp(mp, (x+8)/ 16);
18858 else mp_flush_cur_exp(mp, -((-x+8)/ 16));
18860 mp_bad_unary(mp, angle_op);
18864 @ If the current expression is a pair, but the context wants it to
18865 be a path, we call |pair_to_path|.
18867 @<Declare unary action...@>=
18868 void mp_pair_to_path (MP mp) {
18869 mp->cur_exp=mp_new_knot(mp);
18870 mp->cur_type=mp_path_type;
18873 @ @<Additional cases of unary operators@>=
18876 if ( (mp->cur_type==mp_pair_type)||(mp->cur_type==mp_transform_type) )
18877 mp_take_part(mp, c);
18878 else if ( mp->cur_type==mp_picture_type ) mp_take_pict_part(mp, c);
18879 else mp_bad_unary(mp, c);
18885 if ( mp->cur_type==mp_transform_type ) mp_take_part(mp, c);
18886 else if ( mp->cur_type==mp_picture_type ) mp_take_pict_part(mp, c);
18887 else mp_bad_unary(mp, c);
18892 if ( mp->cur_type==mp_color_type ) mp_take_part(mp, c);
18893 else if ( mp->cur_type==mp_picture_type ) mp_take_pict_part(mp, c);
18894 else mp_bad_unary(mp, c);
18900 if ( mp->cur_type==mp_cmykcolor_type) mp_take_part(mp, c);
18901 else if ( mp->cur_type==mp_picture_type ) mp_take_pict_part(mp, c);
18902 else mp_bad_unary(mp, c);
18905 if ( mp->cur_type==mp_known ) mp->cur_exp=value(c);
18906 else if ( mp->cur_type==mp_picture_type ) mp_take_pict_part(mp, c);
18907 else mp_bad_unary(mp, c);
18909 case color_model_part:
18910 if ( mp->cur_type==mp_picture_type ) mp_take_pict_part(mp, c);
18911 else mp_bad_unary(mp, c);
18914 @ In the following procedure, |cur_exp| points to a capsule, which points to
18915 a big node. We want to delete all but one part of the big node.
18917 @<Declare unary action...@>=
18918 void mp_take_part (MP mp,quarterword c) {
18919 pointer p; /* the big node */
18920 p=value(mp->cur_exp); value(temp_val)=p; type(temp_val)=mp->cur_type;
18921 link(p)=temp_val; mp_free_node(mp, mp->cur_exp,value_node_size);
18922 mp_make_exp_copy(mp, p+mp->sector_offset[c+mp_x_part_sector-x_part]);
18923 mp_recycle_value(mp, temp_val);
18926 @ @<Initialize table entries...@>=
18927 name_type(temp_val)=mp_capsule;
18929 @ @<Additional cases of unary operators@>=
18935 if ( mp->cur_type==mp_picture_type ) mp_take_pict_part(mp, c);
18936 else mp_bad_unary(mp, c);
18939 @ @<Declarations@>=
18940 void mp_scale_edges (MP mp);
18942 @ @<Declare unary action...@>=
18943 void mp_take_pict_part (MP mp,quarterword c) {
18944 pointer p; /* first graphical object in |cur_exp| */
18945 p=link(dummy_loc(mp->cur_exp));
18948 case x_part: case y_part: case xx_part:
18949 case xy_part: case yx_part: case yy_part:
18950 if ( type(p)==mp_text_code ) mp_flush_cur_exp(mp, text_trans_part(p+c));
18951 else goto NOT_FOUND;
18953 case red_part: case green_part: case blue_part:
18954 if ( has_color(p) ) mp_flush_cur_exp(mp, obj_color_part(p+c));
18955 else goto NOT_FOUND;
18957 case cyan_part: case magenta_part: case yellow_part:
18959 if ( has_color(p) ) {
18960 if ( color_model(p)==mp_uninitialized_model )
18961 mp_flush_cur_exp(mp, unity);
18963 mp_flush_cur_exp(mp, obj_color_part(p+c+(red_part-cyan_part)));
18964 } else goto NOT_FOUND;
18967 if ( has_color(p) )
18968 mp_flush_cur_exp(mp, obj_color_part(p+c+(red_part-grey_part)));
18969 else goto NOT_FOUND;
18971 case color_model_part:
18972 if ( has_color(p) ) {
18973 if ( color_model(p)==mp_uninitialized_model )
18974 mp_flush_cur_exp(mp, mp->internal[mp_default_color_model]);
18976 mp_flush_cur_exp(mp, color_model(p)*unity);
18977 } else goto NOT_FOUND;
18979 @<Handle other cases in |take_pict_part| or |goto not_found|@>;
18980 } /* all cases have been enumerated */
18984 @<Convert the current expression to a null value appropriate
18988 @ @<Handle other cases in |take_pict_part| or |goto not_found|@>=
18990 if ( type(p)!=mp_text_code ) goto NOT_FOUND;
18992 mp_flush_cur_exp(mp, text_p(p));
18993 add_str_ref(mp->cur_exp);
18994 mp->cur_type=mp_string_type;
18998 if ( type(p)!=mp_text_code ) goto NOT_FOUND;
19000 mp_flush_cur_exp(mp, rts(mp->font_name[font_n(p)]));
19001 add_str_ref(mp->cur_exp);
19002 mp->cur_type=mp_string_type;
19006 if ( type(p)==mp_text_code ) goto NOT_FOUND;
19007 else if ( is_stop(p) ) mp_confusion(mp, "pict");
19008 @:this can't happen pict}{\quad pict@>
19010 mp_flush_cur_exp(mp, mp_copy_path(mp, path_p(p)));
19011 mp->cur_type=mp_path_type;
19015 if ( ! has_pen(p) ) goto NOT_FOUND;
19017 if ( pen_p(p)==null ) goto NOT_FOUND;
19018 else { mp_flush_cur_exp(mp, copy_pen(pen_p(p)));
19019 mp->cur_type=mp_pen_type;
19024 if ( type(p)!=mp_stroked_code ) goto NOT_FOUND;
19025 else { if ( dash_p(p)==null ) goto NOT_FOUND;
19026 else { add_edge_ref(dash_p(p));
19027 mp->se_sf=dash_scale(p);
19028 mp->se_pic=dash_p(p);
19029 mp_scale_edges(mp);
19030 mp_flush_cur_exp(mp, mp->se_pic);
19031 mp->cur_type=mp_picture_type;
19036 @ Since |scale_edges| had to be declared |forward|, it had to be declared as a
19037 parameterless procedure even though it really takes two arguments and updates
19038 one of them. Hence the following globals are needed.
19041 pointer se_pic; /* edge header used and updated by |scale_edges| */
19042 scaled se_sf; /* the scale factor argument to |scale_edges| */
19044 @ @<Convert the current expression to a null value appropriate...@>=
19046 case text_part: case font_part:
19047 mp_flush_cur_exp(mp, rts(""));
19048 mp->cur_type=mp_string_type;
19051 mp_flush_cur_exp(mp, mp_get_node(mp, knot_node_size));
19052 left_type(mp->cur_exp)=mp_endpoint;
19053 right_type(mp->cur_exp)=mp_endpoint;
19054 link(mp->cur_exp)=mp->cur_exp;
19055 x_coord(mp->cur_exp)=0;
19056 y_coord(mp->cur_exp)=0;
19057 originator(mp->cur_exp)=mp_metapost_user;
19058 mp->cur_type=mp_path_type;
19061 mp_flush_cur_exp(mp, mp_get_pen_circle(mp, 0));
19062 mp->cur_type=mp_pen_type;
19065 mp_flush_cur_exp(mp, mp_get_node(mp, edge_header_size));
19066 mp_init_edges(mp, mp->cur_exp);
19067 mp->cur_type=mp_picture_type;
19070 mp_flush_cur_exp(mp, 0);
19074 @ @<Additional cases of unary...@>=
19076 if ( mp->cur_type!=mp_known ) {
19077 mp_bad_unary(mp, char_op);
19079 mp->cur_exp=mp_round_unscaled(mp, mp->cur_exp) % 256;
19080 mp->cur_type=mp_string_type;
19081 if ( mp->cur_exp<0 ) mp->cur_exp=mp->cur_exp+256;
19085 if ( mp->cur_type!=mp_known ) {
19086 mp_bad_unary(mp, decimal);
19088 mp->old_setting=mp->selector; mp->selector=new_string;
19089 mp_print_scaled(mp, mp->cur_exp); mp->cur_exp=mp_make_string(mp);
19090 mp->selector=mp->old_setting; mp->cur_type=mp_string_type;
19096 if ( mp->cur_type!=mp_string_type ) mp_bad_unary(mp, c);
19097 else mp_str_to_num(mp, c);
19100 if ( mp->cur_type!=mp_string_type ) mp_bad_unary(mp, font_size);
19101 else @<Find the design size of the font whose name is |cur_exp|@>;
19104 @ @<Declare unary action...@>=
19105 void mp_str_to_num (MP mp,quarterword c) { /* converts a string to a number */
19106 integer n; /* accumulator */
19107 ASCII_code m; /* current character */
19108 pool_pointer k; /* index into |str_pool| */
19109 int b; /* radix of conversion */
19110 boolean bad_char; /* did the string contain an invalid digit? */
19111 if ( c==ASCII_op ) {
19112 if ( length(mp->cur_exp)==0 ) n=-1;
19113 else n=mp->str_pool[mp->str_start[mp->cur_exp]];
19115 if ( c==oct_op ) b=8; else b=16;
19116 n=0; bad_char=false;
19117 for (k=mp->str_start[mp->cur_exp];k<=str_stop(mp->cur_exp)-1;k++) {
19119 if ( (m>='0')&&(m<='9') ) m=m-'0';
19120 else if ( (m>='A')&&(m<='F') ) m=m-'A'+10;
19121 else if ( (m>='a')&&(m<='f') ) m=m-'a'+10;
19122 else { bad_char=true; m=0; };
19123 if ( m>=b ) { bad_char=true; m=0; };
19124 if ( n<32768 / b ) n=n*b+m; else n=32767;
19126 @<Give error messages if |bad_char| or |n>=4096|@>;
19128 mp_flush_cur_exp(mp, n*unity);
19131 @ @<Give error messages if |bad_char|...@>=
19133 exp_err("String contains illegal digits");
19134 @.String contains illegal digits@>
19136 help1("I zeroed out characters that weren't in the range 0..7.");
19138 help1("I zeroed out characters that weren't hex digits.");
19140 mp_put_get_error(mp);
19143 if ( mp->internal[mp_warning_check]>0 ) {
19144 print_err("Number too large (");
19145 mp_print_int(mp, n); mp_print_char(mp, ')');
19146 @.Number too large@>
19147 help2("I have trouble with numbers greater than 4095; watch out.")
19148 ("(Set warningcheck:=0 to suppress this message.)");
19149 mp_put_get_error(mp);
19153 @ The length operation is somewhat unusual in that it applies to a variety
19154 of different types of operands.
19156 @<Additional cases of unary...@>=
19158 switch (mp->cur_type) {
19159 case mp_string_type: mp_flush_cur_exp(mp, length(mp->cur_exp)*unity); break;
19160 case mp_path_type: mp_flush_cur_exp(mp, mp_path_length(mp)); break;
19161 case mp_known: mp->cur_exp=abs(mp->cur_exp); break;
19162 case mp_picture_type: mp_flush_cur_exp(mp, mp_pict_length(mp)); break;
19164 if ( mp_nice_pair(mp, mp->cur_exp,mp->cur_type) )
19165 mp_flush_cur_exp(mp, mp_pyth_add(mp,
19166 value(x_part_loc(value(mp->cur_exp))),
19167 value(y_part_loc(value(mp->cur_exp)))));
19168 else mp_bad_unary(mp, c);
19173 @ @<Declare unary action...@>=
19174 scaled mp_path_length (MP mp) { /* computes the length of the current path */
19175 scaled n; /* the path length so far */
19176 pointer p; /* traverser */
19178 if ( left_type(p)==mp_endpoint ) n=-unity; else n=0;
19179 do { p=link(p); n=n+unity; } while (p!=mp->cur_exp);
19183 @ @<Declare unary action...@>=
19184 scaled mp_pict_length (MP mp) {
19185 /* counts interior components in picture |cur_exp| */
19186 scaled n; /* the count so far */
19187 pointer p; /* traverser */
19189 p=link(dummy_loc(mp->cur_exp));
19191 if ( is_start_or_stop(p) )
19192 if ( mp_skip_1component(mp, p)==null ) p=link(p);
19193 while ( p!=null ) {
19194 skip_component(p) return n;
19201 @ Implement |turningnumber|
19203 @<Additional cases of unary...@>=
19205 if ( mp->cur_type==mp_pair_type ) mp_flush_cur_exp(mp, 0);
19206 else if ( mp->cur_type!=mp_path_type ) mp_bad_unary(mp, turning_op);
19207 else if ( left_type(mp->cur_exp)==mp_endpoint )
19208 mp_flush_cur_exp(mp, 0); /* not a cyclic path */
19210 mp_flush_cur_exp(mp, mp_turn_cycles_wrapper(mp, mp->cur_exp));
19213 @ The function |an_angle| returns the value of the |angle| primitive, or $0$ if the
19214 argument is |origin|.
19216 @<Declare unary action...@>=
19217 angle mp_an_angle (MP mp,scaled xpar, scaled ypar) {
19218 if ( (! ((xpar==0) && (ypar==0))) )
19219 return mp_n_arg(mp, xpar,ypar);
19224 @ The actual turning number is (for the moment) computed in a C function
19225 that receives eight integers corresponding to the four controlling points,
19226 and returns a single angle. Besides those, we have to account for discrete
19227 moves at the actual points.
19229 @d floor(a) (a>=0 ? a : -(int)(-a))
19230 @d bezier_error (720<<20)+1
19231 @d sign(v) ((v)>0 ? 1 : ((v)<0 ? -1 : 0 ))
19233 @d out ((double)(xo>>20))
19234 @d mid ((double)(xm>>20))
19235 @d in ((double)(xi>>20))
19236 @d divisor (256*256)
19237 @d double2angle(a) (int)floor(a*256.0*256.0*16.0)
19239 @<Declare unary action...@>=
19240 angle mp_bezier_slope(MP mp, integer AX,integer AY,integer BX,integer BY,
19241 integer CX,integer CY,integer DX,integer DY);
19244 angle mp_bezier_slope(MP mp, integer AX,integer AY,integer BX,integer BY,
19245 integer CX,integer CY,integer DX,integer DY) {
19247 integer deltax,deltay;
19248 double ax,ay,bx,by,cx,cy,dx,dy;
19249 angle xi = 0, xo = 0, xm = 0;
19251 ax=AX/divisor; ay=AY/divisor;
19252 bx=BX/divisor; by=BY/divisor;
19253 cx=CX/divisor; cy=CY/divisor;
19254 dx=DX/divisor; dy=DY/divisor;
19256 deltax = (BX-AX); deltay = (BY-AY);
19257 if (deltax==0 && deltay == 0) { deltax=(CX-AX); deltay=(CY-AY); }
19258 if (deltax==0 && deltay == 0) { deltax=(DX-AX); deltay=(DY-AY); }
19259 xi = mp_an_angle(mp,deltax,deltay);
19261 deltax = (CX-BX); deltay = (CY-BY);
19262 xm = mp_an_angle(mp,deltax,deltay);
19264 deltax = (DX-CX); deltay = (DY-CY);
19265 if (deltax==0 && deltay == 0) { deltax=(DX-BX); deltay=(DY-BY); }
19266 if (deltax==0 && deltay == 0) { deltax=(DX-AX); deltay=(DY-AY); }
19267 xo = mp_an_angle(mp,deltax,deltay);
19269 a = (bx-ax)*(cy-by) - (cx-bx)*(by-ay); /* a = (bp-ap)x(cp-bp); */
19270 b = (bx-ax)*(dy-cy) - (by-ay)*(dx-cx);; /* b = (bp-ap)x(dp-cp);*/
19271 c = (cx-bx)*(dy-cy) - (dx-cx)*(cy-by); /* c = (cp-bp)x(dp-cp);*/
19273 if ((a==0)&&(c==0)) {
19274 res = (b==0 ? 0 : (out-in));
19275 print_roots("no roots (a)");
19276 } else if ((a==0)||(c==0)) {
19277 if ((sign(b) == sign(a)) || (sign(b) == sign(c))) {
19278 res = out-in; /* ? */
19281 else if (res>180.0)
19283 print_roots("no roots (b)");
19285 res = out-in; /* ? */
19286 print_roots("one root (a)");
19288 } else if ((sign(a)*sign(c))<0) {
19289 res = out-in; /* ? */
19292 else if (res>180.0)
19294 print_roots("one root (b)");
19296 if (sign(a) == sign(b)) {
19297 res = out-in; /* ? */
19300 else if (res>180.0)
19302 print_roots("no roots (d)");
19304 if ((b*b) == (4*a*c)) {
19305 res = bezier_error;
19306 print_roots("double root"); /* cusp */
19307 } else if ((b*b) < (4*a*c)) {
19308 res = out-in; /* ? */
19309 if (res<=0.0 &&res>-180.0)
19311 else if (res>=0.0 && res<180.0)
19313 print_roots("no roots (e)");
19318 else if (res>180.0)
19320 print_roots("two roots"); /* two inflections */
19324 return double2angle(res);
19328 @d p_nextnext link(link(p))
19330 @d seven_twenty_deg 05500000000 /* $720\cdot2^{20}$, represents $720^\circ$ */
19332 @<Declare unary action...@>=
19333 scaled mp_new_turn_cycles (MP mp,pointer c) {
19334 angle res,ang; /* the angles of intermediate results */
19335 scaled turns; /* the turn counter */
19336 pointer p; /* for running around the path */
19337 integer xp,yp; /* coordinates of next point */
19338 integer x,y; /* helper coordinates */
19339 angle in_angle,out_angle; /* helper angles */
19340 int old_setting; /* saved |selector| setting */
19344 old_setting = mp->selector; mp->selector=term_only;
19345 if ( mp->internal[mp_tracing_commands]>unity ) {
19346 mp_begin_diagnostic(mp);
19347 mp_print_nl(mp, "");
19348 mp_end_diagnostic(mp, false);
19351 xp = x_coord(p_next); yp = y_coord(p_next);
19352 ang = mp_bezier_slope(mp,x_coord(p), y_coord(p), right_x(p), right_y(p),
19353 left_x(p_next), left_y(p_next), xp, yp);
19354 if ( ang>seven_twenty_deg ) {
19355 print_err("Strange path");
19357 mp->selector=old_setting;
19361 if ( res > one_eighty_deg ) {
19362 res = res - three_sixty_deg;
19363 turns = turns + unity;
19365 if ( res <= -one_eighty_deg ) {
19366 res = res + three_sixty_deg;
19367 turns = turns - unity;
19369 /* incoming angle at next point */
19370 x = left_x(p_next); y = left_y(p_next);
19371 if ( (xp==x)&&(yp==y) ) { x = right_x(p); y = right_y(p); };
19372 if ( (xp==x)&&(yp==y) ) { x = x_coord(p); y = y_coord(p); };
19373 in_angle = mp_an_angle(mp, xp - x, yp - y);
19374 /* outgoing angle at next point */
19375 x = right_x(p_next); y = right_y(p_next);
19376 if ( (xp==x)&&(yp==y) ) { x = left_x(p_nextnext); y = left_y(p_nextnext); };
19377 if ( (xp==x)&&(yp==y) ) { x = x_coord(p_nextnext); y = y_coord(p_nextnext); };
19378 out_angle = mp_an_angle(mp, x - xp, y- yp);
19379 ang = (out_angle - in_angle);
19383 if ( res >= one_eighty_deg ) {
19384 res = res - three_sixty_deg;
19385 turns = turns + unity;
19387 if ( res <= -one_eighty_deg ) {
19388 res = res + three_sixty_deg;
19389 turns = turns - unity;
19394 mp->selector=old_setting;
19399 @ This code is based on Bogus\l{}av Jackowski's
19400 |emergency_turningnumber| macro, with some minor changes by Taco
19401 Hoekwater. The macro code looked more like this:
19403 vardef turning\_number primary p =
19404 ~~save res, ang, turns;
19406 ~~if length p <= 2:
19407 ~~~~if Angle ((point 0 of p) - (postcontrol 0 of p)) >= 0: 1 else: -1 fi
19409 ~~~~for t = 0 upto length p-1 :
19410 ~~~~~~angc := Angle ((point t+1 of p) - (point t of p))
19411 ~~~~~~~~- Angle ((point t of p) - (point t-1 of p));
19412 ~~~~~~if angc > 180: angc := angc - 360; fi;
19413 ~~~~~~if angc < -180: angc := angc + 360; fi;
19414 ~~~~~~res := res + angc;
19419 The general idea is to calculate only the sum of the angles of
19420 straight lines between the points, of a path, not worrying about cusps
19421 or self-intersections in the segments at all. If the segment is not
19422 well-behaved, the result is not necesarily correct. But the old code
19423 was not always correct either, and worse, it sometimes failed for
19424 well-behaved paths as well. All known bugs that were triggered by the
19425 original code no longer occur with this code, and it runs roughly 3
19426 times as fast because the algorithm is much simpler.
19428 @ It is possible to overflow the return value of the |turn_cycles|
19429 function when the path is sufficiently long and winding, but I am not
19430 going to bother testing for that. In any case, it would only return
19431 the looped result value, which is not a big problem.
19433 The macro code for the repeat loop was a bit nicer to look
19434 at than the pascal code, because it could use |point -1 of p|. In
19435 pascal, the fastest way to loop around the path is not to look
19436 backward once, but forward twice. These defines help hide the trick.
19438 @d p_to link(link(p))
19442 @<Declare unary action...@>=
19443 scaled mp_turn_cycles (MP mp,pointer c) {
19444 angle res,ang; /* the angles of intermediate results */
19445 scaled turns; /* the turn counter */
19446 pointer p; /* for running around the path */
19447 res=0; turns= 0; p=c;
19449 ang = mp_an_angle (mp, x_coord(p_to) - x_coord(p_here),
19450 y_coord(p_to) - y_coord(p_here))
19451 - mp_an_angle (mp, x_coord(p_here) - x_coord(p_from),
19452 y_coord(p_here) - y_coord(p_from));
19455 if ( res >= three_sixty_deg ) {
19456 res = res - three_sixty_deg;
19457 turns = turns + unity;
19459 if ( res <= -three_sixty_deg ) {
19460 res = res + three_sixty_deg;
19461 turns = turns - unity;
19468 @ @<Declare unary action...@>=
19469 scaled mp_turn_cycles_wrapper (MP mp,pointer c) {
19471 scaled saved_t_o; /* tracing\_online saved */
19472 if ( (link(c)==c)||(link(link(c))==c) ) {
19473 if ( mp_an_angle (mp, x_coord(c) - right_x(c), y_coord(c) - right_y(c)) > 0 )
19478 nval = mp_new_turn_cycles(mp, c);
19479 oval = mp_turn_cycles(mp, c);
19480 if ( nval!=oval ) {
19481 saved_t_o=mp->internal[mp_tracing_online];
19482 mp->internal[mp_tracing_online]=unity;
19483 mp_begin_diagnostic(mp);
19484 mp_print_nl (mp, "Warning: the turningnumber algorithms do not agree."
19485 " The current computed value is ");
19486 mp_print_scaled(mp, nval);
19487 mp_print(mp, ", but the 'connect-the-dots' algorithm returned ");
19488 mp_print_scaled(mp, oval);
19489 mp_end_diagnostic(mp, false);
19490 mp->internal[mp_tracing_online]=saved_t_o;
19496 @ @<Declare unary action...@>=
19497 scaled mp_count_turns (MP mp,pointer c) {
19498 pointer p; /* a knot in envelope spec |c| */
19499 integer t; /* total pen offset changes counted */
19502 t=t+info(p)-zero_off;
19505 return ((t / 3)*unity);
19508 @ @d type_range(A,B) {
19509 if ( (mp->cur_type>=(A)) && (mp->cur_type<=(B)) )
19510 mp_flush_cur_exp(mp, true_code);
19511 else mp_flush_cur_exp(mp, false_code);
19512 mp->cur_type=mp_boolean_type;
19515 if ( mp->cur_type==(A) ) mp_flush_cur_exp(mp, true_code);
19516 else mp_flush_cur_exp(mp, false_code);
19517 mp->cur_type=mp_boolean_type;
19520 @<Additional cases of unary operators@>=
19521 case mp_boolean_type:
19522 type_range(mp_boolean_type,mp_unknown_boolean); break;
19523 case mp_string_type:
19524 type_range(mp_string_type,mp_unknown_string); break;
19526 type_range(mp_pen_type,mp_unknown_pen); break;
19528 type_range(mp_path_type,mp_unknown_path); break;
19529 case mp_picture_type:
19530 type_range(mp_picture_type,mp_unknown_picture); break;
19531 case mp_transform_type: case mp_color_type: case mp_cmykcolor_type:
19533 type_test(c); break;
19534 case mp_numeric_type:
19535 type_range(mp_known,mp_independent); break;
19536 case known_op: case unknown_op:
19537 mp_test_known(mp, c); break;
19539 @ @<Declare unary action procedures@>=
19540 void mp_test_known (MP mp,quarterword c) {
19541 int b; /* is the current expression known? */
19542 pointer p,q; /* locations in a big node */
19544 switch (mp->cur_type) {
19545 case mp_vacuous: case mp_boolean_type: case mp_string_type:
19546 case mp_pen_type: case mp_path_type: case mp_picture_type:
19550 case mp_transform_type:
19551 case mp_color_type: case mp_cmykcolor_type: case mp_pair_type:
19552 p=value(mp->cur_exp);
19553 q=p+mp->big_node_size[mp->cur_type];
19556 if ( type(q)!=mp_known )
19565 if ( c==known_op ) mp_flush_cur_exp(mp, b);
19566 else mp_flush_cur_exp(mp, true_code+false_code-b);
19567 mp->cur_type=mp_boolean_type;
19570 @ @<Additional cases of unary operators@>=
19572 if ( mp->cur_type!=mp_path_type ) mp_flush_cur_exp(mp, false_code);
19573 else if ( left_type(mp->cur_exp)!=mp_endpoint ) mp_flush_cur_exp(mp, true_code);
19574 else mp_flush_cur_exp(mp, false_code);
19575 mp->cur_type=mp_boolean_type;
19578 @ @<Additional cases of unary operators@>=
19580 if ( mp->cur_type==mp_pair_type ) mp_pair_to_path(mp);
19581 if ( mp->cur_type!=mp_path_type ) mp_bad_unary(mp, arc_length);
19582 else mp_flush_cur_exp(mp, mp_get_arc_length(mp, mp->cur_exp));
19585 @ Here we use the fact that |c-filled_op+fill_code| is the desired graphical
19587 @^data structure assumptions@>
19589 @<Additional cases of unary operators@>=
19595 if ( mp->cur_type!=mp_picture_type ) mp_flush_cur_exp(mp, false_code);
19596 else if ( link(dummy_loc(mp->cur_exp))==null ) mp_flush_cur_exp(mp, false_code);
19597 else if ( type(link(dummy_loc(mp->cur_exp)))==c+mp_fill_code-filled_op )
19598 mp_flush_cur_exp(mp, true_code);
19599 else mp_flush_cur_exp(mp, false_code);
19600 mp->cur_type=mp_boolean_type;
19603 @ @<Additional cases of unary operators@>=
19605 if ( mp->cur_type==mp_pair_type ) mp_pair_to_path(mp);
19606 if ( mp->cur_type!=mp_path_type ) mp_bad_unary(mp, make_pen_op);
19608 mp->cur_type=mp_pen_type;
19609 mp->cur_exp=mp_make_pen(mp, mp->cur_exp,true);
19613 if ( mp->cur_type!=mp_pen_type ) mp_bad_unary(mp, make_path_op);
19615 mp->cur_type=mp_path_type;
19616 mp_make_path(mp, mp->cur_exp);
19620 if ( mp->cur_type==mp_path_type ) {
19621 p=mp_htap_ypoc(mp, mp->cur_exp);
19622 if ( right_type(p)==mp_endpoint ) p=link(p);
19623 mp_toss_knot_list(mp, mp->cur_exp); mp->cur_exp=p;
19624 } else if ( mp->cur_type==mp_pair_type ) mp_pair_to_path(mp);
19625 else mp_bad_unary(mp, reverse);
19628 @ The |pair_value| routine changes the current expression to a
19629 given ordered pair of values.
19631 @<Declare unary action procedures@>=
19632 void mp_pair_value (MP mp,scaled x, scaled y) {
19633 pointer p; /* a pair node */
19634 p=mp_get_node(mp, value_node_size);
19635 mp_flush_cur_exp(mp, p); mp->cur_type=mp_pair_type;
19636 type(p)=mp_pair_type; name_type(p)=mp_capsule; mp_init_big_node(mp, p);
19638 type(x_part_loc(p))=mp_known; value(x_part_loc(p))=x;
19639 type(y_part_loc(p))=mp_known; value(y_part_loc(p))=y;
19642 @ @<Additional cases of unary operators@>=
19644 if ( ! mp_get_cur_bbox(mp) ) mp_bad_unary(mp, ll_corner_op);
19645 else mp_pair_value(mp, minx,miny);
19648 if ( ! mp_get_cur_bbox(mp) ) mp_bad_unary(mp, lr_corner_op);
19649 else mp_pair_value(mp, maxx,miny);
19652 if ( ! mp_get_cur_bbox(mp) ) mp_bad_unary(mp, ul_corner_op);
19653 else mp_pair_value(mp, minx,maxy);
19656 if ( ! mp_get_cur_bbox(mp) ) mp_bad_unary(mp, ur_corner_op);
19657 else mp_pair_value(mp, maxx,maxy);
19660 @ Here is a function that sets |minx|, |maxx|, |miny|, |maxy| to the bounding
19661 box of the current expression. The boolean result is |false| if the expression
19662 has the wrong type.
19664 @<Declare unary action procedures@>=
19665 boolean mp_get_cur_bbox (MP mp) {
19666 switch (mp->cur_type) {
19667 case mp_picture_type:
19668 mp_set_bbox(mp, mp->cur_exp,true);
19669 if ( minx_val(mp->cur_exp)>maxx_val(mp->cur_exp) ) {
19670 minx=0; maxx=0; miny=0; maxy=0;
19672 minx=minx_val(mp->cur_exp);
19673 maxx=maxx_val(mp->cur_exp);
19674 miny=miny_val(mp->cur_exp);
19675 maxy=maxy_val(mp->cur_exp);
19679 mp_path_bbox(mp, mp->cur_exp);
19682 mp_pen_bbox(mp, mp->cur_exp);
19690 @ @<Additional cases of unary operators@>=
19692 case close_from_op:
19693 if ( mp->cur_type!=mp_string_type ) mp_bad_unary(mp, c);
19694 else mp_do_read_or_close(mp,c);
19697 @ Here is a routine that interprets |cur_exp| as a file name and tries to read
19698 a line from the file or to close the file.
19700 @<Declare unary action procedures@>=
19701 void mp_do_read_or_close (MP mp,quarterword c) {
19702 readf_index n,n0; /* indices for searching |rd_fname| */
19703 @<Find the |n| where |rd_fname[n]=cur_exp|; if |cur_exp| must be inserted,
19704 call |start_read_input| and |goto found| or |not_found|@>;
19705 mp_begin_file_reading(mp);
19707 if ( mp_input_ln(mp, mp->rd_file[n] ) )
19709 mp_end_file_reading(mp);
19711 @<Record the end of file and set |cur_exp| to a dummy value@>;
19714 mp_flush_cur_exp(mp, 0); mp->cur_type=mp_vacuous;
19717 mp_flush_cur_exp(mp, 0);
19718 mp_finish_read(mp);
19721 @ Free slots in the |rd_file| and |rd_fname| arrays are marked with NULL's in
19724 @<Find the |n| where |rd_fname[n]=cur_exp|...@>=
19729 fn = str(mp->cur_exp);
19730 while (mp_xstrcmp(fn,mp->rd_fname[n])!=0) {
19733 } else if ( c==close_from_op ) {
19736 if ( n0==mp->read_files ) {
19737 if ( mp->read_files<mp->max_read_files ) {
19738 incr(mp->read_files);
19743 l = mp->max_read_files + (mp->max_read_files>>2);
19744 rd_file = xmalloc((l+1), sizeof(void *));
19745 rd_fname = xmalloc((l+1), sizeof(char *));
19746 for (k=0;k<=l;k++) {
19747 if (k<=mp->max_read_files) {
19748 rd_file[k]=mp->rd_file[k];
19749 rd_fname[k]=mp->rd_fname[k];
19755 xfree(mp->rd_file); xfree(mp->rd_fname);
19756 mp->max_read_files = l;
19757 mp->rd_file = rd_file;
19758 mp->rd_fname = rd_fname;
19762 if ( mp_start_read_input(mp,fn,n) )
19767 if ( mp->rd_fname[n]==NULL ) { n0=n; }
19769 if ( c==close_from_op ) {
19770 (mp->close_file)(mp,mp->rd_file[n]);
19775 @ @<Record the end of file and set |cur_exp| to a dummy value@>=
19776 xfree(mp->rd_fname[n]);
19777 mp->rd_fname[n]=NULL;
19778 if ( n==mp->read_files-1 ) mp->read_files=n;
19779 if ( c==close_from_op )
19781 mp_flush_cur_exp(mp, mp->eof_line);
19782 mp->cur_type=mp_string_type
19784 @ The string denoting end-of-file is a one-byte string at position zero, by definition
19787 str_number eof_line;
19792 @ Finally, we have the operations that combine a capsule~|p|
19793 with the current expression.
19795 @c @<Declare binary action procedures@>;
19796 void mp_do_binary (MP mp,pointer p, quarterword c) {
19797 pointer q,r,rr; /* for list manipulation */
19798 pointer old_p,old_exp; /* capsules to recycle */
19799 integer v; /* for numeric manipulation */
19801 if ( mp->internal[mp_tracing_commands]>two ) {
19802 @<Trace the current binary operation@>;
19804 @<Sidestep |independent| cases in capsule |p|@>;
19805 @<Sidestep |independent| cases in the current expression@>;
19807 case plus: case minus:
19808 @<Add or subtract the current expression from |p|@>;
19810 @<Additional cases of binary operators@>;
19811 }; /* there are no other cases */
19812 mp_recycle_value(mp, p);
19813 mp_free_node(mp, p,value_node_size); /* |return| to avoid this */
19815 @<Recycle any sidestepped |independent| capsules@>;
19818 @ @<Declare binary action...@>=
19819 void mp_bad_binary (MP mp,pointer p, quarterword c) {
19820 mp_disp_err(mp, p,"");
19821 exp_err("Not implemented: ");
19822 @.Not implemented...@>
19823 if ( c>=min_of ) mp_print_op(mp, c);
19824 mp_print_known_or_unknown_type(mp, type(p),p);
19825 if ( c>=min_of ) mp_print(mp, "of"); else mp_print_op(mp, c);
19826 mp_print_known_or_unknown_type(mp, mp->cur_type,mp->cur_exp);
19827 help3("I'm afraid I don't know how to apply that operation to that")
19828 ("combination of types. Continue, and I'll return the second")
19829 ("argument (see above) as the result of the operation.");
19830 mp_put_get_error(mp);
19832 void mp_bad_envelope_pen (MP mp) {
19833 mp_disp_err(mp, null,"");
19834 exp_err("Not implemented: envelope(elliptical pen)of(path)");
19835 @.Not implemented...@>
19836 help3("I'm afraid I don't know how to apply that operation to that")
19837 ("combination of types. Continue, and I'll return the second")
19838 ("argument (see above) as the result of the operation.");
19839 mp_put_get_error(mp);
19842 @ @<Trace the current binary operation@>=
19844 mp_begin_diagnostic(mp); mp_print_nl(mp, "{(");
19845 mp_print_exp(mp,p,0); /* show the operand, but not verbosely */
19846 mp_print_char(mp,')'); mp_print_op(mp,c); mp_print_char(mp,'(');
19847 mp_print_exp(mp,null,0); mp_print(mp,")}");
19848 mp_end_diagnostic(mp, false);
19851 @ Several of the binary operations are potentially complicated by the
19852 fact that |independent| values can sneak into capsules. For example,
19853 we've seen an instance of this difficulty in the unary operation
19854 of negation. In order to reduce the number of cases that need to be
19855 handled, we first change the two operands (if necessary)
19856 to rid them of |independent| components. The original operands are
19857 put into capsules called |old_p| and |old_exp|, which will be
19858 recycled after the binary operation has been safely carried out.
19860 @<Recycle any sidestepped |independent| capsules@>=
19861 if ( old_p!=null ) {
19862 mp_recycle_value(mp, old_p); mp_free_node(mp, old_p,value_node_size);
19864 if ( old_exp!=null ) {
19865 mp_recycle_value(mp, old_exp); mp_free_node(mp, old_exp,value_node_size);
19868 @ A big node is considered to be ``tarnished'' if it contains at least one
19869 independent component. We will define a simple function called `|tarnished|'
19870 that returns |null| if and only if its argument is not tarnished.
19872 @<Sidestep |independent| cases in capsule |p|@>=
19874 case mp_transform_type:
19875 case mp_color_type:
19876 case mp_cmykcolor_type:
19878 old_p=mp_tarnished(mp, p);
19880 case mp_independent: old_p=mp_void; break;
19881 default: old_p=null; break;
19883 if ( old_p!=null ) {
19884 q=mp_stash_cur_exp(mp); old_p=p; mp_make_exp_copy(mp, old_p);
19885 p=mp_stash_cur_exp(mp); mp_unstash_cur_exp(mp, q);
19888 @ @<Sidestep |independent| cases in the current expression@>=
19889 switch (mp->cur_type) {
19890 case mp_transform_type:
19891 case mp_color_type:
19892 case mp_cmykcolor_type:
19894 old_exp=mp_tarnished(mp, mp->cur_exp);
19896 case mp_independent:old_exp=mp_void; break;
19897 default: old_exp=null; break;
19899 if ( old_exp!=null ) {
19900 old_exp=mp->cur_exp; mp_make_exp_copy(mp, old_exp);
19903 @ @<Declare binary action...@>=
19904 pointer mp_tarnished (MP mp,pointer p) {
19905 pointer q; /* beginning of the big node */
19906 pointer r; /* current position in the big node */
19907 q=value(p); r=q+mp->big_node_size[type(p)];
19910 if ( type(r)==mp_independent ) return mp_void;
19915 @ @<Add or subtract the current expression from |p|@>=
19916 if ( (mp->cur_type<mp_color_type)||(type(p)<mp_color_type) ) {
19917 mp_bad_binary(mp, p,c);
19919 if ((mp->cur_type>mp_pair_type)&&(type(p)>mp_pair_type) ) {
19920 mp_add_or_subtract(mp, p,null,c);
19922 if ( mp->cur_type!=type(p) ) {
19923 mp_bad_binary(mp, p,c);
19925 q=value(p); r=value(mp->cur_exp);
19926 rr=r+mp->big_node_size[mp->cur_type];
19928 mp_add_or_subtract(mp, q,r,c);
19935 @ The first argument to |add_or_subtract| is the location of a value node
19936 in a capsule or pair node that will soon be recycled. The second argument
19937 is either a location within a pair or transform node of |cur_exp|,
19938 or it is null (which means that |cur_exp| itself should be the second
19939 argument). The third argument is either |plus| or |minus|.
19941 The sum or difference of the numeric quantities will replace the second
19942 operand. Arithmetic overflow may go undetected; users aren't supposed to
19943 be monkeying around with really big values.
19945 @<Declare binary action...@>=
19946 @<Declare the procedure called |dep_finish|@>;
19947 void mp_add_or_subtract (MP mp,pointer p, pointer q, quarterword c) {
19948 small_number s,t; /* operand types */
19949 pointer r; /* list traverser */
19950 integer v; /* second operand value */
19953 if ( t<mp_dependent ) v=mp->cur_exp; else v=dep_list(mp->cur_exp);
19956 if ( t<mp_dependent ) v=value(q); else v=dep_list(q);
19958 if ( t==mp_known ) {
19959 if ( c==minus ) negate(v);
19960 if ( type(p)==mp_known ) {
19961 v=mp_slow_add(mp, value(p),v);
19962 if ( q==null ) mp->cur_exp=v; else value(q)=v;
19965 @<Add a known value to the constant term of |dep_list(p)|@>;
19967 if ( c==minus ) mp_negate_dep_list(mp, v);
19968 @<Add operand |p| to the dependency list |v|@>;
19972 @ @<Add a known value to the constant term of |dep_list(p)|@>=
19974 while ( info(r)!=null ) r=link(r);
19975 value(r)=mp_slow_add(mp, value(r),v);
19977 q=mp_get_node(mp, value_node_size); mp->cur_exp=q; mp->cur_type=type(p);
19978 name_type(q)=mp_capsule;
19980 dep_list(q)=dep_list(p); type(q)=type(p);
19981 prev_dep(q)=prev_dep(p); link(prev_dep(p))=q;
19982 type(p)=mp_known; /* this will keep the recycler from collecting non-garbage */
19984 @ We prefer |dependent| lists to |mp_proto_dependent| ones, because it is
19985 nice to retain the extra accuracy of |fraction| coefficients.
19986 But we have to handle both kinds, and mixtures too.
19988 @<Add operand |p| to the dependency list |v|@>=
19989 if ( type(p)==mp_known ) {
19990 @<Add the known |value(p)| to the constant term of |v|@>;
19992 s=type(p); r=dep_list(p);
19993 if ( t==mp_dependent ) {
19994 if ( s==mp_dependent ) {
19995 if ( mp_max_coef(mp, r)+mp_max_coef(mp, v)<coef_bound )
19996 v=mp_p_plus_q(mp, v,r,mp_dependent); goto DONE;
19997 } /* |fix_needed| will necessarily be false */
19998 t=mp_proto_dependent;
19999 v=mp_p_over_v(mp, v,unity,mp_dependent,mp_proto_dependent);
20001 if ( s==mp_proto_dependent ) v=mp_p_plus_q(mp, v,r,mp_proto_dependent);
20002 else v=mp_p_plus_fq(mp, v,unity,r,mp_proto_dependent,mp_dependent);
20004 @<Output the answer, |v| (which might have become |known|)@>;
20007 @ @<Add the known |value(p)| to the constant term of |v|@>=
20009 while ( info(v)!=null ) v=link(v);
20010 value(v)=mp_slow_add(mp, value(p),value(v));
20013 @ @<Output the answer, |v| (which might have become |known|)@>=
20014 if ( q!=null ) mp_dep_finish(mp, v,q,t);
20015 else { mp->cur_type=t; mp_dep_finish(mp, v,null,t); }
20017 @ Here's the current situation: The dependency list |v| of type |t|
20018 should either be put into the current expression (if |q=null|) or
20019 into location |q| within a pair node (otherwise). The destination (|cur_exp|
20020 or |q|) formerly held a dependency list with the same
20021 final pointer as the list |v|.
20023 @<Declare the procedure called |dep_finish|@>=
20024 void mp_dep_finish (MP mp, pointer v, pointer q, small_number t) {
20025 pointer p; /* the destination */
20026 scaled vv; /* the value, if it is |known| */
20027 if ( q==null ) p=mp->cur_exp; else p=q;
20028 dep_list(p)=v; type(p)=t;
20029 if ( info(v)==null ) {
20032 mp_flush_cur_exp(mp, vv);
20034 mp_recycle_value(mp, p); type(q)=mp_known; value(q)=vv;
20036 } else if ( q==null ) {
20039 if ( mp->fix_needed ) mp_fix_dependencies(mp);
20042 @ Let's turn now to the six basic relations of comparison.
20044 @<Additional cases of binary operators@>=
20045 case less_than: case less_or_equal: case greater_than:
20046 case greater_or_equal: case equal_to: case unequal_to:
20047 check_arith; /* at this point |arith_error| should be |false|? */
20048 if ( (mp->cur_type>mp_pair_type)&&(type(p)>mp_pair_type) ) {
20049 mp_add_or_subtract(mp, p,null,minus); /* |cur_exp:=(p)-cur_exp| */
20050 } else if ( mp->cur_type!=type(p) ) {
20051 mp_bad_binary(mp, p,c); goto DONE;
20052 } else if ( mp->cur_type==mp_string_type ) {
20053 mp_flush_cur_exp(mp, mp_str_vs_str(mp, value(p),mp->cur_exp));
20054 } else if ((mp->cur_type==mp_unknown_string)||
20055 (mp->cur_type==mp_unknown_boolean) ) {
20056 @<Check if unknowns have been equated@>;
20057 } else if ( (mp->cur_type<=mp_pair_type)&&(mp->cur_type>=mp_transform_type)) {
20058 @<Reduce comparison of big nodes to comparison of scalars@>;
20059 } else if ( mp->cur_type==mp_boolean_type ) {
20060 mp_flush_cur_exp(mp, mp->cur_exp-value(p));
20062 mp_bad_binary(mp, p,c); goto DONE;
20064 @<Compare the current expression with zero@>;
20066 mp->arith_error=false; /* ignore overflow in comparisons */
20069 @ @<Compare the current expression with zero@>=
20070 if ( mp->cur_type!=mp_known ) {
20071 if ( mp->cur_type<mp_known ) {
20072 mp_disp_err(mp, p,"");
20073 help1("The quantities shown above have not been equated.")
20075 help2("Oh dear. I can\'t decide if the expression above is positive,")
20076 ("negative, or zero. So this comparison test won't be `true'.");
20078 exp_err("Unknown relation will be considered false");
20079 @.Unknown relation...@>
20080 mp_put_get_flush_error(mp, false_code);
20083 case less_than: boolean_reset(mp->cur_exp<0); break;
20084 case less_or_equal: boolean_reset(mp->cur_exp<=0); break;
20085 case greater_than: boolean_reset(mp->cur_exp>0); break;
20086 case greater_or_equal: boolean_reset(mp->cur_exp>=0); break;
20087 case equal_to: boolean_reset(mp->cur_exp==0); break;
20088 case unequal_to: boolean_reset(mp->cur_exp!=0); break;
20089 }; /* there are no other cases */
20091 mp->cur_type=mp_boolean_type
20093 @ When two unknown strings are in the same ring, we know that they are
20094 equal. Otherwise, we don't know whether they are equal or not, so we
20097 @<Check if unknowns have been equated@>=
20099 q=value(mp->cur_exp);
20100 while ( (q!=mp->cur_exp)&&(q!=p) ) q=value(q);
20101 if ( q==p ) mp_flush_cur_exp(mp, 0);
20104 @ @<Reduce comparison of big nodes to comparison of scalars@>=
20106 q=value(p); r=value(mp->cur_exp);
20107 rr=r+mp->big_node_size[mp->cur_type]-2;
20108 while (1) { mp_add_or_subtract(mp, q,r,minus);
20109 if ( type(r)!=mp_known ) break;
20110 if ( value(r)!=0 ) break;
20111 if ( r==rr ) break;
20114 mp_take_part(mp, name_type(r)+x_part-mp_x_part_sector);
20117 @ Here we use the sneaky fact that |and_op-false_code=or_op-true_code|.
20119 @<Additional cases of binary operators@>=
20122 if ( (type(p)!=mp_boolean_type)||(mp->cur_type!=mp_boolean_type) )
20123 mp_bad_binary(mp, p,c);
20124 else if ( value(p)==c+false_code-and_op ) mp->cur_exp=value(p);
20127 @ @<Additional cases of binary operators@>=
20129 if ( (mp->cur_type<mp_color_type)||(type(p)<mp_color_type) ) {
20130 mp_bad_binary(mp, p,times);
20131 } else if ( (mp->cur_type==mp_known)||(type(p)==mp_known) ) {
20132 @<Multiply when at least one operand is known@>;
20133 } else if ( (mp_nice_color_or_pair(mp, p,type(p))&&(mp->cur_type>mp_pair_type))
20134 ||(mp_nice_color_or_pair(mp, mp->cur_exp,mp->cur_type)&&
20135 (type(p)>mp_pair_type)) ) {
20136 mp_hard_times(mp, p); return;
20138 mp_bad_binary(mp, p,times);
20142 @ @<Multiply when at least one operand is known@>=
20144 if ( type(p)==mp_known ) {
20145 v=value(p); mp_free_node(mp, p,value_node_size);
20147 v=mp->cur_exp; mp_unstash_cur_exp(mp, p);
20149 if ( mp->cur_type==mp_known ) {
20150 mp->cur_exp=mp_take_scaled(mp, mp->cur_exp,v);
20151 } else if ( (mp->cur_type==mp_pair_type)||(mp->cur_type==mp_color_type)||
20152 (mp->cur_type==mp_cmykcolor_type) ) {
20153 p=value(mp->cur_exp)+mp->big_node_size[mp->cur_type];
20155 p=p-2; mp_dep_mult(mp, p,v,true);
20156 } while (p!=value(mp->cur_exp));
20158 mp_dep_mult(mp, null,v,true);
20163 @ @<Declare binary action...@>=
20164 void mp_dep_mult (MP mp,pointer p, integer v, boolean v_is_scaled) {
20165 pointer q; /* the dependency list being multiplied by |v| */
20166 small_number s,t; /* its type, before and after */
20169 } else if ( type(p)!=mp_known ) {
20172 if ( v_is_scaled ) value(p)=mp_take_scaled(mp, value(p),v);
20173 else value(p)=mp_take_fraction(mp, value(p),v);
20176 t=type(q); q=dep_list(q); s=t;
20177 if ( t==mp_dependent ) if ( v_is_scaled )
20178 if (mp_ab_vs_cd(mp, mp_max_coef(mp,q),abs(v),coef_bound-1,unity)>=0 )
20179 t=mp_proto_dependent;
20180 q=mp_p_times_v(mp, q,v,s,t,v_is_scaled);
20181 mp_dep_finish(mp, q,p,t);
20184 @ Here is a routine that is similar to |times|; but it is invoked only
20185 internally, when |v| is a |fraction| whose magnitude is at most~1,
20186 and when |cur_type>=mp_color_type|.
20188 @c void mp_frac_mult (MP mp,scaled n, scaled d) {
20189 /* multiplies |cur_exp| by |n/d| */
20190 pointer p; /* a pair node */
20191 pointer old_exp; /* a capsule to recycle */
20192 fraction v; /* |n/d| */
20193 if ( mp->internal[mp_tracing_commands]>two ) {
20194 @<Trace the fraction multiplication@>;
20196 switch (mp->cur_type) {
20197 case mp_transform_type:
20198 case mp_color_type:
20199 case mp_cmykcolor_type:
20201 old_exp=mp_tarnished(mp, mp->cur_exp);
20203 case mp_independent: old_exp=mp_void; break;
20204 default: old_exp=null; break;
20206 if ( old_exp!=null ) {
20207 old_exp=mp->cur_exp; mp_make_exp_copy(mp, old_exp);
20209 v=mp_make_fraction(mp, n,d);
20210 if ( mp->cur_type==mp_known ) {
20211 mp->cur_exp=mp_take_fraction(mp, mp->cur_exp,v);
20212 } else if ( mp->cur_type<=mp_pair_type ) {
20213 p=value(mp->cur_exp)+mp->big_node_size[mp->cur_type];
20216 mp_dep_mult(mp, p,v,false);
20217 } while (p!=value(mp->cur_exp));
20219 mp_dep_mult(mp, null,v,false);
20221 if ( old_exp!=null ) {
20222 mp_recycle_value(mp, old_exp);
20223 mp_free_node(mp, old_exp,value_node_size);
20227 @ @<Trace the fraction multiplication@>=
20229 mp_begin_diagnostic(mp);
20230 mp_print_nl(mp, "{("); mp_print_scaled(mp,n); mp_print_char(mp,'/');
20231 mp_print_scaled(mp,d); mp_print(mp,")*("); mp_print_exp(mp,null,0);
20233 mp_end_diagnostic(mp, false);
20236 @ The |hard_times| routine multiplies a nice color or pair by a dependency list.
20238 @<Declare binary action procedures@>=
20239 void mp_hard_times (MP mp,pointer p) {
20240 pointer q; /* a copy of the dependent variable |p| */
20241 pointer r; /* a component of the big node for the nice color or pair */
20242 scaled v; /* the known value for |r| */
20243 if ( type(p)<=mp_pair_type ) {
20244 q=mp_stash_cur_exp(mp); mp_unstash_cur_exp(mp, p); p=q;
20245 }; /* now |cur_type=mp_pair_type| or |cur_type=mp_color_type| */
20246 r=value(mp->cur_exp)+mp->big_node_size[mp->cur_type];
20251 if ( r==value(mp->cur_exp) )
20253 mp_new_dep(mp, r,mp_copy_dep_list(mp, dep_list(p)));
20254 mp_dep_mult(mp, r,v,true);
20256 mp->mem[value_loc(r)]=mp->mem[value_loc(p)];
20257 link(prev_dep(p))=r;
20258 mp_free_node(mp, p,value_node_size);
20259 mp_dep_mult(mp, r,v,true);
20262 @ @<Additional cases of binary operators@>=
20264 if ( (mp->cur_type!=mp_known)||(type(p)<mp_color_type) ) {
20265 mp_bad_binary(mp, p,over);
20267 v=mp->cur_exp; mp_unstash_cur_exp(mp, p);
20269 @<Squeal about division by zero@>;
20271 if ( mp->cur_type==mp_known ) {
20272 mp->cur_exp=mp_make_scaled(mp, mp->cur_exp,v);
20273 } else if ( mp->cur_type<=mp_pair_type ) {
20274 p=value(mp->cur_exp)+mp->big_node_size[mp->cur_type];
20276 p=p-2; mp_dep_div(mp, p,v);
20277 } while (p!=value(mp->cur_exp));
20279 mp_dep_div(mp, null,v);
20286 @ @<Declare binary action...@>=
20287 void mp_dep_div (MP mp,pointer p, scaled v) {
20288 pointer q; /* the dependency list being divided by |v| */
20289 small_number s,t; /* its type, before and after */
20290 if ( p==null ) q=mp->cur_exp;
20291 else if ( type(p)!=mp_known ) q=p;
20292 else { value(p)=mp_make_scaled(mp, value(p),v); return; };
20293 t=type(q); q=dep_list(q); s=t;
20294 if ( t==mp_dependent )
20295 if ( mp_ab_vs_cd(mp, mp_max_coef(mp,q),unity,coef_bound-1,abs(v))>=0 )
20296 t=mp_proto_dependent;
20297 q=mp_p_over_v(mp, q,v,s,t);
20298 mp_dep_finish(mp, q,p,t);
20301 @ @<Squeal about division by zero@>=
20303 exp_err("Division by zero");
20304 @.Division by zero@>
20305 help2("You're trying to divide the quantity shown above the error")
20306 ("message by zero. I'm going to divide it by one instead.");
20307 mp_put_get_error(mp);
20310 @ @<Additional cases of binary operators@>=
20313 if ( (mp->cur_type==mp_known)&&(type(p)==mp_known) ) {
20314 if ( c==pythag_add ) mp->cur_exp=mp_pyth_add(mp, value(p),mp->cur_exp);
20315 else mp->cur_exp=mp_pyth_sub(mp, value(p),mp->cur_exp);
20316 } else mp_bad_binary(mp, p,c);
20319 @ The next few sections of the program deal with affine transformations
20320 of coordinate data.
20322 @<Additional cases of binary operators@>=
20323 case rotated_by: case slanted_by:
20324 case scaled_by: case shifted_by: case transformed_by:
20325 case x_scaled: case y_scaled: case z_scaled:
20326 if ( type(p)==mp_path_type ) {
20327 path_trans(c,p); return;
20328 } else if ( type(p)==mp_pen_type ) {
20330 mp->cur_exp=mp_convex_hull(mp, mp->cur_exp);
20331 /* rounding error could destroy convexity */
20333 } else if ( (type(p)==mp_pair_type)||(type(p)==mp_transform_type) ) {
20334 mp_big_trans(mp, p,c);
20335 } else if ( type(p)==mp_picture_type ) {
20336 mp_do_edges_trans(mp, p,c); return;
20338 mp_bad_binary(mp, p,c);
20342 @ Let |c| be one of the eight transform operators. The procedure call
20343 |set_up_trans(c)| first changes |cur_exp| to a transform that corresponds to
20344 |c| and the original value of |cur_exp|. (In particular, |cur_exp| doesn't
20345 change at all if |c=transformed_by|.)
20347 Then, if all components of the resulting transform are |known|, they are
20348 moved to the global variables |txx|, |txy|, |tyx|, |tyy|, |tx|, |ty|;
20349 and |cur_exp| is changed to the known value zero.
20351 @<Declare binary action...@>=
20352 void mp_set_up_trans (MP mp,quarterword c) {
20353 pointer p,q,r; /* list manipulation registers */
20354 if ( (c!=transformed_by)||(mp->cur_type!=mp_transform_type) ) {
20355 @<Put the current transform into |cur_exp|@>;
20357 @<If the current transform is entirely known, stash it in global variables;
20358 otherwise |return|@>;
20367 scaled ty; /* current transform coefficients */
20369 @ @<Put the current transform...@>=
20371 p=mp_stash_cur_exp(mp);
20372 mp->cur_exp=mp_id_transform(mp);
20373 mp->cur_type=mp_transform_type;
20374 q=value(mp->cur_exp);
20376 @<For each of the eight cases, change the relevant fields of |cur_exp|
20378 but do nothing if capsule |p| doesn't have the appropriate type@>;
20379 }; /* there are no other cases */
20380 mp_disp_err(mp, p,"Improper transformation argument");
20381 @.Improper transformation argument@>
20382 help3("The expression shown above has the wrong type,")
20383 ("so I can\'t transform anything using it.")
20384 ("Proceed, and I'll omit the transformation.");
20385 mp_put_get_error(mp);
20387 mp_recycle_value(mp, p);
20388 mp_free_node(mp, p,value_node_size);
20391 @ @<If the current transform is entirely known, ...@>=
20392 q=value(mp->cur_exp); r=q+transform_node_size;
20395 if ( type(r)!=mp_known ) return;
20397 mp->txx=value(xx_part_loc(q));
20398 mp->txy=value(xy_part_loc(q));
20399 mp->tyx=value(yx_part_loc(q));
20400 mp->tyy=value(yy_part_loc(q));
20401 mp->tx=value(x_part_loc(q));
20402 mp->ty=value(y_part_loc(q));
20403 mp_flush_cur_exp(mp, 0)
20405 @ @<For each of the eight cases...@>=
20407 if ( type(p)==mp_known )
20408 @<Install sines and cosines, then |goto done|@>;
20411 if ( type(p)>mp_pair_type ) {
20412 mp_install(mp, xy_part_loc(q),p); goto DONE;
20416 if ( type(p)>mp_pair_type ) {
20417 mp_install(mp, xx_part_loc(q),p); mp_install(mp, yy_part_loc(q),p);
20422 if ( type(p)==mp_pair_type ) {
20423 r=value(p); mp_install(mp, x_part_loc(q),x_part_loc(r));
20424 mp_install(mp, y_part_loc(q),y_part_loc(r)); goto DONE;
20428 if ( type(p)>mp_pair_type ) {
20429 mp_install(mp, xx_part_loc(q),p); goto DONE;
20433 if ( type(p)>mp_pair_type ) {
20434 mp_install(mp, yy_part_loc(q),p); goto DONE;
20438 if ( type(p)==mp_pair_type )
20439 @<Install a complex multiplier, then |goto done|@>;
20441 case transformed_by:
20445 @ @<Install sines and cosines, then |goto done|@>=
20446 { mp_n_sin_cos(mp, (value(p) % three_sixty_units)*16);
20447 value(xx_part_loc(q))=mp_round_fraction(mp, mp->n_cos);
20448 value(yx_part_loc(q))=mp_round_fraction(mp, mp->n_sin);
20449 value(xy_part_loc(q))=-value(yx_part_loc(q));
20450 value(yy_part_loc(q))=value(xx_part_loc(q));
20454 @ @<Install a complex multiplier, then |goto done|@>=
20457 mp_install(mp, xx_part_loc(q),x_part_loc(r));
20458 mp_install(mp, yy_part_loc(q),x_part_loc(r));
20459 mp_install(mp, yx_part_loc(q),y_part_loc(r));
20460 if ( type(y_part_loc(r))==mp_known ) negate(value(y_part_loc(r)));
20461 else mp_negate_dep_list(mp, dep_list(y_part_loc(r)));
20462 mp_install(mp, xy_part_loc(q),y_part_loc(r));
20466 @ Procedure |set_up_known_trans| is like |set_up_trans|, but it
20467 insists that the transformation be entirely known.
20469 @<Declare binary action...@>=
20470 void mp_set_up_known_trans (MP mp,quarterword c) {
20471 mp_set_up_trans(mp, c);
20472 if ( mp->cur_type!=mp_known ) {
20473 exp_err("Transform components aren't all known");
20474 @.Transform components...@>
20475 help3("I'm unable to apply a partially specified transformation")
20476 ("except to a fully known pair or transform.")
20477 ("Proceed, and I'll omit the transformation.");
20478 mp_put_get_flush_error(mp, 0);
20479 mp->txx=unity; mp->txy=0; mp->tyx=0; mp->tyy=unity;
20480 mp->tx=0; mp->ty=0;
20484 @ Here's a procedure that applies the transform |txx..ty| to a pair of
20485 coordinates in locations |p| and~|q|.
20487 @<Declare binary action...@>=
20488 void mp_trans (MP mp,pointer p, pointer q) {
20489 scaled v; /* the new |x| value */
20490 v=mp_take_scaled(mp, mp->mem[p].sc,mp->txx)+
20491 mp_take_scaled(mp, mp->mem[q].sc,mp->txy)+mp->tx;
20492 mp->mem[q].sc=mp_take_scaled(mp, mp->mem[p].sc,mp->tyx)+
20493 mp_take_scaled(mp, mp->mem[q].sc,mp->tyy)+mp->ty;
20497 @ The simplest transformation procedure applies a transform to all
20498 coordinates of a path. The |path_trans(c)(p)| macro applies
20499 a transformation defined by |cur_exp| and the transform operator |c|
20502 @d path_trans(A,B) { mp_set_up_known_trans(mp, (A));
20503 mp_unstash_cur_exp(mp, (B));
20504 mp_do_path_trans(mp, mp->cur_exp); }
20506 @<Declare binary action...@>=
20507 void mp_do_path_trans (MP mp,pointer p) {
20508 pointer q; /* list traverser */
20511 if ( left_type(q)!=mp_endpoint )
20512 mp_trans(mp, q+3,q+4); /* that's |left_x| and |left_y| */
20513 mp_trans(mp, q+1,q+2); /* that's |x_coord| and |y_coord| */
20514 if ( right_type(q)!=mp_endpoint )
20515 mp_trans(mp, q+5,q+6); /* that's |right_x| and |right_y| */
20516 @^data structure assumptions@>
20521 @ Transforming a pen is very similar, except that there are no |left_type|
20522 and |right_type| fields.
20524 @d pen_trans(A,B) { mp_set_up_known_trans(mp, (A));
20525 mp_unstash_cur_exp(mp, (B));
20526 mp_do_pen_trans(mp, mp->cur_exp); }
20528 @<Declare binary action...@>=
20529 void mp_do_pen_trans (MP mp,pointer p) {
20530 pointer q; /* list traverser */
20531 if ( pen_is_elliptical(p) ) {
20532 mp_trans(mp, p+3,p+4); /* that's |left_x| and |left_y| */
20533 mp_trans(mp, p+5,p+6); /* that's |right_x| and |right_y| */
20537 mp_trans(mp, q+1,q+2); /* that's |x_coord| and |y_coord| */
20538 @^data structure assumptions@>
20543 @ The next transformation procedure applies to edge structures. It will do
20544 any transformation, but the results may be substandard if the picture contains
20545 text that uses downloaded bitmap fonts. The binary action procedure is
20546 |do_edges_trans|, but we also need a function that just scales a picture.
20547 That routine is |scale_edges|. Both it and the underlying routine |edges_trans|
20548 should be thought of as procedures that update an edge structure |h|, except
20549 that they have to return a (possibly new) structure because of the need to call
20552 @<Declare binary action...@>=
20553 pointer mp_edges_trans (MP mp, pointer h) {
20554 pointer q; /* the object being transformed */
20555 pointer r,s; /* for list manipulation */
20556 scaled sx,sy; /* saved transformation parameters */
20557 scaled sqdet; /* square root of determinant for |dash_scale| */
20558 integer sgndet; /* sign of the determinant */
20559 scaled v; /* a temporary value */
20560 h=mp_private_edges(mp, h);
20561 sqdet=mp_sqrt_det(mp, mp->txx,mp->txy,mp->tyx,mp->tyy);
20562 sgndet=mp_ab_vs_cd(mp, mp->txx,mp->tyy,mp->txy,mp->tyx);
20563 if ( dash_list(h)!=null_dash ) {
20564 @<Try to transform the dash list of |h|@>;
20566 @<Make the bounding box of |h| unknown if it can't be updated properly
20567 without scanning the whole structure@>;
20568 q=link(dummy_loc(h));
20569 while ( q!=null ) {
20570 @<Transform graphical object |q|@>;
20575 void mp_do_edges_trans (MP mp,pointer p, quarterword c) {
20576 mp_set_up_known_trans(mp, c);
20577 value(p)=mp_edges_trans(mp, value(p));
20578 mp_unstash_cur_exp(mp, p);
20580 void mp_scale_edges (MP mp) {
20581 mp->txx=mp->se_sf; mp->tyy=mp->se_sf;
20582 mp->txy=0; mp->tyx=0; mp->tx=0; mp->ty=0;
20583 mp->se_pic=mp_edges_trans(mp, mp->se_pic);
20586 @ @<Try to transform the dash list of |h|@>=
20587 if ( (mp->txy!=0)||(mp->tyx!=0)||
20588 (mp->ty!=0)||(abs(mp->txx)!=abs(mp->tyy))) {
20589 mp_flush_dash_list(mp, h);
20591 if ( mp->txx<0 ) { @<Reverse the dash list of |h|@>; }
20592 @<Scale the dash list by |txx| and shift it by |tx|@>;
20593 dash_y(h)=mp_take_scaled(mp, dash_y(h),abs(mp->tyy));
20596 @ @<Reverse the dash list of |h|@>=
20599 dash_list(h)=null_dash;
20600 while ( r!=null_dash ) {
20602 v=start_x(s); start_x(s)=stop_x(s); stop_x(s)=v;
20603 link(s)=dash_list(h);
20608 @ @<Scale the dash list by |txx| and shift it by |tx|@>=
20610 while ( r!=null_dash ) {
20611 start_x(r)=mp_take_scaled(mp, start_x(r),mp->txx)+mp->tx;
20612 stop_x(r)=mp_take_scaled(mp, stop_x(r),mp->txx)+mp->tx;
20616 @ @<Make the bounding box of |h| unknown if it can't be updated properly...@>=
20617 if ( (mp->txx==0)&&(mp->tyy==0) ) {
20618 @<Swap the $x$ and $y$ parameters in the bounding box of |h|@>;
20619 } else if ( (mp->txy!=0)||(mp->tyx!=0) ) {
20620 mp_init_bbox(mp, h);
20623 if ( minx_val(h)<=maxx_val(h) ) {
20624 @<Scale the bounding box by |txx+txy| and |tyx+tyy|; then shift by
20631 @ @<Swap the $x$ and $y$ parameters in the bounding box of |h|@>=
20633 v=minx_val(h); minx_val(h)=miny_val(h); miny_val(h)=v;
20634 v=maxx_val(h); maxx_val(h)=maxy_val(h); maxy_val(h)=v;
20637 @ The sum ``|txx+txy|'' is whichever of |txx| or |txy| is nonzero. The other
20640 @<Scale the bounding box by |txx+txy| and |tyx+tyy|; then shift...@>=
20642 minx_val(h)=mp_take_scaled(mp, minx_val(h),mp->txx+mp->txy)+mp->tx;
20643 maxx_val(h)=mp_take_scaled(mp, maxx_val(h),mp->txx+mp->txy)+mp->tx;
20644 miny_val(h)=mp_take_scaled(mp, miny_val(h),mp->tyx+mp->tyy)+mp->ty;
20645 maxy_val(h)=mp_take_scaled(mp, maxy_val(h),mp->tyx+mp->tyy)+mp->ty;
20646 if ( mp->txx+mp->txy<0 ) {
20647 v=minx_val(h); minx_val(h)=maxx_val(h); maxx_val(h)=v;
20649 if ( mp->tyx+mp->tyy<0 ) {
20650 v=miny_val(h); miny_val(h)=maxy_val(h); maxy_val(h)=v;
20654 @ Now we ready for the main task of transforming the graphical objects in edge
20657 @<Transform graphical object |q|@>=
20659 case mp_fill_code: case mp_stroked_code:
20660 mp_do_path_trans(mp, path_p(q));
20661 @<Transform |pen_p(q)|, making sure polygonal pens stay counter-clockwise@>;
20663 case mp_start_clip_code: case mp_start_bounds_code:
20664 mp_do_path_trans(mp, path_p(q));
20668 @<Transform the compact transformation starting at |r|@>;
20670 case mp_stop_clip_code: case mp_stop_bounds_code:
20672 } /* there are no other cases */
20674 @ Note that the shift parameters |(tx,ty)| apply only to the path being stroked.
20675 The |dash_scale| has to be adjusted to scale the dash lengths in |dash_p(q)|
20676 since the \ps\ output procedures will try to compensate for the transformation
20677 we are applying to |pen_p(q)|. Since this compensation is based on the square
20678 root of the determinant, |sqdet| is the appropriate factor.
20680 @<Transform |pen_p(q)|, making sure...@>=
20681 if ( pen_p(q)!=null ) {
20682 sx=mp->tx; sy=mp->ty;
20683 mp->tx=0; mp->ty=0;
20684 mp_do_pen_trans(mp, pen_p(q));
20685 if ( ((type(q)==mp_stroked_code)&&(dash_p(q)!=null)) )
20686 dash_scale(q)=mp_take_scaled(mp, dash_scale(q),sqdet);
20687 if ( ! pen_is_elliptical(pen_p(q)) )
20689 pen_p(q)=mp_make_pen(mp, mp_copy_path(mp, pen_p(q)),true);
20690 /* this unreverses the pen */
20691 mp->tx=sx; mp->ty=sy;
20694 @ This uses the fact that transformations are stored in the order
20695 |(tx,ty,txx,txy,tyx,tyy)|.
20696 @^data structure assumptions@>
20698 @<Transform the compact transformation starting at |r|@>=
20699 mp_trans(mp, r,r+1);
20700 sx=mp->tx; sy=mp->ty;
20701 mp->tx=0; mp->ty=0;
20702 mp_trans(mp, r+2,r+4);
20703 mp_trans(mp, r+3,r+5);
20704 mp->tx=sx; mp->ty=sy
20706 @ The hard cases of transformation occur when big nodes are involved,
20707 and when some of their components are unknown.
20709 @<Declare binary action...@>=
20710 @<Declare subroutines needed by |big_trans|@>;
20711 void mp_big_trans (MP mp,pointer p, quarterword c) {
20712 pointer q,r,pp,qq; /* list manipulation registers */
20713 small_number s; /* size of a big node */
20714 s=mp->big_node_size[type(p)]; q=value(p); r=q+s;
20717 if ( type(r)!=mp_known ) {
20718 @<Transform an unknown big node and |return|@>;
20721 @<Transform a known big node@>;
20722 }; /* node |p| will now be recycled by |do_binary| */
20724 @ @<Transform an unknown big node and |return|@>=
20726 mp_set_up_known_trans(mp, c); mp_make_exp_copy(mp, p);
20727 r=value(mp->cur_exp);
20728 if ( mp->cur_type==mp_transform_type ) {
20729 mp_bilin1(mp, yy_part_loc(r),mp->tyy,xy_part_loc(q),mp->tyx,0);
20730 mp_bilin1(mp, yx_part_loc(r),mp->tyy,xx_part_loc(q),mp->tyx,0);
20731 mp_bilin1(mp, xy_part_loc(r),mp->txx,yy_part_loc(q),mp->txy,0);
20732 mp_bilin1(mp, xx_part_loc(r),mp->txx,yx_part_loc(q),mp->txy,0);
20734 mp_bilin1(mp, y_part_loc(r),mp->tyy,x_part_loc(q),mp->tyx,mp->ty);
20735 mp_bilin1(mp, x_part_loc(r),mp->txx,y_part_loc(q),mp->txy,mp->tx);
20739 @ Let |p| point to a two-word value field inside a big node of |cur_exp|,
20740 and let |q| point to a another value field. The |bilin1| procedure
20741 replaces |p| by $p\cdot t+q\cdot u+\delta$.
20743 @<Declare subroutines needed by |big_trans|@>=
20744 void mp_bilin1 (MP mp, pointer p, scaled t, pointer q,
20745 scaled u, scaled delta) {
20746 pointer r; /* list traverser */
20747 if ( t!=unity ) mp_dep_mult(mp, p,t,true);
20749 if ( type(q)==mp_known ) {
20750 delta+=mp_take_scaled(mp, value(q),u);
20752 @<Ensure that |type(p)=mp_proto_dependent|@>;
20753 dep_list(p)=mp_p_plus_fq(mp, dep_list(p),u,dep_list(q),
20754 mp_proto_dependent,type(q));
20757 if ( type(p)==mp_known ) {
20761 while ( info(r)!=null ) r=link(r);
20763 if ( r!=dep_list(p) ) value(r)=delta;
20764 else { mp_recycle_value(mp, p); type(p)=mp_known; value(p)=delta; };
20766 if ( mp->fix_needed ) mp_fix_dependencies(mp);
20769 @ @<Ensure that |type(p)=mp_proto_dependent|@>=
20770 if ( type(p)!=mp_proto_dependent ) {
20771 if ( type(p)==mp_known )
20772 mp_new_dep(mp, p,mp_const_dependency(mp, value(p)));
20774 dep_list(p)=mp_p_times_v(mp, dep_list(p),unity,mp_dependent,
20775 mp_proto_dependent,true);
20776 type(p)=mp_proto_dependent;
20779 @ @<Transform a known big node@>=
20780 mp_set_up_trans(mp, c);
20781 if ( mp->cur_type==mp_known ) {
20782 @<Transform known by known@>;
20784 pp=mp_stash_cur_exp(mp); qq=value(pp);
20785 mp_make_exp_copy(mp, p); r=value(mp->cur_exp);
20786 if ( mp->cur_type==mp_transform_type ) {
20787 mp_bilin2(mp, yy_part_loc(r),yy_part_loc(qq),
20788 value(xy_part_loc(q)),yx_part_loc(qq),null);
20789 mp_bilin2(mp, yx_part_loc(r),yy_part_loc(qq),
20790 value(xx_part_loc(q)),yx_part_loc(qq),null);
20791 mp_bilin2(mp, xy_part_loc(r),xx_part_loc(qq),
20792 value(yy_part_loc(q)),xy_part_loc(qq),null);
20793 mp_bilin2(mp, xx_part_loc(r),xx_part_loc(qq),
20794 value(yx_part_loc(q)),xy_part_loc(qq),null);
20796 mp_bilin2(mp, y_part_loc(r),yy_part_loc(qq),
20797 value(x_part_loc(q)),yx_part_loc(qq),y_part_loc(qq));
20798 mp_bilin2(mp, x_part_loc(r),xx_part_loc(qq),
20799 value(y_part_loc(q)),xy_part_loc(qq),x_part_loc(qq));
20800 mp_recycle_value(mp, pp); mp_free_node(mp, pp,value_node_size);
20803 @ Let |p| be a |mp_proto_dependent| value whose dependency list ends
20804 at |dep_final|. The following procedure adds |v| times another
20805 numeric quantity to~|p|.
20807 @<Declare subroutines needed by |big_trans|@>=
20808 void mp_add_mult_dep (MP mp,pointer p, scaled v, pointer r) {
20809 if ( type(r)==mp_known ) {
20810 value(mp->dep_final)+=mp_take_scaled(mp, value(r),v);
20812 dep_list(p)=mp_p_plus_fq(mp, dep_list(p),v,dep_list(r),
20813 mp_proto_dependent,type(r));
20814 if ( mp->fix_needed ) mp_fix_dependencies(mp);
20818 @ The |bilin2| procedure is something like |bilin1|, but with known
20819 and unknown quantities reversed. Parameter |p| points to a value field
20820 within the big node for |cur_exp|; and |type(p)=mp_known|. Parameters
20821 |t| and~|u| point to value fields elsewhere; so does parameter~|q|,
20822 unless it is |null| (which stands for zero). Location~|p| will be
20823 replaced by $p\cdot t+v\cdot u+q$.
20825 @<Declare subroutines needed by |big_trans|@>=
20826 void mp_bilin2 (MP mp,pointer p, pointer t, scaled v,
20827 pointer u, pointer q) {
20828 scaled vv; /* temporary storage for |value(p)| */
20829 vv=value(p); type(p)=mp_proto_dependent;
20830 mp_new_dep(mp, p,mp_const_dependency(mp, 0)); /* this sets |dep_final| */
20832 mp_add_mult_dep(mp, p,vv,t); /* |dep_final| doesn't change */
20833 if ( v!=0 ) mp_add_mult_dep(mp, p,v,u);
20834 if ( q!=null ) mp_add_mult_dep(mp, p,unity,q);
20835 if ( dep_list(p)==mp->dep_final ) {
20836 vv=value(mp->dep_final); mp_recycle_value(mp, p);
20837 type(p)=mp_known; value(p)=vv;
20841 @ @<Transform known by known@>=
20843 mp_make_exp_copy(mp, p); r=value(mp->cur_exp);
20844 if ( mp->cur_type==mp_transform_type ) {
20845 mp_bilin3(mp, yy_part_loc(r),mp->tyy,value(xy_part_loc(q)),mp->tyx,0);
20846 mp_bilin3(mp, yx_part_loc(r),mp->tyy,value(xx_part_loc(q)),mp->tyx,0);
20847 mp_bilin3(mp, xy_part_loc(r),mp->txx,value(yy_part_loc(q)),mp->txy,0);
20848 mp_bilin3(mp, xx_part_loc(r),mp->txx,value(yx_part_loc(q)),mp->txy,0);
20850 mp_bilin3(mp, y_part_loc(r),mp->tyy,value(x_part_loc(q)),mp->tyx,mp->ty);
20851 mp_bilin3(mp, x_part_loc(r),mp->txx,value(y_part_loc(q)),mp->txy,mp->tx);
20854 @ Finally, in |bilin3| everything is |known|.
20856 @<Declare subroutines needed by |big_trans|@>=
20857 void mp_bilin3 (MP mp,pointer p, scaled t,
20858 scaled v, scaled u, scaled delta) {
20860 delta+=mp_take_scaled(mp, value(p),t);
20863 if ( u!=0 ) value(p)=delta+mp_take_scaled(mp, v,u);
20864 else value(p)=delta;
20867 @ @<Additional cases of binary operators@>=
20869 if ( (mp->cur_type==mp_string_type)&&(type(p)==mp_string_type) ) mp_cat(mp, p);
20870 else mp_bad_binary(mp, p,concatenate);
20873 if ( mp_nice_pair(mp, p,type(p))&&(mp->cur_type==mp_string_type) )
20874 mp_chop_string(mp, value(p));
20875 else mp_bad_binary(mp, p,substring_of);
20878 if ( mp->cur_type==mp_pair_type ) mp_pair_to_path(mp);
20879 if ( mp_nice_pair(mp, p,type(p))&&(mp->cur_type==mp_path_type) )
20880 mp_chop_path(mp, value(p));
20881 else mp_bad_binary(mp, p,subpath_of);
20884 @ @<Declare binary action...@>=
20885 void mp_cat (MP mp,pointer p) {
20886 str_number a,b; /* the strings being concatenated */
20887 pool_pointer k; /* index into |str_pool| */
20888 a=value(p); b=mp->cur_exp; str_room(length(a)+length(b));
20889 for (k=mp->str_start[a];k<=str_stop(a)-1;k++) {
20890 append_char(mp->str_pool[k]);
20892 for (k=mp->str_start[b];k<=str_stop(b)-1;k++) {
20893 append_char(mp->str_pool[k]);
20895 mp->cur_exp=mp_make_string(mp); delete_str_ref(b);
20898 @ @<Declare binary action...@>=
20899 void mp_chop_string (MP mp,pointer p) {
20900 integer a, b; /* start and stop points */
20901 integer l; /* length of the original string */
20902 integer k; /* runs from |a| to |b| */
20903 str_number s; /* the original string */
20904 boolean reversed; /* was |a>b|? */
20905 a=mp_round_unscaled(mp, value(x_part_loc(p)));
20906 b=mp_round_unscaled(mp, value(y_part_loc(p)));
20907 if ( a<=b ) reversed=false;
20908 else { reversed=true; k=a; a=b; b=k; };
20909 s=mp->cur_exp; l=length(s);
20920 for (k=mp->str_start[s]+b-1;k>=mp->str_start[s]+a;k--) {
20921 append_char(mp->str_pool[k]);
20924 for (k=mp->str_start[s]+a;k<=mp->str_start[s]+b-1;k++) {
20925 append_char(mp->str_pool[k]);
20928 mp->cur_exp=mp_make_string(mp); delete_str_ref(s);
20931 @ @<Declare binary action...@>=
20932 void mp_chop_path (MP mp,pointer p) {
20933 pointer q; /* a knot in the original path */
20934 pointer pp,qq,rr,ss; /* link variables for copies of path nodes */
20935 scaled a,b,k,l; /* indices for chopping */
20936 boolean reversed; /* was |a>b|? */
20937 l=mp_path_length(mp); a=value(x_part_loc(p)); b=value(y_part_loc(p));
20938 if ( a<=b ) reversed=false;
20939 else { reversed=true; k=a; a=b; b=k; };
20940 @<Dispense with the cases |a<0| and/or |b>l|@>;
20942 while ( a>=unity ) {
20943 q=link(q); a=a-unity; b=b-unity;
20946 @<Construct a path from |pp| to |qq| of length zero@>;
20948 @<Construct a path from |pp| to |qq| of length $\lceil b\rceil$@>;
20950 left_type(pp)=mp_endpoint; right_type(qq)=mp_endpoint; link(qq)=pp;
20951 mp_toss_knot_list(mp, mp->cur_exp);
20953 mp->cur_exp=link(mp_htap_ypoc(mp, pp)); mp_toss_knot_list(mp, pp);
20959 @ @<Dispense with the cases |a<0| and/or |b>l|@>=
20961 if ( left_type(mp->cur_exp)==mp_endpoint ) {
20962 a=0; if ( b<0 ) b=0;
20964 do { a=a+l; b=b+l; } while (a<0); /* a cycle always has length |l>0| */
20968 if ( left_type(mp->cur_exp)==mp_endpoint ) {
20969 b=l; if ( a>l ) a=l;
20977 @ @<Construct a path from |pp| to |qq| of length $\lceil b\rceil$@>=
20979 pp=mp_copy_knot(mp, q); qq=pp;
20981 q=link(q); rr=qq; qq=mp_copy_knot(mp, q); link(rr)=qq; b=b-unity;
20984 ss=pp; pp=link(pp);
20985 mp_split_cubic(mp, ss,a*010000); pp=link(ss);
20986 mp_free_node(mp, ss,knot_node_size);
20988 b=mp_make_scaled(mp, b,unity-a); rr=pp;
20992 mp_split_cubic(mp, rr,(b+unity)*010000);
20993 mp_free_node(mp, qq,knot_node_size);
20998 @ @<Construct a path from |pp| to |qq| of length zero@>=
21000 if ( a>0 ) { mp_split_cubic(mp, q,a*010000); q=link(q); };
21001 pp=mp_copy_knot(mp, q); qq=pp;
21004 @ @<Additional cases of binary operators@>=
21005 case point_of: case precontrol_of: case postcontrol_of:
21006 if ( mp->cur_type==mp_pair_type )
21007 mp_pair_to_path(mp);
21008 if ( (mp->cur_type==mp_path_type)&&(type(p)==mp_known) )
21009 mp_find_point(mp, value(p),c);
21011 mp_bad_binary(mp, p,c);
21013 case pen_offset_of:
21014 if ( (mp->cur_type==mp_pen_type)&& mp_nice_pair(mp, p,type(p)) )
21015 mp_set_up_offset(mp, value(p));
21017 mp_bad_binary(mp, p,pen_offset_of);
21019 case direction_time_of:
21020 if ( mp->cur_type==mp_pair_type ) mp_pair_to_path(mp);
21021 if ( (mp->cur_type==mp_path_type)&& mp_nice_pair(mp, p,type(p)) )
21022 mp_set_up_direction_time(mp, value(p));
21024 mp_bad_binary(mp, p,direction_time_of);
21027 if ( (type(p) != mp_pen_type) || (mp->cur_type != mp_path_type) )
21028 mp_bad_binary(mp, p,envelope_of);
21030 mp_set_up_envelope(mp, p);
21033 @ @<Declare binary action...@>=
21034 void mp_set_up_offset (MP mp,pointer p) {
21035 mp_find_offset(mp, value(x_part_loc(p)),value(y_part_loc(p)),mp->cur_exp);
21036 mp_pair_value(mp, mp->cur_x,mp->cur_y);
21038 void mp_set_up_direction_time (MP mp,pointer p) {
21039 mp_flush_cur_exp(mp, mp_find_direction_time(mp, value(x_part_loc(p)),
21040 value(y_part_loc(p)),mp->cur_exp));
21042 void mp_set_up_envelope (MP mp,pointer p) {
21043 pointer q = mp_copy_path(mp, mp->cur_exp); /* the original path */
21044 /* TODO: accept elliptical pens for straight paths */
21045 if (pen_is_elliptical(value(p))) {
21046 mp_bad_envelope_pen(mp);
21048 mp->cur_type = mp_path_type;
21051 small_number ljoin, lcap;
21053 if ( mp->internal[mp_linejoin]>unity ) ljoin=2;
21054 else if ( mp->internal[mp_linejoin]>0 ) ljoin=1;
21056 if ( mp->internal[mp_linecap]>unity ) lcap=2;
21057 else if ( mp->internal[mp_linecap]>0 ) lcap=1;
21059 if ( mp->internal[mp_miterlimit]<unity )
21062 miterlim=mp->internal[mp_miterlimit];
21063 mp->cur_exp = mp_make_envelope(mp, q, value(p), ljoin,lcap,miterlim);
21064 mp->cur_type = mp_path_type;
21067 @ @<Declare binary action...@>=
21068 void mp_find_point (MP mp,scaled v, quarterword c) {
21069 pointer p; /* the path */
21070 scaled n; /* its length */
21072 if ( left_type(p)==mp_endpoint ) n=-unity; else n=0;
21073 do { p=link(p); n=n+unity; } while (p!=mp->cur_exp);
21076 } else if ( v<0 ) {
21077 if ( left_type(p)==mp_endpoint ) v=0;
21078 else v=n-1-((-v-1) % n);
21079 } else if ( v>n ) {
21080 if ( left_type(p)==mp_endpoint ) v=n;
21084 while ( v>=unity ) { p=link(p); v=v-unity; };
21086 @<Insert a fractional node by splitting the cubic@>;
21088 @<Set the current expression to the desired path coordinates@>;
21091 @ @<Insert a fractional node...@>=
21092 { mp_split_cubic(mp, p,v*010000); p=link(p); }
21094 @ @<Set the current expression to the desired path coordinates...@>=
21097 mp_pair_value(mp, x_coord(p),y_coord(p));
21099 case precontrol_of:
21100 if ( left_type(p)==mp_endpoint ) mp_pair_value(mp, x_coord(p),y_coord(p));
21101 else mp_pair_value(mp, left_x(p),left_y(p));
21103 case postcontrol_of:
21104 if ( right_type(p)==mp_endpoint ) mp_pair_value(mp, x_coord(p),y_coord(p));
21105 else mp_pair_value(mp, right_x(p),right_y(p));
21107 } /* there are no other cases */
21109 @ @<Additional cases of binary operators@>=
21111 if ( mp->cur_type==mp_pair_type )
21112 mp_pair_to_path(mp);
21113 if ( (mp->cur_type==mp_path_type)&&(type(p)==mp_known) )
21114 mp_flush_cur_exp(mp, mp_get_arc_time(mp, mp->cur_exp,value(p)));
21116 mp_bad_binary(mp, p,c);
21119 @ @<Additional cases of bin...@>=
21121 if ( type(p)==mp_pair_type ) {
21122 q=mp_stash_cur_exp(mp); mp_unstash_cur_exp(mp, p);
21123 mp_pair_to_path(mp); p=mp_stash_cur_exp(mp); mp_unstash_cur_exp(mp, q);
21125 if ( mp->cur_type==mp_pair_type ) mp_pair_to_path(mp);
21126 if ( (mp->cur_type==mp_path_type)&&(type(p)==mp_path_type) ) {
21127 mp_path_intersection(mp, value(p),mp->cur_exp);
21128 mp_pair_value(mp, mp->cur_t,mp->cur_tt);
21130 mp_bad_binary(mp, p,intersect);
21134 @ @<Additional cases of bin...@>=
21136 if ( (mp->cur_type!=mp_string_type)||(type(p)!=mp_string_type))
21137 mp_bad_binary(mp, p,in_font);
21138 else { mp_do_infont(mp, p); return; }
21141 @ Function |new_text_node| owns the reference count for its second argument
21142 (the text string) but not its first (the font name).
21144 @<Declare binary action...@>=
21145 void mp_do_infont (MP mp,pointer p) {
21147 q=mp_get_node(mp, edge_header_size);
21148 mp_init_edges(mp, q);
21149 link(obj_tail(q))=mp_new_text_node(mp,str(mp->cur_exp),value(p));
21150 obj_tail(q)=link(obj_tail(q));
21151 mp_free_node(mp, p,value_node_size);
21152 mp_flush_cur_exp(mp, q);
21153 mp->cur_type=mp_picture_type;
21156 @* \[40] Statements and commands.
21157 The chief executive of \MP\ is the |do_statement| routine, which
21158 contains the master switch that causes all the various pieces of \MP\
21159 to do their things, in the right order.
21161 In a sense, this is the grand climax of the program: It applies all the
21162 tools that we have worked so hard to construct. In another sense, this is
21163 the messiest part of the program: It necessarily refers to other pieces
21164 of code all over the place, so that a person can't fully understand what is
21165 going on without paging back and forth to be reminded of conventions that
21166 are defined elsewhere. We are now at the hub of the web.
21168 The structure of |do_statement| itself is quite simple. The first token
21169 of the statement is fetched using |get_x_next|. If it can be the first
21170 token of an expression, we look for an equation, an assignment, or a
21171 title. Otherwise we use a \&{case} construction to branch at high speed to
21172 the appropriate routine for various and sundry other types of commands,
21173 each of which has an ``action procedure'' that does the necessary work.
21175 The program uses the fact that
21176 $$\hbox{|min_primary_command=max_statement_command=type_name|}$$
21177 to interpret a statement that starts with, e.g., `\&{string}',
21178 as a type declaration rather than a boolean expression.
21180 @c void mp_do_statement (MP mp) { /* governs \MP's activities */
21181 mp->cur_type=mp_vacuous; mp_get_x_next(mp);
21182 if ( mp->cur_cmd>max_primary_command ) {
21183 @<Worry about bad statement@>;
21184 } else if ( mp->cur_cmd>max_statement_command ) {
21185 @<Do an equation, assignment, title, or
21186 `$\langle\,$expression$\,\rangle\,$\&{endgroup}'@>;
21188 @<Do a statement that doesn't begin with an expression@>;
21190 if ( mp->cur_cmd<semicolon )
21191 @<Flush unparsable junk that was found after the statement@>;
21195 @ @<Declarations@>=
21196 @<Declare action procedures for use by |do_statement|@>;
21198 @ The only command codes |>max_primary_command| that can be present
21199 at the beginning of a statement are |semicolon| and higher; these
21200 occur when the statement is null.
21202 @<Worry about bad statement@>=
21204 if ( mp->cur_cmd<semicolon ) {
21205 print_err("A statement can't begin with `");
21206 @.A statement can't begin with x@>
21207 mp_print_cmd_mod(mp, mp->cur_cmd,mp->cur_mod); mp_print_char(mp, '\'');
21208 help5("I was looking for the beginning of a new statement.")
21209 ("If you just proceed without changing anything, I'll ignore")
21210 ("everything up to the next `;'. Please insert a semicolon")
21211 ("now in front of anything that you don't want me to delete.")
21212 ("(See Chapter 27 of The METAFONTbook for an example.)");
21213 @:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
21214 mp_back_error(mp); mp_get_x_next(mp);
21218 @ The help message printed here says that everything is flushed up to
21219 a semicolon, but actually the commands |end_group| and |stop| will
21220 also terminate a statement.
21222 @<Flush unparsable junk that was found after the statement@>=
21224 print_err("Extra tokens will be flushed");
21225 @.Extra tokens will be flushed@>
21226 help6("I've just read as much of that statement as I could fathom,")
21227 ("so a semicolon should have been next. It's very puzzling...")
21228 ("but I'll try to get myself back together, by ignoring")
21229 ("everything up to the next `;'. Please insert a semicolon")
21230 ("now in front of anything that you don't want me to delete.")
21231 ("(See Chapter 27 of The METAFONTbook for an example.)");
21232 @:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
21233 mp_back_error(mp); mp->scanner_status=flushing;
21236 @<Decrease the string reference count...@>;
21237 } while (! end_of_statement); /* |cur_cmd=semicolon|, |end_group|, or |stop| */
21238 mp->scanner_status=normal;
21241 @ If |do_statement| ends with |cur_cmd=end_group|, we should have
21242 |cur_type=mp_vacuous| unless the statement was simply an expression;
21243 in the latter case, |cur_type| and |cur_exp| should represent that
21246 @<Do a statement that doesn't...@>=
21248 if ( mp->internal[mp_tracing_commands]>0 )
21250 switch (mp->cur_cmd ) {
21251 case type_name:mp_do_type_declaration(mp); break;
21253 if ( mp->cur_mod>var_def ) mp_make_op_def(mp);
21254 else if ( mp->cur_mod>end_def ) mp_scan_def(mp);
21256 @<Cases of |do_statement| that invoke particular commands@>;
21257 } /* there are no other cases */
21258 mp->cur_type=mp_vacuous;
21261 @ The most important statements begin with expressions.
21263 @<Do an equation, assignment, title, or...@>=
21265 mp->var_flag=assignment; mp_scan_expression(mp);
21266 if ( mp->cur_cmd<end_group ) {
21267 if ( mp->cur_cmd==equals ) mp_do_equation(mp);
21268 else if ( mp->cur_cmd==assignment ) mp_do_assignment(mp);
21269 else if ( mp->cur_type==mp_string_type ) {@<Do a title@> ; }
21270 else if ( mp->cur_type!=mp_vacuous ){
21271 exp_err("Isolated expression");
21272 @.Isolated expression@>
21273 help3("I couldn't find an `=' or `:=' after the")
21274 ("expression that is shown above this error message,")
21275 ("so I guess I'll just ignore it and carry on.");
21276 mp_put_get_error(mp);
21278 mp_flush_cur_exp(mp, 0); mp->cur_type=mp_vacuous;
21284 if ( mp->internal[mp_tracing_titles]>0 ) {
21285 mp_print_nl(mp, ""); mp_print_str(mp, mp->cur_exp); update_terminal;
21289 @ Equations and assignments are performed by the pair of mutually recursive
21291 routines |do_equation| and |do_assignment|. These routines are called when
21292 |cur_cmd=equals| and when |cur_cmd=assignment|, respectively; the left-hand
21293 side is in |cur_type| and |cur_exp|, while the right-hand side is yet
21294 to be scanned. After the routines are finished, |cur_type| and |cur_exp|
21295 will be equal to the right-hand side (which will normally be equal
21296 to the left-hand side).
21298 @<Declare action procedures for use by |do_statement|@>=
21299 @<Declare the procedure called |try_eq|@>;
21300 @<Declare the procedure called |make_eq|@>;
21301 void mp_do_equation (MP mp) ;
21304 void mp_do_equation (MP mp) {
21305 pointer lhs; /* capsule for the left-hand side */
21306 pointer p; /* temporary register */
21307 lhs=mp_stash_cur_exp(mp); mp_get_x_next(mp);
21308 mp->var_flag=assignment; mp_scan_expression(mp);
21309 if ( mp->cur_cmd==equals ) mp_do_equation(mp);
21310 else if ( mp->cur_cmd==assignment ) mp_do_assignment(mp);
21311 if ( mp->internal[mp_tracing_commands]>two )
21312 @<Trace the current equation@>;
21313 if ( mp->cur_type==mp_unknown_path ) if ( type(lhs)==mp_pair_type ) {
21314 p=mp_stash_cur_exp(mp); mp_unstash_cur_exp(mp, lhs); lhs=p;
21315 }; /* in this case |make_eq| will change the pair to a path */
21316 mp_make_eq(mp, lhs); /* equate |lhs| to |(cur_type,cur_exp)| */
21319 @ And |do_assignment| is similar to |do_expression|:
21322 void mp_do_assignment (MP mp);
21324 @ @<Declare action procedures for use by |do_statement|@>=
21325 void mp_do_assignment (MP mp) ;
21328 void mp_do_assignment (MP mp) {
21329 pointer lhs; /* token list for the left-hand side */
21330 pointer p; /* where the left-hand value is stored */
21331 pointer q; /* temporary capsule for the right-hand value */
21332 if ( mp->cur_type!=mp_token_list ) {
21333 exp_err("Improper `:=' will be changed to `='");
21335 help2("I didn't find a variable name at the left of the `:=',")
21336 ("so I'm going to pretend that you said `=' instead.");
21337 mp_error(mp); mp_do_equation(mp);
21339 lhs=mp->cur_exp; mp->cur_type=mp_vacuous;
21340 mp_get_x_next(mp); mp->var_flag=assignment; mp_scan_expression(mp);
21341 if ( mp->cur_cmd==equals ) mp_do_equation(mp);
21342 else if ( mp->cur_cmd==assignment ) mp_do_assignment(mp);
21343 if ( mp->internal[mp_tracing_commands]>two )
21344 @<Trace the current assignment@>;
21345 if ( info(lhs)>hash_end ) {
21346 @<Assign the current expression to an internal variable@>;
21348 @<Assign the current expression to the variable |lhs|@>;
21350 mp_flush_node_list(mp, lhs);
21354 @ @<Trace the current equation@>=
21356 mp_begin_diagnostic(mp); mp_print_nl(mp, "{("); mp_print_exp(mp,lhs,0);
21357 mp_print(mp,")=("); mp_print_exp(mp,null,0);
21358 mp_print(mp,")}"); mp_end_diagnostic(mp, false);
21361 @ @<Trace the current assignment@>=
21363 mp_begin_diagnostic(mp); mp_print_nl(mp, "{");
21364 if ( info(lhs)>hash_end )
21365 mp_print(mp, mp->int_name[info(lhs)-(hash_end)]);
21367 mp_show_token_list(mp, lhs,null,1000,0);
21368 mp_print(mp, ":="); mp_print_exp(mp, null,0);
21369 mp_print_char(mp, '}'); mp_end_diagnostic(mp, false);
21372 @ @<Assign the current expression to an internal variable@>=
21373 if ( mp->cur_type==mp_known ) {
21374 mp->internal[info(lhs)-(hash_end)]=mp->cur_exp;
21376 exp_err("Internal quantity `");
21377 @.Internal quantity...@>
21378 mp_print(mp, mp->int_name[info(lhs)-(hash_end)]);
21379 mp_print(mp, "' must receive a known value");
21380 help2("I can\'t set an internal quantity to anything but a known")
21381 ("numeric value, so I'll have to ignore this assignment.");
21382 mp_put_get_error(mp);
21385 @ @<Assign the current expression to the variable |lhs|@>=
21387 p=mp_find_variable(mp, lhs);
21389 q=mp_stash_cur_exp(mp); mp->cur_type=mp_und_type(mp, p);
21390 mp_recycle_value(mp, p);
21391 type(p)=mp->cur_type; value(p)=null; mp_make_exp_copy(mp, p);
21392 p=mp_stash_cur_exp(mp); mp_unstash_cur_exp(mp, q); mp_make_eq(mp, p);
21394 mp_obliterated(mp, lhs); mp_put_get_error(mp);
21399 @ And now we get to the nitty-gritty. The |make_eq| procedure is given
21400 a pointer to a capsule that is to be equated to the current expression.
21402 @<Declare the procedure called |make_eq|@>=
21403 void mp_make_eq (MP mp,pointer lhs) ;
21407 @c void mp_make_eq (MP mp,pointer lhs) {
21408 small_number t; /* type of the left-hand side */
21409 pointer p,q; /* pointers inside of big nodes */
21410 integer v=0; /* value of the left-hand side */
21413 if ( t<=mp_pair_type ) v=value(lhs);
21415 @<For each type |t|, make an equation and |goto done| unless |cur_type|
21416 is incompatible with~|t|@>;
21417 } /* all cases have been listed */
21418 @<Announce that the equation cannot be performed@>;
21420 check_arith; mp_recycle_value(mp, lhs);
21421 mp_free_node(mp, lhs,value_node_size);
21424 @ @<Announce that the equation cannot be performed@>=
21425 mp_disp_err(mp, lhs,"");
21426 exp_err("Equation cannot be performed (");
21427 @.Equation cannot be performed@>
21428 if ( type(lhs)<=mp_pair_type ) mp_print_type(mp, type(lhs));
21429 else mp_print(mp, "numeric");
21430 mp_print_char(mp, '=');
21431 if ( mp->cur_type<=mp_pair_type ) mp_print_type(mp, mp->cur_type);
21432 else mp_print(mp, "numeric");
21433 mp_print_char(mp, ')');
21434 help2("I'm sorry, but I don't know how to make such things equal.")
21435 ("(See the two expressions just above the error message.)");
21436 mp_put_get_error(mp)
21438 @ @<For each type |t|, make an equation and |goto done| unless...@>=
21439 case mp_boolean_type: case mp_string_type: case mp_pen_type:
21440 case mp_path_type: case mp_picture_type:
21441 if ( mp->cur_type==t+unknown_tag ) {
21442 mp_nonlinear_eq(mp, v,mp->cur_exp,false); goto DONE;
21443 } else if ( mp->cur_type==t ) {
21444 @<Report redundant or inconsistent equation and |goto done|@>;
21447 case unknown_types:
21448 if ( mp->cur_type==t-unknown_tag ) {
21449 mp_nonlinear_eq(mp, mp->cur_exp,lhs,true); goto DONE;
21450 } else if ( mp->cur_type==t ) {
21451 mp_ring_merge(mp, lhs,mp->cur_exp); goto DONE;
21452 } else if ( mp->cur_type==mp_pair_type ) {
21453 if ( t==mp_unknown_path ) {
21454 mp_pair_to_path(mp); goto RESTART;
21458 case mp_transform_type: case mp_color_type:
21459 case mp_cmykcolor_type: case mp_pair_type:
21460 if ( mp->cur_type==t ) {
21461 @<Do multiple equations and |goto done|@>;
21464 case mp_known: case mp_dependent:
21465 case mp_proto_dependent: case mp_independent:
21466 if ( mp->cur_type>=mp_known ) {
21467 mp_try_eq(mp, lhs,null); goto DONE;
21473 @ @<Report redundant or inconsistent equation and |goto done|@>=
21475 if ( mp->cur_type<=mp_string_type ) {
21476 if ( mp->cur_type==mp_string_type ) {
21477 if ( mp_str_vs_str(mp, v,mp->cur_exp)!=0 ) {
21480 } else if ( v!=mp->cur_exp ) {
21483 @<Exclaim about a redundant equation@>; goto DONE;
21485 print_err("Redundant or inconsistent equation");
21486 @.Redundant or inconsistent equation@>
21487 help2("An equation between already-known quantities can't help.")
21488 ("But don't worry; continue and I'll just ignore it.");
21489 mp_put_get_error(mp); goto DONE;
21491 print_err("Inconsistent equation");
21492 @.Inconsistent equation@>
21493 help2("The equation I just read contradicts what was said before.")
21494 ("But don't worry; continue and I'll just ignore it.");
21495 mp_put_get_error(mp); goto DONE;
21498 @ @<Do multiple equations and |goto done|@>=
21500 p=v+mp->big_node_size[t];
21501 q=value(mp->cur_exp)+mp->big_node_size[t];
21503 p=p-2; q=q-2; mp_try_eq(mp, p,q);
21508 @ The first argument to |try_eq| is the location of a value node
21509 in a capsule that will soon be recycled. The second argument is
21510 either a location within a pair or transform node pointed to by
21511 |cur_exp|, or it is |null| (which means that |cur_exp| itself
21512 serves as the second argument). The idea is to leave |cur_exp| unchanged,
21513 but to equate the two operands.
21515 @<Declare the procedure called |try_eq|@>=
21516 void mp_try_eq (MP mp,pointer l, pointer r) ;
21519 @c void mp_try_eq (MP mp,pointer l, pointer r) {
21520 pointer p; /* dependency list for right operand minus left operand */
21521 int t; /* the type of list |p| */
21522 pointer q; /* the constant term of |p| is here */
21523 pointer pp; /* dependency list for right operand */
21524 int tt; /* the type of list |pp| */
21525 boolean copied; /* have we copied a list that ought to be recycled? */
21526 @<Remove the left operand from its container, negate it, and
21527 put it into dependency list~|p| with constant term~|q|@>;
21528 @<Add the right operand to list |p|@>;
21529 if ( info(p)==null ) {
21530 @<Deal with redundant or inconsistent equation@>;
21532 mp_linear_eq(mp, p,t);
21533 if ( r==null ) if ( mp->cur_type!=mp_known ) {
21534 if ( type(mp->cur_exp)==mp_known ) {
21535 pp=mp->cur_exp; mp->cur_exp=value(mp->cur_exp); mp->cur_type=mp_known;
21536 mp_free_node(mp, pp,value_node_size);
21542 @ @<Remove the left operand from its container, negate it, and...@>=
21544 if ( t==mp_known ) {
21545 t=mp_dependent; p=mp_const_dependency(mp, -value(l)); q=p;
21546 } else if ( t==mp_independent ) {
21547 t=mp_dependent; p=mp_single_dependency(mp, l); negate(value(p));
21550 p=dep_list(l); q=p;
21553 if ( info(q)==null ) break;
21556 link(prev_dep(l))=link(q); prev_dep(link(q))=prev_dep(l);
21560 @ @<Deal with redundant or inconsistent equation@>=
21562 if ( abs(value(p))>64 ) { /* off by .001 or more */
21563 print_err("Inconsistent equation");
21564 @.Inconsistent equation@>
21565 mp_print(mp, " (off by "); mp_print_scaled(mp, value(p));
21566 mp_print_char(mp, ')');
21567 help2("The equation I just read contradicts what was said before.")
21568 ("But don't worry; continue and I'll just ignore it.");
21569 mp_put_get_error(mp);
21570 } else if ( r==null ) {
21571 @<Exclaim about a redundant equation@>;
21573 mp_free_node(mp, p,dep_node_size);
21576 @ @<Add the right operand to list |p|@>=
21578 if ( mp->cur_type==mp_known ) {
21579 value(q)=value(q)+mp->cur_exp; goto DONE1;
21582 if ( tt==mp_independent ) pp=mp_single_dependency(mp, mp->cur_exp);
21583 else pp=dep_list(mp->cur_exp);
21586 if ( type(r)==mp_known ) {
21587 value(q)=value(q)+value(r); goto DONE1;
21590 if ( tt==mp_independent ) pp=mp_single_dependency(mp, r);
21591 else pp=dep_list(r);
21594 if ( tt!=mp_independent ) copied=false;
21595 else { copied=true; tt=mp_dependent; };
21596 @<Add dependency list |pp| of type |tt| to dependency list~|p| of type~|t|@>;
21597 if ( copied ) mp_flush_node_list(mp, pp);
21600 @ @<Add dependency list |pp| of type |tt| to dependency list~|p| of type~|t|@>=
21601 mp->watch_coefs=false;
21603 p=mp_p_plus_q(mp, p,pp,t);
21604 } else if ( t==mp_proto_dependent ) {
21605 p=mp_p_plus_fq(mp, p,unity,pp,mp_proto_dependent,mp_dependent);
21608 while ( info(q)!=null ) {
21609 value(q)=mp_round_fraction(mp, value(q)); q=link(q);
21611 t=mp_proto_dependent; p=mp_p_plus_q(mp, p,pp,t);
21613 mp->watch_coefs=true;
21615 @ Our next goal is to process type declarations. For this purpose it's
21616 convenient to have a procedure that scans a $\langle\,$declared
21617 variable$\,\rangle$ and returns the corresponding token list. After the
21618 following procedure has acted, the token after the declared variable
21619 will have been scanned, so it will appear in |cur_cmd|, |cur_mod|,
21622 @<Declare the function called |scan_declared_variable|@>=
21623 pointer mp_scan_declared_variable (MP mp) {
21624 pointer x; /* hash address of the variable's root */
21625 pointer h,t; /* head and tail of the token list to be returned */
21626 pointer l; /* hash address of left bracket */
21627 mp_get_symbol(mp); x=mp->cur_sym;
21628 if ( mp->cur_cmd!=tag_token ) mp_clear_symbol(mp, x,false);
21629 h=mp_get_avail(mp); info(h)=x; t=h;
21632 if ( mp->cur_sym==0 ) break;
21633 if ( mp->cur_cmd!=tag_token ) if ( mp->cur_cmd!=internal_quantity) {
21634 if ( mp->cur_cmd==left_bracket ) {
21635 @<Descend past a collective subscript@>;
21640 link(t)=mp_get_avail(mp); t=link(t); info(t)=mp->cur_sym;
21642 if ( eq_type(x)!=tag_token ) mp_clear_symbol(mp, x,false);
21643 if ( equiv(x)==null ) mp_new_root(mp, x);
21647 @ If the subscript isn't collective, we don't accept it as part of the
21650 @<Descend past a collective subscript@>=
21652 l=mp->cur_sym; mp_get_x_next(mp);
21653 if ( mp->cur_cmd!=right_bracket ) {
21654 mp_back_input(mp); mp->cur_sym=l; mp->cur_cmd=left_bracket; break;
21656 mp->cur_sym=collective_subscript;
21660 @ Type declarations are introduced by the following primitive operations.
21663 mp_primitive(mp, "numeric",type_name,mp_numeric_type);
21664 @:numeric_}{\&{numeric} primitive@>
21665 mp_primitive(mp, "string",type_name,mp_string_type);
21666 @:string_}{\&{string} primitive@>
21667 mp_primitive(mp, "boolean",type_name,mp_boolean_type);
21668 @:boolean_}{\&{boolean} primitive@>
21669 mp_primitive(mp, "path",type_name,mp_path_type);
21670 @:path_}{\&{path} primitive@>
21671 mp_primitive(mp, "pen",type_name,mp_pen_type);
21672 @:pen_}{\&{pen} primitive@>
21673 mp_primitive(mp, "picture",type_name,mp_picture_type);
21674 @:picture_}{\&{picture} primitive@>
21675 mp_primitive(mp, "transform",type_name,mp_transform_type);
21676 @:transform_}{\&{transform} primitive@>
21677 mp_primitive(mp, "color",type_name,mp_color_type);
21678 @:color_}{\&{color} primitive@>
21679 mp_primitive(mp, "rgbcolor",type_name,mp_color_type);
21680 @:color_}{\&{rgbcolor} primitive@>
21681 mp_primitive(mp, "cmykcolor",type_name,mp_cmykcolor_type);
21682 @:color_}{\&{cmykcolor} primitive@>
21683 mp_primitive(mp, "pair",type_name,mp_pair_type);
21684 @:pair_}{\&{pair} primitive@>
21686 @ @<Cases of |print_cmd...@>=
21687 case type_name: mp_print_type(mp, m); break;
21689 @ Now we are ready to handle type declarations, assuming that a
21690 |type_name| has just been scanned.
21692 @<Declare action procedures for use by |do_statement|@>=
21693 void mp_do_type_declaration (MP mp) ;
21696 void mp_do_type_declaration (MP mp) {
21697 small_number t; /* the type being declared */
21698 pointer p; /* token list for a declared variable */
21699 pointer q; /* value node for the variable */
21700 if ( mp->cur_mod>=mp_transform_type )
21703 t=mp->cur_mod+unknown_tag;
21705 p=mp_scan_declared_variable(mp);
21706 mp_flush_variable(mp, equiv(info(p)),link(p),false);
21707 q=mp_find_variable(mp, p);
21709 type(q)=t; value(q)=null;
21711 print_err("Declared variable conflicts with previous vardef");
21712 @.Declared variable conflicts...@>
21713 help2("You can't use, e.g., `numeric foo[]' after `vardef foo'.")
21714 ("Proceed, and I'll ignore the illegal redeclaration.");
21715 mp_put_get_error(mp);
21717 mp_flush_list(mp, p);
21718 if ( mp->cur_cmd<comma ) {
21719 @<Flush spurious symbols after the declared variable@>;
21721 } while (! end_of_statement);
21724 @ @<Flush spurious symbols after the declared variable@>=
21726 print_err("Illegal suffix of declared variable will be flushed");
21727 @.Illegal suffix...flushed@>
21728 help5("Variables in declarations must consist entirely of")
21729 ("names and collective subscripts, e.g., `x[]a'.")
21730 ("Are you trying to use a reserved word in a variable name?")
21731 ("I'm going to discard the junk I found here,")
21732 ("up to the next comma or the end of the declaration.");
21733 if ( mp->cur_cmd==numeric_token )
21734 mp->help_line[2]="Explicit subscripts like `x15a' aren't permitted.";
21735 mp_put_get_error(mp); mp->scanner_status=flushing;
21738 @<Decrease the string reference count...@>;
21739 } while (mp->cur_cmd<comma); /* either |end_of_statement| or |cur_cmd=comma| */
21740 mp->scanner_status=normal;
21743 @ \MP's |main_control| procedure just calls |do_statement| repeatedly
21744 until coming to the end of the user's program.
21745 Each execution of |do_statement| concludes with
21746 |cur_cmd=semicolon|, |end_group|, or |stop|.
21748 @c void mp_main_control (MP mp) {
21750 mp_do_statement(mp);
21751 if ( mp->cur_cmd==end_group ) {
21752 print_err("Extra `endgroup'");
21753 @.Extra `endgroup'@>
21754 help2("I'm not currently working on a `begingroup',")
21755 ("so I had better not try to end anything.");
21756 mp_flush_error(mp, 0);
21758 } while (mp->cur_cmd!=stop);
21760 int __attribute__((noinline))
21762 if (mp->history < mp_fatal_error_stop ) {
21763 @<Install and test the non-local jump buffer@>;
21764 mp_main_control(mp); /* come to life */
21765 mp_final_cleanup(mp); /* prepare for death */
21766 mp_close_files_and_terminate(mp);
21768 return mp->history;
21770 int __attribute__((noinline))
21771 mp_execute (MP mp) {
21772 if (mp->history < mp_fatal_error_stop ) {
21773 mp->history = mp_spotless;
21774 mp->file_offset = 0;
21775 mp->term_offset = 0;
21777 @<Install and test the non-local jump buffer@>;
21778 if (mp->run_state==0) {
21781 mp_input_ln(mp,mp->term_in);
21782 mp_firm_up_the_line(mp);
21783 mp->buffer[limit]='%';
21787 mp_main_control(mp); /* come to life */
21789 return mp->history;
21791 int __attribute__((noinline))
21792 mp_finish (MP mp) {
21793 if (mp->history < mp_fatal_error_stop ) {
21794 @<Install and test the non-local jump buffer@>;
21795 mp_final_cleanup(mp); /* prepare for death */
21796 mp_close_files_and_terminate(mp);
21798 return mp->history;
21800 char * mp_mplib_version (MP mp) {
21802 return mplib_version;
21804 char * mp_metapost_version (MP mp) {
21806 return metapost_version;
21809 @ @<Exported function headers@>=
21810 int mp_run (MP mp);
21811 int mp_execute (MP mp);
21812 int mp_finish (MP mp);
21813 char * mp_mplib_version (MP mp);
21814 char * mp_metapost_version (MP mp);
21817 mp_primitive(mp, "end",stop,0);
21818 @:end_}{\&{end} primitive@>
21819 mp_primitive(mp, "dump",stop,1);
21820 @:dump_}{\&{dump} primitive@>
21822 @ @<Cases of |print_cmd...@>=
21824 if ( m==0 ) mp_print(mp, "end");
21825 else mp_print(mp, "dump");
21829 Let's turn now to statements that are classified as ``commands'' because
21830 of their imperative nature. We'll begin with simple ones, so that it
21831 will be clear how to hook command processing into the |do_statement| routine;
21832 then we'll tackle the tougher commands.
21834 Here's one of the simplest:
21836 @<Cases of |do_statement|...@>=
21837 case mp_random_seed: mp_do_random_seed(mp); break;
21839 @ @<Declare action procedures for use by |do_statement|@>=
21840 void mp_do_random_seed (MP mp) ;
21842 @ @c void mp_do_random_seed (MP mp) {
21844 if ( mp->cur_cmd!=assignment ) {
21845 mp_missing_err(mp, ":=");
21847 help1("Always say `randomseed:=<numeric expression>'.");
21850 mp_get_x_next(mp); mp_scan_expression(mp);
21851 if ( mp->cur_type!=mp_known ) {
21852 exp_err("Unknown value will be ignored");
21853 @.Unknown value...ignored@>
21854 help2("Your expression was too random for me to handle,")
21855 ("so I won't change the random seed just now.");
21856 mp_put_get_flush_error(mp, 0);
21858 @<Initialize the random seed to |cur_exp|@>;
21862 @ @<Initialize the random seed to |cur_exp|@>=
21864 mp_init_randoms(mp, mp->cur_exp);
21865 if ( mp->selector>=log_only && mp->selector<write_file) {
21866 mp->old_setting=mp->selector; mp->selector=log_only;
21867 mp_print_nl(mp, "{randomseed:=");
21868 mp_print_scaled(mp, mp->cur_exp);
21869 mp_print_char(mp, '}');
21870 mp_print_nl(mp, ""); mp->selector=mp->old_setting;
21874 @ And here's another simple one (somewhat different in flavor):
21876 @<Cases of |do_statement|...@>=
21878 mp_print_ln(mp); mp->interaction=mp->cur_mod;
21879 @<Initialize the print |selector| based on |interaction|@>;
21880 if ( mp->log_opened ) mp->selector=mp->selector+2;
21885 mp_primitive(mp, "batchmode",mode_command,mp_batch_mode);
21886 @:mp_batch_mode_}{\&{batchmode} primitive@>
21887 mp_primitive(mp, "nonstopmode",mode_command,mp_nonstop_mode);
21888 @:mp_nonstop_mode_}{\&{nonstopmode} primitive@>
21889 mp_primitive(mp, "scrollmode",mode_command,mp_scroll_mode);
21890 @:mp_scroll_mode_}{\&{scrollmode} primitive@>
21891 mp_primitive(mp, "errorstopmode",mode_command,mp_error_stop_mode);
21892 @:mp_error_stop_mode_}{\&{errorstopmode} primitive@>
21894 @ @<Cases of |print_cmd_mod|...@>=
21897 case mp_batch_mode: mp_print(mp, "batchmode"); break;
21898 case mp_nonstop_mode: mp_print(mp, "nonstopmode"); break;
21899 case mp_scroll_mode: mp_print(mp, "scrollmode"); break;
21900 default: mp_print(mp, "errorstopmode"); break;
21904 @ The `\&{inner}' and `\&{outer}' commands are only slightly harder.
21906 @<Cases of |do_statement|...@>=
21907 case protection_command: mp_do_protection(mp); break;
21910 mp_primitive(mp, "inner",protection_command,0);
21911 @:inner_}{\&{inner} primitive@>
21912 mp_primitive(mp, "outer",protection_command,1);
21913 @:outer_}{\&{outer} primitive@>
21915 @ @<Cases of |print_cmd...@>=
21916 case protection_command:
21917 if ( m==0 ) mp_print(mp, "inner");
21918 else mp_print(mp, "outer");
21921 @ @<Declare action procedures for use by |do_statement|@>=
21922 void mp_do_protection (MP mp) ;
21924 @ @c void mp_do_protection (MP mp) {
21925 int m; /* 0 to unprotect, 1 to protect */
21926 halfword t; /* the |eq_type| before we change it */
21929 mp_get_symbol(mp); t=eq_type(mp->cur_sym);
21931 if ( t>=outer_tag )
21932 eq_type(mp->cur_sym)=t-outer_tag;
21933 } else if ( t<outer_tag ) {
21934 eq_type(mp->cur_sym)=t+outer_tag;
21937 } while (mp->cur_cmd==comma);
21940 @ \MP\ never defines the tokens `\.(' and `\.)' to be primitives, but
21941 plain \MP\ begins with the declaration `\&{delimiters} \.{()}'. Such a
21942 declaration assigns the command code |left_delimiter| to `\.{(}' and
21943 |right_delimiter| to `\.{)}'; the |equiv| of each delimiter is the
21944 hash address of its mate.
21946 @<Cases of |do_statement|...@>=
21947 case delimiters: mp_def_delims(mp); break;
21949 @ @<Declare action procedures for use by |do_statement|@>=
21950 void mp_def_delims (MP mp) ;
21952 @ @c void mp_def_delims (MP mp) {
21953 pointer l_delim,r_delim; /* the new delimiter pair */
21954 mp_get_clear_symbol(mp); l_delim=mp->cur_sym;
21955 mp_get_clear_symbol(mp); r_delim=mp->cur_sym;
21956 eq_type(l_delim)=left_delimiter; equiv(l_delim)=r_delim;
21957 eq_type(r_delim)=right_delimiter; equiv(r_delim)=l_delim;
21961 @ Here is a procedure that is called when \MP\ has reached a point
21962 where some right delimiter is mandatory.
21964 @<Declare the procedure called |check_delimiter|@>=
21965 void mp_check_delimiter (MP mp,pointer l_delim, pointer r_delim) {
21966 if ( mp->cur_cmd==right_delimiter )
21967 if ( mp->cur_mod==l_delim )
21969 if ( mp->cur_sym!=r_delim ) {
21970 mp_missing_err(mp, str(text(r_delim)));
21972 help2("I found no right delimiter to match a left one. So I've")
21973 ("put one in, behind the scenes; this may fix the problem.");
21976 print_err("The token `"); mp_print_text(r_delim);
21977 @.The token...delimiter@>
21978 mp_print(mp, "' is no longer a right delimiter");
21979 help3("Strange: This token has lost its former meaning!")
21980 ("I'll read it as a right delimiter this time;")
21981 ("but watch out, I'll probably miss it later.");
21986 @ The next four commands save or change the values associated with tokens.
21988 @<Cases of |do_statement|...@>=
21991 mp_get_symbol(mp); mp_save_variable(mp, mp->cur_sym); mp_get_x_next(mp);
21992 } while (mp->cur_cmd==comma);
21994 case interim_command: mp_do_interim(mp); break;
21995 case let_command: mp_do_let(mp); break;
21996 case new_internal: mp_do_new_internal(mp); break;
21998 @ @<Declare action procedures for use by |do_statement|@>=
21999 void mp_do_statement (MP mp);
22000 void mp_do_interim (MP mp);
22002 @ @c void mp_do_interim (MP mp) {
22004 if ( mp->cur_cmd!=internal_quantity ) {
22005 print_err("The token `");
22006 @.The token...quantity@>
22007 if ( mp->cur_sym==0 ) mp_print(mp, "(%CAPSULE)");
22008 else mp_print_text(mp->cur_sym);
22009 mp_print(mp, "' isn't an internal quantity");
22010 help1("Something like `tracingonline' should follow `interim'.");
22013 mp_save_internal(mp, mp->cur_mod); mp_back_input(mp);
22015 mp_do_statement(mp);
22018 @ The following procedure is careful not to undefine the left-hand symbol
22019 too soon, lest commands like `{\tt let x=x}' have a surprising effect.
22021 @<Declare action procedures for use by |do_statement|@>=
22022 void mp_do_let (MP mp) ;
22024 @ @c void mp_do_let (MP mp) {
22025 pointer l; /* hash location of the left-hand symbol */
22026 mp_get_symbol(mp); l=mp->cur_sym; mp_get_x_next(mp);
22027 if ( mp->cur_cmd!=equals ) if ( mp->cur_cmd!=assignment ) {
22028 mp_missing_err(mp, "=");
22030 help3("You should have said `let symbol = something'.")
22031 ("But don't worry; I'll pretend that an equals sign")
22032 ("was present. The next token I read will be `something'.");
22036 switch (mp->cur_cmd) {
22037 case defined_macro: case secondary_primary_macro:
22038 case tertiary_secondary_macro: case expression_tertiary_macro:
22039 add_mac_ref(mp->cur_mod);
22044 mp_clear_symbol(mp, l,false); eq_type(l)=mp->cur_cmd;
22045 if ( mp->cur_cmd==tag_token ) equiv(l)=null;
22046 else equiv(l)=mp->cur_mod;
22050 @ @<Declarations@>=
22051 void mp_grow_internals (MP mp, int l);
22052 void mp_do_new_internal (MP mp) ;
22055 void mp_grow_internals (MP mp, int l) {
22059 if ( hash_end+l>max_halfword ) {
22060 mp_confusion(mp, "out of memory space"); /* can't be reached */
22062 int_name = xmalloc ((l+1),sizeof(char *));
22063 internal = xmalloc ((l+1),sizeof(scaled));
22064 for (k=0;k<=l; k++ ) {
22065 if (k<=mp->max_internal) {
22066 internal[k]=mp->internal[k];
22067 int_name[k]=mp->int_name[k];
22073 xfree(mp->internal); xfree(mp->int_name);
22074 mp->int_name = int_name;
22075 mp->internal = internal;
22076 mp->max_internal = l;
22080 void mp_do_new_internal (MP mp) {
22082 if ( mp->int_ptr==mp->max_internal ) {
22083 mp_grow_internals(mp, (mp->max_internal + (mp->max_internal>>2)));
22085 mp_get_clear_symbol(mp); incr(mp->int_ptr);
22086 eq_type(mp->cur_sym)=internal_quantity;
22087 equiv(mp->cur_sym)=mp->int_ptr;
22088 if(mp->int_name[mp->int_ptr]!=NULL)
22089 xfree(mp->int_name[mp->int_ptr]);
22090 mp->int_name[mp->int_ptr]=str(text(mp->cur_sym));
22091 mp->internal[mp->int_ptr]=0;
22093 } while (mp->cur_cmd==comma);
22096 @ @<Dealloc variables@>=
22097 for (k=0;k<=mp->max_internal;k++) {
22098 xfree(mp->int_name[k]);
22100 xfree(mp->internal);
22101 xfree(mp->int_name);
22104 @ The various `\&{show}' commands are distinguished by modifier fields
22107 @d show_token_code 0 /* show the meaning of a single token */
22108 @d show_stats_code 1 /* show current memory and string usage */
22109 @d show_code 2 /* show a list of expressions */
22110 @d show_var_code 3 /* show a variable and its descendents */
22111 @d show_dependencies_code 4 /* show dependent variables in terms of independents */
22114 mp_primitive(mp, "showtoken",show_command,show_token_code);
22115 @:show_token_}{\&{showtoken} primitive@>
22116 mp_primitive(mp, "showstats",show_command,show_stats_code);
22117 @:show_stats_}{\&{showstats} primitive@>
22118 mp_primitive(mp, "show",show_command,show_code);
22119 @:show_}{\&{show} primitive@>
22120 mp_primitive(mp, "showvariable",show_command,show_var_code);
22121 @:show_var_}{\&{showvariable} primitive@>
22122 mp_primitive(mp, "showdependencies",show_command,show_dependencies_code);
22123 @:show_dependencies_}{\&{showdependencies} primitive@>
22125 @ @<Cases of |print_cmd...@>=
22128 case show_token_code:mp_print(mp, "showtoken"); break;
22129 case show_stats_code:mp_print(mp, "showstats"); break;
22130 case show_code:mp_print(mp, "show"); break;
22131 case show_var_code:mp_print(mp, "showvariable"); break;
22132 default: mp_print(mp, "showdependencies"); break;
22136 @ @<Cases of |do_statement|...@>=
22137 case show_command:mp_do_show_whatever(mp); break;
22139 @ The value of |cur_mod| controls the |verbosity| in the |print_exp| routine:
22140 if it's |show_code|, complicated structures are abbreviated, otherwise
22143 @<Declare action procedures for use by |do_statement|@>=
22144 void mp_do_show (MP mp) ;
22146 @ @c void mp_do_show (MP mp) {
22148 mp_get_x_next(mp); mp_scan_expression(mp);
22149 mp_print_nl(mp, ">> ");
22151 mp_print_exp(mp, null,2); mp_flush_cur_exp(mp, 0);
22152 } while (mp->cur_cmd==comma);
22155 @ @<Declare action procedures for use by |do_statement|@>=
22156 void mp_disp_token (MP mp) ;
22158 @ @c void mp_disp_token (MP mp) {
22159 mp_print_nl(mp, "> ");
22161 if ( mp->cur_sym==0 ) {
22162 @<Show a numeric or string or capsule token@>;
22164 mp_print_text(mp->cur_sym); mp_print_char(mp, '=');
22165 if ( eq_type(mp->cur_sym)>=outer_tag ) mp_print(mp, "(outer) ");
22166 mp_print_cmd_mod(mp, mp->cur_cmd,mp->cur_mod);
22167 if ( mp->cur_cmd==defined_macro ) {
22168 mp_print_ln(mp); mp_show_macro(mp, mp->cur_mod,null,100000);
22169 } /* this avoids recursion between |show_macro| and |print_cmd_mod| */
22174 @ @<Show a numeric or string or capsule token@>=
22176 if ( mp->cur_cmd==numeric_token ) {
22177 mp_print_scaled(mp, mp->cur_mod);
22178 } else if ( mp->cur_cmd==capsule_token ) {
22179 mp_print_capsule(mp,mp->cur_mod);
22181 mp_print_char(mp, '"');
22182 mp_print_str(mp, mp->cur_mod); mp_print_char(mp, '"');
22183 delete_str_ref(mp->cur_mod);
22187 @ The following cases of |print_cmd_mod| might arise in connection
22188 with |disp_token|, although they don't correspond to any
22191 @<Cases of |print_cmd_...@>=
22192 case left_delimiter:
22193 case right_delimiter:
22194 if ( c==left_delimiter ) mp_print(mp, "left");
22195 else mp_print(mp, "right");
22196 mp_print(mp, " delimiter that matches ");
22200 if ( m==null ) mp_print(mp, "tag");
22201 else mp_print(mp, "variable");
22203 case defined_macro:
22204 mp_print(mp, "macro:");
22206 case secondary_primary_macro:
22207 case tertiary_secondary_macro:
22208 case expression_tertiary_macro:
22209 mp_print_cmd_mod(mp, macro_def,c);
22210 mp_print(mp, "'d macro:");
22211 mp_print_ln(mp); mp_show_token_list(mp, link(link(m)),null,1000,0);
22214 mp_print(mp, "[repeat the loop]");
22216 case internal_quantity:
22217 mp_print(mp, mp->int_name[m]);
22220 @ @<Declare action procedures for use by |do_statement|@>=
22221 void mp_do_show_token (MP mp) ;
22223 @ @c void mp_do_show_token (MP mp) {
22225 get_t_next; mp_disp_token(mp);
22227 } while (mp->cur_cmd==comma);
22230 @ @<Declare action procedures for use by |do_statement|@>=
22231 void mp_do_show_stats (MP mp) ;
22233 @ @c void mp_do_show_stats (MP mp) {
22234 mp_print_nl(mp, "Memory usage ");
22235 @.Memory usage...@>
22236 mp_print_int(mp, mp->var_used); mp_print_char(mp, '&'); mp_print_int(mp, mp->dyn_used);
22238 mp_print(mp, "unknown");
22239 mp_print(mp, " ("); mp_print_int(mp, mp->hi_mem_min-mp->lo_mem_max-1);
22240 mp_print(mp, " still untouched)"); mp_print_ln(mp);
22241 mp_print_nl(mp, "String usage ");
22242 mp_print_int(mp, mp->strs_in_use-mp->init_str_use);
22243 mp_print_char(mp, '&'); mp_print_int(mp, mp->pool_in_use-mp->init_pool_ptr);
22245 mp_print(mp, "unknown");
22246 mp_print(mp, " (");
22247 mp_print_int(mp, mp->max_strings-1-mp->strs_used_up); mp_print_char(mp, '&');
22248 mp_print_int(mp, mp->pool_size-mp->pool_ptr);
22249 mp_print(mp, " now untouched)"); mp_print_ln(mp);
22253 @ Here's a recursive procedure that gives an abbreviated account
22254 of a variable, for use by |do_show_var|.
22256 @<Declare action procedures for use by |do_statement|@>=
22257 void mp_disp_var (MP mp,pointer p) ;
22259 @ @c void mp_disp_var (MP mp,pointer p) {
22260 pointer q; /* traverses attributes and subscripts */
22261 int n; /* amount of macro text to show */
22262 if ( type(p)==mp_structured ) {
22263 @<Descend the structure@>;
22264 } else if ( type(p)>=mp_unsuffixed_macro ) {
22265 @<Display a variable macro@>;
22266 } else if ( type(p)!=undefined ){
22267 mp_print_nl(mp, ""); mp_print_variable_name(mp, p);
22268 mp_print_char(mp, '=');
22269 mp_print_exp(mp, p,0);
22273 @ @<Descend the structure@>=
22276 do { mp_disp_var(mp, q); q=link(q); } while (q!=end_attr);
22278 while ( name_type(q)==mp_subscr ) {
22279 mp_disp_var(mp, q); q=link(q);
22283 @ @<Display a variable macro@>=
22285 mp_print_nl(mp, ""); mp_print_variable_name(mp, p);
22286 if ( type(p)>mp_unsuffixed_macro )
22287 mp_print(mp, "@@#"); /* |suffixed_macro| */
22288 mp_print(mp, "=macro:");
22289 if ( (int)mp->file_offset>=mp->max_print_line-20 ) n=5;
22290 else n=mp->max_print_line-mp->file_offset-15;
22291 mp_show_macro(mp, value(p),null,n);
22294 @ @<Declare action procedures for use by |do_statement|@>=
22295 void mp_do_show_var (MP mp) ;
22297 @ @c void mp_do_show_var (MP mp) {
22300 if ( mp->cur_sym>0 ) if ( mp->cur_sym<=hash_end )
22301 if ( mp->cur_cmd==tag_token ) if ( mp->cur_mod!=null ) {
22302 mp_disp_var(mp, mp->cur_mod); goto DONE;
22307 } while (mp->cur_cmd==comma);
22310 @ @<Declare action procedures for use by |do_statement|@>=
22311 void mp_do_show_dependencies (MP mp) ;
22313 @ @c void mp_do_show_dependencies (MP mp) {
22314 pointer p; /* link that runs through all dependencies */
22316 while ( p!=dep_head ) {
22317 if ( mp_interesting(mp, p) ) {
22318 mp_print_nl(mp, ""); mp_print_variable_name(mp, p);
22319 if ( type(p)==mp_dependent ) mp_print_char(mp, '=');
22320 else mp_print(mp, " = "); /* extra spaces imply proto-dependency */
22321 mp_print_dependency(mp, dep_list(p),type(p));
22324 while ( info(p)!=null ) p=link(p);
22330 @ Finally we are ready for the procedure that governs all of the
22333 @<Declare action procedures for use by |do_statement|@>=
22334 void mp_do_show_whatever (MP mp) ;
22336 @ @c void mp_do_show_whatever (MP mp) {
22337 if ( mp->interaction==mp_error_stop_mode ) wake_up_terminal;
22338 switch (mp->cur_mod) {
22339 case show_token_code:mp_do_show_token(mp); break;
22340 case show_stats_code:mp_do_show_stats(mp); break;
22341 case show_code:mp_do_show(mp); break;
22342 case show_var_code:mp_do_show_var(mp); break;
22343 case show_dependencies_code:mp_do_show_dependencies(mp); break;
22344 } /* there are no other cases */
22345 if ( mp->internal[mp_showstopping]>0 ){
22348 if ( mp->interaction<mp_error_stop_mode ) {
22349 help0; decr(mp->error_count);
22351 help1("This isn't an error message; I'm just showing something.");
22353 if ( mp->cur_cmd==semicolon ) mp_error(mp);
22354 else mp_put_get_error(mp);
22358 @ The `\&{addto}' command needs the following additional primitives:
22360 @d double_path_code 0 /* command modifier for `\&{doublepath}' */
22361 @d contour_code 1 /* command modifier for `\&{contour}' */
22362 @d also_code 2 /* command modifier for `\&{also}' */
22364 @ Pre and postscripts need two new identifiers:
22366 @d with_pre_script 11
22367 @d with_post_script 13
22370 mp_primitive(mp, "doublepath",thing_to_add,double_path_code);
22371 @:double_path_}{\&{doublepath} primitive@>
22372 mp_primitive(mp, "contour",thing_to_add,contour_code);
22373 @:contour_}{\&{contour} primitive@>
22374 mp_primitive(mp, "also",thing_to_add,also_code);
22375 @:also_}{\&{also} primitive@>
22376 mp_primitive(mp, "withpen",with_option,mp_pen_type);
22377 @:with_pen_}{\&{withpen} primitive@>
22378 mp_primitive(mp, "dashed",with_option,mp_picture_type);
22379 @:dashed_}{\&{dashed} primitive@>
22380 mp_primitive(mp, "withprescript",with_option,with_pre_script);
22381 @:with_pre_script_}{\&{withprescript} primitive@>
22382 mp_primitive(mp, "withpostscript",with_option,with_post_script);
22383 @:with_post_script_}{\&{withpostscript} primitive@>
22384 mp_primitive(mp, "withoutcolor",with_option,mp_no_model);
22385 @:with_color_}{\&{withoutcolor} primitive@>
22386 mp_primitive(mp, "withgreyscale",with_option,mp_grey_model);
22387 @:with_color_}{\&{withgreyscale} primitive@>
22388 mp_primitive(mp, "withcolor",with_option,mp_uninitialized_model);
22389 @:with_color_}{\&{withcolor} primitive@>
22390 /* \&{withrgbcolor} is an alias for \&{withcolor} */
22391 mp_primitive(mp, "withrgbcolor",with_option,mp_rgb_model);
22392 @:with_color_}{\&{withrgbcolor} primitive@>
22393 mp_primitive(mp, "withcmykcolor",with_option,mp_cmyk_model);
22394 @:with_color_}{\&{withcmykcolor} primitive@>
22396 @ @<Cases of |print_cmd...@>=
22398 if ( m==contour_code ) mp_print(mp, "contour");
22399 else if ( m==double_path_code ) mp_print(mp, "doublepath");
22400 else mp_print(mp, "also");
22403 if ( m==mp_pen_type ) mp_print(mp, "withpen");
22404 else if ( m==with_pre_script ) mp_print(mp, "withprescript");
22405 else if ( m==with_post_script ) mp_print(mp, "withpostscript");
22406 else if ( m==mp_no_model ) mp_print(mp, "withoutcolor");
22407 else if ( m==mp_rgb_model ) mp_print(mp, "withrgbcolor");
22408 else if ( m==mp_uninitialized_model ) mp_print(mp, "withcolor");
22409 else if ( m==mp_cmyk_model ) mp_print(mp, "withcmykcolor");
22410 else if ( m==mp_grey_model ) mp_print(mp, "withgreyscale");
22411 else mp_print(mp, "dashed");
22414 @ The |scan_with_list| procedure parses a $\langle$with list$\rangle$ and
22415 updates the list of graphical objects starting at |p|. Each $\langle$with
22416 clause$\rangle$ updates all graphical objects whose |type| is compatible.
22417 Other objects are ignored.
22419 @<Declare action procedures for use by |do_statement|@>=
22420 void mp_scan_with_list (MP mp,pointer p) ;
22422 @ @c void mp_scan_with_list (MP mp,pointer p) {
22423 small_number t; /* |cur_mod| of the |with_option| (should match |cur_type|) */
22424 pointer q; /* for list manipulation */
22425 int old_setting; /* saved |selector| setting */
22426 pointer k; /* for finding the near-last item in a list */
22427 str_number s; /* for string cleanup after combining */
22428 pointer cp,pp,dp,ap,bp;
22429 /* objects being updated; |void| initially; |null| to suppress update */
22430 cp=mp_void; pp=mp_void; dp=mp_void; ap=mp_void; bp=mp_void;
22432 while ( mp->cur_cmd==with_option ){
22435 if ( t!=mp_no_model ) mp_scan_expression(mp);
22436 if (((t==with_pre_script)&&(mp->cur_type!=mp_string_type))||
22437 ((t==with_post_script)&&(mp->cur_type!=mp_string_type))||
22438 ((t==mp_uninitialized_model)&&
22439 ((mp->cur_type!=mp_cmykcolor_type)&&(mp->cur_type!=mp_color_type)
22440 &&(mp->cur_type!=mp_known)&&(mp->cur_type!=mp_boolean_type)))||
22441 ((t==mp_cmyk_model)&&(mp->cur_type!=mp_cmykcolor_type))||
22442 ((t==mp_rgb_model)&&(mp->cur_type!=mp_color_type))||
22443 ((t==mp_grey_model)&&(mp->cur_type!=mp_known))||
22444 ((t==mp_pen_type)&&(mp->cur_type!=t))||
22445 ((t==mp_picture_type)&&(mp->cur_type!=t)) ) {
22446 @<Complain about improper type@>;
22447 } else if ( t==mp_uninitialized_model ) {
22448 if ( cp==mp_void ) @<Make |cp| a colored object in object list~|p|@>;
22450 @<Transfer a color from the current expression to object~|cp|@>;
22451 mp_flush_cur_exp(mp, 0);
22452 } else if ( t==mp_rgb_model ) {
22453 if ( cp==mp_void ) @<Make |cp| a colored object in object list~|p|@>;
22455 @<Transfer a rgbcolor from the current expression to object~|cp|@>;
22456 mp_flush_cur_exp(mp, 0);
22457 } else if ( t==mp_cmyk_model ) {
22458 if ( cp==mp_void ) @<Make |cp| a colored object in object list~|p|@>;
22460 @<Transfer a cmykcolor from the current expression to object~|cp|@>;
22461 mp_flush_cur_exp(mp, 0);
22462 } else if ( t==mp_grey_model ) {
22463 if ( cp==mp_void ) @<Make |cp| a colored object in object list~|p|@>;
22465 @<Transfer a greyscale from the current expression to object~|cp|@>;
22466 mp_flush_cur_exp(mp, 0);
22467 } else if ( t==mp_no_model ) {
22468 if ( cp==mp_void ) @<Make |cp| a colored object in object list~|p|@>;
22470 @<Transfer a noncolor from the current expression to object~|cp|@>;
22471 } else if ( t==mp_pen_type ) {
22472 if ( pp==mp_void ) @<Make |pp| an object in list~|p| that needs a pen@>;
22474 if ( pen_p(pp)!=null ) mp_toss_knot_list(mp, pen_p(pp));
22475 pen_p(pp)=mp->cur_exp; mp->cur_type=mp_vacuous;
22477 } else if ( t==with_pre_script ) {
22480 while ( (ap!=null)&&(! has_color(ap)) )
22483 if ( pre_script(ap)!=null ) { /* build a new,combined string */
22485 old_setting=mp->selector;
22486 mp->selector=new_string;
22487 str_room(length(pre_script(ap))+length(mp->cur_exp)+2);
22488 mp_print_str(mp, mp->cur_exp);
22489 append_char(13); /* a forced \ps\ newline */
22490 mp_print_str(mp, pre_script(ap));
22491 pre_script(ap)=mp_make_string(mp);
22493 mp->selector=old_setting;
22495 pre_script(ap)=mp->cur_exp;
22497 mp->cur_type=mp_vacuous;
22499 } else if ( t==with_post_script ) {
22503 while ( link(k)!=null ) {
22505 if ( has_color(k) ) bp=k;
22508 if ( post_script(bp)!=null ) {
22510 old_setting=mp->selector;
22511 mp->selector=new_string;
22512 str_room(length(post_script(bp))+length(mp->cur_exp)+2);
22513 mp_print_str(mp, post_script(bp));
22514 append_char(13); /* a forced \ps\ newline */
22515 mp_print_str(mp, mp->cur_exp);
22516 post_script(bp)=mp_make_string(mp);
22518 mp->selector=old_setting;
22520 post_script(bp)=mp->cur_exp;
22522 mp->cur_type=mp_vacuous;
22525 if ( dp==mp_void ) {
22526 @<Make |dp| a stroked node in list~|p|@>;
22529 if ( dash_p(dp)!=null ) delete_edge_ref(dash_p(dp));
22530 dash_p(dp)=mp_make_dashes(mp, mp->cur_exp);
22531 dash_scale(dp)=unity;
22532 mp->cur_type=mp_vacuous;
22536 @<Copy the information from objects |cp|, |pp|, and |dp| into the rest
22540 @ @<Complain about improper type@>=
22541 { exp_err("Improper type");
22543 help2("Next time say `withpen <known pen expression>';")
22544 ("I'll ignore the bad `with' clause and look for another.");
22545 if ( t==with_pre_script )
22546 mp->help_line[1]="Next time say `withprescript <known string expression>';";
22547 else if ( t==with_post_script )
22548 mp->help_line[1]="Next time say `withpostscript <known string expression>';";
22549 else if ( t==mp_picture_type )
22550 mp->help_line[1]="Next time say `dashed <known picture expression>';";
22551 else if ( t==mp_uninitialized_model )
22552 mp->help_line[1]="Next time say `withcolor <known color expression>';";
22553 else if ( t==mp_rgb_model )
22554 mp->help_line[1]="Next time say `withrgbcolor <known color expression>';";
22555 else if ( t==mp_cmyk_model )
22556 mp->help_line[1]="Next time say `withcmykcolor <known cmykcolor expression>';";
22557 else if ( t==mp_grey_model )
22558 mp->help_line[1]="Next time say `withgreyscale <known numeric expression>';";;
22559 mp_put_get_flush_error(mp, 0);
22562 @ Forcing the color to be between |0| and |unity| here guarantees that no
22563 picture will ever contain a color outside the legal range for \ps\ graphics.
22565 @<Transfer a color from the current expression to object~|cp|@>=
22566 { if ( mp->cur_type==mp_color_type )
22567 @<Transfer a rgbcolor from the current expression to object~|cp|@>
22568 else if ( mp->cur_type==mp_cmykcolor_type )
22569 @<Transfer a cmykcolor from the current expression to object~|cp|@>
22570 else if ( mp->cur_type==mp_known )
22571 @<Transfer a greyscale from the current expression to object~|cp|@>
22572 else if ( mp->cur_exp==false_code )
22573 @<Transfer a noncolor from the current expression to object~|cp|@>;
22576 @ @<Transfer a rgbcolor from the current expression to object~|cp|@>=
22577 { q=value(mp->cur_exp);
22582 red_val(cp)=value(red_part_loc(q));
22583 green_val(cp)=value(green_part_loc(q));
22584 blue_val(cp)=value(blue_part_loc(q));
22585 color_model(cp)=mp_rgb_model;
22586 if ( red_val(cp)<0 ) red_val(cp)=0;
22587 if ( green_val(cp)<0 ) green_val(cp)=0;
22588 if ( blue_val(cp)<0 ) blue_val(cp)=0;
22589 if ( red_val(cp)>unity ) red_val(cp)=unity;
22590 if ( green_val(cp)>unity ) green_val(cp)=unity;
22591 if ( blue_val(cp)>unity ) blue_val(cp)=unity;
22594 @ @<Transfer a cmykcolor from the current expression to object~|cp|@>=
22595 { q=value(mp->cur_exp);
22596 cyan_val(cp)=value(cyan_part_loc(q));
22597 magenta_val(cp)=value(magenta_part_loc(q));
22598 yellow_val(cp)=value(yellow_part_loc(q));
22599 black_val(cp)=value(black_part_loc(q));
22600 color_model(cp)=mp_cmyk_model;
22601 if ( cyan_val(cp)<0 ) cyan_val(cp)=0;
22602 if ( magenta_val(cp)<0 ) magenta_val(cp)=0;
22603 if ( yellow_val(cp)<0 ) yellow_val(cp)=0;
22604 if ( black_val(cp)<0 ) black_val(cp)=0;
22605 if ( cyan_val(cp)>unity ) cyan_val(cp)=unity;
22606 if ( magenta_val(cp)>unity ) magenta_val(cp)=unity;
22607 if ( yellow_val(cp)>unity ) yellow_val(cp)=unity;
22608 if ( black_val(cp)>unity ) black_val(cp)=unity;
22611 @ @<Transfer a greyscale from the current expression to object~|cp|@>=
22618 color_model(cp)=mp_grey_model;
22619 if ( grey_val(cp)<0 ) grey_val(cp)=0;
22620 if ( grey_val(cp)>unity ) grey_val(cp)=unity;
22623 @ @<Transfer a noncolor from the current expression to object~|cp|@>=
22630 color_model(cp)=mp_no_model;
22633 @ @<Make |cp| a colored object in object list~|p|@>=
22635 while ( cp!=null ){
22636 if ( has_color(cp) ) break;
22641 @ @<Make |pp| an object in list~|p| that needs a pen@>=
22643 while ( pp!=null ) {
22644 if ( has_pen(pp) ) break;
22649 @ @<Make |dp| a stroked node in list~|p|@>=
22651 while ( dp!=null ) {
22652 if ( type(dp)==mp_stroked_code ) break;
22657 @ @<Copy the information from objects |cp|, |pp|, and |dp| into...@>=
22658 @<Copy |cp|'s color into the colored objects linked to~|cp|@>;
22659 if ( pp>mp_void ) {
22660 @<Copy |pen_p(pp)| into stroked and filled nodes linked to |pp|@>;
22662 if ( dp>mp_void ) {
22663 @<Make stroked nodes linked to |dp| refer to |dash_p(dp)|@>;
22667 @ @<Copy |cp|'s color into the colored objects linked to~|cp|@>=
22669 while ( q!=null ) {
22670 if ( has_color(q) ) {
22671 red_val(q)=red_val(cp);
22672 green_val(q)=green_val(cp);
22673 blue_val(q)=blue_val(cp);
22674 black_val(q)=black_val(cp);
22675 color_model(q)=color_model(cp);
22681 @ @<Copy |pen_p(pp)| into stroked and filled nodes linked to |pp|@>=
22683 while ( q!=null ) {
22684 if ( has_pen(q) ) {
22685 if ( pen_p(q)!=null ) mp_toss_knot_list(mp, pen_p(q));
22686 pen_p(q)=copy_pen(pen_p(pp));
22692 @ @<Make stroked nodes linked to |dp| refer to |dash_p(dp)|@>=
22694 while ( q!=null ) {
22695 if ( type(q)==mp_stroked_code ) {
22696 if ( dash_p(q)!=null ) delete_edge_ref(dash_p(q));
22697 dash_p(q)=dash_p(dp);
22698 dash_scale(q)=unity;
22699 if ( dash_p(q)!=null ) add_edge_ref(dash_p(q));
22705 @ One of the things we need to do when we've parsed an \&{addto} or
22706 similar command is find the header of a supposed \&{picture} variable, given
22707 a token list for that variable. Since the edge structure is about to be
22708 updated, we use |private_edges| to make sure that this is possible.
22710 @<Declare action procedures for use by |do_statement|@>=
22711 pointer mp_find_edges_var (MP mp, pointer t) ;
22713 @ @c pointer mp_find_edges_var (MP mp, pointer t) {
22715 pointer cur_edges; /* the return value */
22716 p=mp_find_variable(mp, t); cur_edges=null;
22718 mp_obliterated(mp, t); mp_put_get_error(mp);
22719 } else if ( type(p)!=mp_picture_type ) {
22720 print_err("Variable "); mp_show_token_list(mp, t,null,1000,0);
22721 @.Variable x is the wrong type@>
22722 mp_print(mp, " is the wrong type (");
22723 mp_print_type(mp, type(p)); mp_print_char(mp, ')');
22724 help2("I was looking for a \"known\" picture variable.")
22725 ("So I'll not change anything just now.");
22726 mp_put_get_error(mp);
22728 value(p)=mp_private_edges(mp, value(p));
22729 cur_edges=value(p);
22731 mp_flush_node_list(mp, t);
22735 @ @<Cases of |do_statement|...@>=
22736 case add_to_command: mp_do_add_to(mp); break;
22737 case bounds_command:mp_do_bounds(mp); break;
22740 mp_primitive(mp, "clip",bounds_command,mp_start_clip_code);
22741 @:clip_}{\&{clip} primitive@>
22742 mp_primitive(mp, "setbounds",bounds_command,mp_start_bounds_code);
22743 @:set_bounds_}{\&{setbounds} primitive@>
22745 @ @<Cases of |print_cmd...@>=
22746 case bounds_command:
22747 if ( m==mp_start_clip_code ) mp_print(mp, "clip");
22748 else mp_print(mp, "setbounds");
22751 @ The following function parses the beginning of an \&{addto} or \&{clip}
22752 command: it expects a variable name followed by a token with |cur_cmd=sep|
22753 and then an expression. The function returns the token list for the variable
22754 and stores the command modifier for the separator token in the global variable
22755 |last_add_type|. We must be careful because this variable might get overwritten
22756 any time we call |get_x_next|.
22759 quarterword last_add_type;
22760 /* command modifier that identifies the last \&{addto} command */
22762 @ @<Declare action procedures for use by |do_statement|@>=
22763 pointer mp_start_draw_cmd (MP mp,quarterword sep) ;
22765 @ @c pointer mp_start_draw_cmd (MP mp,quarterword sep) {
22766 pointer lhv; /* variable to add to left */
22767 quarterword add_type=0; /* value to be returned in |last_add_type| */
22769 mp_get_x_next(mp); mp->var_flag=sep; mp_scan_primary(mp);
22770 if ( mp->cur_type!=mp_token_list ) {
22771 @<Abandon edges command because there's no variable@>;
22773 lhv=mp->cur_exp; add_type=mp->cur_mod;
22774 mp->cur_type=mp_vacuous; mp_get_x_next(mp); mp_scan_expression(mp);
22776 mp->last_add_type=add_type;
22780 @ @<Abandon edges command because there's no variable@>=
22781 { exp_err("Not a suitable variable");
22782 @.Not a suitable variable@>
22783 help4("At this point I needed to see the name of a picture variable.")
22784 ("(Or perhaps you have indeed presented me with one; I might")
22785 ("have missed it, if it wasn't followed by the proper token.)")
22786 ("So I'll not change anything just now.");
22787 mp_put_get_flush_error(mp, 0);
22790 @ Here is an example of how to use |start_draw_cmd|.
22792 @<Declare action procedures for use by |do_statement|@>=
22793 void mp_do_bounds (MP mp) ;
22795 @ @c void mp_do_bounds (MP mp) {
22796 pointer lhv,lhe; /* variable on left, the corresponding edge structure */
22797 pointer p; /* for list manipulation */
22798 integer m; /* initial value of |cur_mod| */
22800 lhv=mp_start_draw_cmd(mp, to_token);
22802 lhe=mp_find_edges_var(mp, lhv);
22804 mp_flush_cur_exp(mp, 0);
22805 } else if ( mp->cur_type!=mp_path_type ) {
22806 exp_err("Improper `clip'");
22807 @.Improper `addto'@>
22808 help2("This expression should have specified a known path.")
22809 ("So I'll not change anything just now.");
22810 mp_put_get_flush_error(mp, 0);
22811 } else if ( left_type(mp->cur_exp)==mp_endpoint ) {
22812 @<Complain about a non-cycle@>;
22814 @<Make |cur_exp| into a \&{setbounds} or clipping path and add it to |lhe|@>;
22819 @ @<Complain about a non-cycle@>=
22820 { print_err("Not a cycle");
22822 help2("That contour should have ended with `..cycle' or `&cycle'.")
22823 ("So I'll not change anything just now."); mp_put_get_error(mp);
22826 @ @<Make |cur_exp| into a \&{setbounds} or clipping path and add...@>=
22827 { p=mp_new_bounds_node(mp, mp->cur_exp,m);
22828 link(p)=link(dummy_loc(lhe));
22829 link(dummy_loc(lhe))=p;
22830 if ( obj_tail(lhe)==dummy_loc(lhe) ) obj_tail(lhe)=p;
22831 p=mp_get_node(mp, mp->gr_object_size[stop_type(m)]);
22832 type(p)=stop_type(m);
22833 link(obj_tail(lhe))=p;
22835 mp_init_bbox(mp, lhe);
22838 @ The |do_add_to| procedure is a little like |do_clip| but there are a lot more
22839 cases to deal with.
22841 @<Declare action procedures for use by |do_statement|@>=
22842 void mp_do_add_to (MP mp) ;
22844 @ @c void mp_do_add_to (MP mp) {
22845 pointer lhv,lhe; /* variable on left, the corresponding edge structure */
22846 pointer p; /* the graphical object or list for |scan_with_list| to update */
22847 pointer e; /* an edge structure to be merged */
22848 quarterword add_type; /* |also_code|, |contour_code|, or |double_path_code| */
22849 lhv=mp_start_draw_cmd(mp, thing_to_add); add_type=mp->last_add_type;
22851 if ( add_type==also_code ) {
22852 @<Make sure the current expression is a suitable picture and set |e| and |p|
22855 @<Create a graphical object |p| based on |add_type| and the current
22858 mp_scan_with_list(mp, p);
22859 @<Use |p|, |e|, and |add_type| to augment |lhv| as requested@>;
22863 @ Setting |p:=null| causes the $\langle$with list$\rangle$ to be ignored;
22864 setting |e:=null| prevents anything from being added to |lhe|.
22866 @ @<Make sure the current expression is a suitable picture and set |e|...@>=
22869 if ( mp->cur_type!=mp_picture_type ) {
22870 exp_err("Improper `addto'");
22871 @.Improper `addto'@>
22872 help2("This expression should have specified a known picture.")
22873 ("So I'll not change anything just now."); mp_put_get_flush_error(mp, 0);
22875 e=mp_private_edges(mp, mp->cur_exp); mp->cur_type=mp_vacuous;
22876 p=link(dummy_loc(e));
22880 @ In this case |add_type<>also_code| so setting |p:=null| suppresses future
22881 attempts to add to the edge structure.
22883 @<Create a graphical object |p| based on |add_type| and the current...@>=
22885 if ( mp->cur_type==mp_pair_type ) mp_pair_to_path(mp);
22886 if ( mp->cur_type!=mp_path_type ) {
22887 exp_err("Improper `addto'");
22888 @.Improper `addto'@>
22889 help2("This expression should have specified a known path.")
22890 ("So I'll not change anything just now.");
22891 mp_put_get_flush_error(mp, 0);
22892 } else if ( add_type==contour_code ) {
22893 if ( left_type(mp->cur_exp)==mp_endpoint ) {
22894 @<Complain about a non-cycle@>;
22896 p=mp_new_fill_node(mp, mp->cur_exp);
22897 mp->cur_type=mp_vacuous;
22900 p=mp_new_stroked_node(mp, mp->cur_exp);
22901 mp->cur_type=mp_vacuous;
22905 @ @<Use |p|, |e|, and |add_type| to augment |lhv| as requested@>=
22906 lhe=mp_find_edges_var(mp, lhv);
22908 if ( (e==null)&&(p!=null) ) e=mp_toss_gr_object(mp, p);
22909 if ( e!=null ) delete_edge_ref(e);
22910 } else if ( add_type==also_code ) {
22912 @<Merge |e| into |lhe| and delete |e|@>;
22916 } else if ( p!=null ) {
22917 link(obj_tail(lhe))=p;
22919 if ( add_type==double_path_code )
22920 if ( pen_p(p)==null )
22921 pen_p(p)=mp_get_pen_circle(mp, 0);
22924 @ @<Merge |e| into |lhe| and delete |e|@>=
22925 { if ( link(dummy_loc(e))!=null ) {
22926 link(obj_tail(lhe))=link(dummy_loc(e));
22927 obj_tail(lhe)=obj_tail(e);
22928 obj_tail(e)=dummy_loc(e);
22929 link(dummy_loc(e))=null;
22930 mp_flush_dash_list(mp, lhe);
22932 mp_toss_edges(mp, e);
22935 @ @<Cases of |do_statement|...@>=
22936 case ship_out_command: mp_do_ship_out(mp); break;
22938 @ @<Declare action procedures for use by |do_statement|@>=
22939 @<Declare the function called |tfm_check|@>;
22940 @<Declare the \ps\ output procedures@>;
22941 void mp_do_ship_out (MP mp) ;
22943 @ @c void mp_do_ship_out (MP mp) {
22944 integer c; /* the character code */
22945 mp_get_x_next(mp); mp_scan_expression(mp);
22946 if ( mp->cur_type!=mp_picture_type ) {
22947 @<Complain that it's not a known picture@>;
22949 c=mp_round_unscaled(mp, mp->internal[mp_char_code]) % 256;
22950 if ( c<0 ) c=c+256;
22951 @<Store the width information for character code~|c|@>;
22952 mp_ship_out(mp, mp->cur_exp);
22953 mp_flush_cur_exp(mp, 0);
22957 @ @<Complain that it's not a known picture@>=
22959 exp_err("Not a known picture");
22960 help1("I can only output known pictures.");
22961 mp_put_get_flush_error(mp, 0);
22964 @ The \&{everyjob} command simply assigns a nonzero value to the global variable
22967 @<Cases of |do_statement|...@>=
22968 case every_job_command:
22969 mp_get_symbol(mp); mp->start_sym=mp->cur_sym; mp_get_x_next(mp);
22973 halfword start_sym; /* a symbolic token to insert at beginning of job */
22978 @ Finally, we have only the ``message'' commands remaining.
22981 @d err_message_code 1
22983 @d filename_template_code 3
22984 @d print_with_leading_zeroes(A) g = mp->pool_ptr;
22985 mp_print_int(mp, (A)); g = mp->pool_ptr-g;
22987 mp->pool_ptr = mp->pool_ptr - g;
22989 mp_print_char(mp, '0');
22992 mp_print_int(mp, (A));
22997 mp_primitive(mp, "message",message_command,message_code);
22998 @:message_}{\&{message} primitive@>
22999 mp_primitive(mp, "errmessage",message_command,err_message_code);
23000 @:err_message_}{\&{errmessage} primitive@>
23001 mp_primitive(mp, "errhelp",message_command,err_help_code);
23002 @:err_help_}{\&{errhelp} primitive@>
23003 mp_primitive(mp, "filenametemplate",message_command,filename_template_code);
23004 @:filename_template_}{\&{filenametemplate} primitive@>
23006 @ @<Cases of |print_cmd...@>=
23007 case message_command:
23008 if ( m<err_message_code ) mp_print(mp, "message");
23009 else if ( m==err_message_code ) mp_print(mp, "errmessage");
23010 else if ( m==filename_template_code ) mp_print(mp, "filenametemplate");
23011 else mp_print(mp, "errhelp");
23014 @ @<Cases of |do_statement|...@>=
23015 case message_command: mp_do_message(mp); break;
23017 @ @<Declare action procedures for use by |do_statement|@>=
23018 @<Declare a procedure called |no_string_err|@>;
23019 void mp_do_message (MP mp) ;
23022 @c void mp_do_message (MP mp) {
23023 int m; /* the type of message */
23024 m=mp->cur_mod; mp_get_x_next(mp); mp_scan_expression(mp);
23025 if ( mp->cur_type!=mp_string_type )
23026 mp_no_string_err(mp, "A message should be a known string expression.");
23030 mp_print_nl(mp, ""); mp_print_str(mp, mp->cur_exp);
23032 case err_message_code:
23033 @<Print string |cur_exp| as an error message@>;
23035 case err_help_code:
23036 @<Save string |cur_exp| as the |err_help|@>;
23038 case filename_template_code:
23039 @<Save the filename template@>;
23041 } /* there are no other cases */
23043 mp_flush_cur_exp(mp, 0);
23046 @ @<Declare a procedure called |no_string_err|@>=
23047 void mp_no_string_err (MP mp,char *s) {
23048 exp_err("Not a string");
23051 mp_put_get_error(mp);
23054 @ The global variable |err_help| is zero when the user has most recently
23055 given an empty help string, or if none has ever been given.
23057 @<Save string |cur_exp| as the |err_help|@>=
23059 if ( mp->err_help!=0 ) delete_str_ref(mp->err_help);
23060 if ( length(mp->cur_exp)==0 ) mp->err_help=0;
23061 else { mp->err_help=mp->cur_exp; add_str_ref(mp->err_help); }
23064 @ If \&{errmessage} occurs often in |mp_scroll_mode|, without user-defined
23065 \&{errhelp}, we don't want to give a long help message each time. So we
23066 give a verbose explanation only once.
23069 boolean long_help_seen; /* has the long \.{\\errmessage} help been used? */
23071 @ @<Set init...@>=mp->long_help_seen=false;
23073 @ @<Print string |cur_exp| as an error message@>=
23075 print_err(""); mp_print_str(mp, mp->cur_exp);
23076 if ( mp->err_help!=0 ) {
23077 mp->use_err_help=true;
23078 } else if ( mp->long_help_seen ) {
23079 help1("(That was another `errmessage'.)") ;
23081 if ( mp->interaction<mp_error_stop_mode ) mp->long_help_seen=true;
23082 help4("This error message was generated by an `errmessage'")
23083 ("command, so I can\'t give any explicit help.")
23084 ("Pretend that you're Miss Marple: Examine all clues,")
23086 ("and deduce the truth by inspired guesses.");
23088 mp_put_get_error(mp); mp->use_err_help=false;
23091 @ @<Cases of |do_statement|...@>=
23092 case write_command: mp_do_write(mp); break;
23094 @ @<Declare action procedures for use by |do_statement|@>=
23095 void mp_do_write (MP mp) ;
23097 @ @c void mp_do_write (MP mp) {
23098 str_number t; /* the line of text to be written */
23099 write_index n,n0; /* for searching |wr_fname| and |wr_file| arrays */
23100 int old_setting; /* for saving |selector| during output */
23102 mp_scan_expression(mp);
23103 if ( mp->cur_type!=mp_string_type ) {
23104 mp_no_string_err(mp, "The text to be written should be a known string expression");
23105 } else if ( mp->cur_cmd!=to_token ) {
23106 print_err("Missing `to' clause");
23107 help1("A write command should end with `to <filename>'");
23108 mp_put_get_error(mp);
23110 t=mp->cur_exp; mp->cur_type=mp_vacuous;
23112 mp_scan_expression(mp);
23113 if ( mp->cur_type!=mp_string_type )
23114 mp_no_string_err(mp, "I can\'t write to that file name. It isn't a known string");
23116 @<Write |t| to the file named by |cur_exp|@>;
23120 mp_flush_cur_exp(mp, 0);
23123 @ @<Write |t| to the file named by |cur_exp|@>=
23125 @<Find |n| where |wr_fname[n]=cur_exp| and call |open_write_file| if
23126 |cur_exp| must be inserted@>;
23127 if ( mp_str_vs_str(mp, t,mp->eof_line)==0 ) {
23128 @<Record the end of file on |wr_file[n]|@>;
23130 old_setting=mp->selector;
23131 mp->selector=n+write_file;
23132 mp_print_str(mp, t); mp_print_ln(mp);
23133 mp->selector = old_setting;
23137 @ @<Find |n| where |wr_fname[n]=cur_exp| and call |open_write_file| if...@>=
23139 char *fn = str(mp->cur_exp);
23141 n0=mp->write_files;
23142 while (mp_xstrcmp(fn,mp->wr_fname[n])!=0) {
23143 if ( n==0 ) { /* bottom reached */
23144 if ( n0==mp->write_files ) {
23145 if ( mp->write_files<mp->max_write_files ) {
23146 incr(mp->write_files);
23151 l = mp->max_write_files + (mp->max_write_files>>2);
23152 wr_file = xmalloc((l+1),sizeof(void *));
23153 wr_fname = xmalloc((l+1),sizeof(char *));
23154 for (k=0;k<=l;k++) {
23155 if (k<=mp->max_write_files) {
23156 wr_file[k]=mp->wr_file[k];
23157 wr_fname[k]=mp->wr_fname[k];
23163 xfree(mp->wr_file); xfree(mp->wr_fname);
23164 mp->max_write_files = l;
23165 mp->wr_file = wr_file;
23166 mp->wr_fname = wr_fname;
23170 mp_open_write_file(mp, fn ,n);
23173 if ( mp->wr_fname[n]==NULL ) n0=n;
23178 @ @<Record the end of file on |wr_file[n]|@>=
23179 { (mp->close_file)(mp,mp->wr_file[n]);
23180 xfree(mp->wr_fname[n]);
23181 mp->wr_fname[n]=NULL;
23182 if ( n==mp->write_files-1 ) mp->write_files=n;
23186 @* \[42] Writing font metric data.
23187 \TeX\ gets its knowledge about fonts from font metric files, also called
23188 \.{TFM} files; the `\.T' in `\.{TFM}' stands for \TeX,
23189 but other programs know about them too. One of \MP's duties is to
23190 write \.{TFM} files so that the user's fonts can readily be
23191 applied to typesetting.
23192 @:TFM files}{\.{TFM} files@>
23193 @^font metric files@>
23195 The information in a \.{TFM} file appears in a sequence of 8-bit bytes.
23196 Since the number of bytes is always a multiple of~4, we could
23197 also regard the file as a sequence of 32-bit words, but \MP\ uses the
23198 byte interpretation. The format of \.{TFM} files was designed by
23199 Lyle Ramshaw in 1980. The intent is to convey a lot of different kinds
23200 @^Ramshaw, Lyle Harold@>
23201 of information in a compact but useful form.
23204 void * tfm_file; /* the font metric output goes here */
23205 char * metric_file_name; /* full name of the font metric file */
23207 @ The first 24 bytes (6 words) of a \.{TFM} file contain twelve 16-bit
23208 integers that give the lengths of the various subsequent portions
23209 of the file. These twelve integers are, in order:
23210 $$\vbox{\halign{\hfil#&$\null=\null$#\hfil\cr
23211 |lf|&length of the entire file, in words;\cr
23212 |lh|&length of the header data, in words;\cr
23213 |bc|&smallest character code in the font;\cr
23214 |ec|&largest character code in the font;\cr
23215 |nw|&number of words in the width table;\cr
23216 |nh|&number of words in the height table;\cr
23217 |nd|&number of words in the depth table;\cr
23218 |ni|&number of words in the italic correction table;\cr
23219 |nl|&number of words in the lig/kern table;\cr
23220 |nk|&number of words in the kern table;\cr
23221 |ne|&number of words in the extensible character table;\cr
23222 |np|&number of font parameter words.\cr}}$$
23223 They are all nonnegative and less than $2^{15}$. We must have |bc-1<=ec<=255|,
23225 $$\hbox{|lf=6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np|.}$$
23226 Note that a font may contain as many as 256 characters (if |bc=0| and |ec=255|),
23227 and as few as 0 characters (if |bc=ec+1|).
23229 Incidentally, when two or more 8-bit bytes are combined to form an integer of
23230 16 or more bits, the most significant bytes appear first in the file.
23231 This is called BigEndian order.
23232 @^BigEndian order@>
23234 @ The rest of the \.{TFM} file may be regarded as a sequence of ten data
23237 The most important data type used here is a |fix_word|, which is
23238 a 32-bit representation of a binary fraction. A |fix_word| is a signed
23239 quantity, with the two's complement of the entire word used to represent
23240 negation. Of the 32 bits in a |fix_word|, exactly 12 are to the left of the
23241 binary point; thus, the largest |fix_word| value is $2048-2^{-20}$, and
23242 the smallest is $-2048$. We will see below, however, that all but two of
23243 the |fix_word| values must lie between $-16$ and $+16$.
23245 @ The first data array is a block of header information, which contains
23246 general facts about the font. The header must contain at least two words,
23247 |header[0]| and |header[1]|, whose meaning is explained below. Additional
23248 header information of use to other software routines might also be
23249 included, and \MP\ will generate it if the \.{headerbyte} command occurs.
23250 For example, 16 more words of header information are in use at the Xerox
23251 Palo Alto Research Center; the first ten specify the character coding
23252 scheme used (e.g., `\.{XEROX TEXT}' or `\.{TEX MATHSY}'), the next five
23253 give the font family name (e.g., `\.{HELVETICA}' or `\.{CMSY}'), and the
23254 last gives the ``face byte.''
23256 \yskip\hang|header[0]| is a 32-bit check sum that \MP\ will copy into
23257 the \.{GF} output file. This helps ensure consistency between files,
23258 since \TeX\ records the check sums from the \.{TFM}'s it reads, and these
23259 should match the check sums on actual fonts that are used. The actual
23260 relation between this check sum and the rest of the \.{TFM} file is not
23261 important; the check sum is simply an identification number with the
23262 property that incompatible fonts almost always have distinct check sums.
23265 \yskip\hang|header[1]| is a |fix_word| containing the design size of the
23266 font, in units of \TeX\ points. This number must be at least 1.0; it is
23267 fairly arbitrary, but usually the design size is 10.0 for a ``10 point''
23268 font, i.e., a font that was designed to look best at a 10-point size,
23269 whatever that really means. When a \TeX\ user asks for a font `\.{at}
23270 $\delta$ \.{pt}', the effect is to override the design size and replace it
23271 by $\delta$, and to multiply the $x$ and~$y$ coordinates of the points in
23272 the font image by a factor of $\delta$ divided by the design size. {\sl
23273 All other dimensions in the\/ \.{TFM} file are |fix_word|\kern-1pt\
23274 numbers in design-size units.} Thus, for example, the value of |param[6]|,
23275 which defines the \.{em} unit, is often the |fix_word| value $2^{20}=1.0$,
23276 since many fonts have a design size equal to one em. The other dimensions
23277 must be less than 16 design-size units in absolute value; thus,
23278 |header[1]| and |param[1]| are the only |fix_word| entries in the whole
23279 \.{TFM} file whose first byte might be something besides 0 or 255.
23281 @ Next comes the |char_info| array, which contains one |char_info_word|
23282 per character. Each word in this part of the file contains six fields
23283 packed into four bytes as follows.
23285 \yskip\hang first byte: |width_index| (8 bits)\par
23286 \hang second byte: |height_index| (4 bits) times 16, plus |depth_index|
23288 \hang third byte: |italic_index| (6 bits) times 4, plus |tag|
23290 \hang fourth byte: |remainder| (8 bits)\par
23292 The actual width of a character is \\{width}|[width_index]|, in design-size
23293 units; this is a device for compressing information, since many characters
23294 have the same width. Since it is quite common for many characters
23295 to have the same height, depth, or italic correction, the \.{TFM} format
23296 imposes a limit of 16 different heights, 16 different depths, and
23297 64 different italic corrections.
23299 Incidentally, the relation $\\{width}[0]=\\{height}[0]=\\{depth}[0]=
23300 \\{italic}[0]=0$ should always hold, so that an index of zero implies a
23301 value of zero. The |width_index| should never be zero unless the
23302 character does not exist in the font, since a character is valid if and
23303 only if it lies between |bc| and |ec| and has a nonzero |width_index|.
23305 @ The |tag| field in a |char_info_word| has four values that explain how to
23306 interpret the |remainder| field.
23308 \yskip\hang|tag=0| (|no_tag|) means that |remainder| is unused.\par
23309 \hang|tag=1| (|lig_tag|) means that this character has a ligature/kerning
23310 program starting at location |remainder| in the |lig_kern| array.\par
23311 \hang|tag=2| (|list_tag|) means that this character is part of a chain of
23312 characters of ascending sizes, and not the largest in the chain. The
23313 |remainder| field gives the character code of the next larger character.\par
23314 \hang|tag=3| (|ext_tag|) means that this character code represents an
23315 extensible character, i.e., a character that is built up of smaller pieces
23316 so that it can be made arbitrarily large. The pieces are specified in
23317 |exten[remainder]|.\par
23319 Characters with |tag=2| and |tag=3| are treated as characters with |tag=0|
23320 unless they are used in special circumstances in math formulas. For example,
23321 \TeX's \.{\\sum} operation looks for a |list_tag|, and the \.{\\left}
23322 operation looks for both |list_tag| and |ext_tag|.
23324 @d no_tag 0 /* vanilla character */
23325 @d lig_tag 1 /* character has a ligature/kerning program */
23326 @d list_tag 2 /* character has a successor in a charlist */
23327 @d ext_tag 3 /* character is extensible */
23329 @ The |lig_kern| array contains instructions in a simple programming language
23330 that explains what to do for special letter pairs. Each word in this array is a
23331 |lig_kern_command| of four bytes.
23333 \yskip\hang first byte: |skip_byte|, indicates that this is the final program
23334 step if the byte is 128 or more, otherwise the next step is obtained by
23335 skipping this number of intervening steps.\par
23336 \hang second byte: |next_char|, ``if |next_char| follows the current character,
23337 then perform the operation and stop, otherwise continue.''\par
23338 \hang third byte: |op_byte|, indicates a ligature step if less than~128,
23339 a kern step otherwise.\par
23340 \hang fourth byte: |remainder|.\par
23343 additional space equal to |kern[256*(op_byte-128)+remainder]| is inserted
23344 between the current character and |next_char|. This amount is
23345 often negative, so that the characters are brought closer together
23346 by kerning; but it might be positive.
23348 There are eight kinds of ligature steps, having |op_byte| codes $4a+2b+c$ where
23349 $0\le a\le b+c$ and $0\le b,c\le1$. The character whose code is
23350 |remainder| is inserted between the current character and |next_char|;
23351 then the current character is deleted if $b=0$, and |next_char| is
23352 deleted if $c=0$; then we pass over $a$~characters to reach the next
23353 current character (which may have a ligature/kerning program of its own).
23355 If the very first instruction of the |lig_kern| array has |skip_byte=255|,
23356 the |next_char| byte is the so-called right boundary character of this font;
23357 the value of |next_char| need not lie between |bc| and~|ec|.
23358 If the very last instruction of the |lig_kern| array has |skip_byte=255|,
23359 there is a special ligature/kerning program for a left boundary character,
23360 beginning at location |256*op_byte+remainder|.
23361 The interpretation is that \TeX\ puts implicit boundary characters
23362 before and after each consecutive string of characters from the same font.
23363 These implicit characters do not appear in the output, but they can affect
23364 ligatures and kerning.
23366 If the very first instruction of a character's |lig_kern| program has
23367 |skip_byte>128|, the program actually begins in location
23368 |256*op_byte+remainder|. This feature allows access to large |lig_kern|
23369 arrays, because the first instruction must otherwise
23370 appear in a location |<=255|.
23372 Any instruction with |skip_byte>128| in the |lig_kern| array must satisfy
23374 $$\hbox{|256*op_byte+remainder<nl|.}$$
23375 If such an instruction is encountered during
23376 normal program execution, it denotes an unconditional halt; no ligature
23377 command is performed.
23380 /* value indicating `\.{STOP}' in a lig/kern program */
23381 @d kern_flag (128) /* op code for a kern step */
23382 @d skip_byte(A) mp->lig_kern[(A)].b0
23383 @d next_char(A) mp->lig_kern[(A)].b1
23384 @d op_byte(A) mp->lig_kern[(A)].b2
23385 @d rem_byte(A) mp->lig_kern[(A)].b3
23387 @ Extensible characters are specified by an |extensible_recipe|, which
23388 consists of four bytes called |top|, |mid|, |bot|, and |rep| (in this
23389 order). These bytes are the character codes of individual pieces used to
23390 build up a large symbol. If |top|, |mid|, or |bot| are zero, they are not
23391 present in the built-up result. For example, an extensible vertical line is
23392 like an extensible bracket, except that the top and bottom pieces are missing.
23394 Let $T$, $M$, $B$, and $R$ denote the respective pieces, or an empty box
23395 if the piece isn't present. Then the extensible characters have the form
23396 $TR^kMR^kB$ from top to bottom, for some |k>=0|, unless $M$ is absent;
23397 in the latter case we can have $TR^kB$ for both even and odd values of~|k|.
23398 The width of the extensible character is the width of $R$; and the
23399 height-plus-depth is the sum of the individual height-plus-depths of the
23400 components used, since the pieces are butted together in a vertical list.
23402 @d ext_top(A) mp->exten[(A)].b0 /* |top| piece in a recipe */
23403 @d ext_mid(A) mp->exten[(A)].b1 /* |mid| piece in a recipe */
23404 @d ext_bot(A) mp->exten[(A)].b2 /* |bot| piece in a recipe */
23405 @d ext_rep(A) mp->exten[(A)].b3 /* |rep| piece in a recipe */
23407 @ The final portion of a \.{TFM} file is the |param| array, which is another
23408 sequence of |fix_word| values.
23410 \yskip\hang|param[1]=slant| is the amount of italic slant, which is used
23411 to help position accents. For example, |slant=.25| means that when you go
23412 up one unit, you also go .25 units to the right. The |slant| is a pure
23413 number; it is the only |fix_word| other than the design size itself that is
23414 not scaled by the design size.
23416 \hang|param[2]=space| is the normal spacing between words in text.
23417 Note that character 040 in the font need not have anything to do with
23420 \hang|param[3]=space_stretch| is the amount of glue stretching between words.
23422 \hang|param[4]=space_shrink| is the amount of glue shrinking between words.
23424 \hang|param[5]=x_height| is the size of one ex in the font; it is also
23425 the height of letters for which accents don't have to be raised or lowered.
23427 \hang|param[6]=quad| is the size of one em in the font.
23429 \hang|param[7]=extra_space| is the amount added to |param[2]| at the
23433 If fewer than seven parameters are present, \TeX\ sets the missing parameters
23438 @d space_stretch_code 3
23439 @d space_shrink_code 4
23442 @d extra_space_code 7
23444 @ So that is what \.{TFM} files hold. One of \MP's duties is to output such
23445 information, and it does this all at once at the end of a job.
23446 In order to prepare for such frenetic activity, it squirrels away the
23447 necessary facts in various arrays as information becomes available.
23449 Character dimensions (\&{charwd}, \&{charht}, \&{chardp}, and \&{charic})
23450 are stored respectively in |tfm_width|, |tfm_height|, |tfm_depth|, and
23451 |tfm_ital_corr|. Other information about a character (e.g., about
23452 its ligatures or successors) is accessible via the |char_tag| and
23453 |char_remainder| arrays. Other information about the font as a whole
23454 is kept in additional arrays called |header_byte|, |lig_kern|,
23455 |kern|, |exten|, and |param|.
23457 @d max_tfm_int 32510
23458 @d undefined_label max_tfm_int /* an undefined local label */
23461 #define TFM_ITEMS 257
23463 eight_bits ec; /* smallest and largest character codes shipped out */
23464 scaled tfm_width[TFM_ITEMS]; /* \&{charwd} values */
23465 scaled tfm_height[TFM_ITEMS]; /* \&{charht} values */
23466 scaled tfm_depth[TFM_ITEMS]; /* \&{chardp} values */
23467 scaled tfm_ital_corr[TFM_ITEMS]; /* \&{charic} values */
23468 boolean char_exists[TFM_ITEMS]; /* has this code been shipped out? */
23469 int char_tag[TFM_ITEMS]; /* |remainder| category */
23470 int char_remainder[TFM_ITEMS]; /* the |remainder| byte */
23471 char *header_byte; /* bytes of the \.{TFM} header */
23472 int header_last; /* last initialized \.{TFM} header byte */
23473 int header_size; /* size of the \.{TFM} header */
23474 four_quarters *lig_kern; /* the ligature/kern table */
23475 short nl; /* the number of ligature/kern steps so far */
23476 scaled *kern; /* distinct kerning amounts */
23477 short nk; /* the number of distinct kerns so far */
23478 four_quarters exten[TFM_ITEMS]; /* extensible character recipes */
23479 short ne; /* the number of extensible characters so far */
23480 scaled *param; /* \&{fontinfo} parameters */
23481 short np; /* the largest \&{fontinfo} parameter specified so far */
23482 short nw;short nh;short nd;short ni; /* sizes of \.{TFM} subtables */
23483 short skip_table[TFM_ITEMS]; /* local label status */
23484 boolean lk_started; /* has there been a lig/kern step in this command yet? */
23485 integer bchar; /* right boundary character */
23486 short bch_label; /* left boundary starting location */
23487 short ll;short lll; /* registers used for lig/kern processing */
23488 short label_loc[257]; /* lig/kern starting addresses */
23489 eight_bits label_char[257]; /* characters for |label_loc| */
23490 short label_ptr; /* highest position occupied in |label_loc| */
23492 @ @<Allocate or initialize ...@>=
23493 mp->header_last = 0; mp->header_size = 128; /* just for init */
23494 mp->header_byte = xmalloc(mp->header_size, sizeof(char));
23495 mp->lig_kern = NULL; /* allocated when needed */
23496 mp->kern = NULL; /* allocated when needed */
23497 mp->param = NULL; /* allocated when needed */
23499 @ @<Dealloc variables@>=
23500 xfree(mp->header_byte);
23501 xfree(mp->lig_kern);
23506 for (k=0;k<= 255;k++ ) {
23507 mp->tfm_width[k]=0; mp->tfm_height[k]=0; mp->tfm_depth[k]=0; mp->tfm_ital_corr[k]=0;
23508 mp->char_exists[k]=false; mp->char_tag[k]=no_tag; mp->char_remainder[k]=0;
23509 mp->skip_table[k]=undefined_label;
23511 memset(mp->header_byte,0,mp->header_size);
23512 mp->bc=255; mp->ec=0; mp->nl=0; mp->nk=0; mp->ne=0; mp->np=0;
23513 mp->internal[mp_boundary_char]=-unity;
23514 mp->bch_label=undefined_label;
23515 mp->label_loc[0]=-1; mp->label_ptr=0;
23517 @ @<Declarations@>=
23518 scaled mp_tfm_check (MP mp,small_number m) ;
23520 @ @<Declare the function called |tfm_check|@>=
23521 scaled mp_tfm_check (MP mp,small_number m) {
23522 if ( abs(mp->internal[m])>=fraction_half ) {
23523 print_err("Enormous "); mp_print(mp, mp->int_name[m]);
23524 @.Enormous charwd...@>
23525 @.Enormous chardp...@>
23526 @.Enormous charht...@>
23527 @.Enormous charic...@>
23528 @.Enormous designsize...@>
23529 mp_print(mp, " has been reduced");
23530 help1("Font metric dimensions must be less than 2048pt.");
23531 mp_put_get_error(mp);
23532 if ( mp->internal[m]>0 ) return (fraction_half-1);
23533 else return (1-fraction_half);
23535 return mp->internal[m];
23539 @ @<Store the width information for character code~|c|@>=
23540 if ( c<mp->bc ) mp->bc=c;
23541 if ( c>mp->ec ) mp->ec=c;
23542 mp->char_exists[c]=true;
23543 mp->tfm_width[c]=mp_tfm_check(mp, mp_char_wd);
23544 mp->tfm_height[c]=mp_tfm_check(mp, mp_char_ht);
23545 mp->tfm_depth[c]=mp_tfm_check(mp, mp_char_dp);
23546 mp->tfm_ital_corr[c]=mp_tfm_check(mp, mp_char_ic)
23548 @ Now let's consider \MP's special \.{TFM}-oriented commands.
23550 @<Cases of |do_statement|...@>=
23551 case tfm_command: mp_do_tfm_command(mp); break;
23553 @ @d char_list_code 0
23554 @d lig_table_code 1
23555 @d extensible_code 2
23556 @d header_byte_code 3
23557 @d font_dimen_code 4
23560 mp_primitive(mp, "charlist",tfm_command,char_list_code);
23561 @:char_list_}{\&{charlist} primitive@>
23562 mp_primitive(mp, "ligtable",tfm_command,lig_table_code);
23563 @:lig_table_}{\&{ligtable} primitive@>
23564 mp_primitive(mp, "extensible",tfm_command,extensible_code);
23565 @:extensible_}{\&{extensible} primitive@>
23566 mp_primitive(mp, "headerbyte",tfm_command,header_byte_code);
23567 @:header_byte_}{\&{headerbyte} primitive@>
23568 mp_primitive(mp, "fontdimen",tfm_command,font_dimen_code);
23569 @:font_dimen_}{\&{fontdimen} primitive@>
23571 @ @<Cases of |print_cmd...@>=
23574 case char_list_code:mp_print(mp, "charlist"); break;
23575 case lig_table_code:mp_print(mp, "ligtable"); break;
23576 case extensible_code:mp_print(mp, "extensible"); break;
23577 case header_byte_code:mp_print(mp, "headerbyte"); break;
23578 default: mp_print(mp, "fontdimen"); break;
23582 @ @<Declare action procedures for use by |do_statement|@>=
23583 eight_bits mp_get_code (MP mp) ;
23585 @ @c eight_bits mp_get_code (MP mp) { /* scans a character code value */
23586 integer c; /* the code value found */
23587 mp_get_x_next(mp); mp_scan_expression(mp);
23588 if ( mp->cur_type==mp_known ) {
23589 c=mp_round_unscaled(mp, mp->cur_exp);
23590 if ( c>=0 ) if ( c<256 ) return c;
23591 } else if ( mp->cur_type==mp_string_type ) {
23592 if ( length(mp->cur_exp)==1 ) {
23593 c=mp->str_pool[mp->str_start[mp->cur_exp]];
23597 exp_err("Invalid code has been replaced by 0");
23598 @.Invalid code...@>
23599 help2("I was looking for a number between 0 and 255, or for a")
23600 ("string of length 1. Didn't find it; will use 0 instead.");
23601 mp_put_get_flush_error(mp, 0); c=0;
23605 @ @<Declare action procedures for use by |do_statement|@>=
23606 void mp_set_tag (MP mp,halfword c, small_number t, halfword r) ;
23608 @ @c void mp_set_tag (MP mp,halfword c, small_number t, halfword r) {
23609 if ( mp->char_tag[c]==no_tag ) {
23610 mp->char_tag[c]=t; mp->char_remainder[c]=r;
23612 incr(mp->label_ptr); mp->label_loc[mp->label_ptr]=r;
23613 mp->label_char[mp->label_ptr]=c;
23616 @<Complain about a character tag conflict@>;
23620 @ @<Complain about a character tag conflict@>=
23622 print_err("Character ");
23623 if ( (c>' ')&&(c<127) ) mp_print_char(mp,c);
23624 else if ( c==256 ) mp_print(mp, "||");
23625 else { mp_print(mp, "code "); mp_print_int(mp, c); };
23626 mp_print(mp, " is already ");
23627 @.Character c is already...@>
23628 switch (mp->char_tag[c]) {
23629 case lig_tag: mp_print(mp, "in a ligtable"); break;
23630 case list_tag: mp_print(mp, "in a charlist"); break;
23631 case ext_tag: mp_print(mp, "extensible"); break;
23632 } /* there are no other cases */
23633 help2("It's not legal to label a character more than once.")
23634 ("So I'll not change anything just now.");
23635 mp_put_get_error(mp);
23638 @ @<Declare action procedures for use by |do_statement|@>=
23639 void mp_do_tfm_command (MP mp) ;
23641 @ @c void mp_do_tfm_command (MP mp) {
23642 int c,cc; /* character codes */
23643 int k; /* index into the |kern| array */
23644 int j; /* index into |header_byte| or |param| */
23645 switch (mp->cur_mod) {
23646 case char_list_code:
23648 /* we will store a list of character successors */
23649 while ( mp->cur_cmd==colon ) {
23650 cc=mp_get_code(mp); mp_set_tag(mp, c,list_tag,cc); c=cc;
23653 case lig_table_code:
23654 if (mp->lig_kern==NULL)
23655 mp->lig_kern = xmalloc((max_tfm_int+1),sizeof(four_quarters));
23656 if (mp->kern==NULL)
23657 mp->kern = xmalloc((max_tfm_int+1),sizeof(scaled));
23658 @<Store a list of ligature/kern steps@>;
23660 case extensible_code:
23661 @<Define an extensible recipe@>;
23663 case header_byte_code:
23664 case font_dimen_code:
23665 c=mp->cur_mod; mp_get_x_next(mp);
23666 mp_scan_expression(mp);
23667 if ( (mp->cur_type!=mp_known)||(mp->cur_exp<half_unit) ) {
23668 exp_err("Improper location");
23669 @.Improper location@>
23670 help2("I was looking for a known, positive number.")
23671 ("For safety's sake I'll ignore the present command.");
23672 mp_put_get_error(mp);
23674 j=mp_round_unscaled(mp, mp->cur_exp);
23675 if ( mp->cur_cmd!=colon ) {
23676 mp_missing_err(mp, ":");
23678 help1("A colon should follow a headerbyte or fontinfo location.");
23681 if ( c==header_byte_code ) {
23682 @<Store a list of header bytes@>;
23684 if (mp->param==NULL)
23685 mp->param = xmalloc((max_tfm_int+1),sizeof(scaled));
23686 @<Store a list of font dimensions@>;
23690 } /* there are no other cases */
23693 @ @<Store a list of ligature/kern steps@>=
23695 mp->lk_started=false;
23698 if ((mp->cur_cmd==skip_to)&& mp->lk_started )
23699 @<Process a |skip_to| command and |goto done|@>;
23700 if ( mp->cur_cmd==bchar_label ) { c=256; mp->cur_cmd=colon; }
23701 else { mp_back_input(mp); c=mp_get_code(mp); };
23702 if ((mp->cur_cmd==colon)||(mp->cur_cmd==double_colon)) {
23703 @<Record a label in a lig/kern subprogram and |goto continue|@>;
23705 if ( mp->cur_cmd==lig_kern_token ) {
23706 @<Compile a ligature/kern command@>;
23708 print_err("Illegal ligtable step");
23709 @.Illegal ligtable step@>
23710 help1("I was looking for `=:' or `kern' here.");
23711 mp_back_error(mp); next_char(mp->nl)=qi(0);
23712 op_byte(mp->nl)=qi(0); rem_byte(mp->nl)=qi(0);
23713 skip_byte(mp->nl)=stop_flag+1; /* this specifies an unconditional stop */
23715 if ( mp->nl==max_tfm_int) mp_fatal_error(mp, "ligtable too large");
23717 if ( mp->cur_cmd==comma ) goto CONTINUE;
23718 if ( skip_byte(mp->nl-1)<stop_flag ) skip_byte(mp->nl-1)=stop_flag;
23723 mp_primitive(mp, "=:",lig_kern_token,0);
23724 @:=:_}{\.{=:} primitive@>
23725 mp_primitive(mp, "=:|",lig_kern_token,1);
23726 @:=:/_}{\.{=:\char'174} primitive@>
23727 mp_primitive(mp, "=:|>",lig_kern_token,5);
23728 @:=:/>_}{\.{=:\char'174>} primitive@>
23729 mp_primitive(mp, "|=:",lig_kern_token,2);
23730 @:=:/_}{\.{\char'174=:} primitive@>
23731 mp_primitive(mp, "|=:>",lig_kern_token,6);
23732 @:=:/>_}{\.{\char'174=:>} primitive@>
23733 mp_primitive(mp, "|=:|",lig_kern_token,3);
23734 @:=:/_}{\.{\char'174=:\char'174} primitive@>
23735 mp_primitive(mp, "|=:|>",lig_kern_token,7);
23736 @:=:/>_}{\.{\char'174=:\char'174>} primitive@>
23737 mp_primitive(mp, "|=:|>>",lig_kern_token,11);
23738 @:=:/>_}{\.{\char'174=:\char'174>>} primitive@>
23739 mp_primitive(mp, "kern",lig_kern_token,128);
23740 @:kern_}{\&{kern} primitive@>
23742 @ @<Cases of |print_cmd...@>=
23743 case lig_kern_token:
23745 case 0:mp_print(mp, "=:"); break;
23746 case 1:mp_print(mp, "=:|"); break;
23747 case 2:mp_print(mp, "|=:"); break;
23748 case 3:mp_print(mp, "|=:|"); break;
23749 case 5:mp_print(mp, "=:|>"); break;
23750 case 6:mp_print(mp, "|=:>"); break;
23751 case 7:mp_print(mp, "|=:|>"); break;
23752 case 11:mp_print(mp, "|=:|>>"); break;
23753 default: mp_print(mp, "kern"); break;
23757 @ Local labels are implemented by maintaining the |skip_table| array,
23758 where |skip_table[c]| is either |undefined_label| or the address of the
23759 most recent lig/kern instruction that skips to local label~|c|. In the
23760 latter case, the |skip_byte| in that instruction will (temporarily)
23761 be zero if there were no prior skips to this label, or it will be the
23762 distance to the prior skip.
23764 We may need to cancel skips that span more than 127 lig/kern steps.
23766 @d cancel_skips(A) mp->ll=(A);
23768 mp->lll=qo(skip_byte(mp->ll));
23769 skip_byte(mp->ll)=stop_flag; mp->ll=mp->ll-mp->lll;
23770 } while (mp->lll!=0)
23771 @d skip_error(A) { print_err("Too far to skip");
23772 @.Too far to skip@>
23773 help1("At most 127 lig/kern steps can separate skipto1 from 1::.");
23774 mp_error(mp); cancel_skips((A));
23777 @<Process a |skip_to| command and |goto done|@>=
23780 if ( mp->nl-mp->skip_table[c]>128 ) { /* |skip_table[c]<<nl<=undefined_label| */
23781 skip_error(mp->skip_table[c]); mp->skip_table[c]=undefined_label;
23783 if ( mp->skip_table[c]==undefined_label ) skip_byte(mp->nl-1)=qi(0);
23784 else skip_byte(mp->nl-1)=qi(mp->nl-mp->skip_table[c]-1);
23785 mp->skip_table[c]=mp->nl-1; goto DONE;
23788 @ @<Record a label in a lig/kern subprogram and |goto continue|@>=
23790 if ( mp->cur_cmd==colon ) {
23791 if ( c==256 ) mp->bch_label=mp->nl;
23792 else mp_set_tag(mp, c,lig_tag,mp->nl);
23793 } else if ( mp->skip_table[c]<undefined_label ) {
23794 mp->ll=mp->skip_table[c]; mp->skip_table[c]=undefined_label;
23796 mp->lll=qo(skip_byte(mp->ll));
23797 if ( mp->nl-mp->ll>128 ) {
23798 skip_error(mp->ll); goto CONTINUE;
23800 skip_byte(mp->ll)=qi(mp->nl-mp->ll-1); mp->ll=mp->ll-mp->lll;
23801 } while (mp->lll!=0);
23806 @ @<Compile a ligature/kern...@>=
23808 next_char(mp->nl)=qi(c); skip_byte(mp->nl)=qi(0);
23809 if ( mp->cur_mod<128 ) { /* ligature op */
23810 op_byte(mp->nl)=qi(mp->cur_mod); rem_byte(mp->nl)=qi(mp_get_code(mp));
23812 mp_get_x_next(mp); mp_scan_expression(mp);
23813 if ( mp->cur_type!=mp_known ) {
23814 exp_err("Improper kern");
23816 help2("The amount of kern should be a known numeric value.")
23817 ("I'm zeroing this one. Proceed, with fingers crossed.");
23818 mp_put_get_flush_error(mp, 0);
23820 mp->kern[mp->nk]=mp->cur_exp;
23822 while ( mp->kern[k]!=mp->cur_exp ) incr(k);
23824 if ( mp->nk==max_tfm_int ) mp_fatal_error(mp, "too many TFM kerns");
23827 op_byte(mp->nl)=kern_flag+(k / 256);
23828 rem_byte(mp->nl)=qi((k % 256));
23830 mp->lk_started=true;
23833 @ @d missing_extensible_punctuation(A)
23834 { mp_missing_err(mp, (A));
23835 @.Missing `\char`\#'@>
23836 help1("I'm processing `extensible c: t,m,b,r'."); mp_back_error(mp);
23839 @<Define an extensible recipe@>=
23841 if ( mp->ne==256 ) mp_fatal_error(mp, "too many extensible recipies");
23842 c=mp_get_code(mp); mp_set_tag(mp, c,ext_tag,mp->ne);
23843 if ( mp->cur_cmd!=colon ) missing_extensible_punctuation(":");
23844 ext_top(mp->ne)=qi(mp_get_code(mp));
23845 if ( mp->cur_cmd!=comma ) missing_extensible_punctuation(",");
23846 ext_mid(mp->ne)=qi(mp_get_code(mp));
23847 if ( mp->cur_cmd!=comma ) missing_extensible_punctuation(",");
23848 ext_bot(mp->ne)=qi(mp_get_code(mp));
23849 if ( mp->cur_cmd!=comma ) missing_extensible_punctuation(",");
23850 ext_rep(mp->ne)=qi(mp_get_code(mp));
23854 @ The header could contain ASCII zeroes, so can't use |strdup|.
23856 @<Store a list of header bytes@>=
23858 if ( j>=mp->header_size ) {
23859 int l = mp->header_size + (mp->header_size >> 2);
23860 char *t = xmalloc(l,sizeof(char));
23862 memcpy(t,mp->header_byte,mp->header_size);
23863 xfree (mp->header_byte);
23864 mp->header_byte = t;
23865 mp->header_size = l;
23867 mp->header_byte[j]=mp_get_code(mp);
23868 incr(j); incr(mp->header_last);
23869 } while (mp->cur_cmd==comma)
23871 @ @<Store a list of font dimensions@>=
23873 if ( j>max_tfm_int ) mp_fatal_error(mp, "too many fontdimens");
23874 while ( j>mp->np ) { incr(mp->np); mp->param[mp->np]=0; };
23875 mp_get_x_next(mp); mp_scan_expression(mp);
23876 if ( mp->cur_type!=mp_known ){
23877 exp_err("Improper font parameter");
23878 @.Improper font parameter@>
23879 help1("I'm zeroing this one. Proceed, with fingers crossed.");
23880 mp_put_get_flush_error(mp, 0);
23882 mp->param[j]=mp->cur_exp; incr(j);
23883 } while (mp->cur_cmd==comma)
23885 @ OK: We've stored all the data that is needed for the \.{TFM} file.
23886 All that remains is to output it in the correct format.
23888 An interesting problem needs to be solved in this connection, because
23889 the \.{TFM} format allows at most 256~widths, 16~heights, 16~depths,
23890 and 64~italic corrections. If the data has more distinct values than
23891 this, we want to meet the necessary restrictions by perturbing the
23892 given values as little as possible.
23894 \MP\ solves this problem in two steps. First the values of a given
23895 kind (widths, heights, depths, or italic corrections) are sorted;
23896 then the list of sorted values is perturbed, if necessary.
23898 The sorting operation is facilitated by having a special node of
23899 essentially infinite |value| at the end of the current list.
23901 @<Initialize table entries...@>=
23902 value(inf_val)=fraction_four;
23904 @ Straight linear insertion is good enough for sorting, since the lists
23905 are usually not terribly long. As we work on the data, the current list
23906 will start at |link(temp_head)| and end at |inf_val|; the nodes in this
23907 list will be in increasing order of their |value| fields.
23909 Given such a list, the |sort_in| function takes a value and returns a pointer
23910 to where that value can be found in the list. The value is inserted in
23911 the proper place, if necessary.
23913 At the time we need to do these operations, most of \MP's work has been
23914 completed, so we will have plenty of memory to play with. The value nodes
23915 that are allocated for sorting will never be returned to free storage.
23917 @d clear_the_list link(temp_head)=inf_val
23919 @c pointer mp_sort_in (MP mp,scaled v) {
23920 pointer p,q,r; /* list manipulation registers */
23924 if ( v<=value(q) ) break;
23927 if ( v<value(q) ) {
23928 r=mp_get_node(mp, value_node_size); value(r)=v; link(r)=q; link(p)=r;
23933 @ Now we come to the interesting part, where we reduce the list if necessary
23934 until it has the required size. The |min_cover| routine is basic to this
23935 process; it computes the minimum number~|m| such that the values of the
23936 current sorted list can be covered by |m|~intervals of width~|d|. It
23937 also sets the global value |perturbation| to the smallest value $d'>d$
23938 such that the covering found by this algorithm would be different.
23940 In particular, |min_cover(0)| returns the number of distinct values in the
23941 current list and sets |perturbation| to the minimum distance between
23944 @c integer mp_min_cover (MP mp,scaled d) {
23945 pointer p; /* runs through the current list */
23946 scaled l; /* the least element covered by the current interval */
23947 integer m; /* lower bound on the size of the minimum cover */
23948 m=0; p=link(temp_head); mp->perturbation=el_gordo;
23949 while ( p!=inf_val ){
23950 incr(m); l=value(p);
23951 do { p=link(p); } while (value(p)<=l+d);
23952 if ( value(p)-l<mp->perturbation )
23953 mp->perturbation=value(p)-l;
23959 scaled perturbation; /* quantity related to \.{TFM} rounding */
23960 integer excess; /* the list is this much too long */
23962 @ The smallest |d| such that a given list can be covered with |m| intervals
23963 is determined by the |threshold| routine, which is sort of an inverse
23964 to |min_cover|. The idea is to increase the interval size rapidly until
23965 finding the range, then to go sequentially until the exact borderline has
23968 @c scaled mp_threshold (MP mp,integer m) {
23969 scaled d; /* lower bound on the smallest interval size */
23970 mp->excess=mp_min_cover(mp, 0)-m;
23971 if ( mp->excess<=0 ) {
23975 d=mp->perturbation;
23976 } while (mp_min_cover(mp, d+d)>m);
23977 while ( mp_min_cover(mp, d)>m )
23978 d=mp->perturbation;
23983 @ The |skimp| procedure reduces the current list to at most |m| entries,
23984 by changing values if necessary. It also sets |info(p):=k| if |value(p)|
23985 is the |k|th distinct value on the resulting list, and it sets
23986 |perturbation| to the maximum amount by which a |value| field has
23987 been changed. The size of the resulting list is returned as the
23990 @c integer mp_skimp (MP mp,integer m) {
23991 scaled d; /* the size of intervals being coalesced */
23992 pointer p,q,r; /* list manipulation registers */
23993 scaled l; /* the least value in the current interval */
23994 scaled v; /* a compromise value */
23995 d=mp_threshold(mp, m); mp->perturbation=0;
23996 q=temp_head; m=0; p=link(temp_head);
23997 while ( p!=inf_val ) {
23998 incr(m); l=value(p); info(p)=m;
23999 if ( value(link(p))<=l+d ) {
24000 @<Replace an interval of values by its midpoint@>;
24007 @ @<Replace an interval...@>=
24010 p=link(p); info(p)=m;
24011 decr(mp->excess); if ( mp->excess==0 ) d=0;
24012 } while (value(link(p))<=l+d);
24013 v=l+halfp(value(p)-l);
24014 if ( value(p)-v>mp->perturbation )
24015 mp->perturbation=value(p)-v;
24018 r=link(r); value(r)=v;
24020 link(q)=p; /* remove duplicate values from the current list */
24023 @ A warning message is issued whenever something is perturbed by
24024 more than 1/16\thinspace pt.
24026 @c void mp_tfm_warning (MP mp,small_number m) {
24027 mp_print_nl(mp, "(some ");
24028 mp_print(mp, mp->int_name[m]);
24029 @.some charwds...@>
24030 @.some chardps...@>
24031 @.some charhts...@>
24032 @.some charics...@>
24033 mp_print(mp, " values had to be adjusted by as much as ");
24034 mp_print_scaled(mp, mp->perturbation); mp_print(mp, "pt)");
24037 @ Here's an example of how we use these routines.
24038 The width data needs to be perturbed only if there are 256 distinct
24039 widths, but \MP\ must check for this case even though it is
24042 An integer variable |k| will be defined when we use this code.
24043 The |dimen_head| array will contain pointers to the sorted
24044 lists of dimensions.
24046 @<Massage the \.{TFM} widths@>=
24048 for (k=mp->bc;k<=mp->ec;k++) {
24049 if ( mp->char_exists[k] )
24050 mp->tfm_width[k]=mp_sort_in(mp, mp->tfm_width[k]);
24052 mp->nw=mp_skimp(mp, 255)+1; mp->dimen_head[1]=link(temp_head);
24053 if ( mp->perturbation>=010000 ) mp_tfm_warning(mp, mp_char_wd)
24056 pointer dimen_head[5]; /* lists of \.{TFM} dimensions */
24058 @ Heights, depths, and italic corrections are different from widths
24059 not only because their list length is more severely restricted, but
24060 also because zero values do not need to be put into the lists.
24062 @<Massage the \.{TFM} heights, depths, and italic corrections@>=
24064 for (k=mp->bc;k<=mp->ec;k++) {
24065 if ( mp->char_exists[k] ) {
24066 if ( mp->tfm_height[k]==0 ) mp->tfm_height[k]=zero_val;
24067 else mp->tfm_height[k]=mp_sort_in(mp, mp->tfm_height[k]);
24070 mp->nh=mp_skimp(mp, 15)+1; mp->dimen_head[2]=link(temp_head);
24071 if ( mp->perturbation>=010000 ) mp_tfm_warning(mp, mp_char_ht);
24073 for (k=mp->bc;k<=mp->ec;k++) {
24074 if ( mp->char_exists[k] ) {
24075 if ( mp->tfm_depth[k]==0 ) mp->tfm_depth[k]=zero_val;
24076 else mp->tfm_depth[k]=mp_sort_in(mp, mp->tfm_depth[k]);
24079 mp->nd=mp_skimp(mp, 15)+1; mp->dimen_head[3]=link(temp_head);
24080 if ( mp->perturbation>=010000 ) mp_tfm_warning(mp, mp_char_dp);
24082 for (k=mp->bc;k<=mp->ec;k++) {
24083 if ( mp->char_exists[k] ) {
24084 if ( mp->tfm_ital_corr[k]==0 ) mp->tfm_ital_corr[k]=zero_val;
24085 else mp->tfm_ital_corr[k]=mp_sort_in(mp, mp->tfm_ital_corr[k]);
24088 mp->ni=mp_skimp(mp, 63)+1; mp->dimen_head[4]=link(temp_head);
24089 if ( mp->perturbation>=010000 ) mp_tfm_warning(mp, mp_char_ic)
24091 @ @<Initialize table entries...@>=
24092 value(zero_val)=0; info(zero_val)=0;
24094 @ Bytes 5--8 of the header are set to the design size, unless the user has
24095 some crazy reason for specifying them differently.
24097 Error messages are not allowed at the time this procedure is called,
24098 so a warning is printed instead.
24100 The value of |max_tfm_dimen| is calculated so that
24101 $$\hbox{|make_scaled(16*max_tfm_dimen,internal[mp_design_size])|}
24102 < \\{three\_bytes}.$$
24104 @d three_bytes 0100000000 /* $2^{24}$ */
24107 void mp_fix_design_size (MP mp) {
24108 scaled d; /* the design size */
24109 d=mp->internal[mp_design_size];
24110 if ( (d<unity)||(d>=fraction_half) ) {
24112 mp_print_nl(mp, "(illegal design size has been changed to 128pt)");
24113 @.illegal design size...@>
24114 d=040000000; mp->internal[mp_design_size]=d;
24116 if ( mp->header_byte[4]<0 ) if ( mp->header_byte[5]<0 )
24117 if ( mp->header_byte[6]<0 ) if ( mp->header_byte[7]<0 ) {
24118 mp->header_byte[4]=d / 04000000;
24119 mp->header_byte[5]=(d / 4096) % 256;
24120 mp->header_byte[6]=(d / 16) % 256;
24121 mp->header_byte[7]=(d % 16)*16;
24123 mp->max_tfm_dimen=16*mp->internal[mp_design_size]-mp->internal[mp_design_size] / 010000000;
24124 if ( mp->max_tfm_dimen>=fraction_half ) mp->max_tfm_dimen=fraction_half-1;
24127 @ The |dimen_out| procedure computes a |fix_word| relative to the
24128 design size. If the data was out of range, it is corrected and the
24129 global variable |tfm_changed| is increased by~one.
24131 @c integer mp_dimen_out (MP mp,scaled x) {
24132 if ( abs(x)>mp->max_tfm_dimen ) {
24133 incr(mp->tfm_changed);
24134 if ( x>0 ) x=three_bytes-1; else x=1-three_bytes;
24136 x=mp_make_scaled(mp, x*16,mp->internal[mp_design_size]);
24142 scaled max_tfm_dimen; /* bound on widths, heights, kerns, etc. */
24143 integer tfm_changed; /* the number of data entries that were out of bounds */
24145 @ If the user has not specified any of the first four header bytes,
24146 the |fix_check_sum| procedure replaces them by a ``check sum'' computed
24147 from the |tfm_width| data relative to the design size.
24150 @c void mp_fix_check_sum (MP mp) {
24151 eight_bits k; /* runs through character codes */
24152 eight_bits B1,B2,B3,B4; /* bytes of the check sum */
24153 integer x; /* hash value used in check sum computation */
24154 if ( mp->header_byte[0]==0 && mp->header_byte[1]==0 &&
24155 mp->header_byte[2]==0 && mp->header_byte[3]==0 ) {
24156 @<Compute a check sum in |(b1,b2,b3,b4)|@>;
24157 mp->header_byte[0]=B1; mp->header_byte[1]=B2;
24158 mp->header_byte[2]=B3; mp->header_byte[3]=B4;
24163 @ @<Compute a check sum in |(b1,b2,b3,b4)|@>=
24164 B1=mp->bc; B2=mp->ec; B3=mp->bc; B4=mp->ec; mp->tfm_changed=0;
24165 for (k=mp->bc;k<=mp->ec;k++) {
24166 if ( mp->char_exists[k] ) {
24167 x=mp_dimen_out(mp, value(mp->tfm_width[k]))+(k+4)*020000000; /* this is positive */
24168 B1=(B1+B1+x) % 255;
24169 B2=(B2+B2+x) % 253;
24170 B3=(B3+B3+x) % 251;
24171 B4=(B4+B4+x) % 247;
24175 @ Finally we're ready to actually write the \.{TFM} information.
24176 Here are some utility routines for this purpose.
24178 @d tfm_out(A) do { /* output one byte to |tfm_file| */
24179 unsigned char s=(A);
24180 (mp->write_binary_file)(mp,mp->tfm_file,(void *)&s,1);
24183 @c void mp_tfm_two (MP mp,integer x) { /* output two bytes to |tfm_file| */
24184 tfm_out(x / 256); tfm_out(x % 256);
24186 void mp_tfm_four (MP mp,integer x) { /* output four bytes to |tfm_file| */
24187 if ( x>=0 ) tfm_out(x / three_bytes);
24189 x=x+010000000000; /* use two's complement for negative values */
24191 tfm_out((x / three_bytes) + 128);
24193 x=x % three_bytes; tfm_out(x / unity);
24194 x=x % unity; tfm_out(x / 0400);
24197 void mp_tfm_qqqq (MP mp,four_quarters x) { /* output four quarterwords to |tfm_file| */
24198 tfm_out(qo(x.b0)); tfm_out(qo(x.b1));
24199 tfm_out(qo(x.b2)); tfm_out(qo(x.b3));
24202 @ @<Finish the \.{TFM} file@>=
24203 if ( mp->job_name==NULL ) mp_open_log_file(mp);
24204 mp_pack_job_name(mp, ".tfm");
24205 while ( ! mp_b_open_out(mp, &mp->tfm_file, mp_filetype_metrics) )
24206 mp_prompt_file_name(mp, "file name for font metrics",".tfm");
24207 mp->metric_file_name=xstrdup(mp->name_of_file);
24208 @<Output the subfile sizes and header bytes@>;
24209 @<Output the character information bytes, then
24210 output the dimensions themselves@>;
24211 @<Output the ligature/kern program@>;
24212 @<Output the extensible character recipes and the font metric parameters@>;
24213 if ( mp->internal[mp_tracing_stats]>0 )
24214 @<Log the subfile sizes of the \.{TFM} file@>;
24215 mp_print_nl(mp, "Font metrics written on ");
24216 mp_print(mp, mp->metric_file_name); mp_print_char(mp, '.');
24217 @.Font metrics written...@>
24218 (mp->close_file)(mp,mp->tfm_file)
24220 @ Integer variables |lh|, |k|, and |lk_offset| will be defined when we use
24223 @<Output the subfile sizes and header bytes@>=
24225 LH=(k+3) / 4; /* this is the number of header words */
24226 if ( mp->bc>mp->ec ) mp->bc=1; /* if there are no characters, |ec=0| and |bc=1| */
24227 @<Compute the ligature/kern program offset and implant the
24228 left boundary label@>;
24229 mp_tfm_two(mp,6+LH+(mp->ec-mp->bc+1)+mp->nw+mp->nh+mp->nd+mp->ni+mp->nl
24230 +lk_offset+mp->nk+mp->ne+mp->np);
24231 /* this is the total number of file words that will be output */
24232 mp_tfm_two(mp, LH); mp_tfm_two(mp, mp->bc); mp_tfm_two(mp, mp->ec);
24233 mp_tfm_two(mp, mp->nw); mp_tfm_two(mp, mp->nh);
24234 mp_tfm_two(mp, mp->nd); mp_tfm_two(mp, mp->ni); mp_tfm_two(mp, mp->nl+lk_offset);
24235 mp_tfm_two(mp, mp->nk); mp_tfm_two(mp, mp->ne);
24236 mp_tfm_two(mp, mp->np);
24237 for (k=0;k< 4*LH;k++) {
24238 tfm_out(mp->header_byte[k]);
24241 @ @<Output the character information bytes...@>=
24242 for (k=mp->bc;k<=mp->ec;k++) {
24243 if ( ! mp->char_exists[k] ) {
24244 mp_tfm_four(mp, 0);
24246 tfm_out(info(mp->tfm_width[k])); /* the width index */
24247 tfm_out((info(mp->tfm_height[k]))*16+info(mp->tfm_depth[k]));
24248 tfm_out((info(mp->tfm_ital_corr[k]))*4+mp->char_tag[k]);
24249 tfm_out(mp->char_remainder[k]);
24253 for (k=1;k<=4;k++) {
24254 mp_tfm_four(mp, 0); p=mp->dimen_head[k];
24255 while ( p!=inf_val ) {
24256 mp_tfm_four(mp, mp_dimen_out(mp, value(p))); p=link(p);
24261 @ We need to output special instructions at the beginning of the
24262 |lig_kern| array in order to specify the right boundary character
24263 and/or to handle starting addresses that exceed 255. The |label_loc|
24264 and |label_char| arrays have been set up to record all the
24265 starting addresses; we have $-1=|label_loc|[0]<|label_loc|[1]\le\cdots
24266 \le|label_loc|[|label_ptr]|$.
24268 @<Compute the ligature/kern program offset...@>=
24269 mp->bchar=mp_round_unscaled(mp, mp->internal[mp_boundary_char]);
24270 if ((mp->bchar<0)||(mp->bchar>255))
24271 { mp->bchar=-1; mp->lk_started=false; lk_offset=0; }
24272 else { mp->lk_started=true; lk_offset=1; };
24273 @<Find the minimum |lk_offset| and adjust all remainders@>;
24274 if ( mp->bch_label<undefined_label )
24275 { skip_byte(mp->nl)=qi(255); next_char(mp->nl)=qi(0);
24276 op_byte(mp->nl)=qi(((mp->bch_label+lk_offset)/ 256));
24277 rem_byte(mp->nl)=qi(((mp->bch_label+lk_offset)% 256));
24278 incr(mp->nl); /* possibly |nl=lig_table_size+1| */
24281 @ @<Find the minimum |lk_offset|...@>=
24282 k=mp->label_ptr; /* pointer to the largest unallocated label */
24283 if ( mp->label_loc[k]+lk_offset>255 ) {
24284 lk_offset=0; mp->lk_started=false; /* location 0 can do double duty */
24286 mp->char_remainder[mp->label_char[k]]=lk_offset;
24287 while ( mp->label_loc[k-1]==mp->label_loc[k] ) {
24288 decr(k); mp->char_remainder[mp->label_char[k]]=lk_offset;
24290 incr(lk_offset); decr(k);
24291 } while (! (lk_offset+mp->label_loc[k]<256));
24292 /* N.B.: |lk_offset=256| satisfies this when |k=0| */
24294 if ( lk_offset>0 ) {
24296 mp->char_remainder[mp->label_char[k]]
24297 =mp->char_remainder[mp->label_char[k]]+lk_offset;
24302 @ @<Output the ligature/kern program@>=
24303 for (k=0;k<= 255;k++ ) {
24304 if ( mp->skip_table[k]<undefined_label ) {
24305 mp_print_nl(mp, "(local label "); mp_print_int(mp, k); mp_print(mp, ":: was missing)");
24306 @.local label l:: was missing@>
24307 cancel_skips(mp->skip_table[k]);
24310 if ( mp->lk_started ) { /* |lk_offset=1| for the special |bchar| */
24311 tfm_out(255); tfm_out(mp->bchar); mp_tfm_two(mp, 0);
24313 for (k=1;k<=lk_offset;k++) {/* output the redirection specs */
24314 mp->ll=mp->label_loc[mp->label_ptr];
24315 if ( mp->bchar<0 ) { tfm_out(254); tfm_out(0); }
24316 else { tfm_out(255); tfm_out(mp->bchar); };
24317 mp_tfm_two(mp, mp->ll+lk_offset);
24319 decr(mp->label_ptr);
24320 } while (! (mp->label_loc[mp->label_ptr]<mp->ll));
24323 for (k=0;k<=mp->nl-1;k++) mp_tfm_qqqq(mp, mp->lig_kern[k]);
24324 for (k=0;k<=mp->nk-1;k++) mp_tfm_four(mp, mp_dimen_out(mp, mp->kern[k]))
24326 @ @<Output the extensible character recipes...@>=
24327 for (k=0;k<=mp->ne-1;k++)
24328 mp_tfm_qqqq(mp, mp->exten[k]);
24329 for (k=1;k<=mp->np;k++) {
24331 if ( abs(mp->param[1])<fraction_half ) {
24332 mp_tfm_four(mp, mp->param[1]*16);
24334 incr(mp->tfm_changed);
24335 if ( mp->param[1]>0 ) mp_tfm_four(mp, el_gordo);
24336 else mp_tfm_four(mp, -el_gordo);
24339 mp_tfm_four(mp, mp_dimen_out(mp, mp->param[k]));
24342 if ( mp->tfm_changed>0 ) {
24343 if ( mp->tfm_changed==1 ) mp_print_nl(mp, "(a font metric dimension");
24344 @.a font metric dimension...@>
24346 mp_print_nl(mp, "("); mp_print_int(mp, mp->tfm_changed);
24347 @.font metric dimensions...@>
24348 mp_print(mp, " font metric dimensions");
24350 mp_print(mp, " had to be decreased)");
24353 @ @<Log the subfile sizes of the \.{TFM} file@>=
24357 if ( mp->bch_label<undefined_label ) decr(mp->nl);
24358 snprintf(s,128,"(You used %iw,%ih,%id,%ii,%il,%ik,%ie,%ip metric file positions)",
24359 mp->nw, mp->nh, mp->nd, mp->ni, mp->nl, mp->nk, mp->ne,mp->np);
24363 @* \[43] Reading font metric data.
24365 \MP\ isn't a typesetting program but it does need to find the bounding box
24366 of a sequence of typeset characters. Thus it needs to read \.{TFM} files as
24367 well as write them.
24372 @ All the width, height, and depth information is stored in an array called
24373 |font_info|. This array is allocated sequentially and each font is stored
24374 as a series of |char_info| words followed by the width, height, and depth
24375 tables. Since |font_name| entries are permanent, their |str_ref| values are
24376 set to |max_str_ref|.
24379 typedef unsigned int font_number; /* |0..font_max| */
24381 @ The |font_info| array is indexed via a group directory arrays.
24382 For example, the |char_info| data for character~|c| in font~|f| will be
24383 in |font_info[char_base[f]+c].qqqq|.
24386 font_number font_max; /* maximum font number for included text fonts */
24387 size_t font_mem_size; /* number of words for \.{TFM} information for text fonts */
24388 memory_word *font_info; /* height, width, and depth data */
24389 char **font_enc_name; /* encoding names, if any */
24390 boolean *font_ps_name_fixed; /* are the postscript names fixed already? */
24391 int next_fmem; /* next unused entry in |font_info| */
24392 font_number last_fnum; /* last font number used so far */
24393 scaled *font_dsize; /* 16 times the ``design'' size in \ps\ points */
24394 char **font_name; /* name as specified in the \&{infont} command */
24395 char **font_ps_name; /* PostScript name for use when |internal[mp_prologues]>0| */
24396 font_number last_ps_fnum; /* last valid |font_ps_name| index */
24397 eight_bits *font_bc;
24398 eight_bits *font_ec; /* first and last character code */
24399 int *char_base; /* base address for |char_info| */
24400 int *width_base; /* index for zeroth character width */
24401 int *height_base; /* index for zeroth character height */
24402 int *depth_base; /* index for zeroth character depth */
24403 pointer *font_sizes;
24405 @ @<Allocate or initialize ...@>=
24406 mp->font_mem_size = 10000;
24407 mp->font_info = xmalloc ((mp->font_mem_size+1),sizeof(memory_word));
24408 memset (mp->font_info,0,sizeof(memory_word)*(mp->font_mem_size+1));
24409 mp->font_enc_name = NULL;
24410 mp->font_ps_name_fixed = NULL;
24411 mp->font_dsize = NULL;
24412 mp->font_name = NULL;
24413 mp->font_ps_name = NULL;
24414 mp->font_bc = NULL;
24415 mp->font_ec = NULL;
24416 mp->last_fnum = null_font;
24417 mp->char_base = NULL;
24418 mp->width_base = NULL;
24419 mp->height_base = NULL;
24420 mp->depth_base = NULL;
24421 mp->font_sizes = null;
24423 @ @<Dealloc variables@>=
24424 for (k=1;k<=(int)mp->last_fnum;k++) {
24425 xfree(mp->font_enc_name[k]);
24426 xfree(mp->font_name[k]);
24427 xfree(mp->font_ps_name[k]);
24429 xfree(mp->font_info);
24430 xfree(mp->font_enc_name);
24431 xfree(mp->font_ps_name_fixed);
24432 xfree(mp->font_dsize);
24433 xfree(mp->font_name);
24434 xfree(mp->font_ps_name);
24435 xfree(mp->font_bc);
24436 xfree(mp->font_ec);
24437 xfree(mp->char_base);
24438 xfree(mp->width_base);
24439 xfree(mp->height_base);
24440 xfree(mp->depth_base);
24441 xfree(mp->font_sizes);
24445 void mp_reallocate_fonts (MP mp, font_number l) {
24447 XREALLOC(mp->font_enc_name, l, char *);
24448 XREALLOC(mp->font_ps_name_fixed, l, boolean);
24449 XREALLOC(mp->font_dsize, l, scaled);
24450 XREALLOC(mp->font_name, l, char *);
24451 XREALLOC(mp->font_ps_name, l, char *);
24452 XREALLOC(mp->font_bc, l, eight_bits);
24453 XREALLOC(mp->font_ec, l, eight_bits);
24454 XREALLOC(mp->char_base, l, int);
24455 XREALLOC(mp->width_base, l, int);
24456 XREALLOC(mp->height_base, l, int);
24457 XREALLOC(mp->depth_base, l, int);
24458 XREALLOC(mp->font_sizes, l, pointer);
24459 for (f=(mp->last_fnum+1);f<=l;f++) {
24460 mp->font_enc_name[f]=NULL;
24461 mp->font_ps_name_fixed[f] = false;
24462 mp->font_name[f]=NULL;
24463 mp->font_ps_name[f]=NULL;
24464 mp->font_sizes[f]=null;
24469 @ @<Declare |mp_reallocate| functions@>=
24470 void mp_reallocate_fonts (MP mp, font_number l);
24473 @ A |null_font| containing no characters is useful for error recovery. Its
24474 |font_name| entry starts out empty but is reset each time an erroneous font is
24475 found. This helps to cut down on the number of duplicate error messages without
24476 wasting a lot of space.
24478 @d null_font 0 /* the |font_number| for an empty font */
24480 @<Set initial...@>=
24481 mp->font_dsize[null_font]=0;
24482 mp->font_bc[null_font]=1;
24483 mp->font_ec[null_font]=0;
24484 mp->char_base[null_font]=0;
24485 mp->width_base[null_font]=0;
24486 mp->height_base[null_font]=0;
24487 mp->depth_base[null_font]=0;
24489 mp->last_fnum=null_font;
24490 mp->last_ps_fnum=null_font;
24491 mp->font_name[null_font]="nullfont";
24492 mp->font_ps_name[null_font]="";
24493 mp->font_ps_name_fixed[null_font] = false;
24494 mp->font_enc_name[null_font]=NULL;
24495 mp->font_sizes[null_font]=null;
24497 @ Each |char_info| word is of type |four_quarters|. The |b0| field contains
24498 the |width index|; the |b1| field contains the height
24499 index; the |b2| fields contains the depth index, and the |b3| field used only
24500 for temporary storage. (It is used to keep track of which characters occur in
24501 an edge structure that is being shipped out.)
24502 The corresponding words in the width, height, and depth tables are stored as
24503 |scaled| values in units of \ps\ points.
24505 With the macros below, the |char_info| word for character~|c| in font~|f| is
24506 |char_info(f)(c)| and the width is
24507 $$\hbox{|char_width(f)(char_info(f)(c)).sc|.}$$
24509 @d char_info_end(A) (A)].qqqq
24510 @d char_info(A) mp->font_info[mp->char_base[(A)]+char_info_end
24511 @d char_width_end(A) (A).b0].sc
24512 @d char_width(A) mp->font_info[mp->width_base[(A)]+char_width_end
24513 @d char_height_end(A) (A).b1].sc
24514 @d char_height(A) mp->font_info[mp->height_base[(A)]+char_height_end
24515 @d char_depth_end(A) (A).b2].sc
24516 @d char_depth(A) mp->font_info[mp->depth_base[(A)]+char_depth_end
24517 @d ichar_exists(A) ((A).b0>0)
24519 @ The |font_ps_name| for a built-in font should be what PostScript expects.
24520 A preliminary name is obtained here from the \.{TFM} name as given in the
24521 |fname| argument. This gets updated later from an external table if necessary.
24523 @<Declare text measuring subroutines@>=
24524 @<Declare subroutines for parsing file names@>;
24525 font_number mp_read_font_info (MP mp, char *fname) {
24526 boolean file_opened; /* has |tfm_infile| been opened? */
24527 font_number n; /* the number to return */
24528 halfword lf,tfm_lh,bc,ec,nw,nh,nd; /* subfile size parameters */
24529 size_t whd_size; /* words needed for heights, widths, and depths */
24530 int i,ii; /* |font_info| indices */
24531 int jj; /* counts bytes to be ignored */
24532 scaled z; /* used to compute the design size */
24534 /* height, width, or depth as a fraction of design size times $2^{-8}$ */
24535 eight_bits h_and_d; /* height and depth indices being unpacked */
24536 unsigned char tfbyte; /* a byte read from the file */
24538 @<Open |tfm_infile| for input@>;
24539 @<Read data from |tfm_infile|; if there is no room, say so and |goto done|;
24540 otherwise |goto bad_tfm| or |goto done| as appropriate@>;
24542 @<Complain that the \.{TFM} file is bad@>;
24544 if ( file_opened ) (mp->close_file)(mp,mp->tfm_infile);
24545 if ( n!=null_font ) {
24546 mp->font_ps_name[n]=mp_xstrdup(mp,fname);
24547 mp->font_name[n]=mp_xstrdup(mp,fname);
24552 @ \MP\ doesn't bother to check the entire \.{TFM} file for errors or explain
24553 precisely what is wrong if it does find a problem. Programs called \.{TFtoPL}
24554 @.TFtoPL@> @.PLtoTF@>
24555 and \.{PLtoTF} can be used to debug \.{TFM} files.
24557 @<Complain that the \.{TFM} file is bad@>=
24558 print_err("Font ");
24559 mp_print(mp, fname);
24560 if ( file_opened ) mp_print(mp, " not usable: TFM file is bad");
24561 else mp_print(mp, " not usable: TFM file not found");
24562 help3("I wasn't able to read the size data for this font so this")
24563 ("`infont' operation won't produce anything. If the font name")
24564 ("is right, you might ask an expert to make a TFM file");
24566 mp->help_line[0]="is right, try asking an expert to fix the TFM file";
24569 @ @<Read data from |tfm_infile|; if there is no room, say so...@>=
24570 @<Read the \.{TFM} size fields@>;
24571 @<Use the size fields to allocate space in |font_info|@>;
24572 @<Read the \.{TFM} header@>;
24573 @<Read the character data and the width, height, and depth tables and
24576 @ A bad \.{TFM} file can be shorter than it claims to be. The code given here
24577 might try to read past the end of the file if this happens. Changes will be
24578 needed if it causes a system error to refer to |tfm_infile^| or call
24579 |get_tfm_infile| when |eof(tfm_infile)| is true. For example, the definition
24580 @^system dependencies@>
24581 of |tfget| could be changed to
24582 ``|begin get(tfm_infile); if eof(tfm_infile) then goto bad_tfm; end|.''
24586 void *tfbyte_ptr = &tfbyte;
24587 (mp->read_binary_file)(mp,mp->tfm_infile,&tfbyte_ptr,&wanted);
24588 if (wanted==0) goto BAD_TFM;
24590 @d read_two(A) { (A)=tfbyte;
24591 if ( (A)>127 ) goto BAD_TFM;
24592 tfget; (A)=(A)*0400+tfbyte;
24594 @d tf_ignore(A) { for (jj=(A);jj>=1;jj--) tfget; }
24596 @<Read the \.{TFM} size fields@>=
24597 tfget; read_two(lf);
24598 tfget; read_two(tfm_lh);
24599 tfget; read_two(bc);
24600 tfget; read_two(ec);
24601 if ( (bc>1+ec)||(ec>255) ) goto BAD_TFM;
24602 tfget; read_two(nw);
24603 tfget; read_two(nh);
24604 tfget; read_two(nd);
24605 whd_size=(ec+1-bc)+nw+nh+nd;
24606 if ( lf<(int)(6+tfm_lh+whd_size) ) goto BAD_TFM;
24609 @ Offsets are added to |char_base[n]| and |width_base[n]| so that is not
24610 necessary to apply the |so| and |qo| macros when looking up the width of a
24611 character in the string pool. In order to ensure nonnegative |char_base|
24612 values when |bc>0|, it may be necessary to reserve a few unused |font_info|
24615 @<Use the size fields to allocate space in |font_info|@>=
24616 if ( mp->next_fmem<bc) mp->next_fmem=bc; /* ensure nonnegative |char_base| */
24617 if (mp->last_fnum==mp->font_max)
24618 mp_reallocate_fonts(mp,(mp->font_max+(mp->font_max>>2)));
24619 while (mp->next_fmem+whd_size>=mp->font_mem_size) {
24620 size_t l = mp->font_mem_size+(mp->font_mem_size>>2);
24621 memory_word *font_info;
24622 font_info = xmalloc ((l+1),sizeof(memory_word));
24623 memset (font_info,0,sizeof(memory_word)*(l+1));
24624 memcpy (font_info,mp->font_info,sizeof(memory_word)*(mp->font_mem_size+1));
24625 xfree(mp->font_info);
24626 mp->font_info = font_info;
24627 mp->font_mem_size = l;
24629 incr(mp->last_fnum);
24633 mp->char_base[n]=mp->next_fmem-bc;
24634 mp->width_base[n]=mp->next_fmem+ec-bc+1;
24635 mp->height_base[n]=mp->width_base[n]+nw;
24636 mp->depth_base[n]=mp->height_base[n]+nh;
24637 mp->next_fmem=mp->next_fmem+whd_size;
24640 @ @<Read the \.{TFM} header@>=
24641 if ( tfm_lh<2 ) goto BAD_TFM;
24643 tfget; read_two(z);
24644 tfget; z=z*0400+tfbyte;
24645 tfget; z=z*0400+tfbyte; /* now |z| is 16 times the design size */
24646 mp->font_dsize[n]=mp_take_fraction(mp, z,267432584);
24647 /* times ${72\over72.27}2^{28}$ to convert from \TeX\ points */
24648 tf_ignore(4*(tfm_lh-2))
24650 @ @<Read the character data and the width, height, and depth tables...@>=
24651 ii=mp->width_base[n];
24652 i=mp->char_base[n]+bc;
24654 tfget; mp->font_info[i].qqqq.b0=qi(tfbyte);
24655 tfget; h_and_d=tfbyte;
24656 mp->font_info[i].qqqq.b1=h_and_d / 16;
24657 mp->font_info[i].qqqq.b2=h_and_d % 16;
24661 while ( i<mp->next_fmem ) {
24662 @<Read a four byte dimension, scale it by the design size, store it in
24663 |font_info[i]|, and increment |i|@>;
24667 @ The raw dimension read into |d| should have magnitude at most $2^{24}$ when
24668 interpreted as an integer, and this includes a scale factor of $2^{20}$. Thus
24669 we can multiply it by sixteen and think of it as a |fraction| that has been
24670 divided by sixteen. This cancels the extra scale factor contained in
24673 @<Read a four byte dimension, scale it by the design size, store it in...@>=
24676 if ( d>=0200 ) d=d-0400;
24677 tfget; d=d*0400+tfbyte;
24678 tfget; d=d*0400+tfbyte;
24679 tfget; d=d*0400+tfbyte;
24680 mp->font_info[i].sc=mp_take_fraction(mp, d*16,mp->font_dsize[n]);
24684 @ This function does no longer use the file name parser, because |fname| is
24685 a C string already.
24686 @<Open |tfm_infile| for input@>=
24688 mp_ptr_scan_file(mp, fname);
24689 if ( strlen(mp->cur_area)==0 ) { xfree(mp->cur_area); }
24690 if ( strlen(mp->cur_ext)==0 ) { xfree(mp->cur_ext); mp->cur_ext=xstrdup(".tfm"); }
24692 mp->tfm_infile = (mp->open_file)(mp, mp->name_of_file, "rb",mp_filetype_metrics);
24693 if ( !mp->tfm_infile ) goto BAD_TFM;
24696 @ When we have a font name and we don't know whether it has been loaded yet,
24697 we scan the |font_name| array before calling |read_font_info|.
24699 @<Declare text measuring subroutines@>=
24700 font_number mp_find_font (MP mp, char *f) {
24702 for (n=0;n<=mp->last_fnum;n++) {
24703 if (mp_xstrcmp(f,mp->font_name[n])==0 ) {
24708 n = mp_read_font_info(mp, f);
24713 @ One simple application of |find_font| is the implementation of the |font_size|
24714 operator that gets the design size for a given font name.
24716 @<Find the design size of the font whose name is |cur_exp|@>=
24717 mp_flush_cur_exp(mp, (mp->font_dsize[mp_find_font(mp, str(mp->cur_exp))]+8) / 16)
24719 @ If we discover that the font doesn't have a requested character, we omit it
24720 from the bounding box computation and expect the \ps\ interpreter to drop it.
24721 This routine issues a warning message if the user has asked for it.
24723 @<Declare text measuring subroutines@>=
24724 void mp_lost_warning (MP mp,font_number f, pool_pointer k) {
24725 if ( mp->internal[mp_tracing_lost_chars]>0 ) {
24726 mp_begin_diagnostic(mp);
24727 if ( mp->selector==log_only ) incr(mp->selector);
24728 mp_print_nl(mp, "Missing character: There is no ");
24729 @.Missing character@>
24730 mp_print_str(mp, mp->str_pool[k]);
24731 mp_print(mp, " in font ");
24732 mp_print(mp, mp->font_name[f]); mp_print_char(mp, '!');
24733 mp_end_diagnostic(mp, false);
24737 @ The whole purpose of saving the height, width, and depth information is to be
24738 able to find the bounding box of an item of text in an edge structure. The
24739 |set_text_box| procedure takes a text node and adds this information.
24741 @<Declare text measuring subroutines@>=
24742 void mp_set_text_box (MP mp,pointer p) {
24743 font_number f; /* |font_n(p)| */
24744 ASCII_code bc,ec; /* range of valid characters for font |f| */
24745 pool_pointer k,kk; /* current character and character to stop at */
24746 four_quarters cc; /* the |char_info| for the current character */
24747 scaled h,d; /* dimensions of the current character */
24749 height_val(p)=-el_gordo;
24750 depth_val(p)=-el_gordo;
24754 kk=str_stop(text_p(p));
24755 k=mp->str_start[text_p(p)];
24757 @<Adjust |p|'s bounding box to contain |str_pool[k]|; advance |k|@>;
24759 @<Set the height and depth to zero if the bounding box is empty@>;
24762 @ @<Adjust |p|'s bounding box to contain |str_pool[k]|; advance |k|@>=
24764 if ( (mp->str_pool[k]<bc)||(mp->str_pool[k]>ec) ) {
24765 mp_lost_warning(mp, f,k);
24767 cc=char_info(f)(mp->str_pool[k]);
24768 if ( ! ichar_exists(cc) ) {
24769 mp_lost_warning(mp, f,k);
24771 width_val(p)=width_val(p)+char_width(f)(cc);
24772 h=char_height(f)(cc);
24773 d=char_depth(f)(cc);
24774 if ( h>height_val(p) ) height_val(p)=h;
24775 if ( d>depth_val(p) ) depth_val(p)=d;
24781 @ Let's hope modern compilers do comparisons correctly when the difference would
24784 @<Set the height and depth to zero if the bounding box is empty@>=
24785 if ( height_val(p)<-depth_val(p) ) {
24790 @ The new primitives fontmapfile and fontmapline.
24792 @<Declare action procedures for use by |do_statement|@>=
24793 void mp_do_mapfile (MP mp) ;
24794 void mp_do_mapline (MP mp) ;
24796 @ @c void mp_do_mapfile (MP mp) {
24797 mp_get_x_next(mp); mp_scan_expression(mp);
24798 if ( mp->cur_type!=mp_string_type ) {
24799 @<Complain about improper map operation@>;
24801 mp_map_file(mp,mp->cur_exp);
24804 void mp_do_mapline (MP mp) {
24805 mp_get_x_next(mp); mp_scan_expression(mp);
24806 if ( mp->cur_type!=mp_string_type ) {
24807 @<Complain about improper map operation@>;
24809 mp_map_line(mp,mp->cur_exp);
24813 @ @<Complain about improper map operation@>=
24815 exp_err("Unsuitable expression");
24816 help1("Only known strings can be map files or map lines.");
24817 mp_put_get_error(mp);
24820 @ To print |scaled| value to PDF output we need some subroutines to ensure
24823 @d max_integer 0x7FFFFFFF /* $2^{31}-1$ */
24826 scaled one_bp; /* scaled value corresponds to 1bp */
24827 scaled one_hundred_bp; /* scaled value corresponds to 100bp */
24828 scaled one_hundred_inch; /* scaled value corresponds to 100in */
24829 integer ten_pow[10]; /* $10^0..10^9$ */
24830 integer scaled_out; /* amount of |scaled| that was taken out in |divide_scaled| */
24833 mp->one_bp = 65782; /* 65781.76 */
24834 mp->one_hundred_bp = 6578176;
24835 mp->one_hundred_inch = 473628672;
24836 mp->ten_pow[0] = 1;
24837 for (i = 1;i<= 9; i++ ) {
24838 mp->ten_pow[i] = 10*mp->ten_pow[i - 1];
24841 @ The following function divides |s| by |m|. |dd| is number of decimal digits.
24843 @c scaled mp_divide_scaled (MP mp,scaled s, scaled m, integer dd) {
24847 if ( s < 0 ) { sign = -sign; s = -s; }
24848 if ( m < 0 ) { sign = -sign; m = -m; }
24850 mp_confusion(mp, "arithmetic: divided by zero");
24851 else if ( m >= (max_integer / 10) )
24852 mp_confusion(mp, "arithmetic: number too big");
24855 for (i = 1;i<=dd;i++) {
24856 q = 10*q + (10*r) / m;
24859 if ( 2*r >= m ) { incr(q); r = r - m; }
24860 mp->scaled_out = sign*(s - (r / mp->ten_pow[dd]));
24864 @* \[44] Shipping pictures out.
24865 The |ship_out| procedure, to be described below, is given a pointer to
24866 an edge structure. Its mission is to output a file containing the \ps\
24867 description of an edge structure.
24869 @ Each time an edge structure is shipped out we write a new \ps\ output
24870 file named according to the current \&{charcode}.
24871 @:char_code_}{\&{charcode} primitive@>
24873 This is the only backend function that remains in the main |mpost.w| file.
24874 There are just too many variable accesses needed for status reporting
24875 etcetera to make it worthwile to move the code to |psout.w|.
24877 @<Internal library declarations@>=
24878 void mp_open_output_file (MP mp) ;
24881 char *mp_set_output_file_name (MP mp, integer c) {
24882 char *ss = NULL; /* filename extension proposal */
24883 int old_setting; /* previous |selector| setting */
24884 pool_pointer i; /* indexes into |filename_template| */
24885 integer cc; /* a temporary integer for template building */
24886 integer f,g=0; /* field widths */
24887 if ( mp->job_name==NULL ) mp_open_log_file(mp);
24888 if ( mp->filename_template==0 ) {
24889 char *s; /* a file extension derived from |c| */
24893 @<Use |c| to compute the file extension |s|@>;
24894 mp_pack_job_name(mp, s);
24896 } else { /* initializations */
24897 str_number s, n; /* a file extension derived from |c| */
24898 old_setting=mp->selector;
24899 mp->selector=new_string;
24901 i = mp->str_start[mp->filename_template];
24902 n = rts(""); /* initialize */
24903 while ( i<str_stop(mp->filename_template) ) {
24904 if ( mp->str_pool[i]=='%' ) {
24907 if ( i<str_stop(mp->filename_template) ) {
24908 if ( mp->str_pool[i]=='j' ) {
24909 mp_print(mp, mp->job_name);
24910 } else if ( mp->str_pool[i]=='d' ) {
24911 cc= mp_round_unscaled(mp, mp->internal[mp_day]);
24912 print_with_leading_zeroes(cc);
24913 } else if ( mp->str_pool[i]=='m' ) {
24914 cc= mp_round_unscaled(mp, mp->internal[mp_month]);
24915 print_with_leading_zeroes(cc);
24916 } else if ( mp->str_pool[i]=='y' ) {
24917 cc= mp_round_unscaled(mp, mp->internal[mp_year]);
24918 print_with_leading_zeroes(cc);
24919 } else if ( mp->str_pool[i]=='H' ) {
24920 cc= mp_round_unscaled(mp, mp->internal[mp_time]) / 60;
24921 print_with_leading_zeroes(cc);
24922 } else if ( mp->str_pool[i]=='M' ) {
24923 cc= mp_round_unscaled(mp, mp->internal[mp_time]) % 60;
24924 print_with_leading_zeroes(cc);
24925 } else if ( mp->str_pool[i]=='c' ) {
24926 if ( c<0 ) mp_print(mp, "ps");
24927 else print_with_leading_zeroes(c);
24928 } else if ( (mp->str_pool[i]>='0') &&
24929 (mp->str_pool[i]<='9') ) {
24931 f = (f*10) + mp->str_pool[i]-'0';
24934 mp_print_str(mp, mp->str_pool[i]);
24938 if ( mp->str_pool[i]=='.' )
24940 n = mp_make_string(mp);
24941 mp_print_str(mp, mp->str_pool[i]);
24945 s = mp_make_string(mp);
24946 mp->selector= old_setting;
24947 if (length(n)==0) {
24951 mp_pack_file_name(mp, str(n),"",str(s));
24959 char * mp_get_output_file_name (MP mp) {
24960 char *fname; /* return value */
24961 char *saved_name; /* saved |name_of_file| */
24962 saved_name = mp_xstrdup(mp, mp->name_of_file);
24963 (void)mp_set_output_file_name(mp, mp_round_unscaled(mp, mp->internal[mp_char_code]));
24964 fname = mp_xstrdup(mp, mp->name_of_file);
24965 mp_pack_file_name(mp, saved_name,NULL,NULL);
24969 void mp_open_output_file (MP mp) {
24970 char *ss; /* filename extension proposal */
24971 integer c; /* \&{charcode} rounded to the nearest integer */
24972 c=mp_round_unscaled(mp, mp->internal[mp_char_code]);
24973 ss = mp_set_output_file_name(mp, c);
24974 while ( ! mp_a_open_out(mp, (void *)&mp->ps_file, mp_filetype_postscript) )
24975 mp_prompt_file_name(mp, "file name for output",ss);
24977 @<Store the true output file name if appropriate@>;
24980 @ The file extension created here could be up to five characters long in
24981 extreme cases so it may have to be shortened on some systems.
24982 @^system dependencies@>
24984 @<Use |c| to compute the file extension |s|@>=
24987 snprintf(s,7,".%i",(int)c);
24990 @ The user won't want to see all the output file names so we only save the
24991 first and last ones and a count of how many there were. For this purpose
24992 files are ordered primarily by \&{charcode} and secondarily by order of
24994 @:char_code_}{\&{charcode} primitive@>
24996 @<Store the true output file name if appropriate@>=
24997 if ((c<mp->first_output_code)&&(mp->first_output_code>=0)) {
24998 mp->first_output_code=c;
24999 xfree(mp->first_file_name);
25000 mp->first_file_name=xstrdup(mp->name_of_file);
25002 if ( c>=mp->last_output_code ) {
25003 mp->last_output_code=c;
25004 xfree(mp->last_file_name);
25005 mp->last_file_name=xstrdup(mp->name_of_file);
25009 char * first_file_name;
25010 char * last_file_name; /* full file names */
25011 integer first_output_code;integer last_output_code; /* rounded \&{charcode} values */
25012 @:char_code_}{\&{charcode} primitive@>
25013 integer total_shipped; /* total number of |ship_out| operations completed */
25016 mp->first_file_name=xstrdup("");
25017 mp->last_file_name=xstrdup("");
25018 mp->first_output_code=32768;
25019 mp->last_output_code=-32768;
25020 mp->total_shipped=0;
25022 @ @<Dealloc variables@>=
25023 xfree(mp->first_file_name);
25024 xfree(mp->last_file_name);
25026 @ @<Begin the progress report for the output of picture~|c|@>=
25027 if ( (int)mp->term_offset>mp->max_print_line-6 ) mp_print_ln(mp);
25028 else if ( (mp->term_offset>0)||(mp->file_offset>0) ) mp_print_char(mp, ' ');
25029 mp_print_char(mp, '[');
25030 if ( c>=0 ) mp_print_int(mp, c)
25032 @ @<End progress report@>=
25033 mp_print_char(mp, ']');
25035 incr(mp->total_shipped)
25037 @ @<Explain what output files were written@>=
25038 if ( mp->total_shipped>0 ) {
25039 mp_print_nl(mp, "");
25040 mp_print_int(mp, mp->total_shipped);
25041 mp_print(mp, " output file");
25042 if ( mp->total_shipped>1 ) mp_print_char(mp, 's');
25043 mp_print(mp, " written: ");
25044 mp_print(mp, mp->first_file_name);
25045 if ( mp->total_shipped>1 ) {
25046 if ( 31+strlen(mp->first_file_name)+
25047 strlen(mp->last_file_name)> (unsigned)mp->max_print_line)
25049 mp_print(mp, " .. ");
25050 mp_print(mp, mp->last_file_name);
25054 @ @<Internal library declarations@>=
25055 boolean mp_has_font_size(MP mp, font_number f );
25058 boolean mp_has_font_size(MP mp, font_number f ) {
25059 return (mp->font_sizes[f]!=null);
25062 @ The \&{special} command saves up lines of text to be printed during the next
25063 |ship_out| operation. The saved items are stored as a list of capsule tokens.
25066 pointer last_pending; /* the last token in a list of pending specials */
25069 mp->last_pending=spec_head;
25071 @ @<Cases of |do_statement|...@>=
25072 case special_command:
25073 if ( mp->cur_mod==0 ) mp_do_special(mp); else
25074 if ( mp->cur_mod==1 ) mp_do_mapfile(mp); else
25078 @ @<Declare action procedures for use by |do_statement|@>=
25079 void mp_do_special (MP mp) ;
25081 @ @c void mp_do_special (MP mp) {
25082 mp_get_x_next(mp); mp_scan_expression(mp);
25083 if ( mp->cur_type!=mp_string_type ) {
25084 @<Complain about improper special operation@>;
25086 link(mp->last_pending)=mp_stash_cur_exp(mp);
25087 mp->last_pending=link(mp->last_pending);
25088 link(mp->last_pending)=null;
25092 @ @<Complain about improper special operation@>=
25094 exp_err("Unsuitable expression");
25095 help1("Only known strings are allowed for output as specials.");
25096 mp_put_get_error(mp);
25099 @ On the export side, we need an extra object type for special strings.
25101 @<Graphical object codes@>=
25104 @ @<Export pending specials@>=
25106 while ( p!=null ) {
25107 hq = mp_new_graphic_object(mp,mp_special_code);
25108 gr_pre_script(hq) = str(value(p));
25109 if (hh->body==NULL) hh->body=hq; else gr_link(hp) = hq;
25113 mp_flush_token_list(mp, link(spec_head));
25114 link(spec_head)=null;
25115 mp->last_pending=spec_head
25117 @ We are now ready for the main output procedure. Note that the |selector|
25118 setting is saved in a global variable so that |begin_diagnostic| can access it.
25120 @<Declare the \ps\ output procedures@>=
25121 void mp_ship_out (MP mp, pointer h) ;
25123 @ Once again, the |gr_XXXX| macros are defined in |mppsout.h|
25126 struct mp_edge_object *mp_gr_export(MP mp, pointer h) {
25127 pointer p; /* the current graphical object */
25128 integer t; /* a temporary value */
25129 struct mp_edge_object *hh; /* the first graphical object */
25130 struct mp_graphic_object *hp; /* the current graphical object */
25131 struct mp_graphic_object *hq; /* something |hp| points to */
25132 mp_set_bbox(mp, h, true);
25133 hh = mp_xmalloc(mp,1,sizeof(struct mp_edge_object));
25137 hh->_minx = minx_val(h);
25138 hh->_miny = miny_val(h);
25139 hh->_maxx = maxx_val(h);
25140 hh->_maxy = maxy_val(h);
25141 hh->_filename = mp_get_output_file_name(mp);
25142 @<Export pending specials@>;
25143 p=link(dummy_loc(h));
25144 while ( p!=null ) {
25145 hq = mp_new_graphic_object(mp,type(p));
25148 gr_pen_p(hq) = mp_export_knot_list(mp,pen_p(p));
25149 if ((pen_p(p)==null) || pen_is_elliptical(pen_p(p))) {
25150 gr_path_p(hq) = mp_export_knot_list(mp,path_p(p));
25153 pc = mp_copy_path(mp, path_p(p));
25154 pp = mp_make_envelope(mp, pc, pen_p(p),ljoin_val(p),0,miterlim_val(p));
25155 gr_path_p(hq) = mp_export_knot_list(mp,pp);
25156 mp_toss_knot_list(mp, pp);
25157 pc = mp_htap_ypoc(mp, path_p(p));
25158 pp = mp_make_envelope(mp, pc, pen_p(p),ljoin_val(p),0,miterlim_val(p));
25159 gr_htap_p(hq) = mp_export_knot_list(mp,pp);
25160 mp_toss_knot_list(mp, pp);
25162 @<Export object color@>;
25163 @<Export object scripts@>;
25164 gr_ljoin_val(hq) = ljoin_val(p);
25165 gr_miterlim_val(hq) = miterlim_val(p);
25167 case mp_stroked_code:
25168 gr_pen_p(hq) = mp_export_knot_list(mp,pen_p(p));
25169 if (pen_is_elliptical(pen_p(p))) {
25170 gr_path_p(hq) = mp_export_knot_list(mp,path_p(p));
25173 pc=mp_copy_path(mp, path_p(p));
25175 if ( left_type(pc)!=mp_endpoint ) {
25176 left_type(mp_insert_knot(mp, pc,x_coord(pc),y_coord(pc)))=mp_endpoint;
25177 right_type(pc)=mp_endpoint;
25181 pc=mp_make_envelope(mp,pc,pen_p(p),ljoin_val(p),t,miterlim_val(p));
25182 gr_path_p(hq) = mp_export_knot_list(mp,pc);
25183 mp_toss_knot_list(mp, pc);
25185 @<Export object color@>;
25186 @<Export object scripts@>;
25187 gr_ljoin_val(hq) = ljoin_val(p);
25188 gr_miterlim_val(hq) = miterlim_val(p);
25189 gr_lcap_val(hq) = lcap_val(p);
25190 gr_dash_scale(hq) = dash_scale(p);
25191 gr_dash_p(hq) = mp_export_dashes(mp,dash_p(p));
25194 gr_text_p(hq) = str(text_p(p));
25195 gr_font_n(hq) = font_n(p);
25196 gr_font_name(hq) = mp_xstrdup(mp,mp->font_name[font_n(p)]);
25197 gr_font_dsize(hq) = mp->font_dsize[font_n(p)];
25198 @<Export object color@>;
25199 @<Export object scripts@>;
25200 gr_width_val(hq) = width_val(p);
25201 gr_height_val(hq) = height_val(p);
25202 gr_depth_val(hq) = depth_val(p);
25203 gr_tx_val(hq) = tx_val(p);
25204 gr_ty_val(hq) = ty_val(p);
25205 gr_txx_val(hq) = txx_val(p);
25206 gr_txy_val(hq) = txy_val(p);
25207 gr_tyx_val(hq) = tyx_val(p);
25208 gr_tyy_val(hq) = tyy_val(p);
25210 case mp_start_clip_code:
25211 case mp_start_bounds_code:
25212 gr_path_p(hq) = mp_export_knot_list(mp,path_p(p));
25214 case mp_stop_clip_code:
25215 case mp_stop_bounds_code:
25216 /* nothing to do here */
25219 if (hh->body==NULL) hh->body=hq; else gr_link(hp) = hq;
25226 @ @<Exported function ...@>=
25227 struct mp_edge_object *mp_gr_export(MP mp, int h);
25229 @ This function is now nearly trivial.
25232 void mp_ship_out (MP mp, pointer h) { /* output edge structure |h| */
25233 integer c; /* \&{charcode} rounded to the nearest integer */
25234 c=mp_round_unscaled(mp, mp->internal[mp_char_code]);
25235 @<Begin the progress report for the output of picture~|c|@>;
25236 (mp->shipout_backend) (mp, h);
25237 @<End progress report@>;
25238 if ( mp->internal[mp_tracing_output]>0 )
25239 mp_print_edges(mp, h," (just shipped out)",true);
25242 @ @<Declarations@>=
25243 void mp_shipout_backend (MP mp, pointer h);
25246 void mp_shipout_backend (MP mp, pointer h) {
25247 struct mp_edge_object *hh; /* the first graphical object */
25248 hh = mp_gr_export(mp,h);
25249 mp_gr_ship_out (hh,
25250 (mp->internal[mp_prologues]>>16),
25251 (mp->internal[mp_procset]>>16));
25252 mp_gr_toss_objects(hh);
25255 @ @<Exported types@>=
25256 typedef void (*mp_backend_writer)(MP, int);
25258 @ @<Option variables@>=
25259 mp_backend_writer shipout_backend;
25261 @ @<Allocate or initialize ...@>=
25262 set_callback_option(shipout_backend);
25266 @ Once again, the |gr_XXXX| macros are defined in |mppsout.h|
25268 @<Export object color@>=
25269 gr_color_model(hq) = color_model(p);
25270 gr_cyan_val(hq) = cyan_val(p);
25271 gr_magenta_val(hq) = magenta_val(p);
25272 gr_yellow_val(hq) = yellow_val(p);
25273 gr_black_val(hq) = black_val(p);
25274 gr_red_val(hq) = red_val(p);
25275 gr_green_val(hq) = green_val(p);
25276 gr_blue_val(hq) = blue_val(p);
25277 gr_grey_val(hq) = grey_val(p)
25280 @ @<Export object scripts@>=
25281 if (pre_script(p)!=null)
25282 gr_pre_script(hq) = str(pre_script(p));
25283 if (post_script(p)!=null)
25284 gr_post_script(hq) = str(post_script(p));
25286 @ Now that we've finished |ship_out|, let's look at the other commands
25287 by which a user can send things to the \.{GF} file.
25289 @ @<Determine if a character has been shipped out@>=
25291 mp->cur_exp=mp_round_unscaled(mp, mp->cur_exp) % 256;
25292 if ( mp->cur_exp<0 ) mp->cur_exp=mp->cur_exp+256;
25293 boolean_reset(mp->char_exists[mp->cur_exp]);
25294 mp->cur_type=mp_boolean_type;
25300 @ @<Allocate or initialize ...@>=
25301 mp_backend_initialize(mp);
25304 mp_backend_free(mp);
25307 @* \[45] Dumping and undumping the tables.
25308 After \.{INIMP} has seen a collection of macros, it
25309 can write all the necessary information on an auxiliary file so
25310 that production versions of \MP\ are able to initialize their
25311 memory at high speed. The present section of the program takes
25312 care of such output and input. We shall consider simultaneously
25313 the processes of storing and restoring,
25314 so that the inverse relation between them is clear.
25317 The global variable |mem_ident| is a string that is printed right
25318 after the |banner| line when \MP\ is ready to start. For \.{INIMP} this
25319 string says simply `\.{(INIMP)}'; for other versions of \MP\ it says,
25320 for example, `\.{(mem=plain 90.4.14)}', showing the year,
25321 month, and day that the mem file was created. We have |mem_ident=0|
25322 before \MP's tables are loaded.
25328 mp->mem_ident=NULL;
25330 @ @<Initialize table entries...@>=
25331 mp->mem_ident=xstrdup(" (INIMP)");
25333 @ @<Declare act...@>=
25334 void mp_store_mem_file (MP mp) ;
25336 @ @c void mp_store_mem_file (MP mp) {
25337 integer k; /* all-purpose index */
25338 pointer p,q; /* all-purpose pointers */
25339 integer x; /* something to dump */
25340 four_quarters w; /* four ASCII codes */
25342 @<Create the |mem_ident|, open the mem file,
25343 and inform the user that dumping has begun@>;
25344 @<Dump constants for consistency check@>;
25345 @<Dump the string pool@>;
25346 @<Dump the dynamic memory@>;
25347 @<Dump the table of equivalents and the hash table@>;
25348 @<Dump a few more things and the closing check word@>;
25349 @<Close the mem file@>;
25352 @ Corresponding to the procedure that dumps a mem file, we also have a function
25353 that reads~one~in. The function returns |false| if the dumped mem is
25354 incompatible with the present \MP\ table sizes, etc.
25356 @d off_base 6666 /* go here if the mem file is unacceptable */
25357 @d too_small(A) { wake_up_terminal;
25358 wterm_ln("---! Must increase the "); wterm((A));
25359 @.Must increase the x@>
25364 boolean mp_load_mem_file (MP mp) {
25365 integer k; /* all-purpose index */
25366 pointer p,q; /* all-purpose pointers */
25367 integer x; /* something undumped */
25368 str_number s; /* some temporary string */
25369 four_quarters w; /* four ASCII codes */
25371 @<Undump constants for consistency check@>;
25372 @<Undump the string pool@>;
25373 @<Undump the dynamic memory@>;
25374 @<Undump the table of equivalents and the hash table@>;
25375 @<Undump a few more things and the closing check word@>;
25376 return true; /* it worked! */
25379 wterm_ln("(Fatal mem file error; I'm stymied)\n");
25380 @.Fatal mem file error@>
25384 @ @<Declarations@>=
25385 boolean mp_load_mem_file (MP mp) ;
25387 @ Mem files consist of |memory_word| items, and we use the following
25388 macros to dump words of different types:
25390 @d dump_wd(A) { WW=(A); (mp->write_binary_file)(mp,mp->mem_file,&WW,sizeof(WW)); }
25391 @d dump_int(A) { int cint=(A); (mp->write_binary_file)(mp,mp->mem_file,&cint,sizeof(cint)); }
25392 @d dump_hh(A) { WW.hh=(A); (mp->write_binary_file)(mp,mp->mem_file,&WW,sizeof(WW)); }
25393 @d dump_qqqq(A) { WW.qqqq=(A); (mp->write_binary_file)(mp,mp->mem_file,&WW,sizeof(WW)); }
25394 @d dump_string(A) { dump_int(strlen(A)+1);
25395 (mp->write_binary_file)(mp,mp->mem_file,A,strlen(A)+1); }
25398 void * mem_file; /* for input or output of mem information */
25400 @ The inverse macros are slightly more complicated, since we need to check
25401 the range of the values we are reading in. We say `|undump(a)(b)(x)|' to
25402 read an integer value |x| that is supposed to be in the range |a<=x<=b|.
25405 size_t wanted = sizeof(A);
25407 (mp->read_binary_file)(mp, mp->mem_file,&A_ptr,&wanted);
25408 if (wanted!=sizeof(A)) goto OFF_BASE;
25412 size_t wanted = sizeof(A);
25414 (mp->read_binary_file)(mp, mp->mem_file,&A_ptr,&wanted);
25415 if (wanted!=sizeof(A)) goto OFF_BASE;
25418 @d undump_wd(A) { mgetw(WW); A=WW; }
25419 @d undump_int(A) { int cint; mgeti(cint); A=cint; }
25420 @d undump_hh(A) { mgetw(WW); A=WW.hh; }
25421 @d undump_qqqq(A) { mgetw(WW); A=WW.qqqq; }
25422 @d undump_strings(A,B,C) {
25423 undump_int(x); if ( (x<(A)) || (x>(B)) ) goto OFF_BASE; else C=str(x); }
25424 @d undump(A,B,C) { undump_int(x); if ( (x<(A)) || (x>(int)(B)) ) goto OFF_BASE; else C=x; }
25425 @d undump_size(A,B,C,D) { undump_int(x);
25426 if (x<(A)) goto OFF_BASE;
25427 if (x>(B)) { too_small((C)); } else { D=x;} }
25428 @d undump_string(A) do {
25433 A = xmalloc(XX,sizeof(char));
25434 (mp->read_binary_file)(mp,mp->mem_file,(void **)&A,&wanted);
25435 if (wanted!=(size_t)XX) goto OFF_BASE;
25438 @ The next few sections of the program should make it clear how we use the
25439 dump/undump macros.
25441 @<Dump constants for consistency check@>=
25442 dump_int(mp->mem_top);
25443 dump_int(mp->hash_size);
25444 dump_int(mp->hash_prime)
25445 dump_int(mp->param_size);
25446 dump_int(mp->max_in_open);
25448 @ Sections of a \.{WEB} program that are ``commented out'' still contribute
25449 strings to the string pool; therefore \.{INIMP} and \MP\ will have
25450 the same strings. (And it is, of course, a good thing that they do.)
25454 @<Undump constants for consistency check@>=
25455 undump_int(x); mp->mem_top = x;
25456 undump_int(x); if (mp->hash_size != x) goto OFF_BASE;
25457 undump_int(x); if (mp->hash_prime != x) goto OFF_BASE;
25458 undump_int(x); if (mp->param_size != x) goto OFF_BASE;
25459 undump_int(x); if (mp->max_in_open != x) goto OFF_BASE
25461 @ We do string pool compaction to avoid dumping unused strings.
25464 w.b0=qi(mp->str_pool[k]); w.b1=qi(mp->str_pool[k+1]);
25465 w.b2=qi(mp->str_pool[k+2]); w.b3=qi(mp->str_pool[k+3]);
25468 @<Dump the string pool@>=
25469 mp_do_compaction(mp, mp->pool_size);
25470 dump_int(mp->pool_ptr);
25471 dump_int(mp->max_str_ptr);
25472 dump_int(mp->str_ptr);
25474 while ( (mp->next_str[k]==k+1) && (k<=mp->max_str_ptr) )
25477 while ( k<=mp->max_str_ptr ) {
25478 dump_int(mp->next_str[k]); incr(k);
25482 dump_int(mp->str_start[k]); /* TODO: valgrind warning here */
25483 if ( k==mp->str_ptr ) {
25490 while (k+4<mp->pool_ptr ) {
25491 dump_four_ASCII; k=k+4;
25493 k=mp->pool_ptr-4; dump_four_ASCII;
25494 mp_print_ln(mp); mp_print(mp, "at most "); mp_print_int(mp, mp->max_str_ptr);
25495 mp_print(mp, " strings of total length ");
25496 mp_print_int(mp, mp->pool_ptr)
25498 @ @d undump_four_ASCII
25500 mp->str_pool[k]=qo(w.b0); mp->str_pool[k+1]=qo(w.b1);
25501 mp->str_pool[k+2]=qo(w.b2); mp->str_pool[k+3]=qo(w.b3)
25503 @<Undump the string pool@>=
25504 undump_int(mp->pool_ptr);
25505 mp_reallocate_pool(mp, mp->pool_ptr) ;
25506 undump_int(mp->max_str_ptr);
25507 mp_reallocate_strings (mp,mp->max_str_ptr) ;
25508 undump(0,mp->max_str_ptr,mp->str_ptr);
25509 undump(0,mp->max_str_ptr+1,s);
25510 for (k=0;k<=s-1;k++)
25511 mp->next_str[k]=k+1;
25512 for (k=s;k<=mp->max_str_ptr;k++)
25513 undump(s+1,mp->max_str_ptr+1,mp->next_str[k]);
25514 mp->fixed_str_use=0;
25517 undump(0,mp->pool_ptr,mp->str_start[k]);
25518 if ( k==mp->str_ptr ) break;
25519 mp->str_ref[k]=max_str_ref;
25520 incr(mp->fixed_str_use);
25521 mp->last_fixed_str=k; k=mp->next_str[k];
25524 while ( k+4<mp->pool_ptr ) {
25525 undump_four_ASCII; k=k+4;
25527 k=mp->pool_ptr-4; undump_four_ASCII;
25528 mp->init_str_use=mp->fixed_str_use; mp->init_pool_ptr=mp->pool_ptr;
25529 mp->max_pool_ptr=mp->pool_ptr;
25530 mp->strs_used_up=mp->fixed_str_use;
25531 mp->pool_in_use=mp->str_start[mp->str_ptr]; mp->strs_in_use=mp->fixed_str_use;
25532 mp->max_pl_used=mp->pool_in_use; mp->max_strs_used=mp->strs_in_use;
25533 mp->pact_count=0; mp->pact_chars=0; mp->pact_strs=0;
25535 @ By sorting the list of available spaces in the variable-size portion of
25536 |mem|, we are usually able to get by without having to dump very much
25537 of the dynamic memory.
25539 We recompute |var_used| and |dyn_used|, so that \.{INIMP} dumps valid
25540 information even when it has not been gathering statistics.
25542 @<Dump the dynamic memory@>=
25543 mp_sort_avail(mp); mp->var_used=0;
25544 dump_int(mp->lo_mem_max); dump_int(mp->rover);
25545 p=0; q=mp->rover; x=0;
25547 for (k=p;k<= q+1;k++)
25548 dump_wd(mp->mem[k]);
25549 x=x+q+2-p; mp->var_used=mp->var_used+q-p;
25550 p=q+node_size(q); q=rlink(q);
25551 } while (q!=mp->rover);
25552 mp->var_used=mp->var_used+mp->lo_mem_max-p;
25553 mp->dyn_used=mp->mem_end+1-mp->hi_mem_min;
25554 for (k=p;k<= mp->lo_mem_max;k++ )
25555 dump_wd(mp->mem[k]);
25556 x=x+mp->lo_mem_max+1-p;
25557 dump_int(mp->hi_mem_min); dump_int(mp->avail);
25558 for (k=mp->hi_mem_min;k<=mp->mem_end;k++ )
25559 dump_wd(mp->mem[k]);
25560 x=x+mp->mem_end+1-mp->hi_mem_min;
25562 while ( p!=null ) {
25563 decr(mp->dyn_used); p=link(p);
25565 dump_int(mp->var_used); dump_int(mp->dyn_used);
25566 mp_print_ln(mp); mp_print_int(mp, x);
25567 mp_print(mp, " memory locations dumped; current usage is ");
25568 mp_print_int(mp, mp->var_used); mp_print_char(mp, '&'); mp_print_int(mp, mp->dyn_used)
25570 @ @<Undump the dynamic memory@>=
25571 undump(lo_mem_stat_max+1000,hi_mem_stat_min-1,mp->lo_mem_max);
25572 undump(lo_mem_stat_max+1,mp->lo_mem_max,mp->rover);
25575 for (k=p;k<= q+1; k++)
25576 undump_wd(mp->mem[k]);
25578 if ( (p>mp->lo_mem_max)||((q>=rlink(q))&&(rlink(q)!=mp->rover)) )
25581 } while (q!=mp->rover);
25582 for (k=p;k<=mp->lo_mem_max;k++ )
25583 undump_wd(mp->mem[k]);
25584 undump(mp->lo_mem_max+1,hi_mem_stat_min,mp->hi_mem_min);
25585 undump(null,mp->mem_top,mp->avail); mp->mem_end=mp->mem_top;
25586 for (k=mp->hi_mem_min;k<= mp->mem_end;k++)
25587 undump_wd(mp->mem[k]);
25588 undump_int(mp->var_used); undump_int(mp->dyn_used)
25590 @ A different scheme is used to compress the hash table, since its lower region
25591 is usually sparse. When |text(p)<>0| for |p<=hash_used|, we output three
25592 words: |p|, |hash[p]|, and |eqtb[p]|. The hash table is, of course, densely
25593 packed for |p>=hash_used|, so the remaining entries are output in~a~block.
25595 @<Dump the table of equivalents and the hash table@>=
25596 dump_int(mp->hash_used);
25597 mp->st_count=frozen_inaccessible-1-mp->hash_used;
25598 for (p=1;p<=mp->hash_used;p++) {
25599 if ( text(p)!=0 ) {
25600 dump_int(p); dump_hh(mp->hash[p]); dump_hh(mp->eqtb[p]); incr(mp->st_count);
25603 for (p=mp->hash_used+1;p<=(int)hash_end;p++) {
25604 dump_hh(mp->hash[p]); dump_hh(mp->eqtb[p]);
25606 dump_int(mp->st_count);
25607 mp_print_ln(mp); mp_print_int(mp, mp->st_count); mp_print(mp, " symbolic tokens")
25609 @ @<Undump the table of equivalents and the hash table@>=
25610 undump(1,frozen_inaccessible,mp->hash_used);
25613 undump(p+1,mp->hash_used,p);
25614 undump_hh(mp->hash[p]); undump_hh(mp->eqtb[p]);
25615 } while (p!=mp->hash_used);
25616 for (p=mp->hash_used+1;p<=(int)hash_end;p++ ) {
25617 undump_hh(mp->hash[p]); undump_hh(mp->eqtb[p]);
25619 undump_int(mp->st_count)
25621 @ We have already printed a lot of statistics, so we set |mp_tracing_stats:=0|
25622 to prevent them appearing again.
25624 @<Dump a few more things and the closing check word@>=
25625 dump_int(mp->max_internal);
25626 dump_int(mp->int_ptr);
25627 for (k=1;k<= mp->int_ptr;k++ ) {
25628 dump_int(mp->internal[k]);
25629 dump_string(mp->int_name[k]);
25631 dump_int(mp->start_sym);
25632 dump_int(mp->interaction);
25633 dump_string(mp->mem_ident);
25634 dump_int(mp->bg_loc); dump_int(mp->eg_loc); dump_int(mp->serial_no); dump_int(69073);
25635 mp->internal[mp_tracing_stats]=0
25637 @ @<Undump a few more things and the closing check word@>=
25639 if (x>mp->max_internal) mp_grow_internals(mp,x);
25640 undump_int(mp->int_ptr);
25641 for (k=1;k<= mp->int_ptr;k++) {
25642 undump_int(mp->internal[k]);
25643 undump_string(mp->int_name[k]);
25645 undump(0,frozen_inaccessible,mp->start_sym);
25646 if (mp->interaction==mp_unspecified_mode) {
25647 undump(mp_unspecified_mode,mp_error_stop_mode,mp->interaction);
25649 undump(mp_unspecified_mode,mp_error_stop_mode,x);
25651 undump_string(mp->mem_ident);
25652 undump(1,hash_end,mp->bg_loc);
25653 undump(1,hash_end,mp->eg_loc);
25654 undump_int(mp->serial_no);
25656 if (x!=69073) goto OFF_BASE
25658 @ @<Create the |mem_ident|...@>=
25660 xfree(mp->mem_ident);
25661 mp->mem_ident = xmalloc(256,1);
25662 snprintf(mp->mem_ident,256," (mem=%s %i.%i.%i)",
25664 (int)(mp_round_unscaled(mp, mp->internal[mp_year]) % 100),
25665 (int)mp_round_unscaled(mp, mp->internal[mp_month]),
25666 (int)mp_round_unscaled(mp, mp->internal[mp_day]));
25667 mp_pack_job_name(mp, mem_extension);
25668 while (! mp_w_open_out(mp, &mp->mem_file) )
25669 mp_prompt_file_name(mp, "mem file name", mem_extension);
25670 mp_print_nl(mp, "Beginning to dump on file ");
25671 @.Beginning to dump...@>
25672 mp_print(mp, mp->name_of_file);
25673 mp_print_nl(mp, mp->mem_ident);
25676 @ @<Dealloc variables@>=
25677 xfree(mp->mem_ident);
25679 @ @<Close the mem file@>=
25680 (mp->close_file)(mp,mp->mem_file)
25682 @* \[46] The main program.
25683 This is it: the part of \MP\ that executes all those procedures we have
25686 Well---almost. We haven't put the parsing subroutines into the
25687 program yet; and we'd better leave space for a few more routines that may
25688 have been forgotten.
25690 @c @<Declare the basic parsing subroutines@>;
25691 @<Declare miscellaneous procedures that were declared |forward|@>;
25692 @<Last-minute procedures@>
25694 @ We've noted that there are two versions of \MP. One, called \.{INIMP},
25696 has to be run first; it initializes everything from scratch, without
25697 reading a mem file, and it has the capability of dumping a mem file.
25698 The other one is called `\.{VIRMP}'; it is a ``virgin'' program that needs
25700 to input a mem file in order to get started. \.{VIRMP} typically has
25701 a bit more memory capacity than \.{INIMP}, because it does not need the
25702 space consumed by the dumping/undumping routines and the numerous calls on
25705 The \.{VIRMP} program cannot read a mem file instantaneously, of course;
25706 the best implementations therefore allow for production versions of \MP\ that
25707 not only avoid the loading routine for object code, they also have
25708 a mem file pre-loaded.
25710 @ @<Option variables@>=
25711 int ini_version; /* are we iniMP? */
25713 @ @<Set |ini_version|@>=
25714 mp->ini_version = (opt->ini_version ? true : false);
25716 @ Here we do whatever is needed to complete \MP's job gracefully on the
25717 local operating system. The code here might come into play after a fatal
25718 error; it must therefore consist entirely of ``safe'' operations that
25719 cannot produce error messages. For example, it would be a mistake to call
25720 |str_room| or |make_string| at this time, because a call on |overflow|
25721 might lead to an infinite loop.
25722 @^system dependencies@>
25724 This program doesn't bother to close the input files that may still be open.
25726 @<Last-minute...@>=
25727 void mp_close_files_and_terminate (MP mp) {
25728 integer k; /* all-purpose index */
25729 integer LH; /* the length of the \.{TFM} header, in words */
25730 int lk_offset; /* extra words inserted at beginning of |lig_kern| array */
25731 pointer p; /* runs through a list of \.{TFM} dimensions */
25732 @<Close all open files in the |rd_file| and |wr_file| arrays@>;
25733 if ( mp->internal[mp_tracing_stats]>0 )
25734 @<Output statistics about this job@>;
25736 @<Do all the finishing work on the \.{TFM} file@>;
25737 @<Explain what output files were written@>;
25738 if ( mp->log_opened ){
25740 (mp->close_file)(mp,mp->log_file);
25741 mp->selector=mp->selector-2;
25742 if ( mp->selector==term_only ) {
25743 mp_print_nl(mp, "Transcript written on ");
25744 @.Transcript written...@>
25745 mp_print(mp, mp->log_name); mp_print_char(mp, '.');
25753 @ @<Declarations@>=
25754 void mp_close_files_and_terminate (MP mp) ;
25756 @ @<Close all open files in the |rd_file| and |wr_file| arrays@>=
25757 if (mp->rd_fname!=NULL) {
25758 for (k=0;k<=(int)mp->read_files-1;k++ ) {
25759 if ( mp->rd_fname[k]!=NULL ) {
25760 (mp->close_file)(mp,mp->rd_file[k]);
25764 if (mp->wr_fname!=NULL) {
25765 for (k=0;k<=(int)mp->write_files-1;k++) {
25766 if ( mp->wr_fname[k]!=NULL ) {
25767 (mp->close_file)(mp,mp->wr_file[k]);
25773 for (k=0;k<(int)mp->max_read_files;k++ ) {
25774 if ( mp->rd_fname[k]!=NULL ) {
25775 (mp->close_file)(mp,mp->rd_file[k]);
25776 mp_xfree(mp->rd_fname[k]);
25779 mp_xfree(mp->rd_file);
25780 mp_xfree(mp->rd_fname);
25781 for (k=0;k<(int)mp->max_write_files;k++) {
25782 if ( mp->wr_fname[k]!=NULL ) {
25783 (mp->close_file)(mp,mp->wr_file[k]);
25784 mp_xfree(mp->wr_fname[k]);
25787 mp_xfree(mp->wr_file);
25788 mp_xfree(mp->wr_fname);
25791 @ We want to produce a \.{TFM} file if and only if |mp_fontmaking| is positive.
25793 We reclaim all of the variable-size memory at this point, so that
25794 there is no chance of another memory overflow after the memory capacity
25795 has already been exceeded.
25797 @<Do all the finishing work on the \.{TFM} file@>=
25798 if ( mp->internal[mp_fontmaking]>0 ) {
25799 @<Make the dynamic memory into one big available node@>;
25800 @<Massage the \.{TFM} widths@>;
25801 mp_fix_design_size(mp); mp_fix_check_sum(mp);
25802 @<Massage the \.{TFM} heights, depths, and italic corrections@>;
25803 mp->internal[mp_fontmaking]=0; /* avoid loop in case of fatal error */
25804 @<Finish the \.{TFM} file@>;
25807 @ @<Make the dynamic memory into one big available node@>=
25808 mp->rover=lo_mem_stat_max+1; link(mp->rover)=empty_flag; mp->lo_mem_max=mp->hi_mem_min-1;
25809 if ( mp->lo_mem_max-mp->rover>max_halfword ) mp->lo_mem_max=max_halfword+mp->rover;
25810 node_size(mp->rover)=mp->lo_mem_max-mp->rover;
25811 llink(mp->rover)=mp->rover; rlink(mp->rover)=mp->rover;
25812 link(mp->lo_mem_max)=null; info(mp->lo_mem_max)=null
25814 @ The present section goes directly to the log file instead of using
25815 |print| commands, because there's no need for these strings to take
25816 up |str_pool| memory when a non-{\bf stat} version of \MP\ is being used.
25818 @<Output statistics...@>=
25819 if ( mp->log_opened ) {
25822 wlog_ln("Here is how much of MetaPost's memory you used:");
25823 @.Here is how much...@>
25824 snprintf(s,128," %i string%s out of %i",(int)mp->max_strs_used-mp->init_str_use,
25825 (mp->max_strs_used!=mp->init_str_use+1 ? "s" : ""),
25826 (int)(mp->max_strings-1-mp->init_str_use));
25828 snprintf(s,128," %i string characters out of %i",
25829 (int)mp->max_pl_used-mp->init_pool_ptr,
25830 (int)mp->pool_size-mp->init_pool_ptr);
25832 snprintf(s,128," %i words of memory out of %i",
25833 (int)mp->lo_mem_max+mp->mem_end-mp->hi_mem_min+2,
25834 (int)mp->mem_end+1);
25836 snprintf(s,128," %i symbolic tokens out of %i", (int)mp->st_count, (int)mp->hash_size);
25838 snprintf(s,128," %ii, %in, %ip, %ib stack positions out of %ii, %in, %ip, %ib",
25839 (int)mp->max_in_stack,(int)mp->int_ptr,
25840 (int)mp->max_param_stack,(int)mp->max_buf_stack+1,
25841 (int)mp->stack_size,(int)mp->max_internal,(int)mp->param_size,(int)mp->buf_size);
25843 snprintf(s,128," %i string compactions (moved %i characters, %i strings)",
25844 (int)mp->pact_count,(int)mp->pact_chars,(int)mp->pact_strs);
25848 @ We get to the |final_cleanup| routine when \&{end} or \&{dump} has
25851 @<Last-minute...@>=
25852 void mp_final_cleanup (MP mp) {
25853 small_number c; /* 0 for \&{end}, 1 for \&{dump} */
25855 if ( mp->job_name==NULL ) mp_open_log_file(mp);
25856 while ( mp->input_ptr>0 ) {
25857 if ( token_state ) mp_end_token_list(mp);
25858 else mp_end_file_reading(mp);
25860 while ( mp->loop_ptr!=null ) mp_stop_iteration(mp);
25861 while ( mp->open_parens>0 ) {
25862 mp_print(mp, " )"); decr(mp->open_parens);
25864 while ( mp->cond_ptr!=null ) {
25865 mp_print_nl(mp, "(end occurred when ");
25866 @.end occurred...@>
25867 mp_print_cmd_mod(mp, fi_or_else,mp->cur_if);
25868 /* `\.{if}' or `\.{elseif}' or `\.{else}' */
25869 if ( mp->if_line!=0 ) {
25870 mp_print(mp, " on line "); mp_print_int(mp, mp->if_line);
25872 mp_print(mp, " was incomplete)");
25873 mp->if_line=if_line_field(mp->cond_ptr);
25874 mp->cur_if=name_type(mp->cond_ptr); mp->cond_ptr=link(mp->cond_ptr);
25876 if ( mp->history!=mp_spotless )
25877 if ( ((mp->history==mp_warning_issued)||(mp->interaction<mp_error_stop_mode)) )
25878 if ( mp->selector==term_and_log ) {
25879 mp->selector=term_only;
25880 mp_print_nl(mp, "(see the transcript file for additional information)");
25881 @.see the transcript file...@>
25882 mp->selector=term_and_log;
25885 if (mp->ini_version) {
25886 mp_store_mem_file(mp); return;
25888 mp_print_nl(mp, "(dump is performed only by INIMP)"); return;
25889 @.dump...only by INIMP@>
25893 @ @<Declarations@>=
25894 void mp_final_cleanup (MP mp) ;
25895 void mp_init_prim (MP mp) ;
25896 void mp_init_tab (MP mp) ;
25898 @ @<Last-minute...@>=
25899 void mp_init_prim (MP mp) { /* initialize all the primitives */
25903 void mp_init_tab (MP mp) { /* initialize other tables */
25904 integer k; /* all-purpose index */
25905 @<Initialize table entries (done by \.{INIMP} only)@>;
25909 @ When we begin the following code, \MP's tables may still contain garbage;
25910 the strings might not even be present. Thus we must proceed cautiously to get
25913 But when we finish this part of the program, \MP\ is ready to call on the
25914 |main_control| routine to do its work.
25916 @<Get the first line...@>=
25918 @<Initialize the input routines@>;
25919 if ( (mp->mem_ident==NULL)||(mp->buffer[loc]=='&') ) {
25920 if ( mp->mem_ident!=NULL ) {
25921 mp_do_initialize(mp); /* erase preloaded mem */
25923 if ( ! mp_open_mem_file(mp) ) return mp_fatal_error_stop;
25924 if ( ! mp_load_mem_file(mp) ) {
25925 (mp->close_file)(mp, mp->mem_file);
25926 return mp_fatal_error_stop;
25928 (mp->close_file)(mp, mp->mem_file);
25929 while ( (loc<limit)&&(mp->buffer[loc]==' ') ) incr(loc);
25931 mp->buffer[limit]='%';
25932 mp_fix_date_and_time(mp);
25933 if (mp->random_seed==0)
25934 mp->random_seed = (mp->internal[mp_time] / unity)+mp->internal[mp_day];
25935 mp_init_randoms(mp, mp->random_seed);
25936 @<Initialize the print |selector|...@>;
25937 if ( loc<limit ) if ( mp->buffer[loc]!='\\' )
25938 mp_start_input(mp); /* \&{input} assumed */
25941 @ @<Run inimpost commands@>=
25943 mp_get_strings_started(mp);
25944 mp_init_tab(mp); /* initialize the tables */
25945 mp_init_prim(mp); /* call |primitive| for each primitive */
25946 mp->init_str_use=mp->str_ptr; mp->init_pool_ptr=mp->pool_ptr;
25947 mp->max_str_ptr=mp->str_ptr; mp->max_pool_ptr=mp->pool_ptr;
25948 mp_fix_date_and_time(mp);
25952 @* \[47] Debugging.
25953 Once \MP\ is working, you should be able to diagnose most errors with
25954 the \.{show} commands and other diagnostic features. But for the initial
25955 stages of debugging, and for the revelation of really deep mysteries, you
25956 can compile \MP\ with a few more aids. An additional routine called |debug_help|
25957 will also come into play when you type `\.D' after an error message;
25958 |debug_help| also occurs just before a fatal error causes \MP\ to succumb.
25960 @^system dependencies@>
25962 The interface to |debug_help| is primitive, but it is good enough when used
25963 with a debugger that allows you to set breakpoints and to read
25964 variables and change their values. After getting the prompt `\.{debug \#}', you
25965 type either a negative number (this exits |debug_help|), or zero (this
25966 goes to a location where you can set a breakpoint, thereby entering into
25967 dialog with the debugger), or a positive number |m| followed by
25968 an argument |n|. The meaning of |m| and |n| will be clear from the
25969 program below. (If |m=13|, there is an additional argument, |l|.)
25972 @<Last-minute...@>=
25973 void mp_debug_help (MP mp) { /* routine to display various things */
25980 mp_print_nl(mp, "debug # (-1 to exit):"); update_terminal;
25983 aline = (mp->read_ascii_file)(mp,mp->term_in, &len);
25984 if (len) { sscanf(aline,"%i",&m); xfree(aline); }
25988 aline = (mp->read_ascii_file)(mp,mp->term_in, &len);
25989 if (len) { sscanf(aline,"%i",&n); xfree(aline); }
25991 @<Numbered cases for |debug_help|@>;
25992 default: mp_print(mp, "?"); break;
25997 @ @<Numbered cases...@>=
25998 case 1: mp_print_word(mp, mp->mem[n]); /* display |mem[n]| in all forms */
26000 case 2: mp_print_int(mp, info(n));
26002 case 3: mp_print_int(mp, link(n));
26004 case 4: mp_print_int(mp, eq_type(n)); mp_print_char(mp, ':'); mp_print_int(mp, equiv(n));
26006 case 5: mp_print_variable_name(mp, n);
26008 case 6: mp_print_int(mp, mp->internal[n]);
26010 case 7: mp_do_show_dependencies(mp);
26012 case 9: mp_show_token_list(mp, n,null,100000,0);
26014 case 10: mp_print_str(mp, n);
26016 case 11: mp_check_mem(mp, n>0); /* check wellformedness; print new busy locations if |n>0| */
26018 case 12: mp_search_mem(mp, n); /* look for pointers to |n| */
26022 aline = (mp->read_ascii_file)(mp,mp->term_in, &len);
26023 if (len) { sscanf(aline,"%i",&l); xfree(aline); }
26024 mp_print_cmd_mod(mp, n,l);
26026 case 14: for (k=0;k<=n;k++) mp_print_str(mp, mp->buffer[k]);
26028 case 15: mp->panicking=! mp->panicking;
26032 @ Saving the filename template
26034 @<Save the filename template@>=
26036 if ( mp->filename_template!=0 ) delete_str_ref(mp->filename_template);
26037 if ( length(mp->cur_exp)==0 ) mp->filename_template=0;
26039 mp->filename_template=mp->cur_exp; add_str_ref(mp->filename_template);
26043 @* \[48] System-dependent changes.
26044 This section should be replaced, if necessary, by any special
26045 modification of the program
26046 that are necessary to make \MP\ work at a particular installation.
26047 It is usually best to design your change file so that all changes to
26048 previous sections preserve the section numbering; then everybody's version
26049 will be consistent with the published program. More extensive changes,
26050 which introduce new sections, can be inserted here; then only the index
26051 itself will get a new section number.
26052 @^system dependencies@>
26055 Here is where you can find all uses of each identifier in the program,
26056 with underlined entries pointing to where the identifier was defined.
26057 If the identifier is only one letter long, however, you get to see only
26058 the underlined entries. {\sl All references are to section numbers instead of
26061 This index also lists error messages and other aspects of the program
26062 that you might want to look up some day. For example, the entry
26063 for ``system dependencies'' lists all sections that should receive
26064 special attention from people who are installing \MP\ in a new
26065 operating environment. A list of various things that can't happen appears
26066 under ``this can't happen''.
26067 Approximately 25 sections are listed under ``inner loop''; these account
26068 for more than 60\pct! of \MP's running time, exclusive of input and output.