1% $Id: mp.w 2055 2015-01-22 15:39:12Z luigi $ 2% 3% This file is part of MetaPost; 4% the MetaPost program is in the public domain. 5% See the <Show version...> code in mpost.w for more info. 6 7% Here is TeX material that gets inserted after \input webmac 8\def\hang{\hangindent 3em\noindent\ignorespaces} 9\def\textindent#1{\hangindent2.5em\noindent\hbox to2.5em{\hss#1 }\ignorespaces} 10\def\ps{PostScript} 11\def\psqrt#1{\sqrt{\mathstrut#1}} 12\def\k{_{k+1}} 13\def\pct!{{\char`\%}} % percent sign in ordinary text 14\font\tenlogo=logo10 % font used for the METAFONT logo 15\font\logos=logosl10 16\def\MF{{\tenlogo META}\-{\tenlogo FONT}} 17\def\MP{{\tenlogo META}\-{\tenlogo POST}} 18\def\<#1>{$\langle#1\rangle$} 19\def\section{\mathhexbox278} 20\let\swap=\leftrightarrow 21\def\round{\mathop{\rm round}\nolimits} 22\mathchardef\vbv="026A % synonym for `\|' 23\def\vb{\relax\ifmmode\vbv\else$\vbv$\fi} 24 25\def\(#1){} % this is used to make section names sort themselves better 26\def\9#1{} % this is used for sort keys in the index via @@:sort key}{entry@@> 27\def\title{MetaPost} 28\pdfoutput=1 29\pageno=3 30 31@* Introduction. 32 33This is \MP\ by John Hobby, a graphics-language processor based on D. E. Knuth's \MF. 34 35Much of the original Pascal version of this program was copied with 36permission from MF.web Version 1.9. It interprets a language very 37similar to D.E. Knuth's METAFONT, but with changes designed to make it 38more suitable for PostScript output. 39 40The main purpose of the following program is to explain the algorithms of \MP\ 41as clearly as possible. However, the program has been written so that it 42can be tuned to run efficiently in a wide variety of operating environments 43by making comparatively few changes. Such flexibility is possible because 44the documentation that follows is written in the \.{WEB} language, which is 45at a higher level than C. 46 47A large piece of software like \MP\ has inherent complexity that cannot 48be reduced below a certain level of difficulty, although each individual 49part is fairly simple by itself. The \.{WEB} language is intended to make 50the algorithms as readable as possible, by reflecting the way the 51individual program pieces fit together and by providing the 52cross-references that connect different parts. Detailed comments about 53what is going on, and about why things were done in certain ways, have 54been liberally sprinkled throughout the program. These comments explain 55features 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 58@.WEB@> 59@:METAFONTbook}{\sl The {\logos METAFONT\/}book@> 60{\sl A User's Manual for MetaPost}, Computing Science Technical Report 162, 61AT\AM T Bell Laboratories. 62 63@ The present implementation is a preliminary version, but the possibilities 64for new features are limited by the desire to remain as nearly compatible 65with \MF\ as possible. 66 67On the other hand, the \.{WEB} description can be extended without changing 68the core of the program, and it has been designed so that such 69extensions are not extremely difficult to make. 70The |banner| string defined here should be changed whenever \MP\ 71undergoes any modifications, so that it will be clear which version of 72\MP\ might be the guilty party when a problem arises. 73@^extensions to \MP@> 74@^system dependencies@> 75 76@d default_banner "This is MetaPost, Version 1.999" /* printed when \MP\ starts */ 77@d true 1 78@d false 0 79 80@<Metapost version header@>= 81#define metapost_version "1.999" 82 83@ The external library header for \MP\ is |mplib.h|. It contains a 84few typedefs and the header defintions for the externally used 85fuctions. 86 87The most important of the typedefs is the definition of the structure 88|MP_options|, that acts as a small, configurable front-end to the fairly 89large |MP_instance| structure. 90 91@(mplib.h@>= 92#ifndef MPLIB_H 93#define MPLIB_H 1 94#include <stdlib.h> 95#ifndef HAVE_BOOLEAN 96typedef int boolean; 97#endif 98@<Metapost version header@> 99typedef struct MP_instance *MP; 100@<Exported types@> 101typedef struct MP_options { 102 @<Option variables@> 103} MP_options; 104@<Exported function headers@> 105@<MPlib header stuff@> 106#endif 107 108@ The internal header file is much longer: it not only lists the complete 109|MP_instance|, but also a lot of functions that have to be available to 110the \ps\ backend, that is defined in a separate \.{WEB} file. 111 112The variables from |MP_options| are included inside the |MP_instance| 113wholesale. 114 115@(mpmp.h@>= 116#ifndef MPMP_H 117#define MPMP_H 1 118#include "avl.h" 119#include "mplib.h" 120#include <setjmp.h> 121typedef struct psout_data_struct *psout_data; 122typedef struct svgout_data_struct *svgout_data; 123typedef struct pngout_data_struct *pngout_data; 124#ifndef HAVE_BOOLEAN 125typedef int boolean; 126#endif 127#ifndef INTEGER_TYPE 128typedef int integer; 129#endif 130@<Declare helpers@>; 131@<Enumeration types@>; 132@<Types in the outer block@>; 133@<Constants in the outer block@>; 134typedef struct MP_instance { 135 @<Option variables@> 136 @<Global variables@> 137} MP_instance; 138@<Internal library declarations@> 139@<MPlib internal header stuff@> 140#endif 141 142@ @c 143#define KPATHSEA_DEBUG_H 1 144#include <w2c/config.h> 145#include <stdio.h> 146#include <stdlib.h> 147#include <string.h> 148#include <stdarg.h> 149#include <assert.h> 150#include <math.h> 151#ifdef HAVE_UNISTD_H 152# include <unistd.h> /* for access */ 153#endif 154#include <time.h> /* for struct tm \& co */ 155#include <zlib.h> /* for |ZLIB_VERSION|, zlibVersion() */ 156#include <png.h> /* for |PNG_LIBPNG_VER_STRING|, |png_libpng_ver| */ 157#include <pixman.h> /* for |PIXMAN_VERSION_STRING|, |pixman_version_string()| */ 158#include <cairo.h> /* for |CAIRO_VERSION_STRING|, |cairo_version_string()| */ 159#include <gmp.h> /* for |gmp_version| */ 160#include <mpfr.h> /* for |MPFR_VERSION_STRING|, |mpfr_get_version()| */ 161#include "mplib.h" 162#include "mplibps.h" /* external header */ 163#include "mplibsvg.h" /* external header */ 164#include "mplibpng.h" /* external header */ 165#include "mpmp.h" /* internal header */ 166#include "mppsout.h" /* internal header */ 167#include "mpsvgout.h" /* internal header */ 168#include "mppngout.h" /* internal header */ 169#include "mpmath.h" /* internal header */ 170#include "mpmathdouble.h" /* internal header */ 171#include "mpmathdecimal.h" /* internal header */ 172#include "mpmathbinary.h" /* internal header */ 173#include "mpstrings.h" /* internal header */ 174extern font_number mp_read_font_info (MP mp, char *fname); /* tfmin.w */ 175@h @<Declarations@>; 176@<Basic printing procedures@>; 177@<Error handling procedures@> 178 179@ Some debugging support for development. The trick with the variadic macros 180probably only works in gcc, as this preprocessor feature was not formalized 181until the c99 standard (and that is too new for us). Lets' hope that at least 182most compilers understand the non-debug version. 183@^system dependencies@> 184 185@<MPlib internal header stuff@>= 186#define DEBUG 0 187#if DEBUG 188#define debug_number(A) printf("%d: %s=%.32f (%d)\n", __LINE__, #A, number_to_double(A), number_to_scaled(A)) 189#else 190#define debug_number(A) 191#endif 192#if DEBUG>1 193void do_debug_printf(MP mp, const char *prefix, const char *fmt, ...); 194# define debug_printf(a1,a2,a3) do_debug_printf(mp, "", a1,a2,a3) 195# define FUNCTION_TRACE1(a1) do_debug_printf(mp, "FTRACE: ", a1) 196# define FUNCTION_TRACE2(a1,a2) do_debug_printf(mp, "FTRACE: ", a1,a2) 197# define FUNCTION_TRACE3(a1,a2,a3) do_debug_printf(mp, "FTRACE: ", a1,a2,a3) 198# define FUNCTION_TRACE3X(a1,a2,a3) (void)mp 199# define FUNCTION_TRACE4(a1,a2,a3,a4) do_debug_printf(mp, "FTRACE: ", a1,a2,a3,a4) 200#else 201# define debug_printf(a1,a2,a3) 202# define FUNCTION_TRACE1(a1) (void)mp 203# define FUNCTION_TRACE2(a1,a2) (void)mp 204# define FUNCTION_TRACE3(a1,a2,a3) (void)mp 205# define FUNCTION_TRACE3X(a1,a2,a3) (void)mp 206# define FUNCTION_TRACE4(a1,a2,a3,a4) (void)mp 207#endif 208 209@ This function occasionally crashes (if something is written after the 210log file is already closed), but that is not so important while debugging. 211 212@c 213#if DEBUG 214void do_debug_printf(MP mp, const char *prefix, const char *fmt, ...) ; 215void do_debug_printf(MP mp, const char *prefix, const char *fmt, ...) { 216 va_list ap; 217#if 0 218 va_start (ap, fmt); 219 if (mp->log_file && !ferror((FILE *)mp->log_file)) { 220 fputs(prefix, mp->log_file); 221 vfprintf(mp->log_file, fmt, ap); 222 } 223 va_end(ap); 224#endif 225 va_start (ap, fmt); 226#if 0 227 if (mp->term_out && !ferror((FILE *)mp->term_out)) { 228#else 229 if (false) { 230#endif 231 fputs(prefix, mp->term_out); 232 vfprintf(mp->term_out, fmt, ap); 233 } else { 234 fputs(prefix, stdout); 235 vfprintf(stdout, fmt, ap); 236 } 237 va_end(ap); 238} 239#endif 240 241@ Here are the functions that set up the \MP\ instance. 242 243@<Declarations@>= 244MP_options *mp_options (void); 245MP mp_initialize (MP_options * opt); 246 247@ @c 248MP_options *mp_options (void) { 249 MP_options *opt; 250 size_t l = sizeof (MP_options); 251 opt = malloc (l); 252 if (opt != NULL) { 253 memset (opt, 0, l); 254 } 255 return opt; 256} 257 258 259@ @<Internal library declarations@>= 260@<Declare subroutines for parsing file names@> 261 262 263@ The whole instance structure is initialized with zeroes, 264this greatly reduces the number of statements needed in 265the |Allocate or initialize variables| block. 266 267@d set_callback_option(A) do { mp->A = mp_##A; 268 if (opt->A!=NULL) mp->A = opt->A; 269} while (0) 270 271@c 272static MP mp_do_new (jmp_buf * buf) { 273 MP mp = malloc (sizeof (MP_instance)); 274 if (mp == NULL) { 275 xfree (buf); 276 return NULL; 277 } 278 memset (mp, 0, sizeof (MP_instance)); 279 mp->jump_buf = buf; 280 return mp; 281} 282 283 284@ @c 285static void mp_free (MP mp) { 286 int k; /* loop variable */ 287 @<Dealloc variables@>; 288 if (mp->noninteractive) { 289 @<Finish non-interactive use@>; 290 } 291 xfree (mp->jump_buf); 292 @<Free table entries@>; 293 free_math(); 294 xfree (mp); 295} 296 297 298@ @c 299static void mp_do_initialize (MP mp) { 300 @<Local variables for initialization@>; 301 @<Set initial values of key variables@>; 302} 303 304@ For the retargetable math library, we need to have a pointer, at least. 305 306@<Global variables@>= 307void *math; 308 309@ @<Exported types@>= 310typedef enum { 311 mp_nan_type = 0, 312 mp_scaled_type, 313 mp_fraction_type, 314 mp_angle_type, 315 mp_double_type, 316 mp_binary_type, 317 mp_decimal_type 318} mp_number_type; 319typedef union { 320 void *num; 321 double dval; 322 int val; 323} mp_number_store; 324typedef struct mp_number_data { 325 mp_number_store data; 326 mp_number_type type; 327} mp_number_data; 328typedef struct mp_number_data mp_number; 329#define is_number(A) ((A).type != mp_nan_type) 330 331typedef void (*convert_func) (mp_number *r); 332typedef void (*m_log_func) (MP mp, mp_number *r, mp_number a); 333typedef void (*m_exp_func) (MP mp, mp_number *r, mp_number a); 334typedef void (*m_norm_rand_func) (MP mp, mp_number *ret); 335typedef void (*pyth_add_func) (MP mp, mp_number *r, mp_number a, mp_number b); 336typedef void (*pyth_sub_func) (MP mp, mp_number *r, mp_number a, mp_number b); 337typedef void (*n_arg_func) (MP mp, mp_number *r, mp_number a, mp_number b); 338typedef void (*velocity_func) (MP mp, mp_number *r, mp_number a, mp_number b, mp_number c, mp_number d, mp_number e); 339typedef void (*ab_vs_cd_func) (MP mp, mp_number *r, mp_number a, mp_number b, mp_number c, mp_number d); 340typedef void (*crossing_point_func) (MP mp, mp_number *r, mp_number a, mp_number b, mp_number c); 341typedef void (*number_from_int_func) (mp_number *A, int B); 342typedef void (*number_from_boolean_func) (mp_number *A, int B); 343typedef void (*number_from_scaled_func) (mp_number *A, int B); 344typedef void (*number_from_double_func) (mp_number *A, double B); 345typedef void (*number_from_addition_func) (mp_number *A, mp_number B, mp_number C); 346typedef void (*number_from_substraction_func) (mp_number *A, mp_number B, mp_number C); 347typedef void (*number_from_div_func) (mp_number *A, mp_number B, mp_number C); 348typedef void (*number_from_mul_func) (mp_number *A, mp_number B, mp_number C); 349typedef void (*number_from_int_div_func) (mp_number *A, mp_number B, int C); 350typedef void (*number_from_int_mul_func) (mp_number *A, mp_number B, int C); 351typedef void (*number_from_oftheway_func) (MP mp, mp_number *A, mp_number t, mp_number B, mp_number C); 352typedef void (*number_negate_func) (mp_number *A); 353typedef void (*number_add_func) (mp_number *A, mp_number B); 354typedef void (*number_substract_func) (mp_number *A, mp_number B); 355typedef void (*number_modulo_func) (mp_number *A, mp_number B); 356typedef void (*number_half_func) (mp_number *A); 357typedef void (*number_halfp_func) (mp_number *A); 358typedef void (*number_double_func) (mp_number *A); 359typedef void (*number_abs_func) (mp_number *A); 360typedef void (*number_clone_func) (mp_number *A, mp_number B); 361typedef void (*number_swap_func) (mp_number *A, mp_number *B); 362typedef void (*number_add_scaled_func) (mp_number *A, int b); 363typedef void (*number_multiply_int_func) (mp_number *A, int b); 364typedef void (*number_divide_int_func) (mp_number *A, int b); 365typedef int (*number_to_int_func) (mp_number A); 366typedef int (*number_to_boolean_func) (mp_number A); 367typedef int (*number_to_scaled_func) (mp_number A); 368typedef int (*number_round_func) (mp_number A); 369typedef void (*number_floor_func) (mp_number *A); 370typedef double (*number_to_double_func) (mp_number A); 371typedef int (*number_odd_func) (mp_number A); 372typedef int (*number_equal_func) (mp_number A, mp_number B); 373typedef int (*number_less_func) (mp_number A, mp_number B); 374typedef int (*number_greater_func) (mp_number A, mp_number B); 375typedef int (*number_nonequalabs_func) (mp_number A, mp_number B); 376typedef void (*make_scaled_func) (MP mp, mp_number *ret, mp_number A, mp_number B); 377typedef void (*make_fraction_func) (MP mp, mp_number *ret, mp_number A, mp_number B); 378typedef void (*take_fraction_func) (MP mp, mp_number *ret, mp_number A, mp_number B); 379typedef void (*take_scaled_func) (MP mp, mp_number *ret, mp_number A, mp_number B); 380typedef void (*sin_cos_func) (MP mp, mp_number A, mp_number *S, mp_number *C); 381typedef void (*slow_add_func) (MP mp, mp_number *A, mp_number S, mp_number C); 382typedef void (*sqrt_func) (MP mp, mp_number *ret, mp_number A); 383typedef void (*init_randoms_func) (MP mp, int seed); 384typedef void (*new_number_func) (MP mp, mp_number *A, mp_number_type t); 385typedef void (*free_number_func) (MP mp, mp_number *n); 386typedef void (*fraction_to_round_scaled_func) (mp_number *n); 387typedef void (*print_func) (MP mp, mp_number A); 388typedef char * (*tostring_func) (MP mp, mp_number A); 389typedef void (*scan_func) (MP mp, int A); 390typedef void (*mp_free_func) (MP mp); 391typedef void (*set_precision_func) (MP mp); 392 393typedef struct math_data { 394 mp_number precision_default; 395 mp_number precision_max; 396 mp_number precision_min; 397 mp_number epsilon_t; 398 mp_number inf_t; 399 mp_number one_third_inf_t; 400 mp_number zero_t; 401 mp_number unity_t; 402 mp_number two_t; 403 mp_number three_t; 404 mp_number half_unit_t; 405 mp_number three_quarter_unit_t; 406 mp_number fraction_one_t; 407 mp_number fraction_half_t; 408 mp_number fraction_three_t; 409 mp_number fraction_four_t; 410 mp_number one_eighty_deg_t; 411 mp_number three_sixty_deg_t; 412 mp_number one_k; 413 mp_number sqrt_8_e_k; 414 mp_number twelve_ln_2_k; 415 mp_number coef_bound_k; 416 mp_number coef_bound_minus_1; 417 mp_number twelvebits_3; 418 mp_number arc_tol_k; 419 mp_number twentysixbits_sqrt2_t; 420 mp_number twentyeightbits_d_t; 421 mp_number twentysevenbits_sqrt2_d_t; 422 mp_number fraction_threshold_t; 423 mp_number half_fraction_threshold_t; 424 mp_number scaled_threshold_t; 425 mp_number half_scaled_threshold_t; 426 mp_number near_zero_angle_t; 427 mp_number p_over_v_threshold_t; 428 mp_number equation_threshold_t; 429 mp_number tfm_warn_threshold_t; 430 mp_number warning_limit_t; 431 new_number_func allocate; 432 free_number_func free; 433 number_from_int_func from_int; 434 number_from_boolean_func from_boolean; 435 number_from_scaled_func from_scaled; 436 number_from_double_func from_double; 437 number_from_addition_func from_addition; 438 number_from_substraction_func from_substraction; 439 number_from_div_func from_div; 440 number_from_mul_func from_mul; 441 number_from_int_div_func from_int_div; 442 number_from_int_mul_func from_int_mul; 443 number_from_oftheway_func from_oftheway; 444 number_negate_func negate; 445 number_add_func add; 446 number_substract_func substract; 447 number_half_func half; 448 number_modulo_func modulo; 449 number_halfp_func halfp; 450 number_double_func do_double; 451 number_abs_func abs; 452 number_clone_func clone; 453 number_swap_func swap; 454 number_add_scaled_func add_scaled; 455 number_multiply_int_func multiply_int; 456 number_divide_int_func divide_int; 457 number_to_int_func to_int; 458 number_to_boolean_func to_boolean; 459 number_to_scaled_func to_scaled; 460 number_to_double_func to_double; 461 number_odd_func odd; 462 number_equal_func equal; 463 number_less_func less; 464 number_greater_func greater; 465 number_nonequalabs_func nonequalabs; 466 number_round_func round_unscaled; 467 number_floor_func floor_scaled; 468 make_scaled_func make_scaled; 469 make_fraction_func make_fraction; 470 take_fraction_func take_fraction; 471 take_scaled_func take_scaled; 472 velocity_func velocity; 473 ab_vs_cd_func ab_vs_cd; 474 crossing_point_func crossing_point; 475 n_arg_func n_arg; 476 m_log_func m_log; 477 m_exp_func m_exp; 478 m_norm_rand_func m_norm_rand; 479 pyth_add_func pyth_add; 480 pyth_sub_func pyth_sub; 481 fraction_to_round_scaled_func fraction_to_round_scaled; 482 convert_func fraction_to_scaled; 483 convert_func scaled_to_fraction; 484 convert_func scaled_to_angle; 485 convert_func angle_to_scaled; 486 init_randoms_func init_randoms; 487 sin_cos_func sin_cos; 488 sqrt_func sqrt; 489 slow_add_func slow_add; 490 print_func print; 491 tostring_func tostring; 492 scan_func scan_numeric; 493 scan_func scan_fractional; 494 mp_free_func free_math; 495 set_precision_func set_precision; 496} math_data; 497 498 499 500@ This procedure gets things started properly. 501@c 502MP mp_initialize (MP_options * opt) { 503 MP mp; 504 jmp_buf *buf = malloc (sizeof (jmp_buf)); 505 if (buf == NULL || setjmp (*buf) != 0) 506 return NULL; 507 mp = mp_do_new (buf); 508 if (mp == NULL) 509 return NULL; 510 mp->userdata = opt->userdata; 511 mp->noninteractive = opt->noninteractive; 512 mp->extensions = opt->extensions; 513 set_callback_option (find_file); 514 set_callback_option (open_file); 515 set_callback_option (read_ascii_file); 516 set_callback_option (read_binary_file); 517 set_callback_option (close_file); 518 set_callback_option (eof_file); 519 set_callback_option (flush_file); 520 set_callback_option (write_ascii_file); 521 set_callback_option (write_binary_file); 522 set_callback_option (shipout_backend); 523 set_callback_option (run_script); 524 set_callback_option (make_text); 525 if (opt->banner && *(opt->banner)) { 526 mp->banner = xstrdup (opt->banner); 527 } else { 528 mp->banner = xstrdup (default_banner); 529 } 530 if (opt->command_line && *(opt->command_line)) 531 mp->command_line = xstrdup (opt->command_line); 532 if (mp->noninteractive) { 533 @<Prepare function pointers for non-interactive use@>; 534 } 535 /* open the terminal for output */ 536 t_open_out(); 537#if DEBUG 538 setvbuf(stdout, (char *) NULL, _IONBF, 0); 539 setvbuf(mp->term_out, (char *) NULL, _IONBF, 0); 540#endif 541 if (opt->math_mode == mp_math_scaled_mode) { 542 mp->math = mp_initialize_scaled_math(mp); 543 } else if (opt->math_mode == mp_math_decimal_mode) { 544 mp->math = mp_initialize_decimal_math(mp); 545 } else if (opt->math_mode == mp_math_binary_mode) { 546 mp->math = mp_initialize_binary_math(mp); 547 } else { 548 mp->math = mp_initialize_double_math(mp); 549 } 550 @<Find and load preload file, if required@>; 551 @<Allocate or initialize variables@>; 552 mp_reallocate_paths (mp, 1000); 553 mp_reallocate_fonts (mp, 8); 554 mp->history = mp_fatal_error_stop; /* in case we quit during initialization */ 555 @<Check the ``constant'' values...@>; 556 if (mp->bad > 0) { 557 char ss[256]; 558 mp_snprintf (ss, 256, "Ouch---my internal constants have been clobbered!\n" 559 "---case %i", (int) mp->bad); 560 mp_fputs ((char *) ss, mp->err_out); 561@.Ouch...clobbered@>; 562 return mp; 563 } 564 mp_do_initialize (mp); /* erase preloaded mem */ 565 mp_init_tab (mp); /* initialize the tables */ 566 if (opt->math_mode == mp_math_scaled_mode) { 567 set_internal_string (mp_number_system, mp_intern (mp, "scaled")); 568 } else if (opt->math_mode == mp_math_decimal_mode) { 569 set_internal_string (mp_number_system, mp_intern (mp, "decimal")); 570 } else if (opt->math_mode == mp_math_binary_mode) { 571 set_internal_string (mp_number_system, mp_intern (mp, "binary")); 572 } else { 573 set_internal_string (mp_number_system, mp_intern (mp, "double")); 574 } 575 mp_init_prim (mp); /* call |primitive| for each primitive */ 576 mp_fix_date_and_time (mp); 577 if (!mp->noninteractive) { 578 @<Initialize the output routines@>; 579 @<Get the first line of input and prepare to start@>; 580 @<Initializations after first line is read@>; 581 @<Fix up |mp->internal[mp_job_name]|@>; 582 } else { 583 mp->history = mp_spotless; 584 } 585 set_precision(); 586 return mp; 587} 588 589 590@ @<Initializations after first line is read@>= 591mp_open_log_file (mp); 592mp_set_job_id (mp); 593mp_init_map_file (mp, mp->troff_mode); 594mp->history = mp_spotless; /* ready to go! */ 595if (mp->troff_mode) { 596 number_clone (internal_value (mp_gtroffmode), unity_t); 597 number_clone (internal_value (mp_prologues), unity_t); 598} 599if (mp->start_sym != NULL) { /* insert the `\&{everyjob}' symbol */ 600 set_cur_sym (mp->start_sym); 601 mp_back_input (mp); 602} 603 604@ @<Exported function headers@>= 605extern MP_options *mp_options (void); 606extern MP mp_initialize (MP_options * opt); 607extern int mp_status (MP mp); 608extern void *mp_userdata (MP mp); 609 610@ @c 611int mp_status (MP mp) { 612 return mp->history; 613} 614 615 616@ @c 617void *mp_userdata (MP mp) { 618 return mp->userdata; 619} 620 621 622@ The overall \MP\ program begins with the heading just shown, after which 623comes a bunch of procedure declarations and function declarations. 624Finally we will get to the main program, which begins with the 625comment `|start_here|'. If you want to skip down to the 626main program now, you can look up `|start_here|' in the index. 627But the author suggests that the best way to understand this program 628is to follow pretty much the order of \MP's components as they appear in the 629\.{WEB} description you are now reading, since the present ordering is 630intended to combine the advantages of the ``bottom up'' and ``top down'' 631approaches to the problem of understanding a somewhat complicated system. 632 633@ Some of the code below is intended to be used only when diagnosing the 634strange behavior that sometimes occurs when \MP\ is being installed or 635when system wizards are fooling around with \MP\ without quite knowing 636what they are doing. Such code will not normally be compiled; it is 637delimited by the preprocessor test `|#ifdef DEBUG .. #endif|'. 638 639@ The following parameters can be changed at compile time to extend or 640reduce \MP's capacity. 641@^system dependencies@> 642 643@<Constants...@>= 644#define bistack_size 1500 /* size of stack for bisection algorithms; 645 should probably be left at this value */ 646 647@ Like the preceding parameters, the following quantities can be changed 648to extend or reduce \MP's capacity. 649 650@ @<Glob...@>= 651int pool_size; /* maximum number of characters in strings, including all 652 error messages and help texts, and the names of all identifiers */ 653int max_in_open; /* maximum number of input files and error insertions that 654 can be going on simultaneously */ 655int param_size; /* maximum number of simultaneous macro parameters */ 656 657@ @<Option variables@>= 658int error_line; /* width of context lines on terminal error messages */ 659int half_error_line; /* width of first lines of contexts in terminal 660 error messages; should be between 30 and |error_line-15| */ 661int halt_on_error; /* do we quit at the first error? */ 662int max_print_line; /* width of longest text lines output; should be at least 60 */ 663void *userdata; /* this allows the calling application to setup local */ 664char *banner; /* the banner that is printed to the screen and log */ 665int ini_version; 666 667@ @<Dealloc variables@>= 668xfree (mp->banner); 669 670@ 671@d set_lower_limited_value(a,b,c) do { a=c; if (b>c) a=b; } while (0) 672 673@<Allocate or ...@>= 674mp->param_size = 4; 675mp->max_in_open = 0; 676mp->pool_size = 10000; 677set_lower_limited_value (mp->error_line, opt->error_line, 79); 678set_lower_limited_value (mp->half_error_line, opt->half_error_line, 50); 679if (mp->half_error_line > mp->error_line - 15) 680 mp->half_error_line = mp->error_line - 15; 681mp->max_print_line = 100; 682set_lower_limited_value (mp->max_print_line, opt->max_print_line, 79); 683mp->halt_on_error = (opt->halt_on_error ? true : false); 684mp->ini_version = (opt->ini_version ? true : false); 685 686@ In case somebody has inadvertently made bad settings of the ``constants,'' 687\MP\ checks them using a global variable called |bad|. 688 689This is the second of many sections of \MP\ where global variables are 690defined. 691 692@<Glob...@>= 693integer bad; /* is some ``constant'' wrong? */ 694 695@ Later on we will say `|if ( int_packets+17*int_increment>bistack_size )mp->bad=19;|', 696or something similar. 697 698In case you are wondering about the non-consequtive values of |bad|: most 699of the things that used to be WEB constants are now runtime variables 700with checking at assignment time. 701 702@<Check the ``constant'' values for consistency@>= 703mp->bad = 0; 704 705@ Here are some macros for common programming idioms. 706 707@d incr(A) (A)=(A)+1 /* increase a variable by unity */ 708@d decr(A) (A)=(A)-1 /* decrease a variable by unity */ 709@d negate(A) (A)=-(A) /* change the sign of a variable */ 710@d double(A) (A)=(A)+(A) 711@d odd(A) (abs(A)%2==1) 712 713@* The character set. 714In order to make \MP\ readily portable to a wide variety of 715computers, all of its input text is converted to an internal eight-bit 716code that includes standard ASCII, the ``American Standard Code for 717Information Interchange.'' This conversion is done immediately when each 718character is read in. Conversely, characters are converted from ASCII to 719the user's external representation just before they are output to a 720text file. 721@^ASCII code@> 722 723Such an internal code is relevant to users of \MP\ only with respect to 724the \&{char} and \&{ASCII} operations, and the comparison of strings. 725 726@ Characters of text that have been converted to \MP's internal form 727are said to be of type |ASCII_code|, which is a subrange of the integers. 728 729@<Types...@>= 730typedef unsigned char ASCII_code; /* eight-bit numbers */ 731 732@ The present specification of \MP\ has been written under the assumption 733that the character set contains at least the letters and symbols associated 734with ASCII codes 040 through 0176; all of these characters are now 735available on most computer terminals. 736 737@<Types...@>= 738typedef unsigned char text_char; /* the data type of characters in text files */ 739 740@ @<Local variables for init...@>= 741integer i; 742 743@ The \MP\ processor converts between ASCII code and 744the user's external character set by means of arrays |xord| and |xchr| 745that are analogous to Pascal's |ord| and |chr| functions. 746 747@<MPlib internal header stuff@>= 748#define xchr(A) mp->xchr[(A)] 749#define xord(A) mp->xord[(A)] 750 751@ @<Glob...@>= 752ASCII_code xord[256]; /* specifies conversion of input characters */ 753text_char xchr[256]; /* specifies conversion of output characters */ 754 755@ The core system assumes all 8-bit is acceptable. If it is not, 756a change file has to alter the below section. 757@^system dependencies@> 758 759Additionally, people with extended character sets can 760assign codes arbitrarily, giving an |xchr| equivalent to whatever 761characters the users of \MP\ are allowed to have in their input files. 762Appropriate changes to \MP's |char_class| table should then be made. 763(Unlike \TeX, each installation of \MP\ has a fixed assignment of category 764codes, called the |char_class|.) Such changes make portability of programs 765more difficult, so they should be introduced cautiously if at all. 766@^character set dependencies@> 767@^system dependencies@> 768 769@<Set initial ...@>= 770for (i = 0; i <= 0377; i++) { 771 xchr (i) = (text_char) i; 772} 773 774 775@ The following system-independent code makes the |xord| array contain a 776suitable inverse to the information in |xchr|. Note that if |xchr[i]=xchr[j]| 777where |i<j<0177|, the value of |xord[xchr[i]]| will turn out to be 778|j| or more; hence, standard ASCII code numbers will be used instead of 779codes below 040 in case there is a coincidence. 780 781@<Set initial ...@>= 782for (i = 0; i <= 255; i++) { 783 xord (xchr (i)) = 0177; 784} 785for (i = 0200; i <= 0377; i++) { 786 xord (xchr (i)) = (ASCII_code) i; 787} 788for (i = 0; i <= 0176; i++) { 789 xord (xchr (i)) = (ASCII_code) i; 790} 791 792 793@* Input and output. 794The bane of portability is the fact that different operating systems treat 795input and output quite differently, perhaps because computer scientists 796have not given sufficient attention to this problem. People have felt somehow 797that input and output are not part of ``real'' programming. Well, it is true 798that some kinds of programming are more fun than others. With existing 799input/output conventions being so diverse and so messy, the only sources of 800joy in such parts of the code are the rare occasions when one can find a 801way to make the program a little less bad than it might have been. We have 802two choices, either to attack I/O now and get it over with, or to postpone 803I/O until near the end. Neither prospect is very attractive, so let's 804get it over with. 805 806The basic operations we need to do are (1)~inputting and outputting of 807text, to or from a file or the user's terminal; (2)~inputting and 808outputting of eight-bit bytes, to or from a file; (3)~instructing the 809operating system to initiate (``open'') or to terminate (``close'') input or 810output from a specified file; (4)~testing whether the end of an input 811file has been reached; (5)~display of bits on the user's screen. 812The bit-display operation will be discussed in a later section; we shall 813deal here only with more traditional kinds of I/O. 814 815@ Finding files happens in a slightly roundabout fashion: the \MP\ 816instance object contains a field that holds a function pointer that finds a 817file, and returns its name, or NULL. For this, it receives three 818parameters: the non-qualified name |fname|, the intended |fopen| 819operation type |fmode|, and the type of the file |ftype|. 820 821The file types that are passed on in |ftype| can be used to 822differentiate file searches if a library like kpathsea is used, 823the fopen mode is passed along for the same reason. 824 825@<Types...@>= 826typedef unsigned char eight_bits; /* unsigned one-byte quantity */ 827 828@ @<Exported types@>= 829enum mp_filetype { 830 mp_filetype_terminal = 0, /* the terminal */ 831 mp_filetype_error, /* the terminal */ 832 mp_filetype_program, /* \MP\ language input */ 833 mp_filetype_log, /* the log file */ 834 mp_filetype_postscript, /* the postscript output */ 835 mp_filetype_bitmap, /* the bitmap output file */ 836 mp_filetype_memfile, /* memory dumps, obsolete */ 837 mp_filetype_metrics, /* TeX font metric files */ 838 mp_filetype_fontmap, /* PostScript font mapping files */ 839 mp_filetype_font, /* PostScript type1 font programs */ 840 mp_filetype_encoding, /* PostScript font encoding files */ 841 mp_filetype_text /* first text file for readfrom and writeto primitives */ 842}; 843typedef char *(*mp_file_finder) (MP, const char *, const char *, int); 844typedef char *(*mp_script_runner) (MP, const char *); 845typedef char *(*mp_text_maker) (MP, const char *, int mode); 846typedef void *(*mp_file_opener) (MP, const char *, const char *, int); 847typedef char *(*mp_file_reader) (MP, void *, size_t *); 848typedef void (*mp_binfile_reader) (MP, void *, void **, size_t *); 849typedef void (*mp_file_closer) (MP, void *); 850typedef int (*mp_file_eoftest) (MP, void *); 851typedef void (*mp_file_flush) (MP, void *); 852typedef void (*mp_file_writer) (MP, void *, const char *); 853typedef void (*mp_binfile_writer) (MP, void *, void *, size_t); 854 855@ @<Option variables@>= 856mp_file_finder find_file; 857mp_file_opener open_file; 858mp_script_runner run_script; 859mp_text_maker make_text; 860mp_file_reader read_ascii_file; 861mp_binfile_reader read_binary_file; 862mp_file_closer close_file; 863mp_file_eoftest eof_file; 864mp_file_flush flush_file; 865mp_file_writer write_ascii_file; 866mp_binfile_writer write_binary_file; 867 868@ The default function for finding files is |mp_find_file|. It is 869pretty stupid: it will only find files in the current directory. 870 871@c 872static char *mp_find_file (MP mp, const char *fname, const char *fmode, 873 int ftype) { 874 (void) mp; 875 if (fmode[0] != 'r' || (!access (fname, R_OK)) || ftype) { 876 return mp_strdup (fname); 877 } 878 return NULL; 879} 880 881@ @c 882static char *mp_run_script (MP mp, const char *str) { 883 (void) mp; 884 return mp_strdup (str); 885} 886 887@ @c 888static char *mp_make_text (MP mp, const char *str, int mode) { 889 (void) mp; 890 return mp_strdup (str); 891} 892 893@ Because |mp_find_file| is used so early, it has to be in the helpers 894section. 895 896@<Declarations@>= 897static char *mp_find_file (MP mp, const char *fname, const char *fmode, 898 int ftype); 899static void *mp_open_file (MP mp, const char *fname, const char *fmode, 900 int ftype); 901static char *mp_read_ascii_file (MP mp, void *f, size_t * size); 902static void mp_read_binary_file (MP mp, void *f, void **d, size_t * size); 903static void mp_close_file (MP mp, void *f); 904static int mp_eof_file (MP mp, void *f); 905static void mp_flush_file (MP mp, void *f); 906static void mp_write_ascii_file (MP mp, void *f, const char *s); 907static void mp_write_binary_file (MP mp, void *f, void *s, size_t t); 908static char *mp_run_script (MP mp, const char *str); 909static char *mp_make_text (MP mp, const char *str, int mode); 910 911@ The function to open files can now be very short. 912 913@c 914void *mp_open_file (MP mp, const char *fname, const char *fmode, int ftype) { 915 char realmode[3]; 916 (void) mp; 917 realmode[0] = *fmode; 918 realmode[1] = 'b'; 919 realmode[2] = 0; 920 if (ftype == mp_filetype_terminal) { 921 return (fmode[0] == 'r' ? stdin : stdout); 922 } else if (ftype == mp_filetype_error) { 923 return stderr; 924 } else if (fname != NULL && (fmode[0] != 'r' || (!access (fname, R_OK)))) { 925 return (void *) fopen (fname, realmode); 926 } 927 return NULL; 928} 929 930 931@ (Almost) all file names pass through |name_of_file|. 932 933@<Glob...@>= 934char *name_of_file; /* the name of a system file */ 935 936@ If this parameter is true, the terminal and log will report the found 937file names for input files instead of the requested ones. 938It is off by default because it creates an extra filename lookup. 939 940@<Option variables@>= 941int print_found_names; /* configuration parameter */ 942 943@ @<Allocate or initialize ...@>= 944mp->print_found_names = (opt->print_found_names > 0 ? true : false); 945 946@ The |file_line_error_style| parameter makes \MP\ use a more 947standard compiler error message format instead of the Knuthian 948exclamation mark. It needs the actual version of the current input 949file name, that will be saved by |open_in| in the |long_name|. 950 951TODO: currently these long strings cause memory leaks, because they cannot 952be safely freed as they may appear in the |input_stack| multiple times. 953In fact, the current implementation is just a quick hack in response 954to a bug report for metapost 1.205. 955 956@d long_name mp->cur_input.long_name_field /* long name of the current file */ 957 958@<Option variables@>= 959int file_line_error_style; /* configuration parameter */ 960 961@ @<Allocate or initialize ...@>= 962mp->file_line_error_style = (opt->file_line_error_style > 0 ? true : false); 963 964@ \MP's file-opening procedures return |false| if no file identified by 965|name_of_file| could be opened. 966 967The |do_open_file| function takes care of the |print_found_names| parameter. 968 969@c 970static boolean mp_do_open_file (MP mp, void **f, int ftype, const char *mode) { 971 if (mp->print_found_names || mp->file_line_error_style) { 972 char *s = (mp->find_file)(mp,mp->name_of_file,mode,ftype); 973 if (s!=NULL) { 974 *f = (mp->open_file)(mp,mp->name_of_file,mode, ftype); 975 if (mp->print_found_names) { 976 xfree(mp->name_of_file); 977 mp->name_of_file = xstrdup(s); 978 } 979 if ((*mode == 'r') && (ftype == mp_filetype_program)) { 980 long_name = xstrdup(s); 981 } 982 xfree(s); 983 } else { 984 *f = NULL; 985 } 986 } else { 987 *f = (mp->open_file)(mp,mp->name_of_file,mode, ftype); 988 } 989 return (*f ? true : false); 990} 991@# 992static boolean mp_open_in (MP mp, void **f, int ftype) { 993 /* open a file for input */ 994 return mp_do_open_file (mp, f, ftype, "r"); 995} 996@# 997static boolean mp_open_out (MP mp, void **f, int ftype) { 998 /* open a file for output */ 999 return mp_do_open_file (mp, f, ftype, "w"); 1000} 1001 1002 1003@ @c 1004static char *mp_read_ascii_file (MP mp, void *ff, size_t * size) { 1005 int c; 1006 size_t len = 0, lim = 128; 1007 char *s = NULL; 1008 FILE *f = (FILE *) ff; 1009 *size = 0; 1010 (void) mp; /* for -Wunused */ 1011 if (f == NULL) 1012 return NULL; 1013 c = fgetc (f); 1014 if (c == EOF) 1015 return NULL; 1016 s = malloc (lim); 1017 if (s == NULL) 1018 return NULL; 1019 while (c != EOF && c != '\n' && c != '\r') { 1020 if ((len + 1) == lim) { 1021 s = realloc (s, (lim + (lim >> 2))); 1022 if (s == NULL) 1023 return NULL; 1024 lim += (lim >> 2); 1025 } 1026 s[len++] = (char) c; 1027 c = fgetc (f); 1028 } 1029 if (c == '\r') { 1030 c = fgetc (f); 1031 if (c != EOF && c != '\n') 1032 ungetc (c, f); 1033 } 1034 s[len] = 0; 1035 *size = len; 1036 return s; 1037} 1038 1039 1040@ @c 1041void mp_write_ascii_file (MP mp, void *f, const char *s) { 1042 (void) mp; 1043 if (f != NULL) { 1044 fputs (s, (FILE *) f); 1045 } 1046} 1047 1048 1049@ @c 1050void mp_read_binary_file (MP mp, void *f, void **data, size_t * size) { 1051 size_t len = 0; 1052 (void) mp; 1053 if (f != NULL) 1054 len = fread (*data, 1, *size, (FILE *) f); 1055 *size = len; 1056} 1057 1058 1059@ @c 1060void mp_write_binary_file (MP mp, void *f, void *s, size_t size) { 1061 (void) mp; 1062 if (f != NULL) 1063 (void) fwrite (s, size, 1, (FILE *) f); 1064} 1065 1066 1067@ @c 1068void mp_close_file (MP mp, void *f) { 1069 (void) mp; 1070 if (f != NULL) 1071 fclose ((FILE *) f); 1072} 1073 1074 1075@ @c 1076int mp_eof_file (MP mp, void *f) { 1077 (void) mp; 1078 if (f != NULL) 1079 return feof ((FILE *) f); 1080 else 1081 return 1; 1082} 1083 1084 1085@ @c 1086void mp_flush_file (MP mp, void *f) { 1087 (void) mp; 1088 if (f != NULL) 1089 fflush ((FILE *) f); 1090} 1091 1092 1093@ Input from text files is read one line at a time, using a routine called 1094|input_ln|. This function is defined in terms of global variables called 1095|buffer|, |first|, and |last| that will be described in detail later; for 1096now, it suffices for us to know that |buffer| is an array of |ASCII_code| 1097values, and that |first| and |last| are indices into this array 1098representing the beginning and ending of a line of text. 1099 1100@<Glob...@>= 1101size_t buf_size; /* maximum number of characters simultaneously present in 1102 current lines of open files */ 1103ASCII_code *buffer; /* lines of characters being read */ 1104size_t first; /* the first unused position in |buffer| */ 1105size_t last; /* end of the line just input to |buffer| */ 1106size_t max_buf_stack; /* largest index used in |buffer| */ 1107 1108@ @<Allocate or initialize ...@>= 1109mp->buf_size = 200; 1110mp->buffer = xmalloc ((mp->buf_size + 1), sizeof (ASCII_code)); 1111 1112@ @<Dealloc variables@>= 1113xfree (mp->buffer); 1114 1115@ @c 1116static void mp_reallocate_buffer (MP mp, size_t l) { 1117 ASCII_code *buffer; 1118 if (l > max_halfword) { 1119 mp_confusion (mp, "buffer size"); /* can't happen (I hope) */ 1120 } 1121 buffer = xmalloc ((l + 1), sizeof (ASCII_code)); 1122 (void) memcpy (buffer, mp->buffer, (mp->buf_size + 1)); 1123 xfree (mp->buffer); 1124 mp->buffer = buffer; 1125 mp->buf_size = l; 1126} 1127 1128 1129@ The |input_ln| function brings the next line of input from the specified 1130field into available positions of the buffer array and returns the value 1131|true|, unless the file has already been entirely read, in which case it 1132returns |false| and sets |last:=first|. In general, the |ASCII_code| 1133numbers that represent the next line of the file are input into 1134|buffer[first]|, |buffer[first+1]|, \dots, |buffer[last-1]|; and the 1135global variable |last| is set equal to |first| plus the length of the 1136line. Trailing blanks are removed from the line; thus, either |last=first| 1137(in which case the line was entirely blank) or |buffer[last-1]<>" "|. 1138@^inner loop@> 1139 1140The variable |max_buf_stack|, which is used to keep track of how large 1141the |buf_size| parameter must be to accommodate the present job, is 1142also kept up to date by |input_ln|. 1143 1144@c 1145static boolean mp_input_ln (MP mp, void *f) { 1146 /* inputs the next line or returns |false| */ 1147 char *s; 1148 size_t size = 0; 1149 mp->last = mp->first; /* cf.\ Matthew 19\thinspace:\thinspace30 */ 1150 s = (mp->read_ascii_file) (mp, f, &size); 1151 if (s == NULL) 1152 return false; 1153 if (size > 0) { 1154 mp->last = mp->first + size; 1155 if (mp->last >= mp->max_buf_stack) { 1156 mp->max_buf_stack = mp->last + 1; 1157 while (mp->max_buf_stack > mp->buf_size) { 1158 mp_reallocate_buffer (mp, (mp->buf_size + (mp->buf_size >> 2))); 1159 } 1160 } 1161 (void) memcpy ((mp->buffer + mp->first), s, size); 1162 } 1163 free (s); 1164 return true; 1165} 1166 1167 1168@ The user's terminal acts essentially like other files of text, except 1169that it is used both for input and for output. When the terminal is 1170considered an input file, the file variable is called |term_in|, and when it 1171is considered an output file the file variable is |term_out|. 1172@^system dependencies@> 1173 1174@<Glob...@>= 1175void *term_in; /* the terminal as an input file */ 1176void *term_out; /* the terminal as an output file */ 1177void *err_out; /* the terminal as an output file */ 1178 1179@ Here is how to open the terminal files. In the default configuration, 1180nothing happens except that the command line (if there is one) is copied 1181to the input buffer. The variable |command_line| will be filled by the 1182|main| procedure. 1183 1184@d t_open_out() do {/* open the terminal for text output */ 1185 mp->term_out = (mp->open_file)(mp,"terminal", "w", mp_filetype_terminal); 1186 mp->err_out = (mp->open_file)(mp,"error", "w", mp_filetype_error); 1187} while (0) 1188@d t_open_in() do { /* open the terminal for text input */ 1189 mp->term_in = (mp->open_file)(mp,"terminal", "r", mp_filetype_terminal); 1190 if (mp->command_line!=NULL) { 1191 mp->last = strlen(mp->command_line); 1192 (void)memcpy((void *)mp->buffer,(void *)mp->command_line,mp->last); 1193 xfree(mp->command_line); 1194 } else { 1195 mp->last = 0; 1196 } 1197} while (0) 1198 1199@<Option variables@>= 1200char *command_line; 1201 1202@ Sometimes it is necessary to synchronize the input/output mixture that 1203happens on the user's terminal, and three system-dependent 1204procedures are used for this 1205purpose. The first of these, |update_terminal|, is called when we want 1206to make sure that everything we have output to the terminal so far has 1207actually left the computer's internal buffers and been sent. 1208The second, |clear_terminal|, is called when we wish to cancel any 1209input that the user may have typed ahead (since we are about to 1210issue an unexpected error message). The third, |wake_up_terminal|, 1211is supposed to revive the terminal if the user has disabled it by 1212some instruction to the operating system. The following macros show how 1213these operations can be specified: 1214@^system dependencies@> 1215 1216@<MPlib internal header stuff@>= 1217#define update_terminal() (mp->flush_file)(mp,mp->term_out) /* empty the terminal output buffer */ 1218#define clear_terminal() /* clear the terminal input buffer */ 1219#define wake_up_terminal() (mp->flush_file)(mp,mp->term_out) 1220 /* cancel the user's cancellation of output */ 1221 1222@ We need a special routine to read the first line of \MP\ input from 1223the user's terminal. This line is different because it is read before we 1224have opened the transcript file; there is sort of a ``chicken and 1225egg'' problem here. If the user types `\.{input cmr10}' on the first 1226line, or if some macro invoked by that line does such an \.{input}, 1227the transcript file will be named `\.{cmr10.log}'; but if no \.{input} 1228commands are performed during the first line of terminal input, the transcript 1229file will acquire its default name `\.{mpout.log}'. (The transcript file 1230will not contain error messages generated by the first line before the 1231first \.{input} command.) 1232 1233The first line is even more special. It's nice to let the user start 1234running a \MP\ job by typing a command line like `\.{MP cmr10}'; in 1235such a case, \MP\ will operate as if the first line of input were 1236`\.{cmr10}', i.e., the first line will consist of the remainder of the 1237command line, after the part that invoked \MP. 1238 1239@ Different systems have different ways to get started. But regardless of 1240what conventions are adopted, the routine that initializes the terminal 1241should satisfy the following specifications: 1242 1243\yskip\textindent{1)}It should open file |term_in| for input from the 1244 terminal. (The file |term_out| will already be open for output to the 1245 terminal.) 1246 1247\textindent{2)}If the user has given a command line, this line should be 1248 considered the first line of terminal input. Otherwise the 1249 user should be prompted with `\.{**}', and the first line of input 1250 should be whatever is typed in response. 1251 1252\textindent{3)}The first line of input, which might or might not be a 1253 command line, should appear in locations |first| to |last-1| of the 1254 |buffer| array. 1255 1256\textindent{4)}The global variable |loc| should be set so that the 1257 character to be read next by \MP\ is in |buffer[loc]|. This 1258 character should not be blank, and we should have |loc<last|. 1259 1260\yskip\noindent(It may be necessary to prompt the user several times 1261before a non-blank line comes in. The prompt is `\.{**}' instead of the 1262later `\.*' because the meaning is slightly different: `\.{input}' need 1263not be typed immediately after~`\.{**}'.) 1264 1265@d loc mp->cur_input.loc_field /* location of first unread character in |buffer| */ 1266 1267@c 1268boolean mp_init_terminal (MP mp) { /* gets the terminal input started */ 1269 t_open_in(); 1270 if (mp->last != 0) { 1271 loc = 0; 1272 mp->first = 0; 1273 return true; 1274 } 1275 while (1) { 1276 if (!mp->noninteractive) { 1277 wake_up_terminal(); 1278 mp_fputs ("**", mp->term_out); 1279@.**@>; 1280 update_terminal(); 1281 } 1282 if (!mp_input_ln (mp, mp->term_in)) { /* this shouldn't happen */ 1283 mp_fputs ("\n! End of file on the terminal... why?", mp->term_out); 1284@.End of file on the terminal@>; 1285 return false; 1286 } 1287 loc = (halfword) mp->first; 1288 while ((loc < (int) mp->last) && (mp->buffer[loc] == ' ')) 1289 incr (loc); 1290 if (loc < (int) mp->last) { 1291 return true; /* return unless the line was all blank */ 1292 } 1293 if (!mp->noninteractive) { 1294 mp_fputs ("Please type the name of your input file.\n", mp->term_out); 1295 } 1296 } 1297} 1298 1299 1300@ @<Declarations@>= 1301static boolean mp_init_terminal (MP mp); 1302 1303@* Globals for strings. 1304 1305@ Symbolic token names and diagnostic messages are variable-length strings 1306of eight-bit characters. Many strings \MP\ uses are simply literals 1307in the compiled source, like the error messages and the names of the 1308internal parameters. Other strings are used or defined from the \MP\ input 1309language, and these have to be interned. 1310 1311\MP\ uses strings more extensively than \MF\ does, but the necessary 1312operations can still be handled with a fairly simple data structure. 1313The avl tree |strings| contains all of the known string structures. 1314 1315Each structure contains an |unsigned char| pointer containing the eight-bit 1316data, a |size_t| that holds the length of that data, and an |int| that 1317indicates how often this string is referenced (this will be explained below). 1318Such strings are referred to by structure pointers called |mp_string|. 1319 1320Besides the avl tree, there is a set of three variables called |cur_string|, 1321|cur_length| and |cur_string_size| that are used for strings while they are 1322being built. 1323 1324@<Exported types...@>= 1325typedef struct { 1326 unsigned char *str; /* the string value */ 1327 size_t len; /* its length */ 1328 int refs; /* number of references */ 1329} mp_lstring; 1330typedef mp_lstring *mp_string; /* for pointers to string values */ 1331 1332@ The string handling functions are in \.{mpstrings.w}, but strings 1333need a bunch of globals and those are defined here in the main file. 1334 1335@<Glob...@>= 1336avl_tree strings; /* string avl tree */ 1337unsigned char *cur_string; /* current string buffer */ 1338size_t cur_length; /* current index in that buffer */ 1339size_t cur_string_size; /* malloced size of |cur_string| */ 1340 1341@ @<Allocate or initialize ...@>= 1342mp_initialize_strings(mp); 1343 1344@ @<Dealloc variables@>= 1345mp_dealloc_strings(mp); 1346 1347@ The next four variables are for keeping track of string memory usage. 1348 1349@<Glob...@>= 1350integer pool_in_use; /* total number of string bytes actually in use */ 1351integer max_pl_used; /* maximum |pool_in_use| so far */ 1352integer strs_in_use; /* total number of strings actually in use */ 1353integer max_strs_used; /* maximum |strs_in_use| so far */ 1354 1355 1356@* On-line and off-line printing. 1357Messages that are sent to a user's terminal and to the transcript-log file 1358are produced by several `|print|' procedures. These procedures will 1359direct their output to a variety of places, based on the setting of 1360the global variable |selector|, which has the following possible 1361values: 1362 1363\yskip 1364\hang |term_and_log|, the normal setting, prints on the terminal and on the 1365 transcript file. 1366 1367\hang |log_only|, prints only on the transcript file. 1368 1369\hang |term_only|, prints only on the terminal. 1370 1371\hang |no_print|, doesn't print at all. This is used only in rare cases 1372 before the transcript file is open. 1373 1374\hang |pseudo|, puts output into a cyclic buffer that is used 1375 by the |show_context| routine; when we get to that routine we shall discuss 1376 the reasoning behind this curious mode. 1377 1378\hang |new_string|, appends the output to the current string in the 1379 string pool. 1380 1381\hang |>=write_file| prints on one of the files used for the \&{write} 1382@:write_}{\&{write} primitive@> 1383 command. 1384 1385\yskip 1386\noindent The symbolic names `|term_and_log|', etc., have been assigned 1387numeric codes that satisfy the convenient relations |no_print+1=term_only|, 1388|no_print+2=log_only|, |term_only+2=log_only+1=term_and_log|. These 1389relations are not used when |selector| could be |pseudo|, or |new_string|. 1390We need not check for unprintable characters when |selector<pseudo|. 1391 1392Three additional global variables, |tally|, |term_offset| and |file_offset| 1393record the number of characters that have been printed 1394since they were most recently cleared to zero. We use |tally| to record 1395the length of (possibly very long) stretches of printing; |term_offset|, 1396and |file_offset|, on the other hand, keep track of how many 1397characters have appeared so far on the current line that has been output 1398to the terminal, the transcript file, or the \ps\ output file, respectively. 1399 1400@d new_string 0 /* printing is deflected to the string pool */ 1401@d pseudo 2 /* special |selector| setting for |show_context| */ 1402@d no_print 3 /* |selector| setting that makes data disappear */ 1403@d term_only 4 /* printing is destined for the terminal only */ 1404@d log_only 5 /* printing is destined for the transcript file only */ 1405@d term_and_log 6 /* normal |selector| setting */ 1406@d write_file 7 /* first write file selector */ 1407 1408@<Glob...@>= 1409void *log_file; /* transcript of \MP\ session */ 1410void *output_file; /* the generic font output goes here */ 1411unsigned int selector; /* where to print a message */ 1412integer tally; /* the number of characters recently printed */ 1413unsigned int term_offset; 1414 /* the number of characters on the current terminal line */ 1415unsigned int file_offset; 1416 /* the number of characters on the current file line */ 1417ASCII_code *trick_buf; /* circular buffer for pseudoprinting */ 1418integer trick_count; /* threshold for pseudoprinting, explained later */ 1419integer first_count; /* another variable for pseudoprinting */ 1420 1421@ The first 128 strings will contain 95 standard ASCII characters, and the 1422other 33 characters will be printed in three-symbol form like `\.{\^\^A}' 1423unless a system-dependent change is made here. Installations that have 1424an extended character set, where for example |xchr[032]=@t\.{'^^Z'}@>|, 1425would like string 032 to be printed as the single character 032 instead 1426of the three characters 0136, 0136, 0132 (\.{\^\^Z}). On the other hand, 1427even people with an extended character set will want to represent string 1428015 by \.{\^\^M}, since 015 is ASCII's ``carriage return'' code; the idea is 1429to produce visible strings instead of tabs or line-feeds or carriage-returns 1430or bell-rings or characters that are treated anomalously in text files. 1431 1432The boolean expression defined here should be |true| unless \MP\ internal 1433code number~|k| corresponds to a non-troublesome visible symbol in the 1434local character set. 1435If character |k| cannot be printed, and |k<0200|, then character |k+0100| or 1436|k-0100| must be printable; moreover, ASCII codes |[060..071, 0141..0146]| 1437must be printable. 1438@^character set dependencies@> 1439@^system dependencies@> 1440 1441@<Character |k| cannot be printed@>= 1442(k < ' ') || (k == 127) 1443 1444@ @<Allocate or initialize ...@>= 1445mp->trick_buf = xmalloc ((mp->error_line + 1), sizeof (ASCII_code)); 1446 1447@ @<Dealloc variables@>= 1448xfree (mp->trick_buf); 1449 1450@ @<Initialize the output routines@>= 1451mp->selector = term_only; 1452mp->tally = 0; 1453mp->term_offset = 0; 1454mp->file_offset = 0; 1455 1456@ Macro abbreviations for output to the terminal and to the log file are 1457defined here for convenience. Some systems need special conventions 1458for terminal output, and it is possible to adhere to those conventions 1459by changing |wterm|, |wterm_ln|, and |wterm_cr| here. 1460@^system dependencies@> 1461 1462@<MPlib internal header stuff@>= 1463#define mp_fputs(b,f) (mp->write_ascii_file)(mp,f,b) 1464#define wterm(A) mp_fputs((A), mp->term_out) 1465#define wterm_chr(A) { unsigned char ss[2]; ss[0]=(A); ss[1]='\0'; wterm((char *)ss);} 1466#define wterm_cr mp_fputs("\n", mp->term_out) 1467#define wterm_ln(A) { wterm_cr; mp_fputs((A), mp->term_out); } 1468#define wlog(A) mp_fputs((A), mp->log_file) 1469#define wlog_chr(A) { unsigned char ss[2]; ss[0]=(A); ss[1]='\0'; wlog((char *)ss);} 1470#define wlog_cr mp_fputs("\n", mp->log_file) 1471#define wlog_ln(A) { wlog_cr; mp_fputs((A), mp->log_file); } 1472 1473 1474@ To end a line of text output, we call |print_ln|. Cases |0..max_write_files| 1475use an array |wr_file| that will be declared later. 1476 1477@d mp_print_text(A) mp_print_str(mp,text((A))) 1478 1479@<Internal library ...@>= 1480void mp_print (MP mp, const char *s); 1481void mp_printf (MP mp, const char *ss, ...); 1482void mp_print_ln (MP mp); 1483void mp_print_char (MP mp, ASCII_code k); 1484void mp_print_str (MP mp, mp_string s); 1485void mp_print_nl (MP mp, const char *s); 1486void mp_print_two (MP mp, mp_number x, mp_number y); 1487 1488@ @<Declarations@>= 1489static void mp_print_visible_char (MP mp, ASCII_code s); 1490 1491@ @<Basic print...@>= 1492void mp_print_ln (MP mp) { /* prints an end-of-line */ 1493 switch (mp->selector) { 1494 case term_and_log: 1495 wterm_cr; 1496 wlog_cr; 1497 mp->term_offset = 0; 1498 mp->file_offset = 0; 1499 break; 1500 case log_only: 1501 wlog_cr; 1502 mp->file_offset = 0; 1503 break; 1504 case term_only: 1505 wterm_cr; 1506 mp->term_offset = 0; 1507 break; 1508 case no_print: 1509 case pseudo: 1510 case new_string: 1511 break; 1512 default: 1513 mp_fputs ("\n", mp->wr_file[(mp->selector - write_file)]); 1514 } 1515} /* note that |tally| is not affected */ 1516 1517 1518@ The |print_visible_char| procedure sends one character to the desired 1519destination, using the |xchr| array to map it into an external character 1520compatible with |input_ln|. (It assumes that it is always called with 1521a visible ASCII character.) All printing comes through |print_ln| or 1522|print_char|, which ultimately calls |print_visible_char|, hence these 1523routines are the ones that limit lines to at most |max_print_line| characters. 1524But we must make an exception for the \ps\ output file since it is not safe 1525to cut up lines arbitrarily in \ps. 1526 1527@<Basic printing...@>= 1528static void mp_print_visible_char (MP mp, ASCII_code s) { /* prints a single character */ 1529 switch (mp->selector) { 1530 case term_and_log: 1531 wterm_chr (xchr (s)); 1532 wlog_chr (xchr (s)); 1533 incr (mp->term_offset); 1534 incr (mp->file_offset); 1535 if (mp->term_offset == (unsigned) mp->max_print_line) { 1536 wterm_cr; 1537 mp->term_offset = 0; 1538 }; 1539 if (mp->file_offset == (unsigned) mp->max_print_line) { 1540 wlog_cr; 1541 mp->file_offset = 0; 1542 }; 1543 break; 1544 case log_only: 1545 wlog_chr (xchr (s)); 1546 incr (mp->file_offset); 1547 if (mp->file_offset == (unsigned) mp->max_print_line) 1548 mp_print_ln (mp); 1549 break; 1550 case term_only: 1551 wterm_chr (xchr (s)); 1552 incr (mp->term_offset); 1553 if (mp->term_offset == (unsigned) mp->max_print_line) 1554 mp_print_ln (mp); 1555 break; 1556 case no_print: 1557 break; 1558 case pseudo: 1559 if (mp->tally < mp->trick_count) 1560 mp->trick_buf[mp->tally % mp->error_line] = s; 1561 break; 1562 case new_string: 1563 append_char (s); 1564 break; 1565 default: 1566 { 1567 text_char ss[2] = {0,0}; 1568 ss[0] = xchr (s); 1569 mp_fputs ((char *) ss, mp->wr_file[(mp->selector - write_file)]); 1570 } 1571 } 1572 incr (mp->tally); 1573} 1574 1575 1576@ The |print_char| procedure sends one character to the desired destination. 1577File names and string expressions might contain |ASCII_code| values that 1578can't be printed using |print_visible_char|. These characters will be 1579printed in three- or four-symbol form like `\.{\^\^A}' or `\.{\^\^e4}'. 1580(This procedure assumes that it is safe to bypass all checks for unprintable 1581characters when |selector| is in the range |0..max_write_files-1|. 1582The user might want to write unprintable characters. 1583 1584@<Basic printing...@>= 1585void mp_print_char (MP mp, ASCII_code k) { /* prints a single character */ 1586 if (mp->selector < pseudo || mp->selector >= write_file) { 1587 mp_print_visible_char (mp, k); 1588 } else if (@<Character |k| cannot be printed@>) { 1589 mp_print (mp, "^^"); 1590 if (k < 0100) { 1591 mp_print_visible_char (mp, (ASCII_code) (k + 0100)); 1592 } else if (k < 0200) { 1593 mp_print_visible_char (mp, (ASCII_code) (k - 0100)); 1594 } else { 1595 int l; /* small index or counter */ 1596 l = (k / 16); 1597 mp_print_visible_char (mp, xord (l < 10 ? l + '0' : l - 10 + 'a')); 1598 l = (k % 16); 1599 mp_print_visible_char (mp, xord (l < 10 ? l + '0' : l - 10 + 'a')); 1600 } 1601 } else { 1602 mp_print_visible_char (mp, k); 1603 } 1604} 1605 1606 1607@ An entire string is output by calling |print|. Note that if we are outputting 1608the single standard ASCII character \.c, we could call |print("c")|, since 1609|"c"=99| is the number of a single-character string, as explained above. But 1610|print_char("c")| is quicker, so \MP\ goes directly to the |print_char| 1611routine when it knows that this is safe. (The present implementation 1612assumes that it is always safe to print a visible ASCII character.) 1613@^system dependencies@> 1614 1615@<Basic print...@>= 1616static void mp_do_print (MP mp, const char *ss, size_t len) { /* prints string |s| */ 1617 if (len==0) 1618 return; 1619 if (mp->selector == new_string) { 1620 str_room (len); 1621 memcpy((mp->cur_string+mp->cur_length), ss, len); 1622 mp->cur_length += len; 1623 } else { 1624 size_t j = 0; 1625 while (j < len) { 1626 /* this was |xord((int)ss[j])| but that doesnt work */ 1627 mp_print_char (mp, (ASCII_code) ss[j]); 1628 j++; 1629 } 1630 } 1631} 1632 1633 1634@ 1635@<Basic print...@>= 1636void mp_print (MP mp, const char *ss) { 1637 assert (ss != NULL); 1638 mp_do_print (mp, ss, strlen (ss)); 1639} 1640void mp_printf (MP mp, const char *ss, ...) { 1641 va_list ap; 1642 char pval[256]; 1643 assert (ss != NULL); 1644 va_start(ap, ss); 1645 vsnprintf (pval, 256, ss, ap); 1646 mp_do_print (mp, pval, strlen (pval)); 1647 va_end(ap); 1648} 1649 1650void mp_print_str (MP mp, mp_string s) { 1651 assert (s != NULL); 1652 mp_do_print (mp, (const char *) s->str, s->len); 1653} 1654 1655 1656@ Here is the very first thing that \MP\ prints: a headline that identifies 1657the version number and base name. The |term_offset| variable is temporarily 1658incorrect, but the discrepancy is not serious since we assume that the banner 1659and mem identifier together will occupy at most |max_print_line| 1660character positions. 1661 1662@<Initialize the output...@>= 1663wterm (mp->banner); 1664mp_print_ln (mp); 1665update_terminal(); 1666 1667@ The procedure |print_nl| is like |print|, but it makes sure that the 1668string appears at the beginning of a new line. 1669 1670@<Basic print...@>= 1671void mp_print_nl (MP mp, const char *s) { /* prints string |s| at beginning of line */ 1672 switch (mp->selector) { 1673 case term_and_log: 1674 if ((mp->term_offset > 0) || (mp->file_offset > 0)) 1675 mp_print_ln (mp); 1676 break; 1677 case log_only: 1678 if (mp->file_offset > 0) 1679 mp_print_ln (mp); 1680 break; 1681 case term_only: 1682 if (mp->term_offset > 0) 1683 mp_print_ln (mp); 1684 break; 1685 case no_print: 1686 case pseudo: 1687 case new_string: 1688 break; 1689 } /* there are no other cases */ 1690 mp_print (mp, s); 1691} 1692 1693 1694@ The following procedure, which prints out the decimal representation of a 1695given integer |n|, assumes that all integers fit nicely into a |int|. 1696@^system dependencies@> 1697 1698@<Basic print...@>= 1699void mp_print_int (MP mp, integer n) { /* prints an integer in decimal form */ 1700 char s[12]; 1701 mp_snprintf (s, 12, "%d", (int) n); 1702 mp_print (mp, s); 1703} 1704void mp_print_pointer (MP mp, void *n) { /* prints an pointer in hexadecimal form */ 1705 char s[12]; 1706 mp_snprintf (s, 12, "%p", n); 1707 mp_print (mp, s); 1708} 1709 1710@ @<Internal library ...@>= 1711void mp_print_int (MP mp, integer n); 1712void mp_print_pointer (MP mp, void *n); 1713 1714@ \MP\ also makes use of a trivial procedure to print two digits. The 1715following subroutine is usually called with a parameter in the range |0<=n<=99|. 1716 1717@c 1718static void mp_print_dd (MP mp, integer n) { /* prints two least significant digits */ 1719 n = abs (n) % 100; 1720 mp_print_char (mp, xord ('0' + (n / 10))); 1721 mp_print_char (mp, xord ('0' + (n % 10))); 1722} 1723 1724 1725@ @<Declarations@>= 1726static void mp_print_dd (MP mp, integer n); 1727 1728@ Here is a procedure that asks the user to type a line of input, 1729assuming that the |selector| setting is either |term_only| or |term_and_log|. 1730The input is placed into locations |first| through |last-1| of the 1731|buffer| array, and echoed on the transcript file if appropriate. 1732 1733This procedure is never called when |interaction<mp_scroll_mode|. 1734 1735@d prompt_input(A) do { 1736 if (!mp->noninteractive) { 1737 wake_up_terminal(); 1738 mp_print(mp, (A)); 1739 } 1740 mp_term_input(mp); 1741 } while (0) /* prints a string and gets a line of input */ 1742 1743@c 1744void mp_term_input (MP mp) { /* gets a line from the terminal */ 1745 size_t k; /* index into |buffer| */ 1746 if (mp->noninteractive) { 1747 if (!mp_input_ln (mp, mp->term_in)) 1748 longjmp (*(mp->jump_buf), 1); /* chunk finished */ 1749 mp->buffer[mp->last] = xord ('%'); 1750 } else { 1751 update_terminal(); /* Now the user sees the prompt for sure */ 1752 if (!mp_input_ln (mp, mp->term_in)) { 1753 mp_fatal_error (mp, "End of file on the terminal!"); 1754@.End of file on the terminal@> 1755 } 1756 mp->term_offset = 0; /* the user's line ended with \<\rm return> */ 1757 decr (mp->selector); /* prepare to echo the input */ 1758 if (mp->last != mp->first) { 1759 for (k = mp->first; k < mp->last; k++) { 1760 mp_print_char (mp, mp->buffer[k]); 1761 } 1762 } 1763 mp_print_ln (mp); 1764 mp->buffer[mp->last] = xord ('%'); 1765 incr (mp->selector); /* restore previous status */ 1766 } 1767} 1768 1769 1770@* Reporting errors. 1771 1772The |print_err| procedure supplies a `\.!' before the official message, 1773and makes sure that the terminal is awake if a stop is going to occur. 1774The |error| procedure supplies a `\..' after the official message, then it 1775shows the location of the error; and if |interaction=error_stop_mode|, 1776it also enters into a dialog with the user, during which time the help 1777message may be printed. 1778@^system dependencies@> 1779 1780@ The global variable |interaction| has four settings, representing increasing 1781amounts of user interaction: 1782 1783@<Exported types@>= 1784enum mp_interaction_mode { 1785 mp_unspecified_mode = 0, /* extra value for command-line switch */ 1786 mp_batch_mode, /* omits all stops and omits terminal output */ 1787 mp_nonstop_mode, /* omits all stops */ 1788 mp_scroll_mode, /* omits error stops */ 1789 mp_error_stop_mode /* stops at every opportunity to interact */ 1790}; 1791 1792@ @<Option variables@>= 1793int interaction; /* current level of interaction */ 1794int noninteractive; /* do we have a terminal? */ 1795int extensions; 1796 1797@ Set it here so it can be overwritten by the commandline 1798 1799@<Allocate or initialize ...@>= 1800mp->interaction = opt->interaction; 1801if (mp->interaction == mp_unspecified_mode 1802 || mp->interaction > mp_error_stop_mode) 1803 mp->interaction = mp_error_stop_mode; 1804if (mp->interaction < mp_unspecified_mode) 1805 mp->interaction = mp_batch_mode; 1806 1807@ |print_err| is not merged in |error| because it is also used in |prompt_file_name|, 1808where |error| is not called at all. 1809 1810@<Declarations@>= 1811static void mp_print_err (MP mp, const char *A); 1812 1813@ @c 1814static void mp_print_err (MP mp, const char *A) { 1815 if (mp->interaction == mp_error_stop_mode) 1816 wake_up_terminal(); 1817 if (mp->file_line_error_style && file_state && !terminal_input) { 1818 mp_print_nl (mp, ""); 1819 if (long_name != NULL) { 1820 mp_print (mp, long_name); 1821 } else { 1822 mp_print (mp, mp_str (mp, name)); 1823 } 1824 mp_print (mp, ":"); 1825 mp_print_int (mp, line); 1826 mp_print (mp, ": "); 1827 } else { 1828 mp_print_nl (mp, "! "); 1829 } 1830 mp_print (mp, A); 1831@.!\relax@> 1832} 1833 1834 1835@ \MP\ is careful not to call |error| when the print |selector| setting 1836might be unusual. The only possible values of |selector| at the time of 1837error messages are 1838 1839\yskip\hang|no_print| (when |interaction=mp_batch_mode| 1840 and |log_file| not yet open); 1841 1842\hang|term_only| (when |interaction>mp_batch_mode| and |log_file| not yet open); 1843 1844\hang|log_only| (when |interaction=mp_batch_mode| and |log_file| is open); 1845 1846\hang|term_and_log| (when |interaction>mp_batch_mode| and |log_file| is open). 1847 1848@d initialize_print_selector() mp->selector = (mp->interaction == mp_batch_mode ? no_print : term_only); 1849 1850@ The global variable |history| records the worst level of error that 1851has been detected. It has four possible values: |spotless|, |warning_issued|, 1852|error_message_issued|, and |fatal_error_stop|. 1853 1854Another global variable, |error_count|, is increased by one when an 1855|error| occurs without an interactive dialog, and it is reset to zero at 1856the end of every statement. If |error_count| reaches 100, \MP\ decides 1857that there is no point in continuing further. 1858 1859@<Exported types@>= 1860enum mp_history_state { 1861 mp_spotless = 0, /* |history| value when nothing has been amiss yet */ 1862 mp_warning_issued, /* |history| value when |begin_diagnostic| has been called */ 1863 mp_error_message_issued, /* |history| value when |error| has been called */ 1864 mp_fatal_error_stop, /* |history| value when termination was premature */ 1865 mp_system_error_stop /* |history| value when termination was due to disaster */ 1866}; 1867 1868@ @<Glob...@>= 1869int history; /* has the source input been clean so far? */ 1870int error_count; /* the number of scrolled errors since the last statement ended */ 1871 1872@ The value of |history| is initially |fatal_error_stop|, but it will 1873be changed to |spotless| if \MP\ survives the initialization process. 1874 1875@ Since errors can be detected almost anywhere in \MP, we want to declare the 1876error procedures near the beginning of the program. But the error procedures 1877in turn use some other procedures, which need to be declared |forward| 1878before we get to |error| itself. 1879 1880It is possible for |error| to be called recursively if some error arises 1881when |get_next| is being used to delete a token, and/or if some fatal error 1882occurs while \MP\ is trying to fix a non-fatal one. But such recursion 1883@^recursion@> 1884is never more than two levels deep. 1885 1886@<Declarations@>= 1887static void mp_get_next (MP mp); 1888static void mp_term_input (MP mp); 1889static void mp_show_context (MP mp); 1890static void mp_begin_file_reading (MP mp); 1891static void mp_open_log_file (MP mp); 1892static void mp_clear_for_error_prompt (MP mp); 1893 1894@ @<Internal ...@>= 1895void mp_normalize_selector (MP mp); 1896 1897@ @<Glob...@>= 1898boolean use_err_help; /* should the |err_help| string be shown? */ 1899mp_string err_help; /* a string set up by \&{errhelp} */ 1900 1901@ @<Allocate or ...@>= 1902mp->use_err_help = false; 1903 1904@ The |jump_out| procedure just cuts across all active procedure levels and 1905goes to |end_of_MP|. This is the only nonlocal |goto| statement in the 1906whole program. It is used when there is no recovery from a particular error. 1907 1908The program uses a |jump_buf| to handle this, this is initialized at three 1909spots: the start of |mp_new|, the start of |mp_initialize|, and the start 1910of |mp_run|. Those are the only library enty points. 1911@^system dependencies@> 1912 1913@<Glob...@>= 1914jmp_buf *jump_buf; 1915 1916@ If the array of internals is still |NULL| when |jump_out| is called, a 1917crash occured during initialization, and it is not safe to run the normal 1918cleanup routine. 1919 1920@<Error hand...@>= 1921void mp_jump_out (MP mp) { 1922 if (mp->internal != NULL && mp->history < mp_system_error_stop) 1923 mp_close_files_and_terminate (mp); 1924 longjmp (*(mp->jump_buf), 1); 1925} 1926 1927@ @<Internal ...@>= 1928void mp_jump_out (MP mp); 1929 1930@ 1931 1932@<Error hand...@>= 1933void mp_warn (MP mp, const char *msg) { 1934 unsigned saved_selector = mp->selector; 1935 mp_normalize_selector (mp); 1936 mp_print_nl (mp, "Warning: "); 1937 mp_print (mp, msg); 1938 mp_print_ln (mp); 1939 mp->selector = saved_selector; 1940} 1941 1942@ Here now is the general |error| routine. 1943 1944The argument |deletions_allowed| is set |false| if the |get_next| 1945routine is active when |error| is called; this ensures that |get_next| 1946will never be called recursively. 1947@^recursion@> 1948 1949Individual lines of help are recorded in the array |help_line|, which 1950contains entries in positions |0..(help_ptr-1)|. They should be printed 1951in reverse order, i.e., with |help_line[0]| appearing last. 1952 1953@c 1954void mp_error (MP mp, const char *msg, const char **hlp, boolean deletions_allowed) { 1955 ASCII_code c; /* what the user types */ 1956 integer s1, s2; /* used to save global variables when deleting tokens */ 1957 mp_sym s3; /* likewise */ 1958 int i = 0; 1959 const char *help_line[6]; /* helps for the next |error| */ 1960 unsigned int help_ptr; /* the number of help lines present */ 1961 const char **cnt = NULL; 1962 mp_print_err(mp, msg); 1963 if (hlp) { 1964 cnt = hlp; 1965 while (*cnt) { 1966 i++; cnt++; 1967 } 1968 cnt = hlp; 1969 } 1970 help_ptr=i; 1971 while (i>0) { 1972 help_line[--i]= *cnt++; 1973 } 1974 if (mp->history < mp_error_message_issued) 1975 mp->history = mp_error_message_issued; 1976 mp_print_char (mp, xord ('.')); 1977 mp_show_context (mp); 1978 if (mp->halt_on_error) { 1979 mp->history = mp_fatal_error_stop; 1980 mp_jump_out (mp); 1981 } 1982 if ((!mp->noninteractive) && (mp->interaction == mp_error_stop_mode)) { 1983 @<Get user's advice and |return|@>; 1984 } 1985 incr (mp->error_count); 1986 if (mp->error_count == 100) { 1987 mp_print_nl (mp, "(That makes 100 errors; please try again.)"); 1988@.That makes 100 errors...@>; 1989 mp->history = mp_fatal_error_stop; 1990 mp_jump_out (mp); 1991 } 1992 @<Put help message on the transcript file@>; 1993} 1994 1995 1996@ @<Exported function ...@>= 1997extern void mp_error (MP mp, const char *msg, const char **hlp, boolean deletions_allowed); 1998extern void mp_warn (MP mp, const char *msg); 1999 2000 2001@ @<Get user's advice...@>= 2002while (true) { 2003CONTINUE: 2004 mp_clear_for_error_prompt (mp); 2005 prompt_input ("? "); 2006@.?\relax@>; 2007 if (mp->last == mp->first) 2008 return; 2009 c = mp->buffer[mp->first]; 2010 if (c >= 'a') 2011 c = (ASCII_code) (c + 'A' - 'a'); /* convert to uppercase */ 2012 @<Interpret code |c| and |return| if done@>; 2013} 2014 2015 2016@ It is desirable to provide an `\.E' option here that gives the user 2017an easy way to return from \MP\ to the system editor, with the offending 2018line ready to be edited. But such an extension requires some system 2019wizardry, so the present implementation simply types out the name of the 2020file that should be 2021edited and the relevant line number. 2022@^system dependencies@> 2023 2024@<Exported types@>= 2025typedef void (*mp_editor_cmd) (MP, char *, int); 2026 2027@ @<Option variables@>= 2028mp_editor_cmd run_editor; 2029 2030@ @<Allocate or initialize ...@>= 2031set_callback_option (run_editor); 2032 2033@ @<Declarations@>= 2034static void mp_run_editor (MP mp, char *fname, int fline); 2035 2036@ @c 2037void mp_run_editor (MP mp, char *fname, int fline) { 2038 char *s = xmalloc (256, 1); 2039 mp_snprintf (s, 256, "You want to edit file %s at line %d\n", fname, fline); 2040 wterm_ln (s); 2041@.You want to edit file x@> 2042} 2043 2044 2045@ 2046 2047@<Interpret code |c| and |return| if done@>= 2048switch (c) { 2049case '0': 2050case '1': 2051case '2': 2052case '3': 2053case '4': 2054case '5': 2055case '6': 2056case '7': 2057case '8': 2058case '9': 2059 if (deletions_allowed) { 2060 @<Delete tokens and |continue|@>; 2061 } 2062 break; 2063case 'E': 2064 if (mp->file_ptr > 0) { 2065 mp->interaction = mp_scroll_mode; 2066 mp_close_files_and_terminate (mp); 2067 (mp->run_editor) (mp, 2068 mp_str (mp, mp->input_stack[mp->file_ptr].name_field), 2069 mp_true_line (mp)); 2070 mp_jump_out (mp); 2071 } 2072 break; 2073case 'H': 2074 @<Print the help information and |continue|@>; 2075 /* |break;| */ 2076case 'I': 2077 @<Introduce new material from the terminal and |return|@>; 2078 /* |break;| */ 2079case 'Q': 2080case 'R': 2081case 'S': 2082 @<Change the interaction level and |return|@>; 2083 /* |break;| */ 2084case 'X': 2085 mp->interaction = mp_scroll_mode; 2086 mp_jump_out (mp); 2087 break; 2088default: 2089 break; 2090} 2091@<Print the menu of available options@> 2092 2093 2094@ @<Print the menu...@>= 2095{ 2096 mp_print (mp, "Type <return> to proceed, S to scroll future error messages,"); 2097@.Type <return> to proceed...@>; 2098 mp_print_nl (mp, "R to run without stopping, Q to run quietly,"); 2099 mp_print_nl (mp, "I to insert something, "); 2100 if (mp->file_ptr > 0) 2101 mp_print (mp, "E to edit your file,"); 2102 if (deletions_allowed) 2103 mp_print_nl (mp, 2104 "1 or ... or 9 to ignore the next 1 to 9 tokens of input,"); 2105 mp_print_nl (mp, "H for help, X to quit."); 2106} 2107 2108 2109@ @<Change the interaction...@>= 2110{ 2111 mp->error_count = 0; 2112 mp_print (mp, "OK, entering "); 2113 switch (c) { 2114 case 'Q': 2115 mp->interaction = mp_batch_mode; 2116 mp_print (mp, "batchmode"); 2117 decr (mp->selector); 2118 break; 2119 case 'R': 2120 mp->interaction = mp_nonstop_mode; 2121 mp_print (mp, "nonstopmode"); 2122 break; 2123 case 'S': 2124 mp->interaction = mp_scroll_mode; 2125 mp_print (mp, "scrollmode"); 2126 break; 2127 } /* there are no other cases */ 2128 mp_print (mp, "..."); 2129 mp_print_ln (mp); 2130 update_terminal(); 2131 return; 2132} 2133 2134 2135@ When the following code is executed, |buffer[(first+1)..(last-1)]| may 2136contain the material inserted by the user; otherwise another prompt will 2137be given. In order to understand this part of the program fully, you need 2138to be familiar with \MP's input stacks. 2139 2140@<Introduce new material...@>= 2141{ 2142 mp_begin_file_reading (mp); /* enter a new syntactic level for terminal input */ 2143 if (mp->last > mp->first + 1) { 2144 loc = (halfword) (mp->first + 1); 2145 mp->buffer[mp->first] = xord (' '); 2146 } else { 2147 prompt_input ("insert>"); 2148 loc = (halfword) mp->first; 2149@.insert>@> 2150 } 2151 mp->first = mp->last + 1; 2152 mp->cur_input.limit_field = (halfword) mp->last; 2153 return; 2154} 2155 2156 2157@ We allow deletion of up to 99 tokens at a time. 2158 2159@<Delete tokens...@>= 2160{ 2161 s1 = cur_cmd(); 2162 s2 = cur_mod(); 2163 s3 = cur_sym(); 2164 mp->OK_to_interrupt = false; 2165 if ((mp->last > mp->first + 1) && (mp->buffer[mp->first + 1] >= '0') 2166 && (mp->buffer[mp->first + 1] <= '9')) 2167 c = xord (c * 10 + mp->buffer[mp->first + 1] - '0' * 11); 2168 else 2169 c = (ASCII_code) (c - '0'); 2170 while (c > 0) { 2171 mp_get_next (mp); /* one-level recursive call of |error| is possible */ 2172 @<Decrease the string reference count, if the current token is a string@>; 2173 c--; 2174 }; 2175 set_cur_cmd (s1); 2176 set_cur_mod (s2); 2177 set_cur_sym (s3); 2178 mp->OK_to_interrupt = true; 2179 help_ptr = 2; 2180 help_line[1] = "I have just deleted some text, as you asked."; 2181 help_line[0] = "You can now delete more, or insert, or whatever."; 2182 mp_show_context (mp); 2183 goto CONTINUE; 2184} 2185 2186 2187@ Some wriggling with |help_line| is done here to avoid giving no 2188information whatsoever, or presenting the same information twice 2189in a row. 2190 2191@<Print the help info...@>= 2192{ 2193 if (mp->use_err_help) { 2194 @<Print the string |err_help|, possibly on several lines@>; 2195 mp->use_err_help = false; 2196 } else { 2197 if (help_ptr == 0) { 2198 help_ptr=2; 2199 help_line[1] = "Sorry, I don't know how to help in this situation."; 2200 help_line[0] = "Maybe you should try asking a human?"; 2201 } 2202 do { 2203 decr (help_ptr); 2204 mp_print (mp, help_line[help_ptr]); 2205 mp_print_ln (mp); 2206 } while (help_ptr != 0); 2207 }; 2208 help_ptr=4; 2209 help_line[3] = "Sorry, I already gave what help I could..."; 2210 help_line[2] = "Maybe you should try asking a human?"; 2211 help_line[1] = "An error might have occurred before I noticed any problems."; 2212 help_line[0] = "``If all else fails, read the instructions.''"; 2213 goto CONTINUE; 2214} 2215 2216 2217@ @<Print the string |err_help|, possibly on several lines@>= 2218{ 2219 size_t j = 0; 2220 while (j < mp->err_help->len) { 2221 if (*(mp->err_help->str + j) != '%') 2222 mp_print (mp, (const char *) (mp->err_help->str + j)); 2223 else if (j + 1 == mp->err_help->len) 2224 mp_print_ln (mp); 2225 else if (*(mp->err_help->str + j) != '%') 2226 mp_print_ln (mp); 2227 else { 2228 j++; 2229 mp_print_char (mp, xord ('%')); 2230 }; 2231 j++; 2232 } 2233} 2234 2235 2236@ @<Put help message on the transcript file@>= 2237if (mp->interaction > mp_batch_mode) 2238 decr (mp->selector); /* avoid terminal output */ 2239if (mp->use_err_help) { 2240 mp_print_nl (mp, ""); 2241 @<Print the string |err_help|, possibly on several lines@>; 2242} else { 2243 while (help_ptr > 0) { 2244 decr (help_ptr); 2245 mp_print_nl (mp, help_line[help_ptr]); 2246 }; 2247 mp_print_ln (mp); 2248 if (mp->interaction > mp_batch_mode) 2249 incr (mp->selector); /* re-enable terminal output */ 2250 mp_print_ln (mp); 2251} 2252 2253 2254@ In anomalous cases, the print selector might be in an unknown state; 2255the following subroutine is called to fix things just enough to keep 2256running a bit longer. 2257 2258@c 2259void mp_normalize_selector (MP mp) { 2260 if (mp->log_opened) 2261 mp->selector = term_and_log; 2262 else 2263 mp->selector = term_only; 2264 if (mp->job_name == NULL) 2265 mp_open_log_file (mp); 2266 if (mp->interaction == mp_batch_mode) 2267 decr (mp->selector); 2268} 2269 2270 2271@ The following procedure prints \MP's last words before dying. 2272 2273@<Error hand...@>= 2274void mp_fatal_error (MP mp, const char *s) { /* prints |s|, and that's it */ 2275 const char *hlp[] = {s, NULL} ; 2276 mp_normalize_selector (mp); 2277 if ( mp->interaction==mp_error_stop_mode ) 2278 mp->interaction=mp_scroll_mode; /* no more interaction */ 2279 if ( mp->log_opened ) 2280 mp_error(mp, "Emergency stop", hlp, true); 2281 mp->history=mp_fatal_error_stop; 2282 mp_jump_out(mp); /* irrecoverable error */ 2283@.Emergency stop@> 2284} 2285 2286 2287@ @<Exported function ...@>= 2288extern void mp_fatal_error (MP mp, const char *s); 2289 2290 2291@ @<Internal library declarations@>= 2292void mp_overflow (MP mp, const char *s, integer n); 2293 2294 2295@ @<Error hand...@>= 2296void mp_overflow (MP mp, const char *s, integer n) { /* stop due to finiteness */ 2297 char msg[256]; 2298 const char *hlp[] = { 2299 "If you really absolutely need more capacity,", 2300 "you can ask a wizard to enlarge me.", 2301 NULL }; 2302 mp_normalize_selector (mp); 2303 mp_snprintf (msg, 256, "MetaPost capacity exceeded, sorry [%s=%d]", s, (int) n); 2304@.MetaPost capacity exceeded ...@>; 2305 if ( mp->interaction==mp_error_stop_mode ) 2306 mp->interaction=mp_scroll_mode; /* no more interaction */ 2307 if ( mp->log_opened ) 2308 mp_error(mp, msg, hlp, true); 2309 mp->history=mp_fatal_error_stop; 2310 mp_jump_out(mp); /* irrecoverable error */ 2311} 2312 2313 2314@ The program might sometime run completely amok, at which point there is 2315no choice but to stop. If no previous error has been detected, that's bad 2316news; a message is printed that is really intended for the \MP\ 2317maintenance person instead of the user (unless the user has been 2318particularly diabolical). The index entries for `this can't happen' may 2319help to pinpoint the problem. 2320@^dry rot@> 2321 2322@<Internal library ...@>= 2323void mp_confusion (MP mp, const char *s); 2324 2325@ Consistency check violated; |s| tells where. 2326@<Error hand...@>= 2327void mp_confusion (MP mp, const char *s) { 2328 char msg[256]; 2329 const char *hlp[] = { 2330 "One of your faux pas seems to have wounded me deeply...", 2331 "in fact, I'm barely conscious. Please fix it and try again.", 2332 NULL }; 2333 mp_normalize_selector (mp); 2334 if (mp->history < mp_error_message_issued) { 2335 mp_snprintf (msg, 256, "This can't happen (%s)", s); 2336@.This can't happen@>; 2337 hlp[0] = "I'm broken. Please show this to someone who can fix can fix"; 2338 hlp[1] = NULL; 2339 } else { 2340 mp_snprintf (msg, 256, "I can\'t go on meeting you like this"); 2341@.I can't go on...@>; 2342 } 2343 if ( mp->interaction==mp_error_stop_mode ) 2344 mp->interaction=mp_scroll_mode; /* no more interaction */ 2345 if ( mp->log_opened ) 2346 mp_error(mp, msg, hlp, true); 2347 mp->history=mp_fatal_error_stop; 2348 mp_jump_out(mp); /* irrecoverable error */ 2349} 2350 2351 2352@ Users occasionally want to interrupt \MP\ while it's running. 2353If the runtime system allows this, one can implement 2354a routine that sets the global variable |interrupt| to some nonzero value 2355when such an interrupt is signaled. Otherwise there is probably at least 2356a way to make |interrupt| nonzero using the C debugger. 2357@^system dependencies@> 2358@^debugging@> 2359 2360@d check_interrupt { if ( mp->interrupt!=0 ) 2361 mp_pause_for_instructions(mp); } 2362 2363@<Global...@>= 2364integer interrupt; /* should \MP\ pause for instructions? */ 2365boolean OK_to_interrupt; /* should interrupts be observed? */ 2366integer run_state; /* are we processing input ? */ 2367boolean finished; /* set true by |close_files_and_terminate| */ 2368boolean reading_preload; 2369 2370@ @<Allocate or ...@>= 2371mp->OK_to_interrupt = true; 2372mp->finished = false; 2373 2374@ When an interrupt has been detected, the program goes into its 2375highest interaction level and lets the user have the full flexibility of 2376the |error| routine. \MP\ checks for interrupts only at times when it is 2377safe to do this. 2378 2379@c 2380static void mp_pause_for_instructions (MP mp) { 2381 const char *hlp[] = { "You rang?", 2382 "Try to insert some instructions for me (e.g.,`I show x'),", 2383 "unless you just want to quit by typing `X'.", 2384 NULL } ; 2385 if (mp->OK_to_interrupt) { 2386 mp->interaction = mp_error_stop_mode; 2387 if ((mp->selector == log_only) || (mp->selector == no_print)) 2388 incr (mp->selector); 2389@.Interruption@>; 2390 mp_error (mp, "Interruption", hlp, false); 2391 mp->interrupt = 0; 2392 } 2393} 2394 2395 2396@* Arithmetic with scaled numbers. 2397The principal computations performed by \MP\ are done entirely in terms of 2398integers less than $2^{31}$ in magnitude; thus, the arithmetic specified in this 2399program can be carried out in exactly the same way on a wide variety of 2400computers, including some small ones. 2401@^small computers@> 2402 2403But C does not rigidly define the |/| operation in the case of negative 2404dividends; for example, the result of |(-2*n-1) / 2| is |-(n+1)| on some 2405computers and |-n| on others (is this true ?). There are two principal 2406types of arithmetic: ``translation-preserving,'' in which the identity 2407|(a+q*b)/b=(a/b)+q| is valid; and ``negation-preserving,'' in which 2408|(-a)/b=-(a/b)|. This leads to two \MP s, which can produce 2409different results, although the differences should be negligible when the 2410language is being used properly. The \TeX\ processor has been defined 2411carefully so that both varieties of arithmetic will produce identical 2412output, but it would be too inefficient to constrain \MP\ in a similar way. 2413 2414@d inf_t ((math_data *)mp->math)->inf_t 2415 2416@ A single computation might use several subroutine calls, and it is 2417desirable to avoid producing multiple error messages in case of arithmetic 2418overflow. So the routines below set the global variable |arith_error| to |true| 2419instead of reporting errors directly to the user. 2420@^overflow in arithmetic@> 2421 2422@<Glob...@>= 2423boolean arith_error; /* has arithmetic overflow occurred recently? */ 2424 2425@ @<Allocate or ...@>= 2426mp->arith_error = false; 2427 2428@ At crucial points the program will say |check_arith|, to test if 2429an arithmetic error has been detected. 2430 2431@d check_arith() do { 2432 if ( mp->arith_error ) 2433 mp_clear_arith(mp); 2434} while (0) 2435 2436@c 2437static void mp_clear_arith (MP mp) { 2438 const char *hlp[] = { 2439 "Uh, oh. A little while ago one of the quantities that I was", 2440 "computing got too large, so I'm afraid your answers will be", 2441 "somewhat askew. You'll probably have to adopt different", 2442 "tactics next time. But I shall try to carry on anyway.", 2443 NULL }; 2444 mp_error (mp, "Arithmetic overflow", hlp, true); 2445@.Arithmetic overflow@>; 2446 mp->arith_error = false; 2447} 2448 2449 2450@ The definitions of these are set up by the math initialization. 2451 2452@d arc_tol_k ((math_data *)mp->math)->arc_tol_k 2453@d coef_bound_k ((math_data *)mp->math)->coef_bound_k 2454@d coef_bound_minus_1 ((math_data *)mp->math)->coef_bound_minus_1 2455@d sqrt_8_e_k ((math_data *)mp->math)->sqrt_8_e_k 2456@d twelve_ln_2_k ((math_data *)mp->math)->twelve_ln_2_k 2457@d twelvebits_3 ((math_data *)mp->math)->twelvebits_3 2458@d one_k ((math_data *)mp->math)->one_k 2459@d epsilon_t ((math_data *)mp->math)->epsilon_t 2460@d unity_t ((math_data *)mp->math)->unity_t 2461@d zero_t ((math_data *)mp->math)->zero_t 2462@d two_t ((math_data *)mp->math)->two_t 2463@d three_t ((math_data *)mp->math)->three_t 2464@d half_unit_t ((math_data *)mp->math)->half_unit_t 2465@d three_quarter_unit_t ((math_data *)mp->math)->three_quarter_unit_t 2466@d twentysixbits_sqrt2_t ((math_data *)mp->math)->twentysixbits_sqrt2_t 2467@d twentyeightbits_d_t ((math_data *)mp->math)->twentyeightbits_d_t 2468@d twentysevenbits_sqrt2_d_t ((math_data *)mp->math)->twentysevenbits_sqrt2_d_t 2469@d warning_limit_t ((math_data *)mp->math)->warning_limit_t 2470@d precision_default ((math_data *)mp->math)->precision_default 2471@d precision_max ((math_data *)mp->math)->precision_max 2472@d precision_min ((math_data *)mp->math)->precision_min 2473 2474@ In fact, the two sorts of scaling discussed above aren't quite 2475sufficient; \MP\ has yet another, used internally to keep track of angles. 2476 2477@ We often want to print two scaled quantities in parentheses, 2478separated by a comma. 2479 2480@<Basic printing...@>= 2481void mp_print_two (MP mp, mp_number x, mp_number y) { /* prints `|(x,y)|' */ 2482 mp_print_char (mp, xord ('(')); 2483 print_number (x); 2484 mp_print_char (mp, xord (',')); 2485 print_number (y); 2486 mp_print_char (mp, xord (')')); 2487} 2488 2489 2490@ 2491@d fraction_one_t ((math_data *)mp->math)->fraction_one_t 2492@d fraction_half_t ((math_data *)mp->math)->fraction_half_t 2493@d fraction_three_t ((math_data *)mp->math)->fraction_three_t 2494@d fraction_four_t ((math_data *)mp->math)->fraction_four_t 2495 2496@d one_eighty_deg_t ((math_data *)mp->math)->one_eighty_deg_t 2497@d three_sixty_deg_t ((math_data *)mp->math)->three_sixty_deg_t 2498 2499@ @<Local variables for initialization@>= 2500integer k; /* all-purpose loop index */ 2501 2502@ And now let's complete our collection of numeric utility routines 2503by considering random number generation. 2504\MP\ generates pseudo-random numbers with the additive scheme recommended 2505in Section 3.6 of {\sl The Art of Computer Programming}; however, the 2506results are random fractions between 0 and |fraction_one-1|, inclusive. 2507 2508There's an auxiliary array |randoms| that contains 55 pseudo-random 2509fractions. Using the recurrence $x_n=(x_{n-55}-x_{n-31})\bmod 2^{28}$, 2510we generate batches of 55 new $x_n$'s at a time by calling |new_randoms|. 2511The global variable |j_random| tells which element has most recently 2512been consumed. 2513The global variable |random_seed| was introduced in version 0.9, 2514for the sole reason of stressing the fact that the initial value of the 2515random seed is system-dependant. The initialization code below will initialize 2516this variable to |(internal[mp_time] div unity)+internal[mp_day]|, but this 2517is not good enough on modern fast machines that are capable of running 2518multiple MetaPost processes within the same second. 2519@^system dependencies@> 2520 2521@<Glob...@>= 2522mp_number randoms[55]; /* the last 55 random values generated */ 2523int j_random; /* the number of unused |randoms| */ 2524 2525@ @<Option variables@>= 2526int random_seed; /* the default random seed */ 2527 2528@ @<Allocate or initialize ...@>= 2529mp->random_seed = opt->random_seed; 2530{ 2531 int i; 2532 for (i=0;i<55;i++) { 2533 new_fraction (mp->randoms[i]); 2534 } 2535} 2536 2537@ @<Dealloc...@>= 2538{ 2539 int i; 2540 for (i=0;i<55;i++) { 2541 free_number (mp->randoms[i]); 2542 } 2543} 2544 2545@ @<Internal library ...@>= 2546void mp_new_randoms (MP mp); 2547 2548@ @c 2549void mp_new_randoms (MP mp) { 2550 int k; /* index into |randoms| */ 2551 mp_number x; /* accumulator */ 2552 new_number (x); 2553 for (k = 0; k <= 23; k++) { 2554 set_number_from_substraction(x, mp->randoms[k], mp->randoms[k + 31]); 2555 if (number_negative(x)) 2556 number_add (x, fraction_one_t); 2557 number_clone (mp->randoms[k], x); 2558 } 2559 for (k = 24; k <= 54; k++) { 2560 set_number_from_substraction(x, mp->randoms[k], mp->randoms[k - 24]); 2561 if (number_negative(x)) 2562 number_add (x, fraction_one_t); 2563 number_clone (mp->randoms[k], x); 2564 } 2565 free_number (x); 2566 mp->j_random = 54; 2567} 2568 2569@ To consume a random fraction, the program below will say `|next_random|'. 2570 2571@c 2572static void mp_next_random (MP mp, mp_number *ret) { 2573 if ( mp->j_random==0 ) 2574 mp_new_randoms(mp); 2575 else 2576 decr(mp->j_random); 2577 number_clone (*ret, mp->randoms[mp->j_random]); 2578} 2579 2580 2581@ To produce a uniform random number in the range |0<=u<x| or |0>=u>x| 2582or |0=u=x|, given a |scaled| value~|x|, we proceed as shown here. 2583 2584Note that the call of |take_fraction| will produce the values 0 and~|x| 2585with about half the probability that it will produce any other particular 2586values between 0 and~|x|, because it rounds its answers. 2587 2588@c 2589static void mp_unif_rand (MP mp, mp_number *ret, mp_number x_orig) { 2590 mp_number y; /* trial value */ 2591 mp_number x, abs_x; 2592 mp_number u; 2593 new_fraction (y); 2594 new_number (x); 2595 new_number (abs_x); 2596 new_number (u); 2597 number_clone (x, x_orig); 2598 number_clone (abs_x, x); 2599 number_abs (abs_x); 2600 mp_next_random(mp, &u); 2601 take_fraction (y, abs_x, u); 2602 free_number (u); 2603 if (number_equal(y, abs_x)) { 2604 set_number_to_zero(*ret); 2605 } else if (number_positive(x)) { 2606 number_clone (*ret, y); 2607 } else { 2608 number_clone (*ret, y); 2609 number_negate (*ret); 2610 } 2611 free_number (abs_x); 2612 free_number (x); 2613 free_number (y); 2614} 2615 2616 2617@ Finally, a normal deviate with mean zero and unit standard deviation 2618can readily be obtained with the ratio method (Algorithm 3.4.1R in 2619{\sl The Art of Computer Programming\/}). This is the original one, 2620that stays as reference: 2621Now each number system has its own implementation, 2622true to the original as much as possibile. 2623 2624 2625@c 2626/* Unused. 2627static void mp_norm_rand (MP mp, mp_number *ret) { 2628 mp_number ab_vs_cd; 2629 mp_number abs_x; 2630 mp_number u; 2631 mp_number r; 2632 mp_number la, xa; 2633 new_number (ab_vs_cd); 2634 new_number (la); 2635 new_number (xa); 2636 new_number (abs_x); 2637 new_number (u); 2638 new_number (r); 2639 do { 2640 do { 2641 mp_number v; 2642 new_number (v); 2643 mp_next_random(mp, &v); 2644 number_substract (v, fraction_half_t); 2645 take_fraction (xa, sqrt_8_e_k, v); 2646 free_number (v); 2647 mp_next_random(mp, &u); 2648 number_clone (abs_x, xa); 2649 number_abs (abs_x); 2650 } while (number_greaterequal (abs_x, u)); 2651 make_fraction (r, xa, u); 2652 number_clone (xa, r); 2653 m_log (la, u); 2654 set_number_from_substraction(la, twelve_ln_2_k, la); 2655 ab_vs_cd (ab_vs_cd, one_k, la, xa, xa); 2656 } while (number_negative(ab_vs_cd)); 2657 number_clone (*ret, xa); 2658 free_number (ab_vs_cd); 2659 free_number (r); 2660 free_number (abs_x); 2661 free_number (la); 2662 free_number (xa); 2663 free_number (u); 2664} 2665*/ 2666 2667 2668@* Packed data. 2669 2670@d max_quarterword 0x3FFF /* largest allowable value in a |quarterword| */ 2671@d max_halfword 0xFFFFFFF /* largest allowable value in a |halfword| */ 2672 2673@ The macros |qi| and |qo| are used for input to and output 2674from quarterwords. These are legacy macros. 2675@^system dependencies@> 2676 2677@d qo(A) (A) /* to read eight bits from a quarterword */ 2678@d qi(A) (quarterword)(A) /* to store eight bits in a quarterword */ 2679 2680@ The reader should study the following definitions closely: 2681@^system dependencies@> 2682 2683@<Types...@>= 2684typedef struct mp_value_node_data *mp_value_node; 2685typedef struct mp_node_data *mp_node; 2686typedef struct mp_symbol_entry *mp_sym; 2687typedef short quarterword; /* 1/4 of a word */ 2688typedef int halfword; /* 1/2 of a word */ 2689typedef struct { 2690 integer scale; /* only for |indep_scale|, used together with |serial| */ 2691 integer serial; /* only for |indep_value|, used together with |scale| */ 2692} mp_independent_data; 2693typedef struct { 2694 mp_independent_data indep; 2695 mp_number n; 2696 mp_string str; 2697 mp_sym sym; 2698 mp_node node; 2699 mp_knot p; 2700} mp_value_data; 2701typedef struct { 2702 mp_variable_type type; 2703 mp_value_data data; 2704} mp_value; 2705typedef struct { 2706 quarterword b0, b1, b2, b3; 2707} four_quarters; 2708typedef union { 2709 integer sc; 2710 four_quarters qqqq; 2711} font_data; 2712 2713 2714@ The global variable |math_mode| has four settings, representing the 2715math value type that will be used in this run. 2716 2717the typedef for |mp_number| is here because it has to come very early. 2718 2719@<Exported types@>= 2720typedef enum { 2721 mp_math_scaled_mode = 0, 2722 mp_math_double_mode = 1, 2723 mp_math_binary_mode = 2, 2724 mp_math_decimal_mode = 3 2725} mp_math_mode; 2726 2727@ @<Option variables@>= 2728int math_mode; /* math mode */ 2729 2730@ @<Allocate or initialize ...@>= 2731mp->math_mode = opt->math_mode; 2732 2733@ 2734@d xfree(A) do { mp_xfree(A); A=NULL; } while (0) 2735@d xrealloc(P,A,B) mp_xrealloc(mp,P,(size_t)A,B) 2736@d xmalloc(A,B) mp_xmalloc(mp,(size_t)A,B) 2737@d xstrdup(A) mp_xstrdup(mp,A) 2738@d XREALLOC(a,b,c) a = xrealloc(a,(b+1),sizeof(c)); 2739 2740@<Declare helpers@>= 2741extern void mp_xfree (void *x); 2742extern void *mp_xrealloc (MP mp, void *p, size_t nmem, size_t size); 2743extern void *mp_xmalloc (MP mp, size_t nmem, size_t size); 2744extern void mp_do_snprintf (char *str, int size, const char *fmt, ...); 2745extern void *do_alloc_node(MP mp, size_t size); 2746 2747@ This is an attempt to spend less time in |malloc()|: 2748 2749@d max_num_token_nodes 1000 2750@d max_num_pair_nodes 1000 2751@d max_num_knot_nodes 1000 2752@d max_num_value_nodes 1000 2753@d max_num_symbolic_nodes 1000 2754 2755@<Global ...@>= 2756mp_node token_nodes; 2757int num_token_nodes; 2758mp_node pair_nodes; 2759int num_pair_nodes; 2760mp_knot knot_nodes; 2761int num_knot_nodes; 2762mp_node value_nodes; 2763int num_value_nodes; 2764mp_node symbolic_nodes; 2765int num_symbolic_nodes; 2766 2767@ @<Allocate or initialize ...@>= 2768mp->token_nodes = NULL; 2769mp->num_token_nodes = 0; 2770mp->pair_nodes = NULL; 2771mp->num_pair_nodes = 0; 2772mp->knot_nodes = NULL; 2773mp->num_knot_nodes = 0; 2774mp->value_nodes = NULL; 2775mp->num_value_nodes = 0; 2776mp->symbolic_nodes = NULL; 2777mp->num_symbolic_nodes = 0; 2778 2779@ @<Dealloc ...@>= 2780while (mp->value_nodes) { 2781 mp_node p = mp->value_nodes; 2782 mp->value_nodes = p->link; 2783 mp_free_node(mp,p,value_node_size); 2784} 2785while (mp->symbolic_nodes) { 2786 mp_node p = mp->symbolic_nodes; 2787 mp->symbolic_nodes = p->link; 2788 mp_free_node(mp,p,symbolic_node_size); 2789} 2790while (mp->pair_nodes) { 2791 mp_node p = mp->pair_nodes; 2792 mp->pair_nodes = p->link; 2793 mp_free_node(mp,p,pair_node_size); 2794} 2795while (mp->token_nodes) { 2796 mp_node p = mp->token_nodes; 2797 mp->token_nodes = p->link; 2798 mp_free_node(mp,p,token_node_size); 2799} 2800while (mp->knot_nodes) { 2801 mp_knot p = mp->knot_nodes; 2802 mp->knot_nodes = p->next; 2803 mp_free_knot(mp,p); 2804} 2805 2806@ This is a nicer way of allocating nodes. 2807 2808@d malloc_node(A) do_alloc_node(mp,(A)) 2809 2810@ 2811@c 2812void *do_alloc_node (MP mp, size_t size) { 2813 void *p; 2814 p = xmalloc(1,size); 2815 add_var_used (size); 2816 ((mp_node)p)->link = NULL; 2817 ((mp_node)p)->has_number = 0; 2818 return p; 2819} 2820 2821 2822@ The |max_size_test| guards against overflow, on the assumption that 2823|size_t| is at least 31bits wide. 2824 2825@d max_size_test 0x7FFFFFFF 2826 2827@c 2828void mp_xfree (void *x) { 2829 if (x != NULL) 2830 free (x); 2831} 2832void *mp_xrealloc (MP mp, void *p, size_t nmem, size_t size) { 2833 void *w; 2834 if ((max_size_test / size) < nmem) { 2835 mp_fputs ("Memory size overflow!\n", mp->err_out); 2836 mp->history = mp_fatal_error_stop; 2837 mp_jump_out (mp); 2838 } 2839 w = realloc (p, (nmem * size)); 2840 if (w == NULL) { 2841 mp_fputs ("Out of memory!\n", mp->err_out); 2842 mp->history = mp_system_error_stop; 2843 mp_jump_out (mp); 2844 } 2845 return w; 2846} 2847void *mp_xmalloc (MP mp, size_t nmem, size_t size) { 2848 void *w; 2849#if DEBUG 2850 if ((max_size_test / size) < nmem) { 2851 mp_fputs ("Memory size overflow!\n", mp->err_out); 2852 mp->history = mp_fatal_error_stop; 2853 mp_jump_out (mp); 2854 } 2855#endif 2856 w = malloc (nmem * size); 2857 if (w == NULL) { 2858 mp_fputs ("Out of memory!\n", mp->err_out); 2859 mp->history = mp_system_error_stop; 2860 mp_jump_out (mp); 2861 } 2862 return w; 2863} 2864 2865@ @<Internal library declarations@>= 2866# define mp_snprintf (void)snprintf 2867 2868@* Dynamic memory allocation. 2869 2870The \MP\ system does nearly all of its own memory allocation, so that it 2871can readily be transported into environments that do not have automatic 2872facilities for strings, garbage collection, etc., and so that it can be in 2873control of what error messages the user receives. 2874 2875@d MP_VOID (mp_node)(1) /* |NULL+1|, a |NULL| pointer different from |NULL| */ 2876 2877@d mp_link(A) (A)->link /* the |link| field of a node */ 2878@d set_mp_link(A,B) do { 2879 mp_node d = (B); 2880 /* |printf("set link of %p to %p on line %d\n", (A), d, __LINE__);| */ 2881 mp_link((A)) = d; 2882 } while (0) 2883@d mp_type(A) (A)->type /* identifies what kind of value this is */ 2884@d mp_name_type(A) (A)->name_type /* a clue to the name of this value */ 2885 2886@ @<MPlib internal header stuff@>= 2887#define NODE_BODY \ 2888 mp_variable_type type; \ 2889 mp_name_type_type name_type; \ 2890 unsigned short has_number; \ 2891 struct mp_node_data *link 2892typedef struct mp_node_data { 2893 NODE_BODY; 2894 mp_value_data data; 2895} mp_node_data; 2896typedef struct mp_node_data *mp_symbolic_node; 2897 2898@ Users who wish to study the memory requirements of particular applications can 2899can use the special features that keep track of current and maximum memory usage. 2900\MP\ will report these statistics when |mp_tracing_stats| is positive. 2901 2902@d add_var_used(a) do { 2903 mp->var_used+=(a); 2904 if (mp->var_used>mp->var_used_max) mp->var_used_max=mp->var_used; 2905} while (0) 2906 2907@<Glob...@>= 2908size_t var_used; /* how much memory is in use */ 2909size_t var_used_max; /* how much memory was in use max */ 2910 2911@ These redirect to function to aid in debugging. 2912 2913@c 2914#if DEBUG 2915#define mp_sym_info(A) get_mp_sym_info(mp,(A)) 2916#define set_mp_sym_info(A,B) do_set_mp_sym_info(mp,(A),(B)) 2917#define mp_sym_sym(A) get_mp_sym_sym(mp,(A)) 2918#define set_mp_sym_sym(A,B) do_set_mp_sym_sym(mp,(A),(mp_sym)(B)) 2919static void do_set_mp_sym_info (MP mp, mp_node p, halfword v) { 2920 FUNCTION_TRACE3 ("do_set_mp_sym_info(%p,%d)\n", p, v); 2921 assert (p->type == mp_symbol_node); 2922 set_indep_value(p, v); 2923} 2924static halfword get_mp_sym_info (MP mp, mp_node p) { 2925 FUNCTION_TRACE3 ("%d = get_mp_sym_info(%p)\n", indep_value (p), p); 2926 assert (p->type == mp_symbol_node); 2927 return indep_value(p); 2928} 2929static void do_set_mp_sym_sym (MP mp, mp_node p, mp_sym v) { 2930 mp_symbolic_node pp = (mp_symbolic_node) p; 2931 FUNCTION_TRACE3 ("do_set_mp_sym_sym(%p,%p)\n", pp, v); 2932 assert (pp->type == mp_symbol_node); 2933 pp->data.sym = v; 2934} 2935static mp_sym get_mp_sym_sym (MP mp, mp_node p) { 2936 mp_symbolic_node pp = (mp_symbolic_node) p; 2937 FUNCTION_TRACE3 ("%p = get_mp_sym_sym(%p)\n", pp->data.sym, pp); 2938 assert (pp->type == mp_symbol_node); 2939 return pp->data.sym; 2940} 2941#else 2942#define mp_sym_info(A) indep_value(A) 2943#define set_mp_sym_info(A,B) set_indep_value(A, (B)) 2944#define mp_sym_sym(A) (A)->data.sym 2945#define set_mp_sym_sym(A,B) (A)->data.sym = (mp_sym)(B) 2946#endif 2947 2948@ @<Declarations@>= 2949#if DEBUG 2950static void do_set_mp_sym_info (MP mp, mp_node A, halfword B); 2951static halfword get_mp_sym_info (MP mp, mp_node p); 2952static void do_set_mp_sym_sym (MP mp, mp_node A, mp_sym B); 2953static mp_sym get_mp_sym_sym (MP mp, mp_node p); 2954#endif 2955 2956@ The function |get_symbolic_node| returns a pointer to a new symbolic node whose 2957|link| field is null. 2958@^inner loop@> 2959 2960@d symbolic_node_size sizeof(mp_node_data) 2961@c 2962static mp_node mp_get_symbolic_node (MP mp) { 2963 mp_symbolic_node p; 2964 if (mp->symbolic_nodes) { 2965 p = (mp_symbolic_node)mp->symbolic_nodes; 2966 mp->symbolic_nodes = p->link; 2967 mp->num_symbolic_nodes--; 2968 p->link = NULL; 2969 } else { 2970 p = malloc_node (symbolic_node_size); 2971 new_number(p->data.n); 2972 p->has_number = 1; 2973 } 2974 p->type = mp_symbol_node; 2975 p->name_type = mp_normal_sym; 2976 FUNCTION_TRACE2 ("%p = mp_get_symbolic_node()\n", p); 2977 return (mp_node) p; 2978} 2979 2980 2981@ Conversely, when some node |p| of size |s| is no longer needed, 2982the operation |free_node(p,s)| will make its words available, by inserting 2983|p| as a new empty node just before where |rover| now points. 2984 2985A symbolic node is recycled by calling |free_symbolic_node|. 2986 2987@c 2988void mp_free_node (MP mp, mp_node p, size_t siz) { /* node liberation */ 2989 FUNCTION_TRACE3 ("mp_free_node(%p,%d)\n", p, (int)siz); 2990 if (!p) return; 2991 mp->var_used -= siz; 2992 if (mp->math_mode > mp_math_double_mode) { 2993 if (p->has_number >= 1 && is_number(((mp_symbolic_node)p)->data.n)) { 2994 free_number(((mp_symbolic_node)p)->data.n); 2995 } 2996 if (p->has_number == 2 && is_number(((mp_value_node)p)->subscript_)) { 2997 free_number(((mp_value_node)p)->subscript_); 2998 } 2999 /* There was a quite large |switch| here first, but the |mp_dash_node| 3000 case was the only one that did anything ... */ 3001 if (mp_type (p) == mp_dash_node_type) { 3002 free_number(((mp_dash_node)p)->start_x); 3003 free_number(((mp_dash_node)p)->stop_x); 3004 free_number(((mp_dash_node)p)->dash_y); 3005 } 3006 } 3007 xfree (p); 3008} 3009void mp_free_symbolic_node (MP mp, mp_node p) { /* node liberation */ 3010 FUNCTION_TRACE2 ("mp_free_symbolic_node(%p)\n", p); 3011 if (!p) return; 3012 if (mp->num_symbolic_nodes < max_num_symbolic_nodes) { 3013 p->link = mp->symbolic_nodes; 3014 mp->symbolic_nodes = p; 3015 mp->num_symbolic_nodes++; 3016 return; 3017 } 3018 mp->var_used -= symbolic_node_size; 3019 xfree (p); 3020} 3021void mp_free_value_node (MP mp, mp_node p) { /* node liberation */ 3022 FUNCTION_TRACE2 ("mp_free_value_node(%p)\n", p); 3023 if (!p) return; 3024 if (mp->num_value_nodes < max_num_value_nodes) { 3025 p->link = mp->value_nodes; 3026 mp->value_nodes = p; 3027 mp->num_value_nodes++; 3028 return; 3029 } 3030 mp->var_used -= value_node_size; 3031 assert(p->has_number == 2); 3032 if (mp->math_mode > mp_math_double_mode) { 3033 free_number(((mp_value_node)p)->data.n); 3034 free_number(((mp_value_node)p)->subscript_); 3035 } 3036 xfree (p); 3037} 3038 3039 3040@ @<Internal library declarations@>= 3041void mp_free_node (MP mp, mp_node p, size_t siz); 3042void mp_free_symbolic_node (MP mp, mp_node p); 3043void mp_free_value_node (MP mp, mp_node p); 3044 3045@* Memory layout. 3046Some nodes are created statically, since static allocation is 3047more efficient than dynamic allocation when we can get away with it. 3048 3049@<Glob...@>= 3050mp_dash_node null_dash; 3051mp_value_node dep_head; 3052mp_node inf_val; 3053mp_node zero_val; 3054mp_node temp_val; 3055mp_node end_attr; 3056mp_node bad_vardef; 3057mp_node temp_head; 3058mp_node hold_head; 3059mp_node spec_head; 3060 3061@ The following code gets the memory off to a good start. 3062 3063@<Initialize table entries@>= 3064mp->spec_head = mp_get_symbolic_node (mp); 3065mp->last_pending = mp->spec_head; 3066mp->temp_head = mp_get_symbolic_node (mp); 3067mp->hold_head = mp_get_symbolic_node (mp); 3068 3069@ @<Free table entries@>= 3070mp_free_symbolic_node (mp, mp->spec_head); 3071mp_free_symbolic_node (mp, mp->temp_head); 3072mp_free_symbolic_node (mp, mp->hold_head); 3073 3074@ The procedure |flush_node_list(p)| frees an entire linked list of 3075nodes that starts at a given position, until coming to a |NULL| pointer. 3076@^inner loop@> 3077 3078@c 3079static void mp_flush_node_list (MP mp, mp_node p) { 3080 mp_node q; /* the node being recycled */ 3081 FUNCTION_TRACE2 ("mp_flush_node_list(%p)\n", p); 3082 while (p != NULL) { 3083 q = p; 3084 p = p->link; 3085 if (q->type != mp_symbol_node) 3086 mp_free_token_node (mp, q); 3087 else 3088 mp_free_symbolic_node (mp, q); 3089 } 3090} 3091 3092 3093@* The command codes. 3094Before we can go much further, we need to define symbolic names for the internal 3095code numbers that represent the various commands obeyed by \MP. These codes 3096are somewhat arbitrary, but not completely so. For example, 3097some codes have been made adjacent so that |case| statements in the 3098program need not consider cases that are widely spaced, or so that |case| 3099statements can be replaced by |if| statements. A command can begin an 3100expression if and only if its code lies between |min_primary_command| and 3101|max_primary_command|, inclusive. The first token of a statement that doesn't 3102begin with an expression has a command code between |min_command| and 3103|max_statement_command|, inclusive. Anything less than |min_command| is 3104eliminated during macro expansions, and anything no more than |max_pre_command| 3105is eliminated when expanding \TeX\ material. Ranges such as 3106|min_secondary_command..max_secondary_command| are used when parsing 3107expressions, but the relative ordering within such a range is generally not 3108critical. 3109 3110The ordering of the highest-numbered commands 3111(|comma<semicolon<end_group<stop|) is crucial for the parsing and 3112error-recovery methods of this program as is the ordering |if_test<fi_or_else| 3113for the smallest two commands. The ordering is also important in the ranges 3114|numeric_token..plus_or_minus| and |left_brace..ampersand|. 3115 3116At any rate, here is the list, for future reference. 3117 3118@d mp_max_command_code mp_stop 3119@d mp_max_pre_command mp_mpx_break 3120@d mp_min_command (mp_defined_macro+1) 3121@d mp_max_statement_command mp_type_name 3122@d mp_min_primary_command mp_type_name 3123@d mp_min_suffix_token mp_internal_quantity 3124@d mp_max_suffix_token mp_numeric_token 3125@d mp_max_primary_command mp_plus_or_minus /* should also be |numeric_token+1| */ 3126@d mp_min_tertiary_command mp_plus_or_minus 3127@d mp_max_tertiary_command mp_tertiary_binary 3128@d mp_min_expression_command mp_left_brace 3129@d mp_max_expression_command mp_equals 3130@d mp_min_secondary_command mp_and_command 3131@d mp_max_secondary_command mp_secondary_binary 3132@d mp_end_of_statement (cur_cmd()>mp_comma) 3133 3134 3135@<Enumeration types@>= 3136typedef enum { 3137mp_start_tex=1, /* begin \TeX\ material (\&{btex}, \&{verbatimtex}) */ 3138mp_etex_marker, /* end \TeX\ material (\&{etex}) */ 3139mp_mpx_break, /* stop reading an \.{MPX} file (\&{mpxbreak}) */ 3140mp_if_test, /* conditional text (\&{if}) */ 3141mp_fi_or_else, /* delimiters for conditionals (\&{elseif}, \&{else}, \&{fi}) */ 3142mp_input, /* input a source file (\&{input}, \&{endinput}) */ 3143mp_iteration, /* iterate (\&{for}, \&{forsuffixes}, \&{forever}, \&{endfor}) */ 3144mp_repeat_loop, /* special command substituted for \&{endfor} */ 3145mp_exit_test, /* premature exit from a loop (\&{exitif}) */ 3146mp_relax, /* do nothing (\.{\char`\\}) */ 3147mp_scan_tokens, /* put a string into the input buffer */ 3148mp_runscript, /* put a script result string into the input buffer */ 3149mp_maketext, /* put a script result string into the input buffer */ 3150mp_expand_after, /* look ahead one token */ 3151mp_defined_macro, /* a macro defined by the user */ 3152mp_save_command, /* save a list of tokens (\&{save}) */ 3153mp_interim_command, /* save an internal quantity (\&{interim}) */ 3154mp_let_command, /* redefine a symbolic token (\&{let}) */ 3155mp_new_internal, /* define a new internal quantity (\&{newinternal}) */ 3156mp_macro_def, /* define a macro (\&{def}, \&{vardef}, etc.) */ 3157mp_ship_out_command, /* output a character (\&{shipout}) */ 3158mp_add_to_command, /* add to edges (\&{addto}) */ 3159mp_bounds_command, /* add bounding path to edges (\&{setbounds}, \&{clip}) */ 3160mp_tfm_command, /* command for font metric info (\&{ligtable}, etc.) */ 3161mp_protection_command, /* set protection flag (\&{outer}, \&{inner}) */ 3162mp_show_command, /* diagnostic output (\&{show}, \&{showvariable}, etc.) */ 3163mp_mode_command, /* set interaction level (\&{batchmode}, etc.) */ 3164mp_random_seed, /* initialize random number generator (\&{randomseed}) */ 3165mp_message_command, /* communicate to user (\&{message}, \&{errmessage}) */ 3166mp_every_job_command, /* designate a starting token (\&{everyjob}) */ 3167mp_delimiters, /* define a pair of delimiters (\&{delimiters}) */ 3168mp_special_command, /* output special info (\&{special}) 3169 or font map info (\&{fontmapfile}, \&{fontmapline}) */ 3170mp_write_command, /* write text to a file (\&{write}) */ 3171mp_type_name, /* declare a type (\&{numeric}, \&{pair}, etc.) */ 3172mp_left_delimiter, /* the left delimiter of a matching pair */ 3173mp_begin_group, /* beginning of a group (\&{begingroup}) */ 3174mp_nullary, /* an operator without arguments (e.g., \&{normaldeviate}) */ 3175mp_unary, /* an operator with one argument (e.g., \&{sqrt}) */ 3176mp_str_op, /* convert a suffix to a string (\&{str}) */ 3177mp_cycle, /* close a cyclic path (\&{cycle}) */ 3178mp_primary_binary, /* binary operation taking `\&{of}' (e.g., \&{point}) */ 3179mp_capsule_token, /* a value that has been put into a token list */ 3180mp_string_token, /* a string constant (e.g., |"hello"|) */ 3181mp_internal_quantity, /* internal numeric parameter (e.g., \&{pausing}) */ 3182mp_tag_token, /* a symbolic token without a primitive meaning */ 3183mp_numeric_token, /* a numeric constant (e.g., \.{3.14159}) */ 3184mp_plus_or_minus, /* either `\.+' or `\.-' */ 3185mp_tertiary_secondary_macro, /* a macro defined by \&{secondarydef} */ 3186mp_tertiary_binary, /* an operator at the tertiary level (e.g., `\.{++}') */ 3187mp_left_brace, /* the operator `\.{\char`\{}' */ 3188mp_path_join, /* the operator `\.{..}' */ 3189mp_ampersand, /* the operator `\.\&' */ 3190mp_expression_tertiary_macro, /* a macro defined by \&{tertiarydef} */ 3191mp_expression_binary, /* an operator at the expression level (e.g., `\.<') */ 3192mp_equals, /* the operator `\.=' */ 3193mp_and_command, /* the operator `\&{and}' */ 3194mp_secondary_primary_macro, /* a macro defined by \&{primarydef} */ 3195mp_slash, /* the operator `\./' */ 3196mp_secondary_binary, /* an operator at the binary level (e.g., \&{shifted}) */ 3197mp_param_type, /* type of parameter (\&{primary}, \&{expr}, \&{suffix}, etc.) */ 3198mp_controls, /* specify control points explicitly (\&{controls}) */ 3199mp_tension, /* specify tension between knots (\&{tension}) */ 3200mp_at_least, /* bounded tension value (\&{atleast}) */ 3201mp_curl_command, /* specify curl at an end knot (\&{curl}) */ 3202mp_macro_special, /* special macro operators (\&{quote}, \.{\#\AT!}, etc.) */ 3203mp_right_delimiter, /* the right delimiter of a matching pair */ 3204mp_left_bracket, /* the operator `\.[' */ 3205mp_right_bracket, /* the operator `\.]' */ 3206mp_right_brace, /* the operator `\.{\char`\}}' */ 3207mp_with_option, /* option for filling (\&{withpen}, \&{withweight}, etc.) */ 3208mp_thing_to_add, 3209 /* variant of \&{addto} (\&{contour}, \&{doublepath}, \&{also}) */ 3210mp_of_token, /* the operator `\&{of}' */ 3211mp_to_token, /* the operator `\&{to}' */ 3212mp_step_token, /* the operator `\&{step}' */ 3213mp_until_token, /* the operator `\&{until}' */ 3214mp_within_token, /* the operator `\&{within}' */ 3215mp_lig_kern_token, 3216 /* the operators `\&{kern}' and `\.{=:}' and `\.{=:\char'174}', etc. */ 3217mp_assignment, /* the operator `\.{:=}' */ 3218mp_skip_to, /* the operation `\&{skipto}' */ 3219mp_bchar_label, /* the operator `\.{\char'174\char'174:}' */ 3220mp_double_colon, /* the operator `\.{::}' */ 3221mp_colon, /* the operator `\.:' */ 3222@# 3223mp_comma, /* the operator `\.,', must be |colon+1| */ 3224mp_semicolon, /* the operator `\.;', must be |comma+1| */ 3225mp_end_group, /* end a group (\&{endgroup}), must be |semicolon+1| */ 3226mp_stop, /* end a job (\&{end}, \&{dump}), must be |end_group+1| */ 3227mp_outer_tag, /* protection code added to command code */ 3228mp_undefined_cs, /* protection code added to command code */ 3229} mp_command_code; 3230 3231@ Variables and capsules in \MP\ have a variety of ``types,'' 3232distinguished by the code numbers defined here. These numbers are also 3233not completely arbitrary. Things that get expanded must have types 3234|>mp_independent|; a type remaining after expansion is numeric if and only if 3235its code number is at least |numeric_type|; objects containing numeric 3236parts must have types between |transform_type| and |pair_type|; 3237all other types must be smaller than |transform_type|; and among the types 3238that are not unknown or vacuous, the smallest two must be |boolean_type| 3239and |string_type| in that order. 3240 3241@d unknown_tag 1 /* this constant is added to certain type codes below */ 3242@d unknown_types mp_unknown_boolean: case mp_unknown_string: 3243 case mp_unknown_pen: case mp_unknown_picture: case mp_unknown_path 3244 3245@<Enumeration types@>= 3246typedef enum { 3247 mp_undefined = 0, /* no type has been declared */ 3248 mp_vacuous, /* no expression was present */ 3249 mp_boolean_type, /* \&{boolean} with a known value */ 3250 mp_unknown_boolean, 3251 mp_string_type, /* \&{string} with a known value */ 3252 mp_unknown_string, 3253 mp_pen_type, /* \&{pen} with a known value */ 3254 mp_unknown_pen, 3255 mp_path_type, /* \&{path} with a known value */ 3256 mp_unknown_path, 3257 mp_picture_type, /* \&{picture} with a known value */ 3258 mp_unknown_picture, 3259 mp_transform_type, /* \&{transform} variable or capsule */ 3260 mp_color_type, /* \&{color} variable or capsule */ 3261 mp_cmykcolor_type, /* \&{cmykcolor} variable or capsule */ 3262 mp_pair_type, /* \&{pair} variable or capsule */ 3263 mp_numeric_type, /* variable that has been declared \&{numeric} but not used */ 3264 mp_known, /* \&{numeric} with a known value */ 3265 mp_dependent, /* a linear combination with |fraction| coefficients */ 3266 mp_proto_dependent, /* a linear combination with |scaled| coefficients */ 3267 mp_independent, /* \&{numeric} with unknown value */ 3268 mp_token_list, /* variable name or suffix argument or text argument */ 3269 mp_structured, /* variable with subscripts and attributes */ 3270 mp_unsuffixed_macro, /* variable defined with \&{vardef} but no \.{\AT!\#} */ 3271 mp_suffixed_macro, /* variable defined with \&{vardef} and \.{\AT!\#} */ 3272/* here are some generic node types */ 3273 mp_symbol_node, 3274 mp_token_node_type, 3275 mp_value_node_type, 3276 mp_attr_node_type, 3277 mp_subscr_node_type, 3278 mp_pair_node_type, 3279 mp_transform_node_type, 3280 mp_color_node_type, 3281 mp_cmykcolor_node_type, 3282/* it is important that the next 7 items remain in this order, for export */ 3283 mp_fill_node_type, 3284 mp_stroked_node_type, 3285 mp_text_node_type, 3286 mp_start_clip_node_type, 3287 mp_start_bounds_node_type, 3288 mp_stop_clip_node_type, 3289 mp_stop_bounds_node_type, 3290 mp_dash_node_type, 3291 mp_dep_node_type, 3292 mp_if_node_type, 3293 mp_edge_header_node_type, 3294} mp_variable_type; 3295 3296@ @<Declarations@>= 3297static void mp_print_type (MP mp, quarterword t); 3298 3299@ @<Basic printing procedures@>= 3300static const char *mp_type_string (quarterword t) { 3301 const char *s = NULL; 3302 switch (t) { 3303 case mp_undefined: 3304 s = "undefined"; 3305 break; 3306 case mp_vacuous: 3307 s = "vacuous"; 3308 break; 3309 case mp_boolean_type: 3310 s = "boolean"; 3311 break; 3312 case mp_unknown_boolean: 3313 s = "unknown boolean"; 3314 break; 3315 case mp_string_type: 3316 s = "string"; 3317 break; 3318 case mp_unknown_string: 3319 s = "unknown string"; 3320 break; 3321 case mp_pen_type: 3322 s = "pen"; 3323 break; 3324 case mp_unknown_pen: 3325 s = "unknown pen"; 3326 break; 3327 case mp_path_type: 3328 s = "path"; 3329 break; 3330 case mp_unknown_path: 3331 s = "unknown path"; 3332 break; 3333 case mp_picture_type: 3334 s = "picture"; 3335 break; 3336 case mp_unknown_picture: 3337 s = "unknown picture"; 3338 break; 3339 case mp_transform_type: 3340 s = "transform"; 3341 break; 3342 case mp_color_type: 3343 s = "color"; 3344 break; 3345 case mp_cmykcolor_type: 3346 s = "cmykcolor"; 3347 break; 3348 case mp_pair_type: 3349 s = "pair"; 3350 break; 3351 case mp_known: 3352 s = "known numeric"; 3353 break; 3354 case mp_dependent: 3355 s = "dependent"; 3356 break; 3357 case mp_proto_dependent: 3358 s = "proto-dependent"; 3359 break; 3360 case mp_numeric_type: 3361 s = "numeric"; 3362 break; 3363 case mp_independent: 3364 s = "independent"; 3365 break; 3366 case mp_token_list: 3367 s = "token list"; 3368 break; 3369 case mp_structured: 3370 s = "mp_structured"; 3371 break; 3372 case mp_unsuffixed_macro: 3373 s = "unsuffixed macro"; 3374 break; 3375 case mp_suffixed_macro: 3376 s = "suffixed macro"; 3377 break; 3378 case mp_symbol_node: 3379 s = "symbol node"; 3380 break; 3381 case mp_token_node_type: 3382 s = "token node"; 3383 break; 3384 case mp_value_node_type: 3385 s = "value node"; 3386 break; 3387 case mp_attr_node_type: 3388 s = "attribute node"; 3389 break; 3390 case mp_subscr_node_type: 3391 s = "subscript node"; 3392 break; 3393 case mp_pair_node_type: 3394 s = "pair node"; 3395 break; 3396 case mp_transform_node_type: 3397 s = "transform node"; 3398 break; 3399 case mp_color_node_type: 3400 s = "color node"; 3401 break; 3402 case mp_cmykcolor_node_type: 3403 s = "cmykcolor node"; 3404 break; 3405 case mp_fill_node_type: 3406 s = "fill node"; 3407 break; 3408 case mp_stroked_node_type: 3409 s = "stroked node"; 3410 break; 3411 case mp_text_node_type: 3412 s = "text node"; 3413 break; 3414 case mp_start_clip_node_type: 3415 s = "start clip node"; 3416 break; 3417 case mp_start_bounds_node_type: 3418 s = "start bounds node"; 3419 break; 3420 case mp_stop_clip_node_type: 3421 s = "stop clip node"; 3422 break; 3423 case mp_stop_bounds_node_type: 3424 s = "stop bounds node"; 3425 break; 3426 case mp_dash_node_type: 3427 s = "dash node"; 3428 break; 3429 case mp_dep_node_type: 3430 s = "dependency node"; 3431 break; 3432 case mp_if_node_type: 3433 s = "if node"; 3434 break; 3435 case mp_edge_header_node_type: 3436 s = "edge header node"; 3437 break; 3438 default: 3439 { 3440 char ss[256]; 3441 mp_snprintf (ss, 256, "<unknown type %d>", t); 3442 s = strdup(ss); 3443 } 3444 break; 3445 } 3446 return s; 3447} 3448void mp_print_type (MP mp, quarterword t) { 3449 if (t >= 0 && t <= mp_edge_header_node_type) 3450 mp_print (mp, mp_type_string (t)); 3451 else 3452 mp_print (mp, "unknown"); 3453} 3454 3455 3456@ Values inside \MP\ are stored in non-symbolic nodes that have a |name_type| 3457as well as a |type|. The possibilities for |name_type| are defined 3458here; they will be explained in more detail later. 3459 3460@<Enumeration types...@>= 3461typedef enum { 3462 mp_root = 0, /* |name_type| at the top level of a variable */ 3463 mp_saved_root, /* same, when the variable has been saved */ 3464 mp_structured_root, /* |name_type| where a |mp_structured| branch occurs */ 3465 mp_subscr, /* |name_type| in a subscript node */ 3466 mp_attr, /* |name_type| in an attribute node */ 3467 mp_x_part_sector, /* |name_type| in the \&{xpart} of a node */ 3468 mp_y_part_sector, /* |name_type| in the \&{ypart} of a node */ 3469 mp_xx_part_sector, /* |name_type| in the \&{xxpart} of a node */ 3470 mp_xy_part_sector, /* |name_type| in the \&{xypart} of a node */ 3471 mp_yx_part_sector, /* |name_type| in the \&{yxpart} of a node */ 3472 mp_yy_part_sector, /* |name_type| in the \&{yypart} of a node */ 3473 mp_red_part_sector, /* |name_type| in the \&{redpart} of a node */ 3474 mp_green_part_sector, /* |name_type| in the \&{greenpart} of a node */ 3475 mp_blue_part_sector, /* |name_type| in the \&{bluepart} of a node */ 3476 mp_cyan_part_sector, /* |name_type| in the \&{redpart} of a node */ 3477 mp_magenta_part_sector, /* |name_type| in the \&{greenpart} of a node */ 3478 mp_yellow_part_sector, /* |name_type| in the \&{bluepart} of a node */ 3479 mp_black_part_sector, /* |name_type| in the \&{greenpart} of a node */ 3480 mp_grey_part_sector, /* |name_type| in the \&{bluepart} of a node */ 3481 mp_capsule, /* |name_type| in stashed-away subexpressions */ 3482 mp_token, /* |name_type| in a numeric token or string token */ 3483 /* Symbolic nodes also have |name_type|, which is a different enumeration */ 3484 mp_normal_sym, 3485 mp_internal_sym, /* for values of internals */ 3486 mp_macro_sym, /* for macro names */ 3487 mp_expr_sym, /* for macro parameters if type |expr| */ 3488 mp_suffix_sym, /* for macro parameters if type |suffix| */ 3489 mp_text_sym, /* for macro parameters if type |text| */ 3490 @<Operation codes@> 3491} mp_name_type_type; 3492 3493@ Primitive operations that produce values have a secondary identification 3494code in addition to their command code; it's something like genera and species. 3495For example, `\.*' has the command code |primary_binary|, and its 3496secondary identification is |times|. The secondary codes start such that 3497they don't overlap with the type codes; some type codes (e.g., |mp_string_type|) 3498are used as operators as well as type identifications. The relative values 3499are not critical, except for |true_code..false_code|, |or_op..and_op|, 3500and |filled_op..bounded_op|. The restrictions are that 3501|and_op-false_code=or_op-true_code|, that the ordering of 3502|x_part...blue_part| must match that of |x_part_sector..mp_blue_part_sector|, 3503and the ordering of |filled_op..bounded_op| must match that of the code 3504values they test for. 3505 3506@d mp_min_of mp_substring_of 3507 3508@<Operation codes@>= 3509mp_true_code, /* operation code for \.{true} */ 3510mp_false_code, /* operation code for \.{false} */ 3511mp_null_picture_code, /* operation code for \.{nullpicture} */ 3512mp_null_pen_code, /* operation code for \.{nullpen} */ 3513mp_read_string_op, /* operation code for \.{readstring} */ 3514mp_pen_circle, /* operation code for \.{pencircle} */ 3515mp_normal_deviate, /* operation code for \.{normaldeviate} */ 3516mp_read_from_op, /* operation code for \.{readfrom} */ 3517mp_close_from_op, /* operation code for \.{closefrom} */ 3518mp_odd_op, /* operation code for \.{odd} */ 3519mp_known_op, /* operation code for \.{known} */ 3520mp_unknown_op, /* operation code for \.{unknown} */ 3521mp_not_op, /* operation code for \.{not} */ 3522mp_decimal, /* operation code for \.{decimal} */ 3523mp_reverse, /* operation code for \.{reverse} */ 3524mp_make_path_op, /* operation code for \.{makepath} */ 3525mp_make_pen_op, /* operation code for \.{makepen} */ 3526mp_oct_op, /* operation code for \.{oct} */ 3527mp_hex_op, /* operation code for \.{hex} */ 3528mp_ASCII_op, /* operation code for \.{ASCII} */ 3529mp_char_op, /* operation code for \.{char} */ 3530mp_length_op, /* operation code for \.{length} */ 3531mp_turning_op, /* operation code for \.{turningnumber} */ 3532mp_color_model_part, /* operation code for \.{colormodel} */ 3533mp_x_part, /* operation code for \.{xpart} */ 3534mp_y_part, /* operation code for \.{ypart} */ 3535mp_xx_part, /* operation code for \.{xxpart} */ 3536mp_xy_part, /* operation code for \.{xypart} */ 3537mp_yx_part, /* operation code for \.{yxpart} */ 3538mp_yy_part, /* operation code for \.{yypart} */ 3539mp_red_part, /* operation code for \.{redpart} */ 3540mp_green_part, /* operation code for \.{greenpart} */ 3541mp_blue_part, /* operation code for \.{bluepart} */ 3542mp_cyan_part, /* operation code for \.{cyanpart} */ 3543mp_magenta_part, /* operation code for \.{magentapart} */ 3544mp_yellow_part, /* operation code for \.{yellowpart} */ 3545mp_black_part, /* operation code for \.{blackpart} */ 3546mp_grey_part, /* operation code for \.{greypart} */ 3547mp_font_part, /* operation code for \.{fontpart} */ 3548mp_text_part, /* operation code for \.{textpart} */ 3549mp_path_part, /* operation code for \.{pathpart} */ 3550mp_pen_part, /* operation code for \.{penpart} */ 3551mp_dash_part, /* operation code for \.{dashpart} */ 3552mp_prescript_part, /* operation code for \.{prescriptpart} */ 3553mp_postscript_part, /* operation code for \.{postscriptpart} */ 3554mp_sqrt_op, /* operation code for \.{sqrt} */ 3555mp_m_exp_op, /* operation code for \.{mexp} */ 3556mp_m_log_op, /* operation code for \.{mlog} */ 3557mp_sin_d_op, /* operation code for \.{sind} */ 3558mp_cos_d_op, /* operation code for \.{cosd} */ 3559mp_floor_op, /* operation code for \.{floor} */ 3560mp_uniform_deviate, /* operation code for \.{uniformdeviate} */ 3561mp_char_exists_op, /* operation code for \.{charexists} */ 3562mp_font_size, /* operation code for \.{fontsize} */ 3563mp_ll_corner_op, /* operation code for \.{llcorner} */ 3564mp_lr_corner_op, /* operation code for \.{lrcorner} */ 3565mp_ul_corner_op, /* operation code for \.{ulcorner} */ 3566mp_ur_corner_op, /* operation code for \.{urcorner} */ 3567mp_arc_length, /* operation code for \.{arclength} */ 3568mp_angle_op, /* operation code for \.{angle} */ 3569mp_cycle_op, /* operation code for \.{cycle} */ 3570mp_filled_op, /* operation code for \.{filled} */ 3571mp_stroked_op, /* operation code for \.{stroked} */ 3572mp_textual_op, /* operation code for \.{textual} */ 3573mp_clipped_op, /* operation code for \.{clipped} */ 3574mp_bounded_op, /* operation code for \.{bounded} */ 3575mp_plus, /* operation code for \.+ */ 3576mp_minus, /* operation code for \.- */ 3577mp_times, /* operation code for \.* */ 3578mp_over, /* operation code for \./ */ 3579mp_pythag_add, /* operation code for \.{++} */ 3580mp_pythag_sub, /* operation code for \.{+-+} */ 3581mp_or_op, /* operation code for \.{or} */ 3582mp_and_op, /* operation code for \.{and} */ 3583mp_less_than, /* operation code for \.< */ 3584mp_less_or_equal, /* operation code for \.{<=} */ 3585mp_greater_than, /* operation code for \.> */ 3586mp_greater_or_equal, /* operation code for \.{>=} */ 3587mp_equal_to, /* operation code for \.= */ 3588mp_unequal_to, /* operation code for \.{<>} */ 3589mp_concatenate, /* operation code for \.\& */ 3590mp_rotated_by, /* operation code for \.{rotated} */ 3591mp_slanted_by, /* operation code for \.{slanted} */ 3592mp_scaled_by, /* operation code for \.{scaled} */ 3593mp_shifted_by, /* operation code for \.{shifted} */ 3594mp_transformed_by, /* operation code for \.{transformed} */ 3595mp_x_scaled, /* operation code for \.{xscaled} */ 3596mp_y_scaled, /* operation code for \.{yscaled} */ 3597mp_z_scaled, /* operation code for \.{zscaled} */ 3598mp_in_font, /* operation code for \.{infont} */ 3599mp_intersect, /* operation code for \.{intersectiontimes} */ 3600mp_double_dot, /* operation code for improper \.{..} */ 3601mp_substring_of, /* operation code for \.{substring} */ 3602mp_subpath_of, /* operation code for \.{subpath} */ 3603mp_direction_time_of, /* operation code for \.{directiontime} */ 3604mp_point_of, /* operation code for \.{point} */ 3605mp_precontrol_of, /* operation code for \.{precontrol} */ 3606mp_postcontrol_of, /* operation code for \.{postcontrol} */ 3607mp_pen_offset_of, /* operation code for \.{penoffset} */ 3608mp_arc_time_of, /* operation code for \.{arctime} */ 3609mp_version, /* operation code for \.{mpversion} */ 3610mp_envelope_of, /* operation code for \.{envelope} */ 3611mp_glyph_infont, /* operation code for \.{glyph} */ 3612mp_kern_flag /* operation code for \.{kern} */ 3613 3614@ @c 3615static void mp_print_op (MP mp, quarterword c) { 3616 if (c <= mp_numeric_type) { 3617 mp_print_type (mp, c); 3618 } else { 3619 switch (c) { 3620 case mp_true_code: 3621 mp_print (mp, "true"); 3622 break; 3623 case mp_false_code: 3624 mp_print (mp, "false"); 3625 break; 3626 case mp_null_picture_code: 3627 mp_print (mp, "nullpicture"); 3628 break; 3629 case mp_null_pen_code: 3630 mp_print (mp, "nullpen"); 3631 break; 3632 case mp_read_string_op: 3633 mp_print (mp, "readstring"); 3634 break; 3635 case mp_pen_circle: 3636 mp_print (mp, "pencircle"); 3637 break; 3638 case mp_normal_deviate: 3639 mp_print (mp, "normaldeviate"); 3640 break; 3641 case mp_read_from_op: 3642 mp_print (mp, "readfrom"); 3643 break; 3644 case mp_close_from_op: 3645 mp_print (mp, "closefrom"); 3646 break; 3647 case mp_odd_op: 3648 mp_print (mp, "odd"); 3649 break; 3650 case mp_known_op: 3651 mp_print (mp, "known"); 3652 break; 3653 case mp_unknown_op: 3654 mp_print (mp, "unknown"); 3655 break; 3656 case mp_not_op: 3657 mp_print (mp, "not"); 3658 break; 3659 case mp_decimal: 3660 mp_print (mp, "decimal"); 3661 break; 3662 case mp_reverse: 3663 mp_print (mp, "reverse"); 3664 break; 3665 case mp_make_path_op: 3666 mp_print (mp, "makepath"); 3667 break; 3668 case mp_make_pen_op: 3669 mp_print (mp, "makepen"); 3670 break; 3671 case mp_oct_op: 3672 mp_print (mp, "oct"); 3673 break; 3674 case mp_hex_op: 3675 mp_print (mp, "hex"); 3676 break; 3677 case mp_ASCII_op: 3678 mp_print (mp, "ASCII"); 3679 break; 3680 case mp_char_op: 3681 mp_print (mp, "char"); 3682 break; 3683 case mp_length_op: 3684 mp_print (mp, "length"); 3685 break; 3686 case mp_turning_op: 3687 mp_print (mp, "turningnumber"); 3688 break; 3689 case mp_x_part: 3690 mp_print (mp, "xpart"); 3691 break; 3692 case mp_y_part: 3693 mp_print (mp, "ypart"); 3694 break; 3695 case mp_xx_part: 3696 mp_print (mp, "xxpart"); 3697 break; 3698 case mp_xy_part: 3699 mp_print (mp, "xypart"); 3700 break; 3701 case mp_yx_part: 3702 mp_print (mp, "yxpart"); 3703 break; 3704 case mp_yy_part: 3705 mp_print (mp, "yypart"); 3706 break; 3707 case mp_red_part: 3708 mp_print (mp, "redpart"); 3709 break; 3710 case mp_green_part: 3711 mp_print (mp, "greenpart"); 3712 break; 3713 case mp_blue_part: 3714 mp_print (mp, "bluepart"); 3715 break; 3716 case mp_cyan_part: 3717 mp_print (mp, "cyanpart"); 3718 break; 3719 case mp_magenta_part: 3720 mp_print (mp, "magentapart"); 3721 break; 3722 case mp_yellow_part: 3723 mp_print (mp, "yellowpart"); 3724 break; 3725 case mp_black_part: 3726 mp_print (mp, "blackpart"); 3727 break; 3728 case mp_grey_part: 3729 mp_print (mp, "greypart"); 3730 break; 3731 case mp_color_model_part: 3732 mp_print (mp, "colormodel"); 3733 break; 3734 case mp_font_part: 3735 mp_print (mp, "fontpart"); 3736 break; 3737 case mp_text_part: 3738 mp_print (mp, "textpart"); 3739 break; 3740 case mp_prescript_part: 3741 mp_print (mp, "prescriptpart"); 3742 break; 3743 case mp_postscript_part: 3744 mp_print (mp, "postscriptpart"); 3745 break; 3746 case mp_path_part: 3747 mp_print (mp, "pathpart"); 3748 break; 3749 case mp_pen_part: 3750 mp_print (mp, "penpart"); 3751 break; 3752 case mp_dash_part: 3753 mp_print (mp, "dashpart"); 3754 break; 3755 case mp_sqrt_op: 3756 mp_print (mp, "sqrt"); 3757 break; 3758 case mp_m_exp_op: 3759 mp_print (mp, "mexp"); 3760 break; 3761 case mp_m_log_op: 3762 mp_print (mp, "mlog"); 3763 break; 3764 case mp_sin_d_op: 3765 mp_print (mp, "sind"); 3766 break; 3767 case mp_cos_d_op: 3768 mp_print (mp, "cosd"); 3769 break; 3770 case mp_floor_op: 3771 mp_print (mp, "floor"); 3772 break; 3773 case mp_uniform_deviate: 3774 mp_print (mp, "uniformdeviate"); 3775 break; 3776 case mp_char_exists_op: 3777 mp_print (mp, "charexists"); 3778 break; 3779 case mp_font_size: 3780 mp_print (mp, "fontsize"); 3781 break; 3782 case mp_ll_corner_op: 3783 mp_print (mp, "llcorner"); 3784 break; 3785 case mp_lr_corner_op: 3786 mp_print (mp, "lrcorner"); 3787 break; 3788 case mp_ul_corner_op: 3789 mp_print (mp, "ulcorner"); 3790 break; 3791 case mp_ur_corner_op: 3792 mp_print (mp, "urcorner"); 3793 break; 3794 case mp_arc_length: 3795 mp_print (mp, "arclength"); 3796 break; 3797 case mp_angle_op: 3798 mp_print (mp, "angle"); 3799 break; 3800 case mp_cycle_op: 3801 mp_print (mp, "cycle"); 3802 break; 3803 case mp_filled_op: 3804 mp_print (mp, "filled"); 3805 break; 3806 case mp_stroked_op: 3807 mp_print (mp, "stroked"); 3808 break; 3809 case mp_textual_op: 3810 mp_print (mp, "textual"); 3811 break; 3812 case mp_clipped_op: 3813 mp_print (mp, "clipped"); 3814 break; 3815 case mp_bounded_op: 3816 mp_print (mp, "bounded"); 3817 break; 3818 case mp_plus: 3819 mp_print_char (mp, xord ('+')); 3820 break; 3821 case mp_minus: 3822 mp_print_char (mp, xord ('-')); 3823 break; 3824 case mp_times: 3825 mp_print_char (mp, xord ('*')); 3826 break; 3827 case mp_over: 3828 mp_print_char (mp, xord ('/')); 3829 break; 3830 case mp_pythag_add: 3831 mp_print (mp, "++"); 3832 break; 3833 case mp_pythag_sub: 3834 mp_print (mp, "+-+"); 3835 break; 3836 case mp_or_op: 3837 mp_print (mp, "or"); 3838 break; 3839 case mp_and_op: 3840 mp_print (mp, "and"); 3841 break; 3842 case mp_less_than: 3843 mp_print_char (mp, xord ('<')); 3844 break; 3845 case mp_less_or_equal: 3846 mp_print (mp, "<="); 3847 break; 3848 case mp_greater_than: 3849 mp_print_char (mp, xord ('>')); 3850 break; 3851 case mp_greater_or_equal: 3852 mp_print (mp, ">="); 3853 break; 3854 case mp_equal_to: 3855 mp_print_char (mp, xord ('=')); 3856 break; 3857 case mp_unequal_to: 3858 mp_print (mp, "<>"); 3859 break; 3860 case mp_concatenate: 3861 mp_print (mp, "&"); 3862 break; 3863 case mp_rotated_by: 3864 mp_print (mp, "rotated"); 3865 break; 3866 case mp_slanted_by: 3867 mp_print (mp, "slanted"); 3868 break; 3869 case mp_scaled_by: 3870 mp_print (mp, "scaled"); 3871 break; 3872 case mp_shifted_by: 3873 mp_print (mp, "shifted"); 3874 break; 3875 case mp_transformed_by: 3876 mp_print (mp, "transformed"); 3877 break; 3878 case mp_x_scaled: 3879 mp_print (mp, "xscaled"); 3880 break; 3881 case mp_y_scaled: 3882 mp_print (mp, "yscaled"); 3883 break; 3884 case mp_z_scaled: 3885 mp_print (mp, "zscaled"); 3886 break; 3887 case mp_in_font: 3888 mp_print (mp, "infont"); 3889 break; 3890 case mp_intersect: 3891 mp_print (mp, "intersectiontimes"); 3892 break; 3893 case mp_substring_of: 3894 mp_print (mp, "substring"); 3895 break; 3896 case mp_subpath_of: 3897 mp_print (mp, "subpath"); 3898 break; 3899 case mp_direction_time_of: 3900 mp_print (mp, "directiontime"); 3901 break; 3902 case mp_point_of: 3903 mp_print (mp, "point"); 3904 break; 3905 case mp_precontrol_of: 3906 mp_print (mp, "precontrol"); 3907 break; 3908 case mp_postcontrol_of: 3909 mp_print (mp, "postcontrol"); 3910 break; 3911 case mp_pen_offset_of: 3912 mp_print (mp, "penoffset"); 3913 break; 3914 case mp_arc_time_of: 3915 mp_print (mp, "arctime"); 3916 break; 3917 case mp_version: 3918 mp_print (mp, "mpversion"); 3919 break; 3920 case mp_envelope_of: 3921 mp_print (mp, "envelope"); 3922 break; 3923 case mp_glyph_infont: 3924 mp_print (mp, "glyph"); 3925 break; 3926 default: 3927 mp_print (mp, ".."); 3928 break; 3929 } 3930 } 3931} 3932 3933 3934@ \MP\ also has a bunch of internal parameters that a user might want to 3935fuss with. Every such parameter has an identifying code number, defined here. 3936 3937@<Types...@>= 3938enum mp_given_internal { 3939 mp_output_template = 1, /* a string set up by \&{outputtemplate} */ 3940 mp_output_filename, /* the output file name, accessible as \&{outputfilename} */ 3941 mp_output_format, /* the output format set up by \&{outputformat} */ 3942 mp_output_format_options, /* the output format options set up by \&{outputformatoptions} */ 3943 mp_number_system, /* the number system as set up by \&{numbersystem} */ 3944 mp_number_precision, /* the number system precision as set up by \&{numberprecision} */ 3945 mp_job_name, /* the perceived jobname, as set up from the options stucture, 3946 the name of the input file, or by \&{jobname} */ 3947 mp_tracing_titles, /* show titles online when they appear */ 3948 mp_tracing_equations, /* show each variable when it becomes known */ 3949 mp_tracing_capsules, /* show capsules too */ 3950 mp_tracing_choices, /* show the control points chosen for paths */ 3951 mp_tracing_specs, /* show path subdivision prior to filling with polygonal a pen */ 3952 mp_tracing_commands, /* show commands and operations before they are performed */ 3953 mp_tracing_restores, /* show when a variable or internal is restored */ 3954 mp_tracing_macros, /* show macros before they are expanded */ 3955 mp_tracing_output, /* show digitized edges as they are output */ 3956 mp_tracing_stats, /* show memory usage at end of job */ 3957 mp_tracing_lost_chars, /* show characters that aren't \&{infont} */ 3958 mp_tracing_online, /* show long diagnostics on terminal and in the log file */ 3959 mp_year, /* the current year (e.g., 1984) */ 3960 mp_month, /* the current month (e.g., 3 $\equiv$ March) */ 3961 mp_day, /* the current day of the month */ 3962 mp_time, /* the number of minutes past midnight when this job started */ 3963 mp_hour, /* the number of hours past midnight when this job started */ 3964 mp_minute, /* the number of minutes in that hour when this job started */ 3965 mp_char_code, /* the number of the next character to be output */ 3966 mp_char_ext, /* the extension code of the next character to be output */ 3967 mp_char_wd, /* the width of the next character to be output */ 3968 mp_char_ht, /* the height of the next character to be output */ 3969 mp_char_dp, /* the depth of the next character to be output */ 3970 mp_char_ic, /* the italic correction of the next character to be output */ 3971 mp_design_size, /* the unit of measure used for |mp_char_wd..mp_char_ic|, in points */ 3972 mp_pausing, /* positive to display lines on the terminal before they are read */ 3973 mp_showstopping, /* positive to stop after each \&{show} command */ 3974 mp_fontmaking, /* positive if font metric output is to be produced */ 3975 mp_linejoin, /* as in \ps: 0 for mitered, 1 for round, 2 for beveled */ 3976 mp_linecap, /* as in \ps: 0 for butt, 1 for round, 2 for square */ 3977 mp_miterlimit, /* controls miter length as in \ps */ 3978 mp_warning_check, /* controls error message when variable value is large */ 3979 mp_boundary_char, /* the right boundary character for ligatures */ 3980 mp_prologues, /* positive to output conforming PostScript using built-in fonts */ 3981 mp_true_corners, /* positive to make \&{llcorner} etc. ignore \&{setbounds} */ 3982 mp_default_color_model, /* the default color model for unspecified items */ 3983 mp_restore_clip_color, 3984 mp_procset, /* wether or not create PostScript command shortcuts */ 3985 mp_hppp, /* horizontal pixels per point (for png output) */ 3986 mp_vppp, /* vertical pixels per point (for png output) */ 3987 mp_gtroffmode, /* whether the user specified |-troff| on the command line */ 3988}; 3989typedef struct { 3990 mp_value v; 3991 char *intname; 3992} mp_internal; 3993 3994 3995@ @<MPlib internal header stuff@>= 3996#define internal_value(A) mp->internal[(A)].v.data.n 3997#define set_internal_from_number(A,B) do { \ 3998 number_clone (internal_value ((A)),(B));\ 3999} while (0) 4000#define internal_string(A) (mp_string)mp->internal[(A)].v.data.str 4001#define set_internal_string(A,B) mp->internal[(A)].v.data.str=(B) 4002#define internal_name(A) mp->internal[(A)].intname 4003#define set_internal_name(A,B) mp->internal[(A)].intname=(B) 4004#define internal_type(A) (mp_variable_type)mp->internal[(A)].v.type 4005#define set_internal_type(A,B) mp->internal[(A)].v.type=(B) 4006#define set_internal_from_cur_exp(A) do { \ 4007 if (internal_type ((A)) == mp_string_type) { \ 4008 add_str_ref (cur_exp_str ()); \ 4009 set_internal_string ((A), cur_exp_str ()); \ 4010 } else { \ 4011 set_internal_from_number ((A), cur_exp_value_number ()); \ 4012 } \ 4013} while (0) 4014 4015 4016 4017@ 4018 4019@d max_given_internal mp_gtroffmode 4020 4021@<Glob...@>= 4022mp_internal *internal; /* the values of internal quantities */ 4023int int_ptr; /* the maximum internal quantity defined so far */ 4024int max_internal; /* current maximum number of internal quantities */ 4025 4026@ @<Option variables@>= 4027int troff_mode; 4028 4029@ @<Allocate or initialize ...@>= 4030mp->max_internal = 2 * max_given_internal; 4031mp->internal = xmalloc ((mp->max_internal + 1), sizeof (mp_internal)); 4032memset (mp->internal, 0, 4033 (size_t) (mp->max_internal + 1) * sizeof (mp_internal)); 4034{ 4035 int i; 4036 for (i = 1; i <= mp->max_internal; i++) { 4037 new_number(mp->internal[i].v.data.n); 4038 } 4039 for (i = 1; i <= max_given_internal; i++) { 4040 set_internal_type (i, mp_known); 4041 } 4042} 4043set_internal_type (mp_output_format, mp_string_type); 4044set_internal_type (mp_output_filename, mp_string_type); 4045set_internal_type (mp_output_format_options, mp_string_type); 4046set_internal_type (mp_output_template, mp_string_type); 4047set_internal_type (mp_number_system, mp_string_type); 4048set_internal_type (mp_job_name, mp_string_type); 4049mp->troff_mode = (opt->troff_mode > 0 ? true : false); 4050 4051@ @<Exported function ...@>= 4052int mp_troff_mode (MP mp); 4053 4054@ @c 4055int mp_troff_mode (MP mp) { 4056 return mp->troff_mode; 4057} 4058 4059 4060@ @<Set initial ...@>= 4061mp->int_ptr = max_given_internal; 4062 4063@ The symbolic names for internal quantities are put into \MP's hash table 4064by using a routine called |primitive|, which will be defined later. Let us 4065enter them now, so that we don't have to list all those names again 4066anywhere else. 4067 4068@<Put each of \MP's primitives into the hash table@>= 4069mp_primitive (mp, "tracingtitles", mp_internal_quantity, mp_tracing_titles); 4070@:tracingtitles_}{\&{tracingtitles} primitive@>; 4071mp_primitive (mp, "tracingequations", mp_internal_quantity, mp_tracing_equations); 4072@:mp_tracing_equations_}{\&{tracingequations} primitive@>; 4073mp_primitive (mp, "tracingcapsules", mp_internal_quantity, mp_tracing_capsules); 4074@:mp_tracing_capsules_}{\&{tracingcapsules} primitive@>; 4075mp_primitive (mp, "tracingchoices", mp_internal_quantity, mp_tracing_choices); 4076@:mp_tracing_choices_}{\&{tracingchoices} primitive@>; 4077mp_primitive (mp, "tracingspecs", mp_internal_quantity, mp_tracing_specs); 4078@:mp_tracing_specs_}{\&{tracingspecs} primitive@>; 4079mp_primitive (mp, "tracingcommands", mp_internal_quantity, mp_tracing_commands); 4080@:mp_tracing_commands_}{\&{tracingcommands} primitive@>; 4081mp_primitive (mp, "tracingrestores", mp_internal_quantity, mp_tracing_restores); 4082@:mp_tracing_restores_}{\&{tracingrestores} primitive@>; 4083mp_primitive (mp, "tracingmacros", mp_internal_quantity, mp_tracing_macros); 4084@:mp_tracing_macros_}{\&{tracingmacros} primitive@>; 4085mp_primitive (mp, "tracingoutput", mp_internal_quantity, mp_tracing_output); 4086@:mp_tracing_output_}{\&{tracingoutput} primitive@>; 4087mp_primitive (mp, "tracingstats", mp_internal_quantity, mp_tracing_stats); 4088@:mp_tracing_stats_}{\&{tracingstats} primitive@>; 4089mp_primitive (mp, "tracinglostchars", mp_internal_quantity, mp_tracing_lost_chars); 4090@:mp_tracing_lost_chars_}{\&{tracinglostchars} primitive@>; 4091mp_primitive (mp, "tracingonline", mp_internal_quantity, mp_tracing_online); 4092@:mp_tracing_online_}{\&{tracingonline} primitive@>; 4093mp_primitive (mp, "year", mp_internal_quantity, mp_year); 4094@:mp_year_}{\&{year} primitive@>; 4095mp_primitive (mp, "month", mp_internal_quantity, mp_month); 4096@:mp_month_}{\&{month} primitive@>; 4097mp_primitive (mp, "day", mp_internal_quantity, mp_day); 4098@:mp_day_}{\&{day} primitive@>; 4099mp_primitive (mp, "time", mp_internal_quantity, mp_time); 4100@:time_}{\&{time} primitive@>; 4101mp_primitive (mp, "hour", mp_internal_quantity, mp_hour); 4102@:hour_}{\&{hour} primitive@>; 4103mp_primitive (mp, "minute", mp_internal_quantity, mp_minute); 4104@:minute_}{\&{minute} primitive@>; 4105mp_primitive (mp, "charcode", mp_internal_quantity, mp_char_code); 4106@:mp_char_code_}{\&{charcode} primitive@>; 4107mp_primitive (mp, "charext", mp_internal_quantity, mp_char_ext); 4108@:mp_char_ext_}{\&{charext} primitive@>; 4109mp_primitive (mp, "charwd", mp_internal_quantity, mp_char_wd); 4110@:mp_char_wd_}{\&{charwd} primitive@>; 4111mp_primitive (mp, "charht", mp_internal_quantity, mp_char_ht); 4112@:mp_char_ht_}{\&{charht} primitive@>; 4113mp_primitive (mp, "chardp", mp_internal_quantity, mp_char_dp); 4114@:mp_char_dp_}{\&{chardp} primitive@>; 4115mp_primitive (mp, "charic", mp_internal_quantity, mp_char_ic); 4116@:mp_char_ic_}{\&{charic} primitive@>; 4117mp_primitive (mp, "designsize", mp_internal_quantity, mp_design_size); 4118@:mp_design_size_}{\&{designsize} primitive@>; 4119mp_primitive (mp, "pausing", mp_internal_quantity, mp_pausing); 4120@:mp_pausing_}{\&{pausing} primitive@>; 4121mp_primitive (mp, "showstopping", mp_internal_quantity, mp_showstopping); 4122@:mp_showstopping_}{\&{showstopping} primitive@>; 4123mp_primitive (mp, "fontmaking", mp_internal_quantity, mp_fontmaking); 4124@:mp_fontmaking_}{\&{fontmaking} primitive@>; 4125mp_primitive (mp, "linejoin", mp_internal_quantity, mp_linejoin); 4126@:mp_linejoin_}{\&{linejoin} primitive@>; 4127mp_primitive (mp, "linecap", mp_internal_quantity, mp_linecap); 4128@:mp_linecap_}{\&{linecap} primitive@>; 4129mp_primitive (mp, "miterlimit", mp_internal_quantity, mp_miterlimit); 4130@:mp_miterlimit_}{\&{miterlimit} primitive@>; 4131mp_primitive (mp, "warningcheck", mp_internal_quantity, mp_warning_check); 4132@:mp_warning_check_}{\&{warningcheck} primitive@>; 4133mp_primitive (mp, "boundarychar", mp_internal_quantity, mp_boundary_char); 4134@:mp_boundary_char_}{\&{boundarychar} primitive@>; 4135mp_primitive (mp, "prologues", mp_internal_quantity, mp_prologues); 4136@:mp_prologues_}{\&{prologues} primitive@>; 4137mp_primitive (mp, "truecorners", mp_internal_quantity, mp_true_corners); 4138@:mp_true_corners_}{\&{truecorners} primitive@>; 4139mp_primitive (mp, "mpprocset", mp_internal_quantity, mp_procset); 4140@:mp_procset_}{\&{mpprocset} primitive@>; 4141mp_primitive (mp, "troffmode", mp_internal_quantity, mp_gtroffmode); 4142@:troffmode_}{\&{troffmode} primitive@>; 4143mp_primitive (mp, "defaultcolormodel", mp_internal_quantity, 4144 mp_default_color_model); 4145@:mp_default_color_model_}{\&{defaultcolormodel} primitive@>; 4146mp_primitive (mp, "restoreclipcolor", mp_internal_quantity, mp_restore_clip_color); 4147@:mp_restore_clip_color_}{\&{restoreclipcolor} primitive@>; 4148mp_primitive (mp, "outputtemplate", mp_internal_quantity, mp_output_template); 4149@:mp_output_template_}{\&{outputtemplate} primitive@>; 4150mp_primitive (mp, "outputfilename", mp_internal_quantity, mp_output_filename); 4151@:mp_output_filename_}{\&{outputfilename} primitive@>; 4152mp_primitive (mp, "numbersystem", mp_internal_quantity, mp_number_system); 4153@:mp_number_system_}{\&{numbersystem} primitive@>; 4154mp_primitive (mp, "numberprecision", mp_internal_quantity, mp_number_precision); 4155@:mp_number_precision_}{\&{numberprecision} primitive@>; 4156mp_primitive (mp, "outputformat", mp_internal_quantity, mp_output_format); 4157@:mp_output_format_}{\&{outputformat} primitive@>; 4158mp_primitive (mp, "outputformatoptions", mp_internal_quantity, mp_output_format_options); 4159@:mp_output_format_options_}{\&{outputformatoptions} primitive@>; 4160mp_primitive (mp, "jobname", mp_internal_quantity, mp_job_name); 4161@:mp_job_name_}{\&{jobname} primitive@> 4162mp_primitive (mp, "hppp", mp_internal_quantity, mp_hppp); 4163@:mp_hppp_}{\&{hppp} primitive@>; 4164mp_primitive (mp, "vppp", mp_internal_quantity, mp_vppp); 4165@:mp_vppp_}{\&{vppp} primitive@>; 4166 4167 4168@ Colors can be specified in four color models. In the special 4169case of |no_model|, MetaPost does not output any color operator to 4170the postscript output. 4171 4172Note: these values are passed directly on to |with_option|. This only 4173works because the other possible values passed to |with_option| are 41748 and 10 respectively (from |with_pen| and |with_picture|). 4175 4176There is a first state, that is only used for |gs_colormodel|. It flags 4177the fact that there has not been any kind of color specification by 4178the user so far in the game. 4179 4180@<MPlib header stuff@>= 4181enum mp_color_model { 4182 mp_no_model = 1, 4183 mp_grey_model = 3, 4184 mp_rgb_model = 5, 4185 mp_cmyk_model = 7, 4186 mp_uninitialized_model = 9 4187}; 4188 4189 4190@ @<Initialize table entries@>= 4191set_internal_from_number (mp_default_color_model, unity_t); 4192number_multiply_int (internal_value (mp_default_color_model), mp_rgb_model); 4193number_clone (internal_value (mp_restore_clip_color), unity_t); 4194number_clone (internal_value (mp_hppp), unity_t); 4195number_clone (internal_value (mp_vppp), unity_t); 4196set_internal_string (mp_output_template, mp_intern (mp, "%j.%c")); 4197set_internal_string (mp_output_filename, mp_intern (mp, "")); 4198set_internal_string (mp_output_format, mp_intern (mp, "eps")); 4199set_internal_string (mp_output_format_options, mp_intern (mp, "")); 4200set_internal_string (mp_number_system, mp_intern (mp, "scaled")); 4201set_internal_from_number (mp_number_precision, precision_default); 4202#if DEBUG 4203number_clone (internal_value (mp_tracing_titles), three_t); 4204number_clone (internal_value (mp_tracing_equations), three_t); 4205number_clone (internal_value (mp_tracing_capsules), three_t); 4206number_clone (internal_value (mp_tracing_choices), three_t); 4207number_clone (internal_value (mp_tracing_specs), three_t); 4208number_clone (internal_value (mp_tracing_commands), three_t); 4209number_clone (internal_value (mp_tracing_restores), three_t); 4210number_clone (internal_value (mp_tracing_macros), three_t); 4211number_clone (internal_value (mp_tracing_output), three_t); 4212number_clone (internal_value (mp_tracing_stats), three_t); 4213number_clone (internal_value (mp_tracing_lost_chars), three_t); 4214number_clone (internal_value (mp_tracing_online), three_t); 4215#endif 4216 4217@ Well, we do have to list the names one more time, for use in symbolic 4218printouts. 4219 4220@<Initialize table...@>= 4221set_internal_name (mp_tracing_titles, xstrdup ("tracingtitles")); 4222set_internal_name (mp_tracing_equations, xstrdup ("tracingequations")); 4223set_internal_name (mp_tracing_capsules, xstrdup ("tracingcapsules")); 4224set_internal_name (mp_tracing_choices, xstrdup ("tracingchoices")); 4225set_internal_name (mp_tracing_specs, xstrdup ("tracingspecs")); 4226set_internal_name (mp_tracing_commands, xstrdup ("tracingcommands")); 4227set_internal_name (mp_tracing_restores, xstrdup ("tracingrestores")); 4228set_internal_name (mp_tracing_macros, xstrdup ("tracingmacros")); 4229set_internal_name (mp_tracing_output, xstrdup ("tracingoutput")); 4230set_internal_name (mp_tracing_stats, xstrdup ("tracingstats")); 4231set_internal_name (mp_tracing_lost_chars, xstrdup ("tracinglostchars")); 4232set_internal_name (mp_tracing_online, xstrdup ("tracingonline")); 4233set_internal_name (mp_year, xstrdup ("year")); 4234set_internal_name (mp_month, xstrdup ("month")); 4235set_internal_name (mp_day, xstrdup ("day")); 4236set_internal_name (mp_time, xstrdup ("time")); 4237set_internal_name (mp_hour, xstrdup ("hour")); 4238set_internal_name (mp_minute, xstrdup ("minute")); 4239set_internal_name (mp_char_code, xstrdup ("charcode")); 4240set_internal_name (mp_char_ext, xstrdup ("charext")); 4241set_internal_name (mp_char_wd, xstrdup ("charwd")); 4242set_internal_name (mp_char_ht, xstrdup ("charht")); 4243set_internal_name (mp_char_dp, xstrdup ("chardp")); 4244set_internal_name (mp_char_ic, xstrdup ("charic")); 4245set_internal_name (mp_design_size, xstrdup ("designsize")); 4246set_internal_name (mp_pausing, xstrdup ("pausing")); 4247set_internal_name (mp_showstopping, xstrdup ("showstopping")); 4248set_internal_name (mp_fontmaking, xstrdup ("fontmaking")); 4249set_internal_name (mp_linejoin, xstrdup ("linejoin")); 4250set_internal_name (mp_linecap, xstrdup ("linecap")); 4251set_internal_name (mp_miterlimit, xstrdup ("miterlimit")); 4252set_internal_name (mp_warning_check, xstrdup ("warningcheck")); 4253set_internal_name (mp_boundary_char, xstrdup ("boundarychar")); 4254set_internal_name (mp_prologues, xstrdup ("prologues")); 4255set_internal_name (mp_true_corners, xstrdup ("truecorners")); 4256set_internal_name (mp_default_color_model, xstrdup ("defaultcolormodel")); 4257set_internal_name (mp_procset, xstrdup ("mpprocset")); 4258set_internal_name (mp_gtroffmode, xstrdup ("troffmode")); 4259set_internal_name (mp_restore_clip_color, xstrdup ("restoreclipcolor")); 4260set_internal_name (mp_output_template, xstrdup ("outputtemplate")); 4261set_internal_name (mp_output_filename, xstrdup ("outputfilename")); 4262set_internal_name (mp_output_format, xstrdup ("outputformat")); 4263set_internal_name (mp_output_format_options, xstrdup ("outputformatoptions")); 4264set_internal_name (mp_job_name, xstrdup ("jobname")); 4265set_internal_name (mp_number_system, xstrdup ("numbersystem")); 4266set_internal_name (mp_number_precision, xstrdup ("numberprecision")); 4267set_internal_name (mp_hppp, xstrdup ("hppp")); 4268set_internal_name (mp_vppp, xstrdup ("vppp")); 4269 4270@ The following procedure, which is called just before \MP\ initializes its 4271input and output, establishes the initial values of the date and time. 4272@^system dependencies@> 4273 4274Note that the values are |scaled| integers. Hence \MP\ can no longer 4275be used after the year 32767. 4276 4277@c 4278static void mp_fix_date_and_time (MP mp) { 4279 time_t aclock = time ((time_t *) 0); 4280 struct tm *tmptr = localtime (&aclock); 4281 set_internal_from_number (mp_time, unity_t); 4282 number_multiply_int (internal_value(mp_time), (tmptr->tm_hour * 60 + tmptr->tm_min)); 4283 set_internal_from_number (mp_hour, unity_t); 4284 number_multiply_int (internal_value(mp_hour), (tmptr->tm_hour)); 4285 set_internal_from_number (mp_minute, unity_t); 4286 number_multiply_int (internal_value(mp_minute), (tmptr->tm_min)); 4287 set_internal_from_number (mp_day, unity_t); 4288 number_multiply_int (internal_value(mp_day), (tmptr->tm_mday)); 4289 set_internal_from_number (mp_month, unity_t); 4290 number_multiply_int (internal_value(mp_month), (tmptr->tm_mon + 1)); 4291 set_internal_from_number (mp_year, unity_t); 4292 number_multiply_int (internal_value(mp_year), (tmptr->tm_year + 1900)); 4293} 4294 4295 4296@ @<Declarations@>= 4297static void mp_fix_date_and_time (MP mp); 4298 4299@ \MP\ is occasionally supposed to print diagnostic information that 4300goes only into the transcript file, unless |mp_tracing_online| is positive. 4301Now that we have defined |mp_tracing_online| we can define 4302two routines that adjust the destination of print commands: 4303 4304@<Declarations@>= 4305static void mp_begin_diagnostic (MP mp); 4306static void mp_end_diagnostic (MP mp, boolean blank_line); 4307static void mp_print_diagnostic (MP mp, const char *s, const char *t, 4308 boolean nuline); 4309 4310@ @<Basic printing...@>= 4311void mp_begin_diagnostic (MP mp) { /* prepare to do some tracing */ 4312 mp->old_setting = mp->selector; 4313 if (number_nonpositive(internal_value (mp_tracing_online)) 4314 && (mp->selector == term_and_log)) { 4315 decr (mp->selector); 4316 if (mp->history == mp_spotless) 4317 mp->history = mp_warning_issued; 4318 } 4319} 4320@# 4321void mp_end_diagnostic (MP mp, boolean blank_line) { 4322 /* restore proper conditions after tracing */ 4323 mp_print_nl (mp, ""); 4324 if (blank_line) 4325 mp_print_ln (mp); 4326 mp->selector = mp->old_setting; 4327} 4328 4329 4330@ 4331 4332@<Glob...@>= 4333unsigned int old_setting; 4334 4335@ We will occasionally use |begin_diagnostic| in connection with line-number 4336printing, as follows. (The parameter |s| is typically |"Path"| or 4337|"Cycle spec"|, etc.) 4338 4339@<Basic printing...@>= 4340void mp_print_diagnostic (MP mp, const char *s, const char *t, boolean nuline) { 4341 mp_begin_diagnostic (mp); 4342 if (nuline) 4343 mp_print_nl (mp, s); 4344 else 4345 mp_print (mp, s); 4346 mp_print (mp, " at line "); 4347 mp_print_int (mp, mp_true_line (mp)); 4348 mp_print (mp, t); 4349 mp_print_char (mp, xord (':')); 4350} 4351 4352 4353@ The 256 |ASCII_code| characters are grouped into classes by means of 4354the |char_class| table. Individual class numbers have no semantic 4355or syntactic significance, except in a few instances defined here. 4356There's also |max_class|, which can be used as a basis for additional 4357class numbers in nonstandard extensions of \MP. 4358 4359@d digit_class 0 /* the class number of \.{0123456789} */ 4360@d period_class 1 /* the class number of `\..' */ 4361@d space_class 2 /* the class number of spaces and nonstandard characters */ 4362@d percent_class 3 /* the class number of `\.\%' */ 4363@d string_class 4 /* the class number of `\."' */ 4364@d right_paren_class 8 /* the class number of `\.)' */ 4365@d isolated_classes 5: case 6: case 7: case 8 /* characters that make length-one tokens only */ 4366@d letter_class 9 /* letters and the underline character */ 4367@d mp_left_bracket_class 17 /* `\.[' */ 4368@d mp_right_bracket_class 18 /* `\.]' */ 4369@d invalid_class 20 /* bad character in the input */ 4370@d max_class 20 /* the largest class number */ 4371 4372@<Glob...@>= 4373#define digit_class 0 /* the class number of \.{0123456789} */ 4374int char_class[256]; /* the class numbers */ 4375 4376@ If changes are made to accommodate non-ASCII character sets, they should 4377follow the guidelines in Appendix~C of {\sl The {\logos METAFONT\/}book}. 4378@:METAFONTbook}{\sl The {\logos METAFONT\/}book@> 4379@^system dependencies@> 4380 4381@<Set initial ...@>= 4382for (k = '0'; k <= '9'; k++) 4383 mp->char_class[k] = digit_class; 4384mp->char_class['.'] = period_class; 4385mp->char_class[' '] = space_class; 4386mp->char_class['%'] = percent_class; 4387mp->char_class['"'] = string_class; 4388mp->char_class[','] = 5; 4389mp->char_class[';'] = 6; 4390mp->char_class['('] = 7; 4391mp->char_class[')'] = right_paren_class; 4392for (k = 'A'; k <= 'Z'; k++) 4393 mp->char_class[k] = letter_class; 4394for (k = 'a'; k <= 'z'; k++) 4395 mp->char_class[k] = letter_class; 4396mp->char_class['_'] = letter_class; 4397mp->char_class['<'] = 10; 4398mp->char_class['='] = 10; 4399mp->char_class['>'] = 10; 4400mp->char_class[':'] = 10; 4401mp->char_class['|'] = 10; 4402mp->char_class['`'] = 11; 4403mp->char_class['\''] = 11; 4404mp->char_class['+'] = 12; 4405mp->char_class['-'] = 12; 4406mp->char_class['/'] = 13; 4407mp->char_class['*'] = 13; 4408mp->char_class['\\'] = 13; 4409mp->char_class['!'] = 14; 4410mp->char_class['?'] = 14; 4411mp->char_class['#'] = 15; 4412mp->char_class['&'] = 15; 4413mp->char_class['@@'] = 15; 4414mp->char_class['$'] = 15; 4415mp->char_class['^'] = 16; 4416mp->char_class['~'] = 16; 4417mp->char_class['['] = mp_left_bracket_class; 4418mp->char_class[']'] = mp_right_bracket_class; 4419mp->char_class['{'] = 19; 4420mp->char_class['}'] = 19; 4421for (k = 0; k < ' '; k++) 4422 mp->char_class[k] = invalid_class; 4423mp->char_class['\t'] = space_class; 4424mp->char_class['\f'] = space_class; 4425for (k = 127; k <= 255; k++) 4426 mp->char_class[k] = invalid_class; 4427 4428@* The hash table. 4429 4430Symbolic tokens are stored in and retrieved from an AVL tree. This 4431is not as fast as an actual hash table, but it is easily extensible. 4432 4433A symbolic token contains a pointer to the |mp_string| that 4434contains the string representation of the symbol, a |halfword| 4435that holds the current command value of the token, and an 4436|mp_value| for the associated equivalent. 4437 4438@d set_text(A) do { 4439 FUNCTION_TRACE3 ("set_text(%p, %p)\n",(A),(B)); 4440 (A)->text=(B) ; 4441} while (0) 4442 4443@d set_eq_type(A,B) do { 4444 FUNCTION_TRACE3 ("set_eq_type(%p, %d)\n",(A),(B)); 4445 (A)->type=(B) ; 4446} while (0) 4447 4448@d set_equiv(A,B) do { 4449 FUNCTION_TRACE3 ("set_equiv(%p, %d)\n",(A),(B)); 4450 (A)->v.data.node=NULL ; 4451 (A)->v.data.indep.serial=(B); 4452} while (0) 4453 4454@d set_equiv_node(A,B) do { 4455 FUNCTION_TRACE3 ("set_equiv_node(%p, %p)\n",(A),(B)); 4456 (A)->v.data.node=(B) ; 4457 (A)->v.data.indep.serial=0; 4458} while (0) 4459 4460@d set_equiv_sym(A,B) do { 4461 FUNCTION_TRACE3 ("set_equiv_sym(%p, %p)\n",(A),(B)); 4462 (A)->v.data.node=(mp_node)(B); 4463 (A)->v.data.indep.serial=0; 4464} while (0) 4465 4466@ @c 4467#if DEBUG 4468#define text(A) do_get_text(mp, (A)) 4469#define eq_type(A) do_get_eq_type(mp, (A)) 4470#define equiv(A) do_get_equiv(mp, (A)) 4471#define equiv_node(A) do_get_equiv_node(mp, (A)) 4472#define equiv_sym(A) do_get_equiv_sym(mp, (A)) 4473static mp_string do_get_text (MP mp, mp_sym A) { 4474 FUNCTION_TRACE3 ("%d = do_get_text(%p)\n",A->text,A); 4475 return A->text; 4476} 4477static halfword do_get_eq_type (MP mp, mp_sym A) { 4478 FUNCTION_TRACE3 ("%d = do_get_eq_type(%p)\n",A->type,A); 4479 return A->type; 4480} 4481static halfword do_get_equiv (MP mp, mp_sym A) { 4482 FUNCTION_TRACE3 ("%d = do_get_equiv(%p)\n",A->v.data.indep.serial,A); 4483 return A->v.data.indep.serial; 4484} 4485static mp_node do_get_equiv_node (MP mp, mp_sym A) { 4486 FUNCTION_TRACE3 ("%p = do_get_equiv_node(%p)\n",A->v.data.node,A); 4487 return A->v.data.node; 4488} 4489static mp_sym do_get_equiv_sym (MP mp, mp_sym A) { 4490 FUNCTION_TRACE3 ("%p = do_get_equiv_sym(%p)\n",A->v.data.node,A); 4491 return (mp_sym)A->v.data.node; 4492} 4493#else 4494#define text(A) (A)->text 4495#define eq_type(A) (A)->type 4496#define equiv(A) (A)->v.data.indep.serial 4497#define equiv_node(A) (A)->v.data.node 4498#define equiv_sym(A) (mp_sym)(A)->v.data.node 4499#endif 4500 4501@ @<Declarations...@>= 4502#if DEBUG 4503static mp_string do_get_text (MP mp, mp_sym A); 4504static halfword do_get_eq_type (MP mp, mp_sym A); 4505static halfword do_get_equiv (MP mp, mp_sym A); 4506static mp_node do_get_equiv_node (MP mp, mp_sym A); 4507static mp_sym do_get_equiv_sym (MP mp, mp_sym A); 4508#endif 4509 4510@ @<Types...@>= 4511typedef struct mp_symbol_entry { 4512 halfword type; 4513 mp_value v; 4514 mp_string text; 4515 void *parent; 4516} mp_symbol_entry; 4517 4518@ @<Glob...@>= 4519integer st_count; /* total number of known identifiers */ 4520avl_tree symbols; /* avl tree of symbolic tokens */ 4521avl_tree frozen_symbols; /* avl tree of frozen symbolic tokens */ 4522mp_sym frozen_bad_vardef; 4523mp_sym frozen_colon; 4524mp_sym frozen_end_def; 4525mp_sym frozen_end_for; 4526mp_sym frozen_end_group; 4527mp_sym frozen_etex; 4528mp_sym frozen_fi; 4529mp_sym frozen_inaccessible; 4530mp_sym frozen_left_bracket; 4531mp_sym frozen_mpx_break; 4532mp_sym frozen_repeat_loop; 4533mp_sym frozen_right_delimiter; 4534mp_sym frozen_semicolon; 4535mp_sym frozen_slash; 4536mp_sym frozen_undefined; 4537mp_sym frozen_dump; 4538 4539 4540@ Here are the functions needed for the avl construction. 4541 4542@<Declarations@>= 4543static int comp_symbols_entry (void *p, const void *pa, const void *pb); 4544static void *copy_symbols_entry (const void *p); 4545static void *delete_symbols_entry (void *p); 4546 4547 4548@ The avl comparison function is a straightword version of |strcmp|, 4549except that checks for the string lengths first. 4550 4551@c 4552static int comp_symbols_entry (void *p, const void *pa, const void *pb) { 4553 const mp_symbol_entry *a = (const mp_symbol_entry *) pa; 4554 const mp_symbol_entry *b = (const mp_symbol_entry *) pb; 4555 (void) p; 4556 if (a->text->len != b->text->len) { 4557 return (a->text->len > b->text->len ? 1 : -1); 4558 } 4559 return strncmp ((const char *) a->text->str, (const char *) b->text->str, 4560 a->text->len); 4561} 4562 4563 4564@ Copying a symbol happens when an item is inserted into an AVL tree. 4565The |text| and |mp_number| needs to be deep copied, every thing else 4566can be reassigned. 4567 4568@c 4569static void *copy_symbols_entry (const void *p) { 4570 MP mp; 4571 mp_sym ff; 4572 const mp_symbol_entry *fp; 4573 fp = (const mp_symbol_entry *) p; 4574 mp = (MP)fp->parent; 4575 ff = malloc (sizeof (mp_symbol_entry)); 4576 if (ff == NULL) 4577 return NULL; 4578 ff->text = copy_strings_entry (fp->text); 4579 if (ff->text == NULL) 4580 return NULL; 4581 ff->v = fp->v; 4582 ff->type = fp->type; 4583 ff->parent = mp; 4584 new_number(ff->v.data.n); 4585 number_clone(ff->v.data.n, fp->v.data.n); 4586 return ff; 4587} 4588 4589 4590@ In the current implementation, symbols are not freed until the 4591end of the run. 4592 4593@c 4594static void *delete_symbols_entry (void *p) { 4595 MP mp; 4596 mp_sym ff = (mp_sym) p; 4597 mp = (MP)ff->parent; 4598 free_number(ff->v.data.n); 4599 mp_xfree (ff->text->str); 4600 mp_xfree (ff->text); 4601 mp_xfree (ff); 4602 return NULL; 4603} 4604 4605 4606@ @<Allocate or initialize ...@>= 4607mp->symbols = avl_create (comp_symbols_entry, 4608 copy_symbols_entry, 4609 delete_symbols_entry, malloc, free, NULL); 4610mp->frozen_symbols = avl_create (comp_symbols_entry, 4611 copy_symbols_entry, 4612 delete_symbols_entry, malloc, free, NULL); 4613 4614@ @<Dealloc variables@>= 4615if (mp->symbols != NULL) 4616 avl_destroy (mp->symbols); 4617if (mp->frozen_symbols != NULL) 4618 avl_destroy (mp->frozen_symbols); 4619 4620@ Actually creating symbols is done by |id_lookup|, but in order to 4621do so it needs a way to create a new, empty symbol structure. 4622 4623@<Declarations@>= 4624static mp_sym new_symbols_entry (MP mp, unsigned char *nam, size_t len); 4625 4626@ @c 4627static mp_sym new_symbols_entry (MP mp, unsigned char *nam, size_t len) { 4628 mp_sym ff; 4629 ff = mp_xmalloc (mp, 1, sizeof (mp_symbol_entry)); 4630 memset (ff, 0, sizeof (mp_symbol_entry)); 4631 ff->parent = mp; 4632 ff->text = mp_xmalloc (mp, 1, sizeof (mp_lstring)); 4633 ff->text->str = nam; 4634 ff->text->len = len; 4635 ff->type = mp_tag_token; 4636 ff->v.type = mp_known; 4637 new_number(ff->v.data.n); 4638 FUNCTION_TRACE4 ("%p = new_symbols_entry(\"%s\",%d)\n", ff, nam, (int)len); 4639 return ff; 4640} 4641 4642 4643@ There is one global variable so that |id_lookup| does not always have to 4644create a new entry just for testing. This is not freed because it creates 4645a double-free thanks to the |NULL| init. 4646 4647@<Global ...@>= 4648mp_sym id_lookup_test; 4649 4650@ @<Initialize table entries@>= 4651mp->id_lookup_test = new_symbols_entry (mp, NULL, 0); 4652 4653@ Certain symbols are ``frozen'' and not redefinable, since they are 4654used 4655in error recovery. 4656 4657@<Initialize table entries@>= 4658mp->st_count = 0; 4659mp->frozen_bad_vardef = mp_frozen_primitive (mp, "a bad variable", mp_tag_token, 0); 4660mp->frozen_right_delimiter = mp_frozen_primitive (mp, ")", mp_right_delimiter, 0); 4661mp->frozen_inaccessible = mp_frozen_primitive (mp, " INACCESSIBLE", mp_tag_token, 0); 4662mp->frozen_undefined = mp_frozen_primitive (mp, " UNDEFINED", mp_tag_token, 0); 4663 4664@ Here is the subroutine that searches the avl tree for an identifier 4665that matches a given string of length~|l| appearing in |buffer[j.. 4666(j+l-1)]|. If the identifier is not found, it is inserted if 4667|insert_new| is |true|, and the corresponding symbol will be returned. 4668 4669There are two variations on the lookup function: one for the normal 4670symbol table, and one for the table of error recovery symbols. 4671 4672@d mp_id_lookup(A,B,C,D) mp_do_id_lookup ((A), mp->symbols, (B), (C), (D)) 4673 4674@c 4675static mp_sym mp_do_id_lookup (MP mp, avl_tree symbols, char *j, 4676 size_t l, boolean insert_new) { 4677 /* search an avl tree */ 4678 mp_sym str; 4679 mp->id_lookup_test->text->str = (unsigned char *)j; 4680 mp->id_lookup_test->text->len = l; 4681 str = (mp_sym) avl_find (mp->id_lookup_test, symbols); 4682 if (str == NULL && insert_new) { 4683 unsigned char *nam = (unsigned char *) mp_xstrldup (mp, j, l); 4684 mp_sym s = new_symbols_entry (mp, nam, l); 4685 mp->st_count++; 4686 assert (avl_ins (s, symbols, avl_false) > 0); 4687 str = (mp_sym) avl_find (s, symbols); 4688 delete_symbols_entry (s); 4689 } 4690 return str; 4691} 4692static mp_sym mp_frozen_id_lookup (MP mp, char *j, size_t l, 4693 boolean insert_new) { 4694 /* search the error recovery symbol table */ 4695 return mp_do_id_lookup (mp, mp->frozen_symbols, j, l, insert_new); 4696} 4697 4698/* see mp_print_sym (mp_sym sym) */ 4699@ Get a numeric value from \MP\ is not easy. We have to consider 4700the macro and the loops, as also the internal type (this is a 4701first attempt, and more work is needed). If we are inside 4702a \&{for} loop, then the global |loop_ptr| is not null and the other loops 4703eventually nested are available by mean of |loop_ptr->link|. 4704The current numeric value is stored in |old_value|. 4705 4706@c 4707double mp_get_numeric_value (MP mp, const char *s, size_t l) { 4708 char *ss = mp_xstrdup(mp,s); 4709 if (ss) { 4710 mp_sym sym = mp_id_lookup(mp,ss,l,false); 4711 if (sym != NULL) { 4712 if (mp->loop_ptr != NULL) { 4713 mp_loop_data *s; 4714 s = mp->loop_ptr; 4715 while (s != NULL && sym != s->var) 4716 s = mp->loop_ptr->link; 4717 if (s != NULL && sym == s->var ){ 4718 mp_xfree (ss); 4719 return number_to_double(s->old_value) ; 4720 } 4721 } 4722 if (mp_type(sym) == mp_internal_quantity) { 4723 halfword qq = equiv(sym); 4724 mp_xfree (ss); 4725 if (internal_type (qq) != mp_string_type) 4726 return number_to_double(internal_value(qq)); 4727 else 4728 return 0; 4729 } 4730 if (sym->v.data.node != NULL && mp_type(sym->v.data.node) == mp_known) { 4731 mp_xfree (ss); 4732 return number_to_double(sym->v.data.node->data.n) ; 4733 } 4734 } 4735 } 4736 mp_xfree (ss); 4737 return 0 ; 4738} 4739 4740int mp_get_boolean_value (MP mp, const char *s, size_t l) { 4741 char *ss = mp_xstrdup(mp,s); 4742 if (ss) { 4743 mp_sym sym = mp_id_lookup(mp,ss,l,false); 4744 if (sym != NULL) { 4745 if (mp_type(sym->v.data.node) == mp_boolean_type) { 4746 if (number_to_boolean (sym->v.data.node->data.n) == mp_true_code) { 4747 mp_xfree(ss); 4748 return 1 ; 4749 } 4750 } 4751 } 4752 } 4753 mp_xfree (ss); 4754 return 0; 4755} 4756 4757char *mp_get_string_value (MP mp, const char *s, size_t l) { 4758 char *ss = mp_xstrdup(mp,s); 4759 if (ss) { 4760 mp_sym sym = mp_id_lookup(mp,ss,l,false); 4761 if (sym != NULL) { 4762 if (mp_type(sym->v.data.node) == mp_string_type) { 4763 mp_xfree (ss); 4764 return (char *) sym->v.data.node->data.str->str; 4765 } 4766 } 4767 } 4768 mp_xfree (ss); 4769 return NULL; 4770} 4771 4772@ @<Exported function headers@>= 4773double mp_get_numeric_value(MP mp,const char *s,size_t l); 4774int mp_get_boolean_value(MP mp,const char *s,size_t l); 4775char *mp_get_string_value(MP mp,const char *s,size_t l); 4776 4777@ We need to put \MP's ``primitive'' symbolic tokens into the hash 4778table, together with their command code (which will be the |eq_type|) 4779and an operand (which will be the |equiv|). The |primitive| procedure 4780does this, in a way that no \MP\ user can. The global value |cur_sym| 4781contains the new |eqtb| pointer after |primitive| has acted. 4782 4783@c 4784static void mp_primitive (MP mp, const char *ss, halfword c, halfword o) { 4785 char *s = mp_xstrdup (mp, ss); 4786 set_cur_sym (mp_id_lookup (mp, s, strlen (s), true)); 4787 mp_xfree (s); 4788 set_eq_type (cur_sym(), c); 4789 set_equiv (cur_sym(), o); 4790} 4791 4792 4793@ Some other symbolic tokens only exist for error recovery. 4794 4795@c 4796static mp_sym mp_frozen_primitive (MP mp, const char *ss, halfword c, 4797 halfword o) { 4798 char *s = mp_xstrdup (mp, ss); 4799 mp_sym str = mp_frozen_id_lookup (mp, s, strlen (ss), true); 4800 mp_xfree (s); 4801 str->type = c; 4802 str->v.data.indep.serial = o; 4803 return str; 4804} 4805 4806 4807@ This routine returns |true| if the argument is an un-redefinable symbol 4808because it is one of the error recovery tokens (as explained elsewhere, 4809|frozen_inaccessible| actuall is redefinable). 4810 4811@c 4812static boolean mp_is_frozen (MP mp, mp_sym sym) { 4813 mp_sym temp = mp_frozen_id_lookup (mp, (char *) sym->text->str, sym->text->len, false); 4814 if (temp==mp->frozen_inaccessible) 4815 return false; 4816 return (temp == sym); 4817} 4818 4819 4820@ Many of \MP's primitives need no |equiv|, since they are identifiable 4821by their |eq_type| alone. These primitives are loaded into the hash table 4822as follows: 4823 4824@<Put each of \MP's primitives into the hash table@>= 4825mp_primitive (mp, "..", mp_path_join, 0); 4826@:.._}{\.{..} primitive@>; 4827mp_primitive (mp, "[", mp_left_bracket, 0); 4828mp->frozen_left_bracket = mp_frozen_primitive (mp, "[", mp_left_bracket, 0); 4829@:[ }{\.{[} primitive@>; 4830mp_primitive (mp, "]", mp_right_bracket, 0); 4831@:] }{\.{]} primitive@>; 4832mp_primitive (mp, "}", mp_right_brace, 0); 4833@:]]}{\.{\char`\}} primitive@>; 4834mp_primitive (mp, "{", mp_left_brace, 0); 4835@:][}{\.{\char`\{} primitive@>; 4836mp_primitive (mp, ":", mp_colon, 0); 4837mp->frozen_colon = mp_frozen_primitive (mp, ":", mp_colon, 0); 4838@:: }{\.{:} primitive@>; 4839mp_primitive (mp, "::", mp_double_colon, 0); 4840@::: }{\.{::} primitive@>; 4841mp_primitive (mp, "||:", mp_bchar_label, 0); 4842@:::: }{\.{\char'174\char'174:} primitive@>; 4843mp_primitive (mp, ":=", mp_assignment, 0); 4844@::=_}{\.{:=} primitive@>; 4845mp_primitive (mp, ",", mp_comma, 0); 4846@:, }{\., primitive@>; 4847mp_primitive (mp, ";", mp_semicolon, 0); 4848mp->frozen_semicolon = mp_frozen_primitive (mp, ";", mp_semicolon, 0); 4849@:; }{\.; primitive@>; 4850mp_primitive (mp, "\\", mp_relax, 0); 4851@:]]\\}{\.{\char`\\} primitive@>; 4852mp_primitive (mp, "addto", mp_add_to_command, 0); 4853@:add_to_}{\&{addto} primitive@>; 4854mp_primitive (mp, "atleast", mp_at_least, 0); 4855@:at_least_}{\&{atleast} primitive@>; 4856mp_primitive (mp, "begingroup", mp_begin_group, 0); 4857mp->bg_loc = cur_sym(); 4858@:begin_group_}{\&{begingroup} primitive@>; 4859mp_primitive (mp, "controls", mp_controls, 0); 4860@:controls_}{\&{controls} primitive@>; 4861mp_primitive (mp, "curl", mp_curl_command, 0); 4862@:curl_}{\&{curl} primitive@>; 4863mp_primitive (mp, "delimiters", mp_delimiters, 0); 4864@:delimiters_}{\&{delimiters} primitive@>; 4865mp_primitive (mp, "endgroup", mp_end_group, 0); 4866mp->eg_loc = cur_sym(); 4867mp->frozen_end_group = mp_frozen_primitive (mp, "endgroup", mp_end_group, 0); 4868@:endgroup_}{\&{endgroup} primitive@>; 4869mp_primitive (mp, "everyjob", mp_every_job_command, 0); 4870@:every_job_}{\&{everyjob} primitive@>; 4871mp_primitive (mp, "exitif", mp_exit_test, 0); 4872@:exit_if_}{\&{exitif} primitive@>; 4873mp_primitive (mp, "expandafter", mp_expand_after, 0); 4874@:expand_after_}{\&{expandafter} primitive@>; 4875mp_primitive (mp, "interim", mp_interim_command, 0); 4876@:interim_}{\&{interim} primitive@>; 4877mp_primitive (mp, "let", mp_let_command, 0); 4878@:let_}{\&{let} primitive@>; 4879mp_primitive (mp, "newinternal", mp_new_internal, 0); 4880@:new_internal_}{\&{newinternal} primitive@>; 4881mp_primitive (mp, "of", mp_of_token, 0); 4882@:of_}{\&{of} primitive@>; 4883mp_primitive (mp, "randomseed", mp_random_seed, 0); 4884@:mp_random_seed_}{\&{randomseed} primitive@>; 4885mp_primitive (mp, "save", mp_save_command, 0); 4886@:save_}{\&{save} primitive@>; 4887mp_primitive (mp, "scantokens", mp_scan_tokens, 0); 4888@:scan_tokens_}{\&{scantokens} primitive@>; 4889 4890mp_primitive (mp, "runscript", mp_runscript, 0); 4891@:run_script_}{\&{runscript} primitive@>; 4892mp_primitive (mp, "maketext", mp_maketext, 0); 4893@:make_text_}{\&{maketext} primitive@>; 4894 4895mp_primitive (mp, "shipout", mp_ship_out_command, 0); 4896@:ship_out_}{\&{shipout} primitive@>; 4897mp_primitive (mp, "skipto", mp_skip_to, 0); 4898@:skip_to_}{\&{skipto} primitive@>; 4899mp_primitive (mp, "special", mp_special_command, 0); 4900@:special}{\&{special} primitive@>; 4901mp_primitive (mp, "fontmapfile", mp_special_command, 1); 4902@:fontmapfile}{\&{fontmapfile} primitive@>; 4903mp_primitive (mp, "fontmapline", mp_special_command, 2); 4904@:fontmapline}{\&{fontmapline} primitive@>; 4905mp_primitive (mp, "step", mp_step_token, 0); 4906@:step_}{\&{step} primitive@>; 4907mp_primitive (mp, "str", mp_str_op, 0); 4908@:str_}{\&{str} primitive@>; 4909mp_primitive (mp, "tension", mp_tension, 0); 4910@:tension_}{\&{tension} primitive@>; 4911mp_primitive (mp, "to", mp_to_token, 0); 4912@:to_}{\&{to} primitive@>; 4913mp_primitive (mp, "until", mp_until_token, 0); 4914@:until_}{\&{until} primitive@>; 4915mp_primitive (mp, "within", mp_within_token, 0); 4916@:within_}{\&{within} primitive@>; 4917mp_primitive (mp, "write", mp_write_command, 0); 4918@:write_}{\&{write} primitive@> 4919 4920 4921@ Each primitive has a corresponding inverse, so that it is possible to 4922display the cryptic numeric contents of |eqtb| in symbolic form. 4923Every call of |primitive| in this program is therefore accompanied by some 4924straightforward code that forms part of the |print_cmd_mod| routine 4925explained below. 4926 4927@<Cases of |print_cmd_mod| for symbolic printing of primitives@>= 4928case mp_add_to_command: 4929mp_print (mp, "addto"); 4930break; 4931case mp_assignment: 4932mp_print (mp, ":="); 4933break; 4934case mp_at_least: 4935mp_print (mp, "atleast"); 4936break; 4937case mp_bchar_label: 4938mp_print (mp, "||:"); 4939break; 4940case mp_begin_group: 4941mp_print (mp, "begingroup"); 4942break; 4943case mp_colon: 4944mp_print (mp, ":"); 4945break; 4946case mp_comma: 4947mp_print (mp, ","); 4948break; 4949case mp_controls: 4950mp_print (mp, "controls"); 4951break; 4952case mp_curl_command: 4953mp_print (mp, "curl"); 4954break; 4955case mp_delimiters: 4956mp_print (mp, "delimiters"); 4957break; 4958case mp_double_colon: 4959mp_print (mp, "::"); 4960break; 4961case mp_end_group: 4962mp_print (mp, "endgroup"); 4963break; 4964case mp_every_job_command: 4965mp_print (mp, "everyjob"); 4966break; 4967case mp_exit_test: 4968mp_print (mp, "exitif"); 4969break; 4970case mp_expand_after: 4971mp_print (mp, "expandafter"); 4972break; 4973case mp_interim_command: 4974mp_print (mp, "interim"); 4975break; 4976case mp_left_brace: 4977mp_print (mp, "{"); 4978break; 4979case mp_left_bracket: 4980mp_print (mp, "["); 4981break; 4982case mp_let_command: 4983mp_print (mp, "let"); 4984break; 4985case mp_new_internal: 4986mp_print (mp, "newinternal"); 4987break; 4988case mp_of_token: 4989mp_print (mp, "of"); 4990break; 4991case mp_path_join: 4992mp_print (mp, ".."); 4993break; 4994case mp_random_seed: 4995mp_print (mp, "randomseed"); 4996break; 4997case mp_relax: 4998mp_print_char (mp, xord ('\\')); 4999break; 5000case mp_right_brace: 5001mp_print_char (mp, xord ('}')); 5002break; 5003case mp_right_bracket: 5004mp_print_char (mp, xord (']')); 5005break; 5006case mp_save_command: 5007mp_print (mp, "save"); 5008break; 5009case mp_scan_tokens: 5010mp_print (mp, "scantokens"); 5011break; 5012case mp_runscript: 5013mp_print (mp, "runscript"); 5014break; 5015case mp_maketext: 5016mp_print (mp, "maketext"); 5017break; 5018case mp_semicolon: 5019mp_print_char (mp, xord (';')); 5020break; 5021case mp_ship_out_command: 5022mp_print (mp, "shipout"); 5023break; 5024case mp_skip_to: 5025mp_print (mp, "skipto"); 5026break; 5027case mp_special_command: 5028if (m == 2) 5029 mp_print (mp, "fontmapline"); 5030else if (m == 1) 5031 mp_print (mp, "fontmapfile"); 5032else 5033 mp_print (mp, "special"); 5034break; 5035case mp_step_token: 5036mp_print (mp, "step"); 5037break; 5038case mp_str_op: 5039mp_print (mp, "str"); 5040break; 5041case mp_tension: 5042mp_print (mp, "tension"); 5043break; 5044case mp_to_token: 5045mp_print (mp, "to"); 5046break; 5047case mp_until_token: 5048mp_print (mp, "until"); 5049break; 5050case mp_within_token: 5051mp_print (mp, "within"); 5052break; 5053case mp_write_command: 5054mp_print (mp, "write"); 5055break; 5056 5057@ We will deal with the other primitives later, at some point in the program 5058where their |eq_type| and |equiv| values are more meaningful. For example, 5059the primitives for macro definitions will be loaded when we consider the 5060routines that define macros. It is easy to find where each particular 5061primitive was treated by looking in the index at the end; for example, the 5062section where |"def"| entered |eqtb| is listed under `\&{def} primitive'. 5063 5064@* Token lists. 5065 5066A \MP\ token is either symbolic or numeric or a string, or it denotes a macro 5067parameter or capsule or an internal; so there are six corresponding ways to 5068encode it internally: 5069@^token@> 5070 5071(1)~A symbolic token for symbol |p| is represented by the pointer |p|, 5072in the |sym_sym| field of a symbolic node in~|mem|. The |type| field is |symbol_node|; 5073and it has a |name_type| to differentiate various subtypes of symbolic tokens, 5074which is usually |normal_sym|, but |macro_sym| for macro names. 5075 5076(2)~A numeric token whose |scaled| value is~|v| is 5077represented in a non-symbolic node of~|mem|; the |type| field is |known|, 5078the |name_type| field is |token|, and the |value| field holds~|v|. 5079 5080(3)~A string token is also represented in a non-symbolic node; the |type| 5081field is |mp_string_type|, the |name_type| field is |token|, and the 5082|value| field holds the corresponding |mp_string|. 5083 5084(4)~Capsules have |name_type=capsule|, and their |type| and |value| fields 5085represent arbitrary values, with |type| different from |symbol_node| 5086(in ways to be explained later). 5087 5088(5)~Macro parameters appear in |sym_info| fields of symbolic nodes. The |type| 5089field is |symbol_node|; the $k$th parameter is represented by |k| in |sym_info|; 5090and |expr_sym| in |name_type|, if it is of type \&{expr}, or |suffix_sym| if it 5091is of type \&{suffix}, or by |text_sym| if it is of type \&{text}. 5092 5093(6)~The $k$th internal is also represented by |k| in |sym_info|; the |type| field is 5094|symbol_node| as for the other symbolic tokens; and |internal_sym| is its |name_type|; 5095 5096Actual values of the parameters and internals are kept in a separate 5097stack, as we will see later. 5098 5099Note that the `\\{type}' field of a node has nothing to do with ``type'' in a 5100printer's sense. It's curious that the same word is used in such different ways. 5101 5102@d token_node_size sizeof(mp_node_data) /* the number of words in a large token node */ 5103 5104@d set_value_sym(A,B) do_set_value_sym(mp, (mp_token_node)(A), (B)) 5105@d set_value_number(A,B) do_set_value_number(mp, (mp_token_node)(A), (B)) 5106@d set_value_node(A,B) do_set_value_node(mp, (mp_token_node)(A), (B)) 5107@d set_value_str(A,B) do_set_value_str(mp, (mp_token_node)(A), (B)) 5108@d set_value_knot(A,B) do_set_value_knot(mp, (mp_token_node)A, (B)) 5109 5110@d value_sym_NEW(A) (mp_sym)mp_link(A) 5111@d set_value_sym_NEW(A,B) set_mp_link(A,(mp_node)B) 5112 5113@<MPlib internal header stuff@>= 5114typedef struct mp_node_data *mp_token_node; 5115 5116@ @c 5117#if DEBUG 5118#define value_sym(A) do_get_value_sym(mp,(mp_token_node)(A)) 5119/* |#define value_number(A) do_get_value_number(mp,(mp_token_node)(A))| */ 5120#define value_number(A) ((mp_token_node)(A))->data.n 5121#define value_node(A) do_get_value_node(mp,(mp_token_node)(A)) 5122#define value_str(A) do_get_value_str(mp,(mp_token_node)(A)) 5123#define value_knot(A) do_get_value_knot(mp,(mp_token_node)(A)) 5124#else 5125#define value_sym(A) ((mp_token_node)(A))->data.sym 5126#define value_number(A) ((mp_token_node)(A))->data.n 5127#define value_node(A) ((mp_token_node)(A))->data.node 5128#define value_str(A) ((mp_token_node)(A))->data.str 5129#define value_knot(A) ((mp_token_node)(A))->data.p 5130#endif 5131static void do_set_value_sym(MP mp, mp_token_node A, mp_sym B) { 5132 FUNCTION_TRACE3 ("set_value_sym(%p,%p)\n", (A),(B)); 5133 A->data.sym=(B); 5134} 5135static void do_set_value_number(MP mp, mp_token_node A, mp_number B) { 5136 FUNCTION_TRACE3 ("set_value(%p,%s)\n", (A), number_tostring(B)); 5137 A->data.p = NULL; 5138 A->data.str = NULL; 5139 A->data.node = NULL; 5140 number_clone (A->data.n, B); 5141} 5142static void do_set_value_str(MP mp, mp_token_node A, mp_string B) { 5143 FUNCTION_TRACE3 ("set_value_str(%p,%p)\n", (A),(B)); 5144 assert (A->type != mp_structured); 5145 A->data.p = NULL; 5146 A->data.str = (B); 5147 add_str_ref((B)); 5148 A->data.node = NULL; 5149 number_clone (A->data.n, zero_t); 5150} 5151static void do_set_value_node(MP mp, mp_token_node A, mp_node B) { 5152 /* store the value in a large token node */ 5153 FUNCTION_TRACE3 ("set_value_node(%p,%p)\n", A,B); 5154 assert (A->type != mp_structured); 5155 A->data.p = NULL; 5156 A->data.str = NULL; 5157 A->data.node = B; 5158 number_clone (A->data.n, zero_t); 5159} 5160static void do_set_value_knot(MP mp, mp_token_node A, mp_knot B) { 5161 FUNCTION_TRACE3 ("set_value_knot(%p,%p)\n", (A),(B)); 5162 assert (A->type != mp_structured); 5163 A->data.p = (B); 5164 A->data.str = NULL; 5165 A->data.node = NULL; 5166 number_clone (A->data.n, zero_t); 5167} 5168 5169 5170@ @c 5171#if DEBUG 5172static mp_sym do_get_value_sym (MP mp, mp_token_node A) { 5173 /* |A->type| can be structured in this case */ 5174 FUNCTION_TRACE3 ("%p = get_value_sym(%p)\n", A->data.sym, A); 5175 return A->data.sym ; 5176} 5177static mp_node do_get_value_node (MP mp, mp_token_node A) { 5178 assert (A->type != mp_structured); 5179 FUNCTION_TRACE3 ("%p = get_value_node(%p)\n", A->data.node, A); 5180 return A->data.node ; 5181} 5182static mp_string do_get_value_str (MP mp, mp_token_node A) { 5183 assert (A->type != mp_structured); 5184 FUNCTION_TRACE3 ("%p = get_value_str(%p)\n", A->data.str, A); 5185 return A->data.str ; 5186} 5187static mp_knot do_get_value_knot (MP mp, mp_token_node A) { 5188 assert (A->type != mp_structured); 5189 FUNCTION_TRACE3 ("%p = get_value_knot(%p)\n", A->data.p, A); 5190 return A->data.p ; 5191} 5192static mp_number do_get_value_number (MP mp, mp_token_node A) { 5193 assert (A->type != mp_structured); 5194 FUNCTION_TRACE3 ("%d = get_value_number(%p)\n", A->data.n.type, A); 5195 return A->data.n ; 5196} 5197#endif 5198 5199@ @<Declarations@>= 5200#if DEBUG 5201static mp_number do_get_value_number (MP mp, mp_token_node A); 5202static mp_sym do_get_value_sym (MP mp, mp_token_node A); 5203static mp_node do_get_value_node (MP mp, mp_token_node A); 5204static mp_string do_get_value_str (MP mp, mp_token_node A) ; 5205static mp_knot do_get_value_knot (MP mp, mp_token_node A) ; 5206#endif 5207static void do_set_value_sym (MP mp, mp_token_node A, mp_sym B); 5208static void do_set_value_number (MP mp, mp_token_node A, mp_number B); 5209static void do_set_value_node (MP mp, mp_token_node A, mp_node B); 5210static void do_set_value_str (MP mp, mp_token_node A, mp_string B); 5211static void do_set_value_knot (MP mp, mp_token_node A, mp_knot B); 5212 5213@ 5214@c 5215static mp_node mp_get_token_node (MP mp) { 5216 mp_node p; 5217 if (mp->token_nodes) { 5218 p = mp->token_nodes; 5219 mp->token_nodes = p->link; 5220 mp->num_token_nodes--; 5221 p->link = NULL; 5222 } else { 5223 p = malloc_node (token_node_size); 5224 new_number(p->data.n); 5225 p->has_number = 1; 5226 } 5227 p->type = mp_token_node_type; 5228 FUNCTION_TRACE2 ("%p = mp_get_token_node()\n", p); 5229 return (mp_node) p; 5230} 5231 5232@ @c 5233static void mp_free_token_node (MP mp, mp_node p) { 5234 FUNCTION_TRACE2 ("mp_free_token_node(%p)\n", p); 5235 if (!p) return; 5236 if (mp->num_token_nodes < max_num_token_nodes) { 5237 p->link = mp->token_nodes; 5238 mp->token_nodes = p; 5239 mp->num_token_nodes++; 5240 return; 5241 } 5242 mp->var_used -= token_node_size; 5243 if (mp->math_mode > mp_math_double_mode) { 5244 free_number(((mp_value_node)p)->data.n); 5245 } 5246 xfree (p); 5247} 5248 5249@ @<Declarations@>= 5250static void mp_free_token_node (MP mp, mp_node p); 5251 5252@ A numeric token is created by the following trivial routine. 5253 5254@c 5255static mp_node mp_new_num_tok (MP mp, mp_number v) { 5256 mp_node p; /* the new node */ 5257 p = mp_get_token_node (mp); 5258 set_value_number (p, v); 5259 p->type = mp_known; 5260 p->name_type = mp_token; 5261 FUNCTION_TRACE3 ("%p = mp_new_num_tok(%p)\n", p, v); 5262 return p; 5263} 5264 5265 5266@ A token list is a singly linked list of nodes in |mem|, where 5267each node contains a token and a link. Here's a subroutine that gets rid 5268of a token list when it is no longer needed. 5269 5270@c 5271static void mp_flush_token_list (MP mp, mp_node p) { 5272 mp_node q; /* the node being recycled */ 5273 FUNCTION_TRACE2 ("mp_flush_token_list(%p)\n", p); 5274 while (p != NULL) { 5275 q = p; 5276 p = mp_link (p); 5277 if (mp_type (q) == mp_symbol_node) { 5278 mp_free_symbolic_node (mp, q); 5279 } else { 5280 switch (mp_type (q)) { 5281 case mp_vacuous: 5282 case mp_boolean_type: 5283 case mp_known: 5284 break; 5285 case mp_string_type: 5286 delete_str_ref (value_str (q)); 5287 break; 5288 case unknown_types: 5289 case mp_pen_type: 5290 case mp_path_type: 5291 case mp_picture_type: 5292 case mp_pair_type: 5293 case mp_color_type: 5294 case mp_cmykcolor_type: 5295 case mp_transform_type: 5296 case mp_dependent: 5297 case mp_proto_dependent: 5298 case mp_independent: 5299 mp_recycle_value (mp, q); 5300 break; 5301 default: 5302 mp_confusion (mp, "token"); 5303@:this can't happen token}{\quad token@>; 5304 } 5305 mp_free_token_node (mp, q); 5306 } 5307 } 5308} 5309 5310 5311@ The procedure |show_token_list|, which prints a symbolic form of 5312the token list that starts at a given node |p|, illustrates these 5313conventions. The token list being displayed should not begin with a reference 5314count. 5315 5316An additional parameter |q| is also given; this parameter is either NULL 5317or it points to a node in the token list where a certain magic computation 5318takes place that will be explained later. (Basically, |q| is non-NULL when 5319we are printing the two-line context information at the time of an error 5320message; |q| marks the place corresponding to where the second line 5321should begin.) 5322 5323The generation will stop, and `\.{\char`\ ETC.}' will be printed, if the length 5324of printing exceeds a given limit~|l|; the length of printing upon entry is 5325assumed to be a given amount called |null_tally|. (Note that 5326|show_token_list| sometimes uses itself recursively to print 5327variable names within a capsule.) 5328@^recursion@> 5329 5330Unusual entries are printed in the form of all-caps tokens 5331preceded by a space, e.g., `\.{\char`\ BAD}'. 5332 5333@<Declarations@>= 5334static void mp_show_token_list (MP mp, mp_node p, mp_node q, integer l, 5335 integer null_tally); 5336 5337@ @c 5338void mp_show_token_list (MP mp, mp_node p, mp_node q, integer l, 5339 integer null_tally) { 5340 quarterword cclass, c; /* the |char_class| of previous and new tokens */ 5341 cclass = percent_class; 5342 mp->tally = null_tally; 5343 while ((p != NULL) && (mp->tally < l)) { 5344 if (p == q) { 5345 set_trick_count(); 5346 } 5347 /* Display token |p| and set |c| to its class; but |return| if there are problems */ 5348 c = letter_class; /* the default */ 5349 if (mp_type (p) != mp_symbol_node) { 5350 /* Display non-symbolic token */ 5351 if (mp_name_type (p) == mp_token) { 5352 if (mp_type (p) == mp_known) { 5353 /* Display a numeric token */ 5354 if (cclass == digit_class) 5355 mp_print_char (mp, xord (' ')); 5356 if (number_negative (value_number (p))) { 5357 if (cclass == mp_left_bracket_class) 5358 mp_print_char (mp, xord (' ')); 5359 mp_print_char (mp, xord ('[')); 5360 print_number (value_number (p)); 5361 mp_print_char (mp, xord (']')); 5362 c = mp_right_bracket_class; 5363 } else { 5364 print_number (value_number (p)); 5365 c = digit_class; 5366 } 5367 5368 } else if (mp_type (p) != mp_string_type) { 5369 mp_print (mp, " BAD"); 5370 } else { 5371 mp_print_char (mp, xord ('"')); 5372 mp_print_str (mp, value_str (p)); 5373 mp_print_char (mp, xord ('"')); 5374 c = string_class; 5375 } 5376 } else if ((mp_name_type (p) != mp_capsule) || (mp_type (p) < mp_vacuous) 5377 || (mp_type (p) > mp_independent)) { 5378 mp_print (mp, " BAD"); 5379 } else { 5380 mp_print_capsule (mp, p); 5381 c = right_paren_class; 5382 } 5383 5384 } else { 5385 if (mp_name_type (p) == mp_expr_sym || 5386 mp_name_type (p) == mp_suffix_sym || mp_name_type (p) == mp_text_sym) { 5387 integer r; /* temporary register */ 5388 r = mp_sym_info (p); 5389 if (mp_name_type (p) == mp_expr_sym) { 5390 mp_print (mp, "(EXPR"); 5391 } else if (mp_name_type (p) == mp_suffix_sym) { 5392 mp_print (mp, "(SUFFIX"); 5393 } else { 5394 mp_print (mp, "(TEXT"); 5395 } 5396 mp_print_int (mp, r); 5397 mp_print_char (mp, xord (')')); 5398 c = right_paren_class; 5399 } else { 5400 mp_sym sr = mp_sym_sym (p); 5401 if (sr == collective_subscript) { 5402 /* Display a collective subscript */ 5403 if (cclass == mp_left_bracket_class) 5404 mp_print_char (mp, xord (' ')); 5405 mp_print (mp, "[]"); 5406 c = mp_right_bracket_class; 5407 5408 } else { 5409 mp_string rr = text (sr); 5410 if (rr == NULL || rr->str == NULL) { 5411 mp_print (mp, " NONEXISTENT"); 5412 } else { 5413 /* Print string |r| as a symbolic token and set |c| to its class */ 5414 c = (quarterword) mp->char_class[(rr->str[0])]; 5415 if (c == cclass) { 5416 switch (c) { 5417 case letter_class: 5418 mp_print_char (mp, xord ('.')); 5419 break; 5420 case isolated_classes: 5421 break; 5422 default: 5423 mp_print_char (mp, xord (' ')); 5424 break; 5425 } 5426 } 5427 mp_print_str (mp, rr); 5428 5429 } 5430 } 5431 } 5432 } 5433 5434 cclass = c; 5435 p = mp_link (p); 5436 } 5437 if (p != NULL) 5438 mp_print (mp, " ETC."); 5439 return; 5440} 5441 5442 5443@ @<Declarations@>= 5444static void mp_print_capsule (MP mp, mp_node p); 5445 5446@ @<Declare miscellaneous procedures that were declared |forward|@>= 5447void mp_print_capsule (MP mp, mp_node p) { 5448 mp_print_char (mp, xord ('(')); 5449 mp_print_exp (mp, p, 0); 5450 mp_print_char (mp, xord (')')); 5451} 5452 5453 5454@ Macro definitions are kept in \MP's memory in the form of token lists 5455that have a few extra symbolic nodes at the beginning. 5456 5457The first node contains a reference count that is used to tell when the 5458list is no longer needed. To emphasize the fact that a reference count is 5459present, we shall refer to the |sym_info| field of this special node as the 5460|ref_count| field. 5461@^reference counts@> 5462 5463The next node or nodes after the reference count serve to describe the 5464formal parameters. They consist of zero or more parameter tokens followed 5465by a code for the type of macro. 5466 5467/* reference count preceding a macro definition or picture header */ 5468@d ref_count(A) indep_value(A) 5469@d set_ref_count(A,B) set_indep_value(A,B) 5470@d add_mac_ref(A) set_ref_count((A),ref_count((A))+1) /* make a new reference to a macro list */ 5471@d decr_mac_ref(A) set_ref_count((A),ref_count((A))-1) /* remove a reference to a macro list */ 5472 5473@<Types...@>= 5474typedef enum { 5475 mp_general_macro, /* preface to a macro defined with a parameter list */ 5476 mp_primary_macro, /* preface to a macro with a \&{primary} parameter */ 5477 mp_secondary_macro, /* preface to a macro with a \&{secondary} parameter */ 5478 mp_tertiary_macro, /* preface to a macro with a \&{tertiary} parameter */ 5479 mp_expr_macro, /* preface to a macro with an undelimited \&{expr} parameter */ 5480 mp_of_macro, /* preface to a macro with undelimited `\&{expr} |x| \&{of}~|y|' parameters */ 5481 mp_suffix_macro, /* preface to a macro with an undelimited \&{suffix} parameter */ 5482 mp_text_macro, /* preface to a macro with an undelimited \&{text} parameter */ 5483 mp_expr_param, /* used by \.{expr} primitive */ 5484 mp_suffix_param, /* used by \.{suffix} primitive */ 5485 mp_text_param /* used by \.{text} primitive */ 5486} mp_macro_info; 5487 5488@ @c 5489static void mp_delete_mac_ref (MP mp, mp_node p) { 5490 /* |p| points to the reference count of a macro list that is 5491 losing one reference */ 5492 if (ref_count (p) == 0) 5493 mp_flush_token_list (mp, p); 5494 else 5495 decr_mac_ref (p); 5496} 5497 5498 5499@ The following subroutine displays a macro, given a pointer to its 5500reference count. 5501 5502@c 5503static void mp_show_macro (MP mp, mp_node p, mp_node q, integer l) { 5504 mp_node r; /* temporary storage */ 5505 p = mp_link (p); /* bypass the reference count */ 5506 while (mp_name_type (p) != mp_macro_sym) { 5507 r = mp_link (p); 5508 mp_link (p) = NULL; 5509 mp_show_token_list (mp, p, NULL, l, 0); 5510 mp_link (p) = r; 5511 p = r; 5512 if (l > 0) 5513 l = l - mp->tally; 5514 else 5515 return; 5516 } /* control printing of `\.{ETC.}' */ 5517@.ETC@>; 5518 mp->tally = 0; 5519 switch (mp_sym_info (p)) { 5520 case mp_general_macro: 5521 mp_print (mp, "->"); 5522 break; 5523@.->@> 5524 case mp_primary_macro: 5525 case mp_secondary_macro: 5526 case mp_tertiary_macro: 5527 mp_print_char (mp, xord ('<')); 5528 mp_print_cmd_mod (mp, mp_param_type, mp_sym_info (p)); 5529 mp_print (mp, ">->"); 5530 break; 5531 case mp_expr_macro: 5532 mp_print (mp, "<expr>->"); 5533 break; 5534 case mp_of_macro: 5535 mp_print (mp, "<expr>of<primary>->"); 5536 break; 5537 case mp_suffix_macro: 5538 mp_print (mp, "<suffix>->"); 5539 break; 5540 case mp_text_macro: 5541 mp_print (mp, "<text>->"); 5542 break; 5543 } /* there are no other cases */ 5544 mp_show_token_list (mp, mp_link (p), q, l - mp->tally, 0); 5545} 5546 5547 5548@* Data structures for variables. 5549The variables of \MP\ programs can be simple, like `\.x', or they can 5550combine the structural properties of arrays and records, like `\.{x20a.b}'. 5551A \MP\ user assigns a type to a variable like \.{x20a.b} by saying, for 5552example, `\.{boolean} \.{x[]a.b}'. It's time for us to study how such 5553things are represented inside of the computer. 5554 5555Each variable value occupies two consecutive words, either in a non-symbolic 5556node called a value node, or as a non-symbolic subfield of a larger node. One 5557of those two words is called the |value| field; it is an integer, 5558containing either a |scaled| numeric value or the representation of some 5559other type of quantity. (It might also be subdivided into halfwords, in 5560which case it is referred to by other names instead of |value|.) The other 5561word is broken into subfields called |type|, |name_type|, and |link|. The 5562|type| field is a quarterword that specifies the variable's type, and 5563|name_type| is a quarterword from which \MP\ can reconstruct the 5564variable's name (sometimes by using the |link| field as well). Thus, only 55651.25 words are actually devoted to the value itself; the other 5566three-quarters of a word are overhead, but they aren't wasted because they 5567allow \MP\ to deal with sparse arrays and to provide meaningful diagnostics. 5568 5569In this section we shall be concerned only with the structural aspects of 5570variables, not their values. Later parts of the program will change the 5571|type| and |value| fields, but we shall treat those fields as black boxes 5572whose contents should not be touched. 5573 5574However, if the |type| field is |mp_structured|, there is no |value| field, 5575and the second word is broken into two pointer fields called |attr_head| 5576and |subscr_head|. Those fields point to additional nodes that 5577contain structural information, as we shall see. 5578 5579TH Note: DEK and JDH had a nice theoretical split between |value|, 5580|attr| and |subscr| nodes, as documented above and further 5581below. However, all three types had a bad habit of transmuting into 5582each other in practice while pointers to them still lived on 5583elsewhere, so using three different C structures is simply not 5584workable. All three are now represented as a single C structure called 5585|mp_value_node|. 5586 5587There is a potential union in this structure in the interest of space 5588saving: |subscript_| and |hashloc_| are mutually exclusive. 5589 5590Actually, so are |attr_head_| + |subscr_head_| on one side and and 5591|value_| on the other, but because of all the access macros that are 5592used in the code base to get at values, those cannot be folded into a 5593union (yet); this would have required creating a similar union in 5594|mp_token_node| where it would only serve to confuse things. 5595 5596Finally, |parent_| only applies in |attr| nodes (the ones that have 5597|hashloc_|), but creating an extra substructure inside the union just 5598for that does not save space and the extra complication in the 5599structure is not worth the minimal extra code clarification. 5600 5601@d attr_head(A) do_get_attr_head(mp,(mp_value_node)(A)) 5602@d set_attr_head(A,B) do_set_attr_head(mp,(mp_value_node)(A),(mp_node)(B)) 5603 5604@d subscr_head(A) do_get_subscr_head(mp,(mp_value_node)(A)) 5605@d set_subscr_head(A,B) do_set_subscr_head(mp,(mp_value_node)(A),(mp_node)(B)) 5606 5607@<MPlib internal header stuff@>= 5608typedef struct mp_value_node_data { 5609 NODE_BODY; 5610 mp_value_data data; 5611 mp_number subscript_; 5612 mp_sym hashloc_; 5613 mp_node parent_; 5614 mp_node attr_head_; 5615 mp_node subscr_head_; 5616} mp_value_node_data; 5617 5618@ @c 5619static mp_node do_get_attr_head (MP mp, mp_value_node A) { 5620 assert (A->type == mp_structured); 5621 FUNCTION_TRACE3 ("%p = get_attr_head(%p)\n", A->attr_head_, A); 5622 return A->attr_head_; 5623} 5624static mp_node do_get_subscr_head (MP mp, mp_value_node A) { 5625 assert (A->type == mp_structured); 5626 FUNCTION_TRACE3 ("%p = get_subscr_head(%p)\n", A->subscr_head_, A); 5627 return A->subscr_head_; 5628} 5629static void do_set_attr_head (MP mp, mp_value_node A, mp_node d) { 5630 FUNCTION_TRACE4 ("set_attr_head(%p,%p) on line %d\n", (A), d, __LINE__); 5631 assert (A->type == mp_structured); 5632 A->attr_head_ = d; 5633} 5634static void do_set_subscr_head (MP mp, mp_value_node A, mp_node d) { 5635 FUNCTION_TRACE4 ("set_subscr_head(%p,%p) on line %d\n", (A), d, __LINE__); 5636 assert (A->type == mp_structured); 5637 A->subscr_head_ = d; 5638} 5639 5640@ @<Declarations@>= 5641static mp_node do_get_subscr_head (MP mp, mp_value_node A); 5642static mp_node do_get_attr_head (MP mp, mp_value_node A); 5643static void do_set_attr_head (MP mp, mp_value_node A, mp_node d); 5644static void do_set_subscr_head (MP mp, mp_value_node A, mp_node d); 5645 5646@ It would have been nicer to make |mp_get_value_node| return 5647|mp_value_node| variables, but with |eqtb| as it stands that 5648became messy: lots of typecasts. So, it returns a simple 5649|mp_node| for now. 5650 5651@d value_node_size sizeof(struct mp_value_node_data) 5652 5653@c 5654static mp_node mp_get_value_node (MP mp) { 5655 mp_value_node p; 5656 if (mp->value_nodes) { 5657 p = (mp_value_node)mp->value_nodes; 5658 mp->value_nodes = p->link; 5659 mp->num_value_nodes--; 5660 p->link = NULL; 5661 } else { 5662 p = malloc_node (value_node_size); 5663 new_number(p->data.n); 5664 new_number(p->subscript_); 5665 p->has_number = 2; 5666 } 5667 mp_type (p) = mp_value_node_type; 5668 FUNCTION_TRACE2 ("%p = mp_get_value_node()\n", p); 5669 return (mp_node)p; 5670} 5671#if DEBUG > 1 5672static void debug_dump_value_node (mp_node x) { 5673 mp_value_node qq = (mp_value_node)x; 5674 fprintf (stdout, "\nnode %p:\n", qq); 5675 fprintf (stdout, " type=%s\n", mp_type_string(qq->type)); 5676 fprintf (stdout, " name_type=%d\n", qq->name_type); 5677 fprintf (stdout, " link=%p\n", qq->link); 5678 fprintf (stdout, " data.n=%d\n", qq->data.n.type); 5679 if (is_number(qq->data.n)) { 5680 fprintf (stdout, " data.n.data.val=%d\n", qq->data.n.data.val); 5681 fprintf (stdout, " data.n.data.dval=%f\n", qq->data.n.data.dval); 5682 } 5683 fprintf (stdout, " data.str=%p\n", qq->data.str); 5684 if (qq->data.str != NULL) { 5685 fprintf (stdout, " data.str->len=%d\n", (int)qq->data.str->len); 5686 fprintf (stdout, " data.str->str=%s\n", qq->data.str->str); 5687 } 5688 fprintf (stdout, " data.indep.serial=%d\n data.indep.scale=%d\n", qq->data.indep.serial, 5689 qq->data.indep.scale); 5690 fprintf (stdout, " data.sym=%p\n", qq->data.sym); 5691 fprintf (stdout, " data.p=%p\n", qq->data.p); 5692 fprintf (stdout, " data.node=%p\n", qq->data.node); 5693 fprintf (stdout, " subscript=%d\n", qq->subscript_.type); 5694 if (is_number(qq->subscript_)) { 5695 fprintf (stdout, " subscript_.data.val=%d\n", qq->subscript_.data.val); 5696 fprintf (stdout, " subscript_.data.dval=%f\n", qq->subscript_.data.dval); 5697 } 5698 fprintf (stdout, " hashloc=%p\n", qq->hashloc_); 5699 fprintf (stdout, " parent=%p\n", qq->parent_); 5700 fprintf (stdout, " attr_head=%p\n", qq->attr_head_); 5701 fprintf (stdout, " subscr_head=%p\n\n", qq->subscr_head_); 5702} 5703#endif 5704 5705@ @<Declarations@>= 5706static mp_node mp_get_value_node (MP mp); 5707#if DEBUG > 1 5708static void debug_dump_value_node (mp_node x); 5709#endif 5710 5711@ An attribute node is three words long. Two of these words contain |type| 5712and |value| fields as described above, and the third word contains 5713additional information: There is an |hashloc| field, which contains the 5714hash address of the token that names this attribute; and there's also a 5715|parent| field, which points to the value node of |mp_structured| type at the 5716next higher level (i.e., at the level to which this attribute is 5717subsidiary). The |name_type| in an attribute node is `|attr|'. The 5718|link| field points to the next attribute with the same parent; these are 5719arranged in increasing order, so that |hashloc(mp_link(p))>hashloc(p)|. The 5720final attribute node links to the constant |end_attr|, whose |hashloc| 5721field is greater than any legal hash address. The |attr_head| in the 5722parent points to a node whose |name_type| is |mp_structured_root|; this 5723node represents the NULL attribute, i.e., the variable that is relevant 5724when no attributes are attached to the parent. The |attr_head| node 5725has the fields of either 5726a value node, a subscript node, or an attribute node, depending on what 5727the parent would be if it were not structured; but the subscript and 5728attribute fields are ignored, so it effectively contains only the data of 5729a value node. The |link| field in this special node points to an attribute 5730node whose |hashloc| field is zero; the latter node represents a collective 5731subscript `\.{[]}' attached to the parent, and its |link| field points to 5732the first non-special attribute node (or to |end_attr| if there are none). 5733 5734A subscript node likewise occupies three words, with |type| and |value| fields 5735plus extra information; its |name_type| is |subscr|. In this case the 5736third word is called the |subscript| field, which is a |scaled| integer. 5737The |link| field points to the subscript node with the next larger 5738subscript, if any; otherwise the |link| points to the attribute node 5739for collective subscripts at this level. We have seen that the latter node 5740contains an upward pointer, so that the parent can be deduced. 5741 5742The |name_type| in a parent-less value node is |root|, and the |link| 5743is the hash address of the token that names this value. 5744 5745In other words, variables have a hierarchical structure that includes 5746enough threads running around so that the program is able to move easily 5747between siblings, parents, and children. An example should be helpful: 5748(The reader is advised to draw a picture while reading the following 5749description, since that will help to firm up the ideas.) 5750Suppose that `\.x' and `\.{x.a}' and `\.{x[]b}' and `\.{x5}' 5751and `\.{x20b}' have been mentioned in a user's program, where 5752\.{x[]b} has been declared to be of \&{boolean} type. Let |h(x)|, |h(a)|, 5753and |h(b)| be the hash addresses of \.x, \.a, and~\.b. Then 5754|eq_type(h(x))=name| and |equiv(h(x))=p|, where |p|~is a non-symbolic value 5755node with |mp_name_type(p)=root| and |mp_link(p)=h(x)|. We have |type(p)=mp_structured|, 5756|attr_head(p)=q|, and |subscr_head(p)=r|, where |q| points to a value 5757node and |r| to a subscript node. (Are you still following this? Use 5758a pencil to draw a diagram.) The lone variable `\.x' is represented by 5759|type(q)| and |value(q)|; furthermore 5760|mp_name_type(q)=mp_structured_root| and |mp_link(q)=q1|, where |q1| points 5761to an attribute node representing `\.{x[]}'. Thus |mp_name_type(q1)=attr|, 5762|hashloc(q1)=collective_subscript=0|, |parent(q1)=p|, 5763|type(q1)=mp_structured|, |attr_head(q1)=qq|, and |subscr_head(q1)=qq1|; 5764|qq| is a three-word ``attribute-as-value'' node with |type(qq)=numeric_type| 5765(assuming that \.{x5} is numeric, because |qq| represents `\.{x[]}' 5766with no further attributes), |mp_name_type(qq)=structured_root|, 5767|hashloc(qq)=0|, |parent(qq)=p|, and 5768|mp_link(qq)=qq1|. (Now pay attention to the next part.) Node |qq1| is 5769an attribute node representing `\.{x[][]}', which has never yet 5770occurred; its |type| field is |undefined|, and its |value| field is 5771undefined. We have |mp_name_type(qq1)=attr|, |hashloc(qq1)=collective_subscript|, 5772|parent(qq1)=q1|, and |mp_link(qq1)=qq2|. Since |qq2| represents 5773`\.{x[]b}', |type(qq2)=mp_unknown_boolean|; also |hashloc(qq2)=h(b)|, 5774|parent(qq2)=q1|, |mp_name_type(qq2)=attr|, |mp_link(qq2)=end_attr|. 5775(Maybe colored lines will help untangle your picture.) 5776 Node |r| is a subscript node with |type| and |value| 5777representing `\.{x5}'; |mp_name_type(r)=subscr|, |subscript(r)=5.0|, 5778and |mp_link(r)=r1| is another subscript node. To complete the picture, 5779see if you can guess what |mp_link(r1)| is; give up? It's~|q1|. 5780Furthermore |subscript(r1)=20.0|, |mp_name_type(r1)=subscr|, 5781|type(r1)=mp_structured|, |attr_head(r1)=qqq|, |subscr_head(r1)=qqq1|, 5782and we finish things off with three more nodes 5783|qqq|, |qqq1|, and |qqq2| hung onto~|r1|. (Perhaps you should start again 5784with a larger sheet of paper.) The value of variable \.{x20b} 5785appears in node~|qqq2|, as you can well imagine. 5786 5787If the example in the previous paragraph doesn't make things crystal 5788clear, a glance at some of the simpler subroutines below will reveal how 5789things work out in practice. 5790 5791The only really unusual thing about these conventions is the use of 5792collective subscript attributes. The idea is to avoid repeating a lot of 5793type information when many elements of an array are identical macros 5794(for which distinct values need not be stored) or when they don't have 5795all of the possible attributes. Branches of the structure below collective 5796subscript attributes do not carry actual values except for macro identifiers; 5797branches of the structure below subscript nodes do not carry significant 5798information in their collective subscript attributes. 5799 5800 5801@c 5802#if DEBUG 5803#define hashloc(A) do_get_hashloc(mp,(mp_value_node)(A)) 5804#define set_hashloc(A,B) do_set_hashloc (mp,(mp_value_node)A, B) 5805#define parent(A) do_get_parent(mp, A) 5806#define set_parent(A,B) do_set_parent (mp,(mp_value_node)A, B) 5807static mp_sym do_get_hashloc (MP mp, mp_value_node A) { 5808 assert((A)->type == mp_attr_node_type || (A)->name_type == mp_attr); 5809 return (A)->hashloc_; 5810} 5811static void do_set_hashloc (MP mp, mp_value_node A, mp_sym B) { 5812 FUNCTION_TRACE4 ("set_hashloc(%p,%p) on line %d\n", (A), (B), __LINE__); 5813 assert((A)->type == mp_attr_node_type || (A)->name_type == mp_attr); 5814 A->hashloc_ = B; 5815} 5816static mp_node do_get_parent (MP mp, mp_value_node A) { 5817 assert((A)->type == mp_attr_node_type || (A)->name_type == mp_attr); 5818 return (A)->parent_; /* pointer to |mp_structured| variable */ 5819} 5820static void do_set_parent (MP mp, mp_value_node A, mp_node d) { 5821 assert((A)->type == mp_attr_node_type || (A)->name_type == mp_attr); 5822 FUNCTION_TRACE4 ("set_parent(%p,%p) on line %d\n", (A), d, __LINE__); 5823 A->parent_ = d; 5824} 5825#else 5826#define hashloc(A) ((mp_value_node)(A))->hashloc_ 5827#define set_hashloc(A,B) ((mp_value_node)(A))->hashloc_ = B 5828#define parent(A) ((mp_value_node)(A))->parent_ 5829#define set_parent(A,B) ((mp_value_node)(A))->parent_ = B 5830#endif 5831 5832@ 5833@d mp_free_attr_node(a,b) do { 5834 assert((b)->type == mp_attr_node_type || (b)->name_type == mp_attr); 5835 mp_free_value_node(a,b); 5836} while (0) 5837 5838@c 5839static mp_value_node mp_get_attr_node (MP mp) { 5840 mp_value_node p = (mp_value_node) mp_get_value_node (mp); 5841 mp_type (p) = mp_attr_node_type; 5842 return p; 5843} 5844 5845 5846@ Setting the |hashloc| field of |end_attr| to a value greater than 5847any legal hash address is done by assigning $-1$ typecasted to 5848|mp_sym|, hopefully resulting in all bits being set. On systems that 5849support negative pointer values or where typecasting $-1$ does not 5850result in all bits in a pointer being set, something else needs to be done. 5851@^system dependencies@> 5852 5853@<Initialize table...@>= 5854mp->end_attr = (mp_node) mp_get_attr_node (mp); 5855set_hashloc (mp->end_attr, (mp_sym)-1); 5856set_parent ((mp_value_node) mp->end_attr, NULL); 5857 5858@ @<Free table...@>= 5859mp_free_attr_node (mp, mp->end_attr); 5860 5861@ 5862@d collective_subscript (void *)0 /* code for the attribute `\.{[]}' */ 5863@d subscript(A) ((mp_value_node)(A))->subscript_ 5864@d set_subscript(A,B) do_set_subscript (mp, (mp_value_node)(A), B) 5865 5866@c 5867static void do_set_subscript (MP mp, mp_value_node A, mp_number B) { 5868 FUNCTION_TRACE3("set_subscript(%p,%p)\n", (A), (B)); 5869 assert((A)->type == mp_subscr_node_type || (A)->name_type == mp_subscr); 5870 number_clone(A->subscript_,B); /* subscript of this variable */ 5871} 5872 5873@ 5874@c 5875static mp_value_node mp_get_subscr_node (MP mp) { 5876 mp_value_node p = (mp_value_node) mp_get_value_node (mp); 5877 mp_type (p) = mp_subscr_node_type; 5878 return p; 5879} 5880 5881 5882@ Variables of type \&{pair} will have values that point to four-word 5883nodes containing two numeric values. The first of these values has 5884|name_type=mp_x_part_sector| and the second has |name_type=mp_y_part_sector|; 5885the |link| in the first points back to the node whose |value| points 5886to this four-word node. 5887 5888@d x_part(A) ((mp_pair_node)(A))->x_part_ /* where the \&{xpart} is found in a pair node */ 5889@d y_part(A) ((mp_pair_node)(A))->y_part_ /* where the \&{ypart} is found in a pair node */ 5890 5891@<MPlib internal header stuff@>= 5892typedef struct mp_pair_node_data { 5893 NODE_BODY; 5894 mp_node x_part_; 5895 mp_node y_part_; 5896} mp_pair_node_data; 5897typedef struct mp_pair_node_data *mp_pair_node; 5898 5899@ 5900@d pair_node_size sizeof(struct mp_pair_node_data) /* the number of words in a subscript node */ 5901 5902@c 5903static mp_node mp_get_pair_node (MP mp) { 5904 mp_node p; 5905 if (mp->pair_nodes) { 5906 p = mp->pair_nodes; 5907 mp->pair_nodes = p->link; 5908 mp->num_pair_nodes--; 5909 p->link = NULL; 5910 } else { 5911 p = malloc_node (pair_node_size); 5912 } 5913 mp_type (p) = mp_pair_node_type; 5914 FUNCTION_TRACE2("get_pair_node(): %p\n", p); 5915 return (mp_node) p; 5916} 5917 5918@ @<Declarations@>= 5919void mp_free_pair_node (MP mp, mp_node p); 5920 5921@ @c 5922void mp_free_pair_node (MP mp, mp_node p) { 5923 FUNCTION_TRACE2 ("mp_free_pair_node(%p)\n", p); 5924 if (!p) return; 5925 if (mp->num_pair_nodes < max_num_pair_nodes) { 5926 p->link = mp->pair_nodes; 5927 mp->pair_nodes = p; 5928 mp->num_pair_nodes++; 5929 return; 5930 } 5931 mp->var_used -= pair_node_size; 5932 xfree (p); 5933} 5934 5935 5936@ If |type(p)=mp_pair_type| or if |value(p)=NULL|, the procedure call |init_pair_node(p)| will 5937allocate a pair node for~|p|. The individual parts of such nodes are initially of type 5938|mp_independent|. 5939 5940@c 5941static void mp_init_pair_node (MP mp, mp_node p) { 5942 mp_node q; /* the new node */ 5943 mp_type (p) = mp_pair_type; 5944 q = mp_get_pair_node (mp); 5945 y_part (q) = mp_get_value_node (mp); 5946 mp_new_indep (mp, y_part (q)); /* sets |type(q)| and |value(q)| */ 5947 mp_name_type (y_part (q)) = (quarterword) (mp_y_part_sector); 5948 mp_link (y_part (q)) = p; 5949 x_part (q) = mp_get_value_node (mp); 5950 mp_new_indep (mp, x_part (q)); /* sets |type(q)| and |value(q)| */ 5951 mp_name_type (x_part (q)) = (quarterword) (mp_x_part_sector); 5952 mp_link (x_part (q)) = p; 5953 set_value_node (p, q); 5954} 5955 5956 5957@ 5958Variables of type \&{transform} are similar, but in this case their 5959|value| points to a 12-word node containing six values, identified by 5960|x_part_sector|, |y_part_sector|, |mp_xx_part_sector|, |mp_xy_part_sector|, 5961|mp_yx_part_sector|, and |mp_yy_part_sector|. 5962 5963@d tx_part(A) ((mp_transform_node)(A))->tx_part_ /* where the \&{xpart} is found in a transform node */ 5964@d ty_part(A) ((mp_transform_node)(A))->ty_part_ /* where the \&{ypart} is found in a transform node */ 5965@d xx_part(A) ((mp_transform_node)(A))->xx_part_ /* where the \&{xxpart} is found in a transform node */ 5966@d xy_part(A) ((mp_transform_node)(A))->xy_part_ /* where the \&{xypart} is found in a transform node */ 5967@d yx_part(A) ((mp_transform_node)(A))->yx_part_ /* where the \&{yxpart} is found in a transform node */ 5968@d yy_part(A) ((mp_transform_node)(A))->yy_part_ /* where the \&{yypart} is found in a transform node */ 5969 5970@<MPlib internal header stuff@>= 5971typedef struct mp_transform_node_data { 5972 NODE_BODY; 5973 mp_node tx_part_; 5974 mp_node ty_part_; 5975 mp_node xx_part_; 5976 mp_node yx_part_; 5977 mp_node xy_part_; 5978 mp_node yy_part_; 5979} mp_transform_node_data; 5980typedef struct mp_transform_node_data *mp_transform_node; 5981 5982@ 5983@d transform_node_size sizeof(struct mp_transform_node_data) /* the number of words in a subscript node */ 5984 5985@c 5986static mp_node mp_get_transform_node (MP mp) { 5987 mp_transform_node p = (mp_transform_node) malloc_node (transform_node_size); 5988 mp_type (p) = mp_transform_node_type; 5989 return (mp_node) p; 5990} 5991 5992 5993@ @c 5994static void mp_init_transform_node (MP mp, mp_node p) { 5995 mp_node q; /* the new node */ 5996 mp_type (p) = mp_transform_type; 5997 q = mp_get_transform_node (mp); /* big node */ 5998 yy_part (q) = mp_get_value_node (mp); 5999 mp_new_indep (mp, yy_part (q)); /* sets |type(q)| and |value(q)| */ 6000 mp_name_type (yy_part (q)) = (quarterword) (mp_yy_part_sector); 6001 mp_link (yy_part (q)) = p; 6002 yx_part (q) = mp_get_value_node (mp); 6003 mp_new_indep (mp, yx_part (q)); /* sets |type(q)| and |value(q)| */ 6004 mp_name_type (yx_part (q)) = (quarterword) (mp_yx_part_sector); 6005 mp_link (yx_part (q)) = p; 6006 xy_part (q) = mp_get_value_node (mp); 6007 mp_new_indep (mp, xy_part (q)); /* sets |type(q)| and |value(q)| */ 6008 mp_name_type (xy_part (q)) = (quarterword) (mp_xy_part_sector); 6009 mp_link (xy_part (q)) = p; 6010 xx_part (q) = mp_get_value_node (mp); 6011 mp_new_indep (mp, xx_part (q)); /* sets |type(q)| and |value(q)| */ 6012 mp_name_type (xx_part (q)) = (quarterword) (mp_xx_part_sector); 6013 mp_link (xx_part (q)) = p; 6014 ty_part (q) = mp_get_value_node (mp); 6015 mp_new_indep (mp, ty_part (q)); /* sets |type(q)| and |value(q)| */ 6016 mp_name_type (ty_part (q)) = (quarterword) (mp_y_part_sector); 6017 mp_link (ty_part (q)) = p; 6018 tx_part (q) = mp_get_value_node (mp); 6019 mp_new_indep (mp, tx_part (q)); /* sets |type(q)| and |value(q)| */ 6020 mp_name_type (tx_part (q)) = (quarterword) (mp_x_part_sector); 6021 mp_link (tx_part (q)) = p; 6022 set_value_node (p, q); 6023} 6024 6025 6026@ 6027Variables of type \&{color} have 3~values in 6~words identified by |mp_red_part_sector|, 6028|mp_green_part_sector|, and |mp_blue_part_sector|. 6029 6030@d red_part(A) ((mp_color_node)(A))->red_part_ /* where the \&{redpart} is found in a color node */ 6031@d green_part(A) ((mp_color_node)(A))->green_part_ /* where the \&{greenpart} is found in a color node */ 6032@d blue_part(A) ((mp_color_node)(A))->blue_part_ /* where the \&{bluepart} is found in a color node */ 6033 6034@d grey_part(A) red_part(A) /* where the \&{greypart} is found in a color node */ 6035 6036@<MPlib internal header stuff@>= 6037typedef struct mp_color_node_data { 6038 NODE_BODY; 6039 mp_node red_part_; 6040 mp_node green_part_; 6041 mp_node blue_part_; 6042} mp_color_node_data; 6043typedef struct mp_color_node_data *mp_color_node; 6044 6045@ 6046@d color_node_size sizeof(struct mp_color_node_data) /* the number of words in a subscript node */ 6047 6048@c 6049static mp_node mp_get_color_node (MP mp) { 6050 mp_color_node p = (mp_color_node) malloc_node (color_node_size); 6051 mp_type (p) = mp_color_node_type; 6052 p->link = NULL; 6053 return (mp_node) p; 6054} 6055 6056 6057@ 6058@c 6059static void mp_init_color_node (MP mp, mp_node p) { 6060 mp_node q; /* the new node */ 6061 mp_type (p) = mp_color_type; 6062 q = mp_get_color_node (mp); /* big node */ 6063 blue_part (q) = mp_get_value_node (mp); 6064 mp_new_indep (mp, blue_part (q)); /* sets |type(q)| and |value(q)| */ 6065 mp_name_type (blue_part (q)) = (quarterword) (mp_blue_part_sector); 6066 mp_link (blue_part (q)) = p; 6067 green_part (q) = mp_get_value_node (mp); 6068 mp_new_indep (mp, green_part (q)); /* sets |type(q)| and |value(q)| */ 6069 mp_name_type (y_part (q)) = (quarterword) (mp_green_part_sector); 6070 mp_link (green_part (q)) = p; 6071 red_part (q) = mp_get_value_node (mp); 6072 mp_new_indep (mp, red_part (q)); /* sets |type(q)| and |value(q)| */ 6073 mp_name_type (red_part (q)) = (quarterword) (mp_red_part_sector); 6074 mp_link (red_part (q)) = p; 6075 set_value_node (p, q); 6076} 6077 6078 6079@ Finally, variables of type |cmykcolor|. 6080 6081@d cyan_part(A) ((mp_cmykcolor_node)(A))->cyan_part_ /* where the \&{cyanpart} is found in a color node */ 6082@d magenta_part(A) ((mp_cmykcolor_node)(A))->magenta_part_ /* where the \&{magentapart} is found in a color node */ 6083@d yellow_part(A) ((mp_cmykcolor_node)(A))->yellow_part_ /* where the \&{yellowpart} is found in a color node */ 6084@d black_part(A) ((mp_cmykcolor_node)(A))->black_part_ /* where the \&{blackpart} is found in a color node */ 6085 6086@<MPlib internal header stuff@>= 6087typedef struct mp_cmykcolor_node_data { 6088 NODE_BODY; 6089 mp_node cyan_part_; 6090 mp_node magenta_part_; 6091 mp_node yellow_part_; 6092 mp_node black_part_; 6093} mp_cmykcolor_node_data; 6094typedef struct mp_cmykcolor_node_data *mp_cmykcolor_node; 6095 6096@ 6097@d cmykcolor_node_size sizeof(struct mp_cmykcolor_node_data) /* the number of words in a subscript node */ 6098 6099@c 6100static mp_node mp_get_cmykcolor_node (MP mp) { 6101 mp_cmykcolor_node p = (mp_cmykcolor_node) malloc_node (cmykcolor_node_size); 6102 mp_type (p) = mp_cmykcolor_node_type; 6103 p->link = NULL; 6104 return (mp_node) p; 6105} 6106 6107 6108@ 6109@c 6110static void mp_init_cmykcolor_node (MP mp, mp_node p) { 6111 mp_node q; /* the new node */ 6112 mp_type (p) = mp_cmykcolor_type; 6113 q = mp_get_cmykcolor_node (mp); /* big node */ 6114 black_part (q) = mp_get_value_node (mp); 6115 mp_new_indep (mp, black_part (q)); /* sets |type(q)| and |value(q)| */ 6116 mp_name_type (black_part (q)) = (quarterword) (mp_black_part_sector); 6117 mp_link (black_part (q)) = p; 6118 yellow_part (q) = mp_get_value_node (mp); 6119 mp_new_indep (mp, yellow_part (q)); /* sets |type(q)| and |value(q)| */ 6120 mp_name_type (yellow_part (q)) = (quarterword) (mp_yellow_part_sector); 6121 mp_link (yellow_part (q)) = p; 6122 magenta_part (q) = mp_get_value_node (mp); 6123 mp_new_indep (mp, magenta_part (q)); /* sets |type(q)| and |value(q)| */ 6124 mp_name_type (magenta_part (q)) = (quarterword) (mp_magenta_part_sector); 6125 mp_link (magenta_part (q)) = p; 6126 cyan_part (q) = mp_get_value_node (mp); 6127 mp_new_indep (mp, cyan_part (q)); /* sets |type(q)| and |value(q)| */ 6128 mp_name_type (cyan_part (q)) = (quarterword) (mp_cyan_part_sector); 6129 mp_link (cyan_part (q)) = p; 6130 set_value_node (p, q); 6131} 6132 6133 6134@ When an entire structured variable is saved, the |root| indication 6135is temporarily replaced by |saved_root|. 6136 6137Some variables have no name; they just are used for temporary storage 6138while expressions are being evaluated. We call them {\sl capsules}. 6139 6140@ The |id_transform| function creates a capsule for the 6141identity transformation. 6142 6143@c 6144static mp_node mp_id_transform (MP mp) { 6145 mp_node p, q; /* list manipulation registers */ 6146 p = mp_get_value_node (mp); 6147 mp_name_type (p) = mp_capsule; 6148 set_value_number (p, zero_t); /* todo: this was |null| */ 6149 mp_init_transform_node (mp, p); 6150 q = value_node (p); 6151 mp_type (tx_part (q)) = mp_known; 6152 set_value_number (tx_part (q), zero_t); 6153 mp_type (ty_part (q)) = mp_known; 6154 set_value_number (ty_part (q), zero_t); 6155 mp_type (xy_part (q)) = mp_known; 6156 set_value_number (xy_part (q), zero_t); 6157 mp_type (yx_part (q)) = mp_known; 6158 set_value_number (yx_part (q), zero_t); 6159 mp_type (xx_part (q)) = mp_known; 6160 set_value_number (xx_part (q), unity_t); 6161 mp_type (yy_part (q)) = mp_known; 6162 set_value_number (yy_part (q), unity_t); 6163 return p; 6164} 6165 6166 6167@ Tokens are of type |tag_token| when they first appear, but they point 6168to |NULL| until they are first used as the root of a variable. 6169The following subroutine establishes the root node on such grand occasions. 6170 6171@c 6172static void mp_new_root (MP mp, mp_sym x) { 6173 mp_node p; /* the new node */ 6174 p = mp_get_value_node (mp); 6175 mp_type (p) = mp_undefined; 6176 mp_name_type (p) = mp_root; 6177 set_value_sym (p, x); 6178 set_equiv_node (x, p); 6179} 6180 6181 6182@ These conventions for variable representation are illustrated by the 6183|print_variable_name| routine, which displays the full name of a 6184variable given only a pointer to its value. 6185 6186@<Declarations@>= 6187static void mp_print_variable_name (MP mp, mp_node p); 6188 6189@ @c 6190void mp_print_variable_name (MP mp, mp_node p) { 6191 mp_node q; /* a token list that will name the variable's suffix */ 6192 mp_node r; /* temporary for token list creation */ 6193 while (mp_name_type (p) >= mp_x_part_sector) { 6194 switch (mp_name_type (p)) { 6195 case mp_x_part_sector: mp_print (mp, "xpart "); break; 6196 case mp_y_part_sector: mp_print (mp, "ypart "); break; 6197 case mp_xx_part_sector: mp_print (mp, "xxpart "); break; 6198 case mp_xy_part_sector: mp_print (mp, "xypart "); break; 6199 case mp_yx_part_sector: mp_print (mp, "yxpart "); break; 6200 case mp_yy_part_sector: mp_print (mp, "yypart "); break; 6201 case mp_red_part_sector: mp_print (mp, "redpart "); break; 6202 case mp_green_part_sector: mp_print (mp, "greenpart "); break; 6203 case mp_blue_part_sector: mp_print (mp, "bluepart "); break; 6204 case mp_cyan_part_sector: mp_print (mp, "cyanpart "); break; 6205 case mp_magenta_part_sector:mp_print (mp, "magentapart ");break; 6206 case mp_yellow_part_sector: mp_print (mp, "yellowpart "); break; 6207 case mp_black_part_sector: mp_print (mp, "blackpart "); break; 6208 case mp_grey_part_sector: mp_print (mp, "greypart "); break; 6209 case mp_capsule: mp_printf (mp, "%%CAPSULE%p",p); return; break; 6210 /* this is to please the compiler: the remaining cases are operation codes */ 6211 default: break; 6212 } 6213 p = mp_link (p); 6214 } 6215 q = NULL; 6216 while (mp_name_type (p) > mp_saved_root) { 6217 /* Ascend one level, pushing a token onto list |q| 6218 and replacing |p| by its parent */ 6219 if (mp_name_type (p) == mp_subscr) { 6220 r = mp_new_num_tok (mp, subscript (p)); 6221 do { 6222 p = mp_link (p); 6223 } while (mp_name_type (p) != mp_attr); 6224 } else if (mp_name_type (p) == mp_structured_root) { 6225 p = mp_link (p); 6226 goto FOUND; 6227 } else { 6228 if (mp_name_type (p) != mp_attr) 6229 mp_confusion (mp, "var"); 6230 r = mp_get_symbolic_node (mp); 6231 set_mp_sym_sym (r, hashloc (p)); /* the hash address */ 6232 } 6233 set_mp_link (r, q); 6234 q = r; 6235 FOUND: 6236 p = parent ((mp_value_node) p); 6237 6238 } 6239 /* now |link(p)| is the hash address of |p|, and 6240 |name_type(p)| is either |root| or |saved_root|. 6241 Have to prepend a token to |q| for |show_token_list|. */ 6242 r = mp_get_symbolic_node (mp); 6243 set_mp_sym_sym (r, value_sym (p)); 6244 mp_link (r) = q; 6245 if (mp_name_type (p) == mp_saved_root) 6246 mp_print (mp, "(SAVED)"); 6247 mp_show_token_list (mp, r, NULL, max_integer, mp->tally); 6248 mp_flush_token_list (mp, r); 6249} 6250 6251@ The |interesting| function returns |true| if a given variable is not 6252in a capsule, or if the user wants to trace capsules. 6253 6254@c 6255static boolean mp_interesting (MP mp, mp_node p) { 6256 mp_name_type_type t; /* a |name_type| */ 6257 if (number_positive(internal_value (mp_tracing_capsules))) { 6258 return true; 6259 } else { 6260 t = mp_name_type (p); 6261 if (t >= mp_x_part_sector && t != mp_capsule) { 6262 mp_node tt = value_node(mp_link(p)); 6263 switch (t) { 6264 case mp_x_part_sector: 6265 t = mp_name_type (x_part (tt)); 6266 break; 6267 case mp_y_part_sector: 6268 t = mp_name_type (y_part (tt)); 6269 break; 6270 case mp_xx_part_sector: 6271 t = mp_name_type (xx_part (tt)); 6272 break; 6273 case mp_xy_part_sector: 6274 t = mp_name_type (xy_part (tt)); 6275 break; 6276 case mp_yx_part_sector: 6277 t = mp_name_type (yx_part (tt)); 6278 break; 6279 case mp_yy_part_sector: 6280 t = mp_name_type (yy_part (tt)); 6281 break; 6282 case mp_red_part_sector: 6283 t = mp_name_type (red_part (tt)); 6284 break; 6285 case mp_green_part_sector: 6286 t = mp_name_type (green_part (tt)); 6287 break; 6288 case mp_blue_part_sector: 6289 t = mp_name_type (blue_part (tt)); 6290 break; 6291 case mp_cyan_part_sector: 6292 t = mp_name_type (cyan_part (tt)); 6293 break; 6294 case mp_magenta_part_sector: 6295 t = mp_name_type (magenta_part (tt)); 6296 break; 6297 case mp_yellow_part_sector: 6298 t = mp_name_type (yellow_part (tt)); 6299 break; 6300 case mp_black_part_sector: 6301 t = mp_name_type (black_part (tt)); 6302 break; 6303 case mp_grey_part_sector: 6304 t = mp_name_type (grey_part (tt)); 6305 break; 6306 default: 6307 break; 6308 } 6309 } 6310 } 6311 return (t != mp_capsule); 6312} 6313 6314 6315@ Now here is a subroutine that converts an unstructured type into an 6316equivalent structured type, by inserting a |mp_structured| node that is 6317capable of growing. This operation is done only when |mp_name_type(p)=root|, 6318|subscr|, or |attr|. 6319 6320The procedure returns a pointer to the new node that has taken node~|p|'s 6321place in the structure. Node~|p| itself does not move, nor are its 6322|value| or |type| fields changed in any way. 6323 6324@c 6325static mp_node mp_new_structure (MP mp, mp_node p) { 6326 mp_node q, r = NULL; /* list manipulation registers */ 6327 mp_sym qq = NULL; 6328 switch (mp_name_type (p)) { 6329 case mp_root: 6330 { 6331 qq = value_sym (p); 6332 r = mp_get_value_node (mp); 6333 set_equiv_node (qq, r); 6334 } 6335 break; 6336 case mp_subscr: 6337 /* Link a new subscript node |r| in place of node |p| */ 6338 { 6339 mp_node q_new; 6340 q = p; 6341 do { 6342 q = mp_link (q); 6343 } while (mp_name_type (q) != mp_attr); 6344 q = parent ((mp_value_node) q); 6345 r = mp->temp_head; 6346 set_mp_link (r, subscr_head (q)); 6347 do { 6348 q_new = r; 6349 r = mp_link (r); 6350 } while (r != p); 6351 r = (mp_node) mp_get_subscr_node (mp); 6352 if (q_new == mp->temp_head) { 6353 set_subscr_head (q, r); 6354 } else { 6355 set_mp_link (q_new, r); 6356 } 6357 set_subscript (r, subscript (p)); 6358 } 6359 6360 break; 6361 case mp_attr: 6362 /* Link a new attribute node |r| in place of node |p| */ 6363 /* If the attribute is |collective_subscript|, there are two pointers to 6364 node~|p|, so we must change both of them. */ 6365 { 6366 mp_value_node rr; 6367 q = parent ((mp_value_node) p); 6368 r = attr_head (q); 6369 do { 6370 q = r; 6371 r = mp_link (r); 6372 } while (r != p); 6373 rr = mp_get_attr_node (mp); 6374 r = (mp_node) rr; 6375 set_mp_link (q, (mp_node) rr); 6376 set_hashloc (rr, hashloc (p)); 6377 set_parent (rr, parent ((mp_value_node) p)); 6378 if (hashloc (p) == collective_subscript) { 6379 q = mp->temp_head; 6380 set_mp_link (q, subscr_head (parent ((mp_value_node) p))); 6381 while (mp_link (q) != p) 6382 q = mp_link (q); 6383 if (q == mp->temp_head) 6384 set_subscr_head (parent ((mp_value_node) p), (mp_node) rr); 6385 else 6386 set_mp_link (q, (mp_node) rr); 6387 } 6388 } 6389 6390 break; 6391 default: 6392 mp_confusion (mp, "struct"); 6393 break; 6394 } 6395 set_mp_link (r, mp_link (p)); 6396 set_value_sym (r, value_sym (p)); 6397 mp_type (r) = mp_structured; 6398 mp_name_type (r) = mp_name_type (p); 6399 set_attr_head (r, p); 6400 mp_name_type (p) = mp_structured_root; 6401 { 6402 mp_value_node qqr = mp_get_attr_node (mp); 6403 set_mp_link (p, (mp_node) qqr); 6404 set_subscr_head (r, (mp_node) qqr); 6405 set_parent (qqr, r); 6406 mp_type (qqr) = mp_undefined; 6407 mp_name_type (qqr) = mp_attr; 6408 set_mp_link (qqr, mp->end_attr); 6409 set_hashloc (qqr, collective_subscript); 6410 } 6411 return r; 6412} 6413 6414@ The |find_variable| routine is given a pointer~|t| to a nonempty token 6415list of suffixes; it returns a pointer to the corresponding non-symbolic 6416value. For example, if |t| points to token \.x followed by a numeric 6417token containing the value~7, |find_variable| finds where the value of 6418\.{x7} is stored in memory. This may seem a simple task, and it 6419usually is, except when \.{x7} has never been referenced before. 6420Indeed, \.x may never have even been subscripted before; complexities 6421arise with respect to updating the collective subscript information. 6422 6423If a macro type is detected anywhere along path~|t|, or if the first 6424item on |t| isn't a |tag_token|, the value |NULL| is returned. 6425Otherwise |p| will be a non-NULL pointer to a node such that 6426|undefined<type(p)<mp_structured|. 6427 6428@c 6429static mp_node mp_find_variable (MP mp, mp_node t) { 6430 mp_node p, q, r, s; /* nodes in the ``value'' line */ 6431 mp_sym p_sym; 6432 mp_node pp, qq, rr, ss; /* nodes in the ``collective'' line */ 6433@^inner loop@>; 6434 p_sym = mp_sym_sym (t); 6435 t = mp_link (t); 6436 if ((eq_type (p_sym) % mp_outer_tag) != mp_tag_token) 6437 return NULL; 6438 if (equiv_node (p_sym) == NULL) 6439 mp_new_root (mp, p_sym); 6440 p = equiv_node (p_sym); 6441 pp = p; 6442 while (t != NULL) { 6443 /* Make sure that both nodes |p| and |pp| are of |mp_structured| type */ 6444 /* Although |pp| and |p| begin together, they diverge when a subscript occurs; 6445 |pp|~stays in the collective line while |p|~goes through actual subscript 6446 values. */ 6447 if (mp_type (pp) != mp_structured) { 6448 if (mp_type (pp) > mp_structured) 6449 return NULL; 6450 ss = mp_new_structure (mp, pp); 6451 if (p == pp) 6452 p = ss; 6453 pp = ss; 6454 } /* now |type(pp)=mp_structured| */ 6455 if (mp_type (p) != mp_structured) { /* it cannot be |>mp_structured| */ 6456 p = mp_new_structure (mp, p); /* now |type(p)=mp_structured| */ 6457 } 6458 6459 if (mp_type (t) != mp_symbol_node) { 6460 /* Descend one level for the subscript |value(t)| */ 6461 /* We want this part of the program to be reasonably fast, in case there are 6462 lots of subscripts at the same level of the data structure. Therefore 6463 we store an ``infinite'' value in the word that appears at the end of the 6464 subscript list, even though that word isn't part of a subscript node. */ 6465 mp_number nn, save_subscript; /* temporary storage */ 6466 new_number (nn); 6467 new_number (save_subscript); 6468 number_clone (nn, value_number (t)); 6469 pp = mp_link (attr_head (pp)); /* now |hashloc(pp)=collective_subscript| */ 6470 q = mp_link (attr_head (p)); 6471 number_clone (save_subscript, subscript (q)); 6472 set_number_to_inf(subscript (q)); 6473 s = mp->temp_head; 6474 set_mp_link (s, subscr_head (p)); 6475 do { 6476 r = s; 6477 s = mp_link (s); 6478 } while (number_greater (nn, subscript (s))); 6479 if (number_equal(nn, subscript (s))) { 6480 p = s; 6481 } else { 6482 mp_value_node p1 = mp_get_subscr_node (mp); 6483 if (r == mp->temp_head) 6484 set_subscr_head (p, (mp_node) p1); 6485 else 6486 set_mp_link (r, (mp_node) p1); 6487 set_mp_link (p1, s); 6488 number_clone (subscript (p1), nn); 6489 mp_name_type (p1) = mp_subscr; 6490 mp_type (p1) = mp_undefined; 6491 p = (mp_node) p1; 6492 } 6493 number_clone (subscript (q), save_subscript); 6494 free_number (save_subscript); 6495 free_number (nn); 6496 } else { 6497 /* Descend one level for the attribute |mp_sym_info(t)| */ 6498 mp_sym nn1 = mp_sym_sym (t); 6499 ss = attr_head (pp); 6500 do { 6501 rr = ss; 6502 ss = mp_link (ss); 6503 } while (nn1 > hashloc (ss)); 6504 if (nn1 < hashloc (ss)) { 6505 qq = (mp_node) mp_get_attr_node (mp); 6506 set_mp_link (rr, qq); 6507 set_mp_link (qq, ss); 6508 set_hashloc (qq, nn1); 6509 mp_name_type (qq) = mp_attr; 6510 mp_type (qq) = mp_undefined; 6511 set_parent ((mp_value_node) qq, pp); 6512 ss = qq; 6513 } 6514 if (p == pp) { 6515 p = ss; 6516 pp = ss; 6517 } else { 6518 pp = ss; 6519 s = attr_head (p); 6520 do { 6521 r = s; 6522 s = mp_link (s); 6523 } while (nn1 > hashloc (s)); 6524 if (nn1 == hashloc (s)) { 6525 p = s; 6526 } else { 6527 q = (mp_node) mp_get_attr_node (mp); 6528 set_mp_link (r, q); 6529 set_mp_link (q, s); 6530 set_hashloc (q, nn1); 6531 mp_name_type (q) = mp_attr; 6532 mp_type (q) = mp_undefined; 6533 set_parent ((mp_value_node) q, p); 6534 p = q; 6535 } 6536 } 6537 } 6538 t = mp_link (t); 6539 } 6540 if (mp_type (pp) >= mp_structured) { 6541 if (mp_type (pp) == mp_structured) 6542 pp = attr_head (pp); 6543 else 6544 return NULL; 6545 } 6546 if (mp_type (p) == mp_structured) 6547 p = attr_head (p); 6548 if (mp_type (p) == mp_undefined) { 6549 if (mp_type (pp) == mp_undefined) { 6550 mp_type (pp) = mp_numeric_type; 6551 set_value_number (pp, zero_t); 6552 } 6553 mp_type (p) = mp_type (pp); 6554 set_value_number (p, zero_t); 6555 } 6556 return p; 6557} 6558 6559 6560@ Variables lose their former values when they appear in a type declaration, 6561or when they are defined to be macros or \&{let} equal to something else. 6562A subroutine will be defined later that recycles the storage associated 6563with any particular |type| or |value|; our goal now is to study a higher 6564level process called |flush_variable|, which selectively frees parts of a 6565variable structure. 6566 6567This routine has some complexity because of examples such as 6568`\hbox{\tt numeric x[]a[]b}' 6569which recycles all variables of the form \.{x[i]a[j]b} (and no others), while 6570`\hbox{\tt vardef x[]a[]=...}' 6571discards all variables of the form \.{x[i]a[j]} followed by an arbitrary 6572suffix, except for the collective node \.{x[]a[]} itself. The obvious way 6573to handle such examples is to use recursion; so that's what we~do. 6574@^recursion@> 6575 6576Parameter |p| points to the root information of the variable; 6577parameter |t| points to a list of symbolic nodes that represent 6578suffixes, with |info=collective_subscript| for subscripts. 6579 6580@<Declarations@>= 6581void mp_flush_cur_exp (MP mp, mp_value v); 6582 6583@ @c 6584static void mp_flush_variable (MP mp, mp_node p, mp_node t, 6585 boolean discard_suffixes) { 6586 mp_node q, r = NULL; /* list manipulation */ 6587 mp_sym n; /* attribute to match */ 6588 while (t != NULL) { 6589 if (mp_type (p) != mp_structured) { 6590 return; 6591 } 6592 n = mp_sym_sym (t); 6593 t = mp_link (t); 6594 if (n == collective_subscript) { 6595 q = subscr_head (p); 6596 while (mp_name_type (q) == mp_subscr) { 6597 mp_flush_variable (mp, q, t, discard_suffixes); 6598 if (t == NULL) { 6599 if (mp_type (q) == mp_structured) { 6600 r = q; 6601 } else { 6602 if (r==NULL) 6603 set_subscr_head (p, mp_link (q)); 6604 else 6605 set_mp_link (r, mp_link (q)); 6606 mp_free_value_node (mp, q); 6607 } 6608 } else { 6609 r = q; 6610 } 6611 q = (r==NULL ? subscr_head (p) : mp_link (r)); 6612 } 6613 } 6614 p = attr_head (p); 6615 do { 6616 p = mp_link (p); 6617 } while (hashloc (p) < n); 6618 if (hashloc (p) != n) { 6619 return; 6620 } 6621 } 6622 if (discard_suffixes) { 6623 mp_flush_below_variable (mp, p); 6624 } else { 6625 if (mp_type (p) == mp_structured) { 6626 p = attr_head (p); 6627 } 6628 mp_recycle_value (mp, p); 6629 } 6630} 6631 6632 6633@ The next procedure is simpler; it wipes out everything but |p| itself, 6634which becomes undefined. 6635 6636@<Declarations@>= 6637static void mp_flush_below_variable (MP mp, mp_node p); 6638 6639@ @c 6640void mp_flush_below_variable (MP mp, mp_node p) { 6641 mp_node q, r; /* list manipulation registers */ 6642 FUNCTION_TRACE2 ("mp_flush_below_variable(%p)\n", p); 6643 if (mp_type (p) != mp_structured) { 6644 mp_recycle_value (mp, p); /* this sets |type(p)=undefined| */ 6645 } else { 6646 q = subscr_head (p); 6647 while (mp_name_type (q) == mp_subscr) { 6648 mp_flush_below_variable (mp, q); 6649 r = q; 6650 q = mp_link (q); 6651 mp_free_value_node (mp, r); 6652 } 6653 r = attr_head (p); 6654 q = mp_link (r); 6655 mp_recycle_value (mp, r); 6656 mp_free_value_node (mp, r); 6657 do { 6658 mp_flush_below_variable (mp, q); 6659 r = q; 6660 q = mp_link (q); 6661 mp_free_value_node (mp, r); 6662 } while (q != mp->end_attr); 6663 mp_type (p) = mp_undefined; 6664 } 6665} 6666 6667 6668@ Just before assigning a new value to a variable, we will recycle the 6669old value and make the old value undefined. The |und_type| routine 6670determines what type of undefined value should be given, based on 6671the current type before recycling. 6672 6673@c 6674static quarterword mp_und_type (MP mp, mp_node p) { 6675 (void) mp; 6676 switch (mp_type (p)) { 6677 case mp_vacuous: 6678 return mp_undefined; 6679 case mp_boolean_type: 6680 case mp_unknown_boolean: 6681 return mp_unknown_boolean; 6682 case mp_string_type: 6683 case mp_unknown_string: 6684 return mp_unknown_string; 6685 case mp_pen_type: 6686 case mp_unknown_pen: 6687 return mp_unknown_pen; 6688 case mp_path_type: 6689 case mp_unknown_path: 6690 return mp_unknown_path; 6691 case mp_picture_type: 6692 case mp_unknown_picture: 6693 return mp_unknown_picture; 6694 case mp_transform_type: 6695 case mp_color_type: 6696 case mp_cmykcolor_type: 6697 case mp_pair_type: 6698 case mp_numeric_type: 6699 return mp_type (p); 6700 case mp_known: 6701 case mp_dependent: 6702 case mp_proto_dependent: 6703 case mp_independent: 6704 return mp_numeric_type; 6705 default: /* there are no other valid cases, but please the compiler */ 6706 return 0; 6707 } 6708 return 0; 6709} 6710 6711 6712@ The |clear_symbol| routine is used when we want to redefine the equivalent 6713of a symbolic token. It must remove any variable structure or macro 6714definition that is currently attached to that symbol. If the |saving| 6715parameter is true, a subsidiary structure is saved instead of destroyed. 6716 6717@c 6718static void mp_clear_symbol (MP mp, mp_sym p, boolean saving) { 6719 mp_node q; /* |equiv(p)| */ 6720 FUNCTION_TRACE3 ("mp_clear_symbol(%p,%d)\n", p, saving); 6721 q = equiv_node (p); 6722 switch (eq_type (p) % mp_outer_tag) { 6723 case mp_defined_macro: 6724 case mp_secondary_primary_macro: 6725 case mp_tertiary_secondary_macro: 6726 case mp_expression_tertiary_macro: 6727 if (!saving) 6728 mp_delete_mac_ref (mp, q); 6729 break; 6730 case mp_tag_token: 6731 if (q != NULL) { 6732 if (saving) { 6733 mp_name_type (q) = mp_saved_root; 6734 } else { 6735 mp_flush_below_variable (mp, q); 6736 mp_free_value_node (mp, q); 6737 } 6738 } 6739 break; 6740 default: 6741 break; 6742 } 6743 set_equiv (p, mp->frozen_undefined->v.data.indep.serial); 6744 set_eq_type (p, mp->frozen_undefined->type); 6745} 6746 6747 6748@* Saving and restoring equivalents. 6749The nested structure given by \&{begingroup} and \&{endgroup} 6750allows |eqtb| entries to be saved and restored, so that temporary changes 6751can be made without difficulty. When the user requests a current value to 6752be saved, \MP\ puts that value into its ``save stack.'' An appearance of 6753\&{endgroup} ultimately causes the old values to be removed from the save 6754stack and put back in their former places. 6755 6756The save stack is a linked list containing three kinds of entries, 6757distinguished by their |type| fields. If |p| points to a saved item, 6758then 6759 6760\smallskip\hang 6761|p->type=0| stands for a group boundary; each \&{begingroup} contributes 6762such an item to the save stack and each \&{endgroup} cuts back the stack 6763until the most recent such entry has been removed. 6764 6765\smallskip\hang 6766|p->type=mp_normal_sym| means that |p->value| holds the former 6767contents of |eqtb[q]| (saved in the |knot| field of the value, which 6768is otherwise unused for variables). Such save stack entries are generated by \&{save} 6769commands. 6770 6771\smallskip\hang 6772|p->type=mp_internal_sym| means that |p->value| is a |mp_internal| 6773to be restored to internal parameter number~|q| (saved in the |serial| field of the value, which 6774is otherwise unused for internals). Such entries are generated by \&{interim} commands. 6775 6776\smallskip\noindent 6777The global variable |save_ptr| points to the top item on the save stack. 6778 6779@<Types...@>= 6780typedef struct mp_save_data { 6781 quarterword type; 6782 mp_internal value; 6783 struct mp_save_data *link; 6784} mp_save_data; 6785 6786@ @<Glob...@>= 6787mp_save_data *save_ptr; /* the most recently saved item */ 6788 6789@ @<Set init...@>= 6790mp->save_ptr = NULL; 6791 6792@ Saving a boundary item 6793@c 6794static void mp_save_boundary (MP mp) { 6795 mp_save_data *p; /* temporary register */ 6796 FUNCTION_TRACE1 ("mp_save_boundary ()\n"); 6797 p = xmalloc (1, sizeof (mp_save_data)); 6798 p->type = 0; 6799 p->link = mp->save_ptr; 6800 mp->save_ptr = p; 6801} 6802 6803 6804@ The |save_variable| routine is given a hash address |q|; it salts this 6805address in the save stack, together with its current equivalent, 6806then makes token~|q| behave as though it were brand new. 6807 6808Nothing is stacked when |save_ptr=NULL|, however; there's no way to remove 6809things from the stack when the program is not inside a group, so there's 6810no point in wasting the space. 6811 6812@c 6813static void mp_save_variable (MP mp, mp_sym q) { 6814 mp_save_data *p; /* temporary register */ 6815 FUNCTION_TRACE2 ("mp_save_variable (%p)\n", q); 6816 if (mp->save_ptr != NULL) { 6817 p = xmalloc (1, sizeof (mp_save_data)); 6818 p->type = mp_normal_sym; 6819 p->link = mp->save_ptr; 6820 p->value.v.data.indep.scale = eq_type (q); 6821 p->value.v.data.indep.serial = equiv(q); 6822 p->value.v.data.node = equiv_node(q); 6823 p->value.v.data.p = (mp_knot)q; 6824 mp->save_ptr = p; 6825 } 6826 mp_clear_symbol (mp, q, (mp->save_ptr != NULL)); 6827} 6828static void mp_unsave_variable (MP mp) { 6829 mp_sym q = (mp_sym)mp->save_ptr->value.v.data.p; 6830 if (number_positive(internal_value (mp_tracing_restores))) { 6831 mp_begin_diagnostic (mp); 6832 mp_print_nl (mp, "{restoring "); 6833 mp_print_text (q); 6834 mp_print_char (mp, xord ('}')); 6835 mp_end_diagnostic (mp, false); 6836 } 6837 mp_clear_symbol (mp, q, false); 6838 set_eq_type(q, mp->save_ptr->value.v.data.indep.scale); 6839 set_equiv (q,mp->save_ptr->value.v.data.indep.serial); 6840 q->v.data.node = mp->save_ptr->value.v.data.node; 6841 if (eq_type (q) % mp_outer_tag == mp_tag_token) { 6842 mp_node pp = q->v.data.node; 6843 if (pp != NULL) 6844 mp_name_type (pp) = mp_root; 6845 } 6846} 6847 6848@ Similarly, |save_internal| is given the location |q| of an internal 6849quantity like |mp_tracing_pens|. It creates a save stack entry of the 6850third kind. 6851 6852@c 6853static void mp_save_internal (MP mp, halfword q) { 6854 mp_save_data *p; /* new item for the save stack */ 6855 FUNCTION_TRACE2 ("mp_save_internal (%d)\n", q); 6856 if (mp->save_ptr != NULL) { 6857 p = xmalloc (1, sizeof (mp_save_data)); 6858 p->type = mp_internal_sym; 6859 p->link = mp->save_ptr; 6860 p->value = mp->internal[q]; 6861 p->value.v.data.indep.serial = q; 6862 new_number(p->value.v.data.n); 6863 number_clone(p->value.v.data.n, mp->internal[q].v.data.n); 6864 mp->save_ptr = p; 6865 } 6866} 6867 6868static void mp_unsave_internal (MP mp) { 6869 halfword q = mp->save_ptr->value.v.data.indep.serial; 6870 mp_internal saved = mp->save_ptr->value; 6871 if (number_positive(internal_value (mp_tracing_restores))) { 6872 mp_begin_diagnostic (mp); 6873 mp_print_nl (mp, "{restoring "); 6874 mp_print (mp, internal_name (q)); 6875 mp_print_char (mp, xord ('=')); 6876 if (internal_type (q) == mp_known) { 6877 print_number (saved.v.data.n); 6878 } else if (internal_type (q) == mp_string_type) { 6879 char *s = mp_str (mp, saved.v.data.str); 6880 mp_print (mp, s); 6881 } else { 6882 mp_confusion (mp, "internal_restore"); 6883 } 6884 mp_print_char (mp, xord ('}')); 6885 mp_end_diagnostic (mp, false); 6886 } 6887 free_number (mp->internal[q].v.data.n); 6888 mp->internal[q] = saved; 6889} 6890 6891@ At the end of a group, the |unsave| routine restores all of the saved 6892equivalents in reverse order. This routine will be called only when there 6893is at least one boundary item on the save stack. 6894 6895@c 6896static void mp_unsave (MP mp) { 6897 mp_save_data *p; /* saved item */ 6898 FUNCTION_TRACE1 ("mp_unsave ()\n"); 6899 while (mp->save_ptr->type != 0) { 6900 if (mp->save_ptr->type == mp_internal_sym) { 6901 mp_unsave_internal(mp); 6902 } else { 6903 mp_unsave_variable(mp); 6904 } 6905 p = mp->save_ptr->link; 6906 xfree (mp->save_ptr); 6907 mp->save_ptr = p; 6908 } 6909 p = mp->save_ptr->link; 6910 xfree (mp->save_ptr); 6911 mp->save_ptr = p; 6912} 6913 6914 6915@* Data structures for paths. 6916When a \MP\ user specifies a path, \MP\ will create a list of knots 6917and control points for the associated cubic spline curves. If the 6918knots are $z_0$, $z_1$, \dots, $z_n$, there are control points 6919$z_k^+$ and $z_{k+1}^-$ such that the cubic splines between knots 6920$z_k$ and $z_{k+1}$ are defined by B\'ezier's formula 6921@:Bezier}{B\'ezier, Pierre Etienne@> 6922$$\eqalign{z(t)&=B(z_k,z_k^+,z_{k+1}^-,z_{k+1};t)\cr 6923&=(1-t)^3z_k+3(1-t)^2tz_k^++3(1-t)t^2z_{k+1}^-+t^3z_{k+1}\cr}$$ 6924for |0<=t<=1|. 6925 6926There is a 8-word node for each knot $z_k$, containing one word of 6927control information and six words for the |x| and |y| coordinates of 6928$z_k^-$ and $z_k$ and~$z_k^+$. The control information appears in the 6929|mp_left_type| and |mp_right_type| fields, which each occupy a quarter of 6930the first word in the node; they specify properties of the curve as it 6931enters and leaves the knot. There's also a halfword |link| field, 6932which points to the following knot, and a final supplementary word (of 6933which only a quarter is used). 6934 6935If the path is a closed contour, knots 0 and |n| are identical; 6936i.e., the |link| in knot |n-1| points to knot~0. But if the path 6937is not closed, the |mp_left_type| of knot~0 and the |mp_right_type| of knot~|n| 6938are equal to |endpoint|. In the latter case the |link| in knot~|n| points 6939to knot~0, and the control points $z_0^-$ and $z_n^+$ are not used. 6940 6941@d mp_next_knot(A) (A)->next /* the next knot in this list */ 6942@d mp_left_type(A) (A)->data.types.left_type /* characterizes the path entering this knot */ 6943@d mp_right_type(A) (A)->data.types.right_type /* characterizes the path leaving this knot */ 6944@d mp_prev_knot(A) (A)->data.prev /* the previous knot in this list (only for pens) */ 6945@d mp_knot_info(A) (A)->data.info /* temporary info, used during splitting */ 6946 6947@<Exported types...@>= 6948typedef struct mp_knot_data *mp_knot; 6949typedef struct mp_knot_data { 6950 mp_number x_coord; /* the |x| coordinate of this knot */ 6951 mp_number y_coord; /* the |y| coordinate of this knot */ 6952 mp_number left_x; /* the |x| coordinate of previous control point */ 6953 mp_number left_y; /* the |y| coordinate of previous control point */ 6954 mp_number right_x; /* the |x| coordinate of next control point */ 6955 mp_number right_y; /* the |y| coordinate of next control point */ 6956 mp_knot next; 6957 union { 6958 struct { 6959 unsigned short left_type; 6960 unsigned short right_type; 6961 } types; 6962 mp_knot prev; 6963 signed int info; 6964 } data; 6965 unsigned char originator; 6966} mp_knot_data; 6967 6968 6969@ 6970@d mp_gr_next_knot(A) (A)->next /* the next knot in this list */ 6971 6972@<Exported types...@>= 6973typedef struct mp_gr_knot_data *mp_gr_knot; 6974typedef struct mp_gr_knot_data { 6975 double x_coord; 6976 double y_coord; 6977 double left_x; 6978 double left_y; 6979 double right_x; 6980 double right_y; 6981 mp_gr_knot next; 6982 union { 6983 struct { 6984 unsigned short left_type; 6985 unsigned short right_type; 6986 } types; 6987 mp_gr_knot prev; 6988 signed int info; 6989 } data; 6990 unsigned char originator; 6991} mp_gr_knot_data; 6992 6993 6994@ @<MPlib header stuff@>= 6995enum mp_knot_type { 6996 mp_endpoint = 0, /* |mp_left_type| at path beginning and |mp_right_type| at path end */ 6997 mp_explicit, /* |mp_left_type| or |mp_right_type| when control points are known */ 6998 mp_given, /* |mp_left_type| or |mp_right_type| when a direction is given */ 6999 mp_curl, /* |mp_left_type| or |mp_right_type| when a curl is desired */ 7000 mp_open, /* |mp_left_type| or |mp_right_type| when \MP\ should choose the direction */ 7001 mp_end_cycle 7002}; 7003 7004@ Before the B\'ezier control points have been calculated, the memory 7005space they will ultimately occupy is taken up by information that can be 7006used to compute them. There are four cases: 7007 7008\yskip 7009\textindent{$\bullet$} If |mp_right_type=mp_open|, the curve should leave 7010the knot in the same direction it entered; \MP\ will figure out a 7011suitable direction. 7012 7013\yskip 7014\textindent{$\bullet$} If |mp_right_type=mp_curl|, the curve should leave the 7015knot in a direction depending on the angle at which it enters the next 7016knot and on the curl parameter stored in |right_curl|. 7017 7018\yskip 7019\textindent{$\bullet$} If |mp_right_type=mp_given|, the curve should leave the 7020knot in a nonzero direction stored as an |angle| in |right_given|. 7021 7022\yskip 7023\textindent{$\bullet$} If |mp_right_type=mp_explicit|, the B\'ezier control 7024point for leaving this knot has already been computed; it is in the 7025|mp_right_x| and |mp_right_y| fields. 7026 7027\yskip\noindent 7028The rules for |mp_left_type| are similar, but they refer to the curve entering 7029the knot, and to \\{left} fields instead of \\{right} fields. 7030 7031Non-|explicit| control points will be chosen based on ``tension'' parameters 7032in the |left_tension| and |right_tension| fields. The 7033`\&{atleast}' option is represented by negative tension values. 7034@:at_least_}{\&{atleast} primitive@> 7035 7036For example, the \MP\ path specification 7037$$\.{z0..z1..tension atleast 1..\{curl 2\}z2..z3\{-1,-2\}..tension 7038 3 and 4..p},$$ 7039where \.p is the path `\.{z4..controls z45 and z54..z5}', will be represented 7040by the six knots 7041\def\lodash{\hbox to 1.1em{\thinspace\hrulefill\thinspace}} 7042$$\vbox{\halign{#\hfil&&\qquad#\hfil\cr 7043|mp_left_type|&\\{left} info&|x_coord,y_coord|&|mp_right_type|&\\{right} info\cr 7044\noalign{\yskip} 7045|endpoint|&\lodash$,\,$\lodash&$x_0,y_0$&|curl|&$1.0,1.0$\cr 7046|open|&\lodash$,1.0$&$x_1,y_1$&|open|&\lodash$,-1.0$\cr 7047|curl|&$2.0,-1.0$&$x_2,y_2$&|curl|&$2.0,1.0$\cr 7048|given|&$d,1.0$&$x_3,y_3$&|given|&$d,3.0$\cr 7049|open|&\lodash$,4.0$&$x_4,y_4$&|explicit|&$x_{45},y_{45}$\cr 7050|explicit|&$x_{54},y_{54}$&$x_5,y_5$&|endpoint|&\lodash$,\,$\lodash\cr}}$$ 7051Here |d| is the |angle| obtained by calling |n_arg(-unity,-two)|. 7052Of course, this example is more complicated than anything a normal user 7053would ever write. 7054 7055These types must satisfy certain restrictions because of the form of \MP's 7056path syntax: 7057(i)~|open| type never appears in the same node together with |endpoint|, 7058|given|, or |curl|. 7059(ii)~The |mp_right_type| of a node is |explicit| if and only if the 7060|mp_left_type| of the following node is |explicit|. 7061(iii)~|endpoint| types occur only at the ends, as mentioned above. 7062 7063@d left_curl left_x /* curl information when entering this knot */ 7064@d left_given left_x /* given direction when entering this knot */ 7065@d left_tension left_y /* tension information when entering this knot */ 7066@d right_curl right_x /* curl information when leaving this knot */ 7067@d right_given right_x /* given direction when leaving this knot */ 7068@d right_tension right_y /* tension information when leaving this knot */ 7069 7070@ Knots can be user-supplied, or they can be created by program code, 7071like the |split_cubic| function, or |copy_path|. The distinction is 7072needed for the cleanup routine that runs after |split_cubic|, because 7073it should only delete knots it has previously inserted, and never 7074anything that was user-supplied. In order to be able to differentiate 7075one knot from another, we will set |originator(p):=mp_metapost_user| when 7076it appeared in the actual metapost program, and 7077|originator(p):=mp_program_code| in all other cases. 7078 7079@d mp_originator(A) (A)->originator /* the creator of this knot */ 7080 7081@<Exported types@>= 7082enum mp_knot_originator { 7083 mp_program_code = 0, /* not created by a user */ 7084 mp_metapost_user /* created by a user */ 7085}; 7086 7087@ Here is a routine that prints a given knot list 7088in symbolic form. It illustrates the conventions discussed above, 7089and checks for anomalies that might arise while \MP\ is being debugged. 7090 7091@<Declarations@>= 7092static void mp_pr_path (MP mp, mp_knot h); 7093 7094@ @c 7095void mp_pr_path (MP mp, mp_knot h) { 7096 mp_knot p, q; /* for list traversal */ 7097 p = h; 7098 do { 7099 q = mp_next_knot (p); 7100 if ((p == NULL) || (q == NULL)) { 7101 mp_print_nl (mp, "???"); 7102 return; /* this won't happen */ 7103@.???@> 7104 } 7105 @<Print information for adjacent knots |p| and |q|@>; 7106 DONE1: 7107 p = q; 7108 if (p && ((p != h) || (mp_left_type (h) != mp_endpoint))) { 7109 @<Print two dots, followed by |given| or |curl| if present@>; 7110 } 7111 } while (p != h); 7112 if (mp_left_type (h) != mp_endpoint) 7113 mp_print (mp, "cycle"); 7114} 7115 7116 7117@ @<Print information for adjacent knots...@>= 7118mp_print_two (mp, p->x_coord, p->y_coord); 7119switch (mp_right_type (p)) { 7120case mp_endpoint: 7121 if (mp_left_type (p) == mp_open) 7122 mp_print (mp, "{open?}"); /* can't happen */ 7123@.open?@>; 7124 if ((mp_left_type (q) != mp_endpoint) || (q != h)) 7125 q = NULL; /* force an error */ 7126 goto DONE1; 7127 break; 7128case mp_explicit: 7129 @<Print control points between |p| and |q|, then |goto done1|@>; 7130 break; 7131case mp_open: 7132 @<Print information for a curve that begins |open|@>; 7133 break; 7134case mp_curl: 7135case mp_given: 7136 @<Print information for a curve that begins |curl| or |given|@>; 7137 break; 7138default: 7139 mp_print (mp, "???"); /* can't happen */ 7140@.???@>; 7141 break; 7142} 7143if (mp_left_type (q) <= mp_explicit) { 7144 mp_print (mp, "..control?"); /* can't happen */ 7145@.control?@> 7146} else if ((!number_equal(p->right_tension, unity_t)) || (!number_equal(q->left_tension, unity_t))) { 7147 @<Print tension between |p| and |q|@>; 7148} 7149 7150@ Since |n_sin_cos| produces |fraction| results, which we will print as if they 7151were |scaled|, the magnitude of a |given| direction vector will be~4096. 7152 7153@<Print two dots...@>= 7154{ 7155 mp_number n_sin, n_cos; 7156 new_fraction (n_sin); 7157 new_fraction (n_cos); 7158 mp_print_nl (mp, " .."); 7159 if (mp_left_type (p) == mp_given) { 7160 n_sin_cos (p->left_given, n_cos, n_sin); 7161 mp_print_char (mp, xord ('{')); 7162 print_number (n_cos); 7163 mp_print_char (mp, xord (',')); 7164 print_number (n_sin); 7165 mp_print_char (mp, xord ('}')); 7166 } else if (mp_left_type (p) == mp_curl) { 7167 mp_print (mp, "{curl "); 7168 print_number (p->left_curl); 7169 mp_print_char (mp, xord ('}')); 7170 } 7171 free_number (n_sin); 7172 free_number (n_cos); 7173} 7174 7175 7176@ @<Print tension between |p| and |q|@>= 7177{ 7178 mp_number v1; 7179 new_number (v1); 7180 mp_print (mp, "..tension "); 7181 if (number_negative(p->right_tension)) 7182 mp_print (mp, "atleast"); 7183 number_clone (v1, p->right_tension); 7184 number_abs (v1); 7185 print_number (v1); 7186 if (!number_equal(p->right_tension, q->left_tension)) { 7187 mp_print (mp, " and "); 7188 if (number_negative(q->left_tension)) 7189 mp_print (mp, "atleast"); 7190 number_clone (v1, p->left_tension); 7191 number_abs (v1); 7192 print_number (v1); 7193 } 7194 free_number (v1); 7195} 7196 7197 7198@ @<Print control points between |p| and |q|, then |goto done1|@>= 7199{ 7200 mp_print (mp, "..controls "); 7201 mp_print_two (mp, p->right_x, p->right_y); 7202 mp_print (mp, " and "); 7203 if (mp_left_type (q) != mp_explicit) { 7204 mp_print (mp, "??"); /* can't happen */ 7205@.??@> 7206 } else { 7207 mp_print_two (mp, q->left_x, q->left_y); 7208 } 7209 goto DONE1; 7210} 7211 7212 7213@ @<Print information for a curve that begins |open|@>= 7214if ((mp_left_type (p) != mp_explicit) && (mp_left_type (p) != mp_open)) { 7215 mp_print (mp, "{open?}"); /* can't happen */ 7216@.open?@> 7217} 7218 7219@ A curl of 1 is shown explicitly, so that the user sees clearly that 7220\MP's default curl is present. 7221 7222@<Print information for a curve that begins |curl|...@>= 7223{ 7224 if (mp_left_type (p) == mp_open) 7225 mp_print (mp, "??"); /* can't happen */ 7226@.??@>; 7227 if (mp_right_type (p) == mp_curl) { 7228 mp_print (mp, "{curl "); 7229 print_number (p->right_curl); 7230 } else { 7231 mp_number n_sin, n_cos; 7232 new_fraction (n_sin); 7233 new_fraction (n_cos); 7234 n_sin_cos (p->right_given, n_cos, n_sin); 7235 mp_print_char (mp, xord ('{')); 7236 print_number (n_cos); 7237 mp_print_char (mp, xord (',')); 7238 print_number (n_sin); 7239 free_number (n_sin); 7240 free_number (n_cos); 7241 } 7242 mp_print_char (mp, xord ('}')); 7243} 7244 7245 7246@ It is convenient to have another version of |pr_path| that prints the path 7247as a diagnostic message. 7248 7249@<Declarations@>= 7250static void mp_print_path (MP mp, mp_knot h, const char *s, boolean nuline); 7251 7252@ @c 7253void mp_print_path (MP mp, mp_knot h, const char *s, boolean nuline) { 7254 mp_print_diagnostic (mp, "Path", s, nuline); 7255 mp_print_ln (mp); 7256@.Path at line...@>; 7257 mp_pr_path (mp, h); 7258 mp_end_diagnostic (mp, true); 7259} 7260 7261 7262@ @<Declarations@>= 7263static mp_knot mp_new_knot (MP mp); 7264 7265@ @c 7266static mp_knot mp_new_knot (MP mp) { 7267 mp_knot q; 7268 if (mp->knot_nodes) { 7269 q = mp->knot_nodes; 7270 mp->knot_nodes = q->next; 7271 mp->num_knot_nodes--; 7272 } else { 7273 q = mp_xmalloc (mp, 1, sizeof (struct mp_knot_data)); 7274 } 7275 memset(q,0,sizeof (struct mp_knot_data)); 7276 new_number(q->x_coord); 7277 new_number(q->y_coord); 7278 new_number(q->left_x); 7279 new_number(q->left_y); 7280 new_number(q->right_x); 7281 new_number(q->right_y); 7282 return q; 7283} 7284 7285 7286@ @<Declarations@>= 7287static mp_gr_knot mp_gr_new_knot (MP mp); 7288 7289@ @c 7290static mp_gr_knot mp_gr_new_knot (MP mp) { 7291 mp_gr_knot q = mp_xmalloc (mp, 1, sizeof (struct mp_gr_knot_data)); 7292 return q; 7293} 7294 7295 7296@ If we want to duplicate a knot node, we can say |copy_knot|: 7297 7298@c 7299static mp_knot mp_copy_knot (MP mp, mp_knot p) { 7300 mp_knot q; 7301 if (mp->knot_nodes) { 7302 q = mp->knot_nodes; 7303 mp->knot_nodes = q->next; 7304 mp->num_knot_nodes--; 7305 } else { 7306 q = mp_xmalloc (mp, 1, sizeof (struct mp_knot_data)); 7307 } 7308 memcpy (q, p, sizeof (struct mp_knot_data)); 7309 if (mp->math_mode > mp_math_double_mode) { 7310 new_number(q->x_coord); 7311 new_number(q->y_coord); 7312 new_number(q->left_x); 7313 new_number(q->left_y); 7314 new_number(q->right_x); 7315 new_number(q->right_y); 7316 number_clone(q->x_coord, p->x_coord); 7317 number_clone(q->y_coord, p->y_coord); 7318 number_clone(q->left_x, p->left_x); 7319 number_clone(q->left_y, p->left_y); 7320 number_clone(q->right_x, p->right_x); 7321 number_clone(q->right_y, p->right_y); 7322 } 7323 mp_next_knot (q) = NULL; 7324 return q; 7325} 7326 7327@ If we want to export a knot node, we can say |export_knot|: 7328 7329@c 7330static mp_gr_knot mp_export_knot (MP mp, mp_knot p) { 7331 mp_gr_knot q; /* the copy */ 7332 q = mp_gr_new_knot (mp); 7333 q->x_coord = number_to_double(p->x_coord); 7334 q->y_coord = number_to_double(p->y_coord); 7335 q->left_x = number_to_double(p->left_x); 7336 q->left_y = number_to_double(p->left_y); 7337 q->right_x = number_to_double(p->right_x); 7338 q->right_y = number_to_double(p->right_y); 7339 q->data.types.left_type = mp_left_type(p); 7340 q->data.types.right_type = mp_left_type(p); 7341 q->data.info = mp_knot_info(p); 7342 mp_gr_next_knot (q) = NULL; 7343 return q; 7344} 7345 7346 7347@ The |copy_path| routine makes a clone of a given path. 7348 7349@c 7350static mp_knot mp_copy_path (MP mp, mp_knot p) { 7351 mp_knot q, pp, qq; /* for list manipulation */ 7352 if (p == NULL) 7353 return NULL; 7354 q = mp_copy_knot (mp, p); 7355 qq = q; 7356 pp = mp_next_knot (p); 7357 while (pp != p) { 7358 mp_next_knot (qq) = mp_copy_knot (mp, pp); 7359 qq = mp_next_knot (qq); 7360 pp = mp_next_knot (pp); 7361 } 7362 mp_next_knot (qq) = q; 7363 return q; 7364} 7365 7366@ The |export_path| routine makes a clone of a given path 7367and converts the |value|s therein to |double|s. 7368 7369@c 7370static mp_gr_knot mp_export_path (MP mp, mp_knot p) { 7371 mp_knot pp; /* for list manipulation */ 7372 mp_gr_knot q, qq; 7373 if (p == NULL) 7374 return NULL; 7375 q = mp_export_knot (mp, p); 7376 qq = q; 7377 pp = mp_next_knot (p); 7378 while (pp != p) { 7379 mp_gr_next_knot (qq) = mp_export_knot (mp, pp); 7380 qq = mp_gr_next_knot (qq); 7381 pp = mp_next_knot (pp); 7382 } 7383 mp_gr_next_knot (qq) = q; 7384 return q; 7385} 7386 7387@ If we want to import a knot node, we can say |import_knot|: 7388 7389@c 7390static mp_knot mp_import_knot (MP mp, mp_gr_knot p) { 7391 mp_knot q; /* the copy */ 7392 q = mp_new_knot (mp); 7393 set_number_from_double(q->x_coord, p->x_coord); 7394 set_number_from_double(q->y_coord, p->y_coord); 7395 set_number_from_double(q->left_x, p->left_x); 7396 set_number_from_double(q->left_y, p->left_y); 7397 set_number_from_double(q->right_x, p->right_x); 7398 set_number_from_double(q->right_y, p->right_y); 7399 mp_left_type(q) = p->data.types.left_type; 7400 mp_left_type(q) = p->data.types.right_type; 7401 mp_knot_info(q) = p->data.info; 7402 mp_next_knot (q) = NULL; 7403 return q; 7404} 7405 7406 7407@ The |import_path| routine makes a clone of a given path 7408and converts the |value|s therein to |scaled|s. 7409 7410@c 7411static mp_knot mp_import_path (MP mp, mp_gr_knot p) { 7412 mp_gr_knot pp; /* for list manipulation */ 7413 mp_knot q, qq; 7414 if (p == NULL) 7415 return NULL; 7416 q = mp_import_knot (mp, p); 7417 qq = q; 7418 pp = mp_gr_next_knot (p); 7419 while (pp != p) { 7420 mp_next_knot (qq) = mp_import_knot (mp, pp); 7421 qq = mp_next_knot (qq); 7422 pp = mp_gr_next_knot (pp); 7423 } 7424 mp_next_knot (qq) = q; 7425 return q; 7426} 7427 7428 7429@ Just before |ship_out|, knot lists are exported for printing. 7430 7431@ The |export_knot_list| routine therefore also makes a clone 7432of a given path. 7433 7434@c 7435static mp_gr_knot mp_export_knot_list (MP mp, mp_knot p) { 7436 mp_gr_knot q; /* the exported copy */ 7437 if (p == NULL) 7438 return NULL; 7439 q = mp_export_path (mp, p); 7440 return q; 7441} 7442static mp_knot mp_import_knot_list (MP mp, mp_gr_knot q) { 7443 mp_knot p; /* the imported copy */ 7444 if (q == NULL) 7445 return NULL; 7446 p = mp_import_path (mp, q); 7447 return p; 7448} 7449 7450@ Similarly, there's a way to copy the {\sl reverse\/} of a path. This procedure 7451returns a pointer to the first node of the copy, if the path is a cycle, 7452but to the final node of a non-cyclic copy. The global 7453variable |path_tail| will point to the final node of the original path; 7454this trick makes it easier to implement `\&{doublepath}'. 7455 7456All node types are assumed to be |endpoint| or |explicit| only. 7457 7458@c 7459static mp_knot mp_htap_ypoc (MP mp, mp_knot p) { 7460 mp_knot q, pp, qq, rr; /* for list manipulation */ 7461 q = mp_new_knot (mp); /* this will correspond to |p| */ 7462 qq = q; 7463 pp = p; 7464 while (1) { 7465 mp_right_type (qq) = mp_left_type (pp); 7466 mp_left_type (qq) = mp_right_type (pp); 7467 number_clone (qq->x_coord, pp->x_coord); 7468 number_clone (qq->y_coord, pp->y_coord); 7469 number_clone (qq->right_x, pp->left_x); 7470 number_clone (qq->right_y, pp->left_y); 7471 number_clone (qq->left_x, pp->right_x); 7472 number_clone (qq->left_y, pp->right_y); 7473 mp_originator (qq) = mp_originator (pp); 7474 if (mp_next_knot (pp) == p) { 7475 mp_next_knot (q) = qq; 7476 mp->path_tail = pp; 7477 return q; 7478 } 7479 rr = mp_new_knot (mp); 7480 mp_next_knot (rr) = qq; 7481 qq = rr; 7482 pp = mp_next_knot (pp); 7483 } 7484} 7485 7486 7487@ @<Glob...@>= 7488mp_knot path_tail; /* the node that links to the beginning of a path */ 7489 7490@ When a cyclic list of knot nodes is no longer needed, it can be recycled by 7491calling the following subroutine. 7492 7493@<Declarations@>= 7494static void mp_toss_knot_list (MP mp, mp_knot p); 7495static void mp_toss_knot (MP mp, mp_knot p); 7496static void mp_free_knot (MP mp, mp_knot p); 7497 7498@ @c 7499void mp_free_knot (MP mp, mp_knot q) { 7500 free_number (q->x_coord); 7501 free_number (q->y_coord); 7502 free_number (q->left_x); 7503 free_number (q->left_y); 7504 free_number (q->right_x); 7505 free_number (q->right_y); 7506 mp_xfree (q); 7507} 7508void mp_toss_knot (MP mp, mp_knot q) { 7509 if (mp->num_knot_nodes < max_num_knot_nodes) { 7510 q->next = mp->knot_nodes; 7511 mp->knot_nodes = q; 7512 mp->num_knot_nodes++; 7513 return; 7514 } 7515 if (mp->math_mode > mp_math_double_mode) { 7516 mp_free_knot(mp,q); 7517 } else { 7518 mp_xfree (q); 7519 } 7520} 7521void mp_toss_knot_list (MP mp, mp_knot p) { 7522 mp_knot q; /* the node being freed */ 7523 mp_knot r; /* the next node */ 7524 if (p == NULL) 7525 return; 7526 q = p; 7527 if (mp->math_mode > mp_math_double_mode) { 7528 do { 7529 r = mp_next_knot (q); 7530 mp_toss_knot(mp, q); 7531 q = r; 7532 } while (q != p); 7533 } else { 7534 do { 7535 r = mp_next_knot (q); 7536 if (mp->num_knot_nodes < max_num_knot_nodes) { 7537 q->next = mp->knot_nodes; 7538 mp->knot_nodes = q; 7539 mp->num_knot_nodes++; 7540 } else { 7541 mp_xfree (q); 7542 } 7543 q = r; 7544 } while (q != p); 7545 } 7546} 7547 7548 7549@* Choosing control points. 7550Now we must actually delve into one of \MP's more difficult routines, 7551the |make_choices| procedure that chooses angles and control points for 7552the splines of a curve when the user has not specified them explicitly. 7553The parameter to |make_choices| points to a list of knots and 7554path information, as described above. 7555 7556A path decomposes into independent segments at ``breakpoint'' knots, 7557which are knots whose left and right angles are both prespecified in 7558some way (i.e., their |mp_left_type| and |mp_right_type| aren't both open). 7559 7560@c 7561void mp_make_choices (MP mp, mp_knot knots) { 7562 mp_knot h; /* the first breakpoint */ 7563 mp_knot p, q; /* consecutive breakpoints being processed */ 7564 @<Other local variables for |make_choices|@>; 7565 FUNCTION_TRACE1 ("make_choices()\n"); 7566 check_arith(); /* make sure that |arith_error=false| */ 7567 if (number_positive(internal_value (mp_tracing_choices))) 7568 mp_print_path (mp, knots, ", before choices", true); 7569 @<If consecutive knots are equal, join them explicitly@>; 7570 @<Find the first breakpoint, |h|, on the path; 7571 insert an artificial breakpoint if the path is an unbroken cycle@>; 7572 p = h; 7573 do { 7574 @<Fill in the control points between |p| and the next breakpoint, 7575 then advance |p| to that breakpoint@>; 7576 } while (p != h); 7577 if (number_positive(internal_value (mp_tracing_choices))) 7578 mp_print_path (mp, knots, ", after choices", true); 7579 if (mp->arith_error) { 7580 @<Report an unexpected problem during the choice-making@>; 7581 } 7582} 7583 7584@ @<Internal ...@>= 7585void mp_make_choices (MP mp, mp_knot knots); 7586 7587@ @<Report an unexpected problem during the choice...@>= 7588{ 7589 const char *hlp[] = { 7590 "The path that I just computed is out of range.", 7591 "So it will probably look funny. Proceed, for a laugh.", 7592 NULL }; 7593 mp_back_error (mp, "Some number got too big", hlp, true); 7594@.Some number got too big@>; 7595 mp_get_x_next (mp); 7596 mp->arith_error = false; 7597} 7598 7599 7600@ Two knots in a row with the same coordinates will always be joined 7601by an explicit ``curve'' whose control points are identical with the 7602knots. 7603 7604@<If consecutive knots are equal, join them explicitly@>= 7605p = knots; 7606do { 7607 q = mp_next_knot (p); 7608 if (number_equal (p->x_coord, q->x_coord) && 7609 number_equal (p->y_coord, q->y_coord) && 7610 mp_right_type (p) > mp_explicit) { 7611 mp_right_type (p) = mp_explicit; 7612 if (mp_left_type (p) == mp_open) { 7613 mp_left_type (p) = mp_curl; 7614 set_number_to_unity(p->left_curl); 7615 } 7616 mp_left_type (q) = mp_explicit; 7617 if (mp_right_type (q) == mp_open) { 7618 mp_right_type (q) = mp_curl; 7619 set_number_to_unity(q->right_curl); 7620 } 7621 number_clone (p->right_x, p->x_coord); 7622 number_clone (q->left_x, p->x_coord); 7623 number_clone (p->right_y, p->y_coord); 7624 number_clone (q->left_y, p->y_coord); 7625 } 7626 p = q; 7627} while (p != knots) 7628 7629@ If there are no breakpoints, it is necessary to compute the direction 7630angles around an entire cycle. In this case the |mp_left_type| of the first 7631node is temporarily changed to |end_cycle|. 7632 7633@<Find the first breakpoint, |h|, on the path...@>= 7634h = knots; 7635while (1) { 7636 if (mp_left_type (h) != mp_open) 7637 break; 7638 if (mp_right_type (h) != mp_open) 7639 break; 7640 h = mp_next_knot (h); 7641 if (h == knots) { 7642 mp_left_type (h) = mp_end_cycle; 7643 break; 7644 } 7645} 7646 7647 7648@ If |mp_right_type(p)<given| and |q=mp_link(p)|, we must have 7649|mp_right_type(p)=mp_left_type(q)=mp_explicit| or |endpoint|. 7650 7651@<Fill in the control points between |p| and the next breakpoint...@>= 7652q = mp_next_knot (p); 7653if (mp_right_type (p) >= mp_given) { 7654 while ((mp_left_type (q) == mp_open) && (mp_right_type (q) == mp_open)) { 7655 q = mp_next_knot (q); 7656 } 7657 @<Fill in the control information between consecutive breakpoints |p| and |q|@>; 7658} else if (mp_right_type (p) == mp_endpoint) { 7659 @<Give reasonable values for the unused control points between |p| and~|q|@>; 7660} 7661p = q 7662 7663@ This step makes it possible to transform an explicitly computed path without 7664checking the |mp_left_type| and |mp_right_type| fields. 7665 7666@<Give reasonable values for the unused control points between |p| and~|q|@>= 7667{ 7668 number_clone (p->right_x, p->x_coord); 7669 number_clone (p->right_y, p->y_coord); 7670 number_clone (q->left_x, q->x_coord); 7671 number_clone (q->left_y, q->y_coord); 7672} 7673 7674 7675@ Before we can go further into the way choices are made, we need to 7676consider the underlying theory. The basic ideas implemented in |make_choices| 7677are due to John Hobby, who introduced the notion of ``mock curvature'' 7678@^Hobby, John Douglas@> 7679at a knot. Angles are chosen so that they preserve mock curvature when 7680a knot is passed, and this has been found to produce excellent results. 7681 7682It is convenient to introduce some notations that simplify the necessary 7683formulas. Let $d_{k,k+1}=\vert z\k-z_k\vert$ be the (nonzero) distance 7684between knots |k| and |k+1|; and let 7685$${z\k-z_k\over z_k-z_{k-1}}={d_{k,k+1}\over d_{k-1,k}}e^{i\psi_k}$$ 7686so that a polygonal line from $z_{k-1}$ to $z_k$ to $z\k$ turns left 7687through an angle of~$\psi_k$. We assume that $\vert\psi_k\vert\L180^\circ$. 7688The control points for the spline from $z_k$ to $z\k$ will be denoted by 7689$$\eqalign{z_k^+&=z_k+ 7690 \textstyle{1\over3}\rho_k e^{i\theta_k}(z\k-z_k),\cr 7691 z\k^-&=z\k- 7692 \textstyle{1\over3}\sigma\k e^{-i\phi\k}(z\k-z_k),\cr}$$ 7693where $\rho_k$ and $\sigma\k$ are nonnegative ``velocity ratios'' at the 7694beginning and end of the curve, while $\theta_k$ and $\phi\k$ are the 7695corresponding ``offset angles.'' These angles satisfy the condition 7696$$\theta_k+\phi_k+\psi_k=0,\eqno(*)$$ 7697whenever the curve leaves an intermediate knot~|k| in the direction that 7698it enters. 7699 7700@ Let $\alpha_k$ and $\beta\k$ be the reciprocals of the ``tension'' of 7701the curve at its beginning and ending points. This means that 7702$\rho_k=\alpha_k f(\theta_k,\phi\k)$ and $\sigma\k=\beta\k f(\phi\k,\theta_k)$, 7703where $f(\theta,\phi)$ is \MP's standard velocity function defined in 7704the |velocity| subroutine. The cubic spline $B(z_k^{\phantom+},z_k^+, 7705z\k^-,z\k^{\phantom+};t)$ 7706has curvature 7707@^curvature@> 7708$${2\sigma\k\sin(\theta_k+\phi\k)-6\sin\theta_k\over\rho_k^2d_{k,k+1}} 7709\qquad{\rm and}\qquad 7710{2\rho_k\sin(\theta_k+\phi\k)-6\sin\phi\k\over\sigma\k^2d_{k,k+1}}$$ 7711at |t=0| and |t=1|, respectively. The mock curvature is the linear 7712@^mock curvature@> 7713approximation to this true curvature that arises in the limit for 7714small $\theta_k$ and~$\phi\k$, if second-order terms are discarded. 7715The standard velocity function satisfies 7716$$f(\theta,\phi)=1+O(\theta^2+\theta\phi+\phi^2);$$ 7717hence the mock curvatures are respectively 7718$${2\beta\k(\theta_k+\phi\k)-6\theta_k\over\alpha_k^2d_{k,k+1}} 7719\qquad{\rm and}\qquad 7720{2\alpha_k(\theta_k+\phi\k)-6\phi\k\over\beta\k^2d_{k,k+1}}.\eqno(**)$$ 7721 7722@ The turning angles $\psi_k$ are given, and equation $(*)$ above 7723determines $\phi_k$ when $\theta_k$ is known, so the task of 7724angle selection is essentially to choose appropriate values for each 7725$\theta_k$. When equation~$(*)$ is used to eliminate $\phi$~variables 7726from $(**)$, we obtain a system of linear equations of the form 7727$$A_k\theta_{k-1}+(B_k+C_k)\theta_k+D_k\theta\k=-B_k\psi_k-D_k\psi\k,$$ 7728where 7729$$A_k={\alpha_{k-1}\over\beta_k^2d_{k-1,k}}, 7730\qquad B_k={3-\alpha_{k-1}\over\beta_k^2d_{k-1,k}}, 7731\qquad C_k={3-\beta\k\over\alpha_k^2d_{k,k+1}}, 7732\qquad D_k={\beta\k\over\alpha_k^2d_{k,k+1}}.$$ 7733The tensions are always $3\over4$ or more, hence each $\alpha$ and~$\beta$ 7734will be at most $4\over3$. It follows that $B_k\G{5\over4}A_k$ and 7735$C_k\G{5\over4}D_k$; hence the equations are diagonally dominant; 7736hence they have a unique solution. Moreover, in most cases the tensions 7737are equal to~1, so that $B_k=2A_k$ and $C_k=2D_k$. This makes the 7738solution numerically stable, and there is an exponential damping 7739effect: The data at knot $k\pm j$ affects the angle at knot~$k$ by 7740a factor of~$O(2^{-j})$. 7741 7742@ However, we still must consider the angles at the starting and ending 7743knots of a non-cyclic path. These angles might be given explicitly, or 7744they might be specified implicitly in terms of an amount of ``curl.'' 7745 7746Let's assume that angles need to be determined for a non-cyclic path 7747starting at $z_0$ and ending at~$z_n$. Then equations of the form 7748$$A_k\theta_{k-1}+(B_k+C_k)\theta_k+D_k\theta_{k+1}=R_k$$ 7749have been given for $0<k<n$, and it will be convenient to introduce 7750equations of the same form for $k=0$ and $k=n$, where 7751$$A_0=B_0=C_n=D_n=0.$$ 7752If $\theta_0$ is supposed to have a given value $E_0$, we simply 7753define $C_0=1$, $D_0=0$, and $R_0=E_0$. Otherwise a curl 7754parameter, $\gamma_0$, has been specified at~$z_0$; this means 7755that the mock curvature at $z_0$ should be $\gamma_0$ times the 7756mock curvature at $z_1$; i.e., 7757$${2\beta_1(\theta_0+\phi_1)-6\theta_0\over\alpha_0^2d_{01}} 7758=\gamma_0{2\alpha_0(\theta_0+\phi_1)-6\phi_1\over\beta_1^2d_{01}}.$$ 7759This equation simplifies to 7760$$(\alpha_0\chi_0+3-\beta_1)\theta_0+ 7761 \bigl((3-\alpha_0)\chi_0+\beta_1\bigr)\theta_1= 7762 -\bigl((3-\alpha_0)\chi_0+\beta_1\bigr)\psi_1,$$ 7763where $\chi_0=\alpha_0^2\gamma_0/\beta_1^2$; so we can set $C_0= 7764\chi_0\alpha_0+3-\beta_1$, $D_0=(3-\alpha_0)\chi_0+\beta_1$, $R_0=-D_0\psi_1$. 7765It can be shown that $C_0>0$ and $C_0B_1-A_1D_0>0$ when $\gamma_0\G0$, 7766hence the linear equations remain nonsingular. 7767 7768Similar considerations apply at the right end, when the final angle $\phi_n$ 7769may or may not need to be determined. It is convenient to let $\psi_n=0$, 7770hence $\theta_n=-\phi_n$. We either have an explicit equation $\theta_n=E_n$, 7771or we have 7772$$\bigl((3-\beta_n)\chi_n+\alpha_{n-1}\bigr)\theta_{n-1}+ 7773(\beta_n\chi_n+3-\alpha_{n-1})\theta_n=0,\qquad 7774 \chi_n={\beta_n^2\gamma_n\over\alpha_{n-1}^2}.$$ 7775 7776When |make_choices| chooses angles, it must compute the coefficients of 7777these linear equations, then solve the equations. To compute the coefficients, 7778it is necessary to compute arctangents of the given turning angles~$\psi_k$. 7779When the equations are solved, the chosen directions $\theta_k$ are put 7780back into the form of control points by essentially computing sines and 7781cosines. 7782 7783@ OK, we are ready to make the hard choices of |make_choices|. 7784Most of the work is relegated to an auxiliary procedure 7785called |solve_choices|, which has been introduced to keep 7786|make_choices| from being extremely long. 7787 7788@<Fill in the control information between...@>= 7789@<Calculate the turning angles $\psi_k$ and the distances $d_{k,k+1}$; 7790 set $n$ to the length of the path@>; 7791@<Remove |open| types at the breakpoints@>; 7792mp_solve_choices (mp, p, q, n) 7793 7794 7795@ It's convenient to precompute quantities that will be needed several 7796times later. The values of |delta_x[k]| and |delta_y[k]| will be the 7797coordinates of $z\k-z_k$, and the magnitude of this vector will be 7798|delta[k]=@t$d_{k,k+1}$@>|. The path angle $\psi_k$ between $z_k-z_{k-1}$ 7799and $z\k-z_k$ will be stored in |psi[k]|. 7800 7801@<Glob...@>= 7802int path_size; /* maximum number of knots between breakpoints of a path */ 7803mp_number *delta_x; 7804mp_number *delta_y; 7805mp_number *delta; /* knot differences */ 7806mp_number *psi; /* turning angles */ 7807 7808@ @<Dealloc variables@>= 7809{ 7810 int k; 7811 for (k = 0; k<mp->path_size; k++) { 7812 free_number (mp->delta_x[k]); 7813 free_number (mp->delta_y[k]); 7814 free_number (mp->delta[k]); 7815 free_number (mp->psi[k]); 7816 } 7817 xfree (mp->delta_x); 7818 xfree (mp->delta_y); 7819 xfree (mp->delta); 7820 xfree (mp->psi); 7821} 7822 7823@ @<Other local variables for |make_choices|@>= 7824int k, n; /* current and final knot numbers */ 7825mp_knot s, t; /* registers for list traversal */ 7826 7827@ @<Calculate the turning angles...@>= 7828{ 7829 mp_number sine, cosine; /* trig functions of various angles */ 7830 new_fraction (sine); 7831 new_fraction (cosine); 7832RESTART: 7833 k = 0; 7834 s = p; 7835 n = mp->path_size; 7836 do { 7837 t = mp_next_knot (s); 7838 set_number_from_substraction(mp->delta_x[k], t->x_coord, s->x_coord); 7839 set_number_from_substraction(mp->delta_y[k], t->y_coord, s->y_coord); 7840 pyth_add (mp->delta[k], mp->delta_x[k], mp->delta_y[k]); 7841 if (k > 0) { 7842 mp_number arg1, arg2, r1, r2; 7843 new_number (arg1); 7844 new_number (arg2); 7845 new_fraction (r1); 7846 new_fraction (r2); 7847 make_fraction (r1, mp->delta_y[k - 1], mp->delta[k - 1]); 7848 number_clone (sine, r1); 7849 make_fraction (r2, mp->delta_x[k - 1], mp->delta[k - 1]); 7850 number_clone (cosine, r2); 7851 take_fraction (r1, mp->delta_x[k], cosine); 7852 take_fraction (r2, mp->delta_y[k], sine); 7853 set_number_from_addition (arg1, r1, r2); 7854 take_fraction (r1, mp->delta_y[k], cosine); 7855 take_fraction (r2, mp->delta_x[k], sine); 7856 set_number_from_substraction (arg2, r1, r2); 7857 n_arg (mp->psi[k], arg1, arg2 ); 7858 free_number (r1); 7859 free_number (r2); 7860 free_number (arg1); 7861 free_number (arg2); 7862 } 7863 incr (k); 7864 s = t; 7865 if (k == mp->path_size) { 7866 mp_reallocate_paths (mp, mp->path_size + (mp->path_size / 4)); 7867 goto RESTART; /* retry, loop size has changed */ 7868 } 7869 if (s == q) 7870 n = k; 7871 } while (!((k >= n) && (mp_left_type (s) != mp_end_cycle))); 7872 if (k == n) 7873 set_number_to_zero(mp->psi[k]); 7874 else 7875 number_clone(mp->psi[k], mp->psi[1]); 7876 free_number (sine); 7877 free_number (cosine); 7878} 7879 7880 7881@ When we get to this point of the code, |mp_right_type(p)| is either 7882|given| or |curl| or |open|. If it is |open|, we must have 7883|mp_left_type(p)=mp_end_cycle| or |mp_left_type(p)=mp_explicit|. In the latter 7884case, the |open| type is converted to |given|; however, if the 7885velocity coming into this knot is zero, the |open| type is 7886converted to a |curl|, since we don't know the incoming direction. 7887 7888Similarly, |mp_left_type(q)| is either |given| or |curl| or |open| or 7889|mp_end_cycle|. The |open| possibility is reduced either to |given| or to |curl|. 7890 7891@<Remove |open| types at the breakpoints@>= 7892{ 7893 mp_number delx, dely; /* directions where |open| meets |explicit| */ 7894 new_number(delx); 7895 new_number(dely); 7896 if (mp_left_type (q) == mp_open) { 7897 set_number_from_substraction(delx, q->right_x, q->x_coord); 7898 set_number_from_substraction(dely, q->right_y, q->y_coord); 7899 if (number_zero(delx) && number_zero(dely)) { 7900 mp_left_type (q) = mp_curl; 7901 set_number_to_unity(q->left_curl); 7902 } else { 7903 mp_left_type (q) = mp_given; 7904 n_arg (q->left_given, delx, dely); 7905 } 7906 } 7907 if ((mp_right_type (p) == mp_open) && (mp_left_type (p) == mp_explicit)) { 7908 set_number_from_substraction(delx, p->x_coord, p->left_x); 7909 set_number_from_substraction(dely, p->y_coord, p->left_y); 7910 if (number_zero(delx) && number_zero(dely)) { 7911 mp_right_type (p) = mp_curl; 7912 set_number_to_unity(p->right_curl); 7913 } else { 7914 mp_right_type (p) = mp_given; 7915 n_arg (p->right_given, delx, dely); 7916 } 7917 } 7918 free_number (delx); 7919 free_number (dely); 7920} 7921 7922@ Linear equations need to be solved whenever |n>1|; and also when |n=1| 7923and exactly one of the breakpoints involves a curl. The simplest case occurs 7924when |n=1| and there is a curl at both breakpoints; then we simply draw 7925a straight line. 7926 7927But before coding up the simple cases, we might as well face the general case, 7928since we must deal with it sooner or later, and since the general case 7929is likely to give some insight into the way simple cases can be handled best. 7930 7931When there is no cycle, the linear equations to be solved form a tridiagonal 7932system, and we can apply the standard technique of Gaussian elimination 7933to convert that system to a sequence of equations of the form 7934$$\theta_0+u_0\theta_1=v_0,\quad 7935\theta_1+u_1\theta_2=v_1,\quad\ldots,\quad 7936\theta_{n-1}+u_{n-1}\theta_n=v_{n-1},\quad 7937\theta_n=v_n.$$ 7938It is possible to do this diagonalization while generating the equations. 7939Once $\theta_n$ is known, it is easy to determine $\theta_{n-1}$, \dots, 7940$\theta_1$, $\theta_0$; thus, the equations will be solved. 7941 7942The procedure is slightly more complex when there is a cycle, but the 7943basic idea will be nearly the same. In the cyclic case the right-hand 7944sides will be $v_k+w_k\theta_0$ instead of simply $v_k$, and we will start 7945the process off with $u_0=v_0=0$, $w_0=1$. The final equation will be not 7946$\theta_n=v_n$ but $\theta_n+u_n\theta_1=v_n+w_n\theta_0$; an appropriate 7947ending routine will take account of the fact that $\theta_n=\theta_0$ and 7948eliminate the $w$'s from the system, after which the solution can be 7949obtained as before. 7950 7951When $u_k$, $v_k$, and $w_k$ are being computed, the three pointer 7952variables |r|, |s|,~|t| will point respectively to knots |k-1|, |k|, 7953and~|k+1|. The $u$'s and $w$'s are scaled by $2^{28}$, i.e., they are 7954of type |fraction|; the $\theta$'s and $v$'s are of type |angle|. 7955 7956@<Glob...@>= 7957mp_number *theta; /* values of $\theta_k$ */ 7958mp_number *uu; /* values of $u_k$ */ 7959mp_number *vv; /* values of $v_k$ */ 7960mp_number *ww; /* values of $w_k$ */ 7961 7962@ @<Dealloc variables@>= 7963{ 7964 int k; 7965 for (k = 0; k<mp->path_size; k++) { 7966 free_number (mp->theta[k]); 7967 free_number (mp->uu[k]); 7968 free_number (mp->vv[k]); 7969 free_number (mp->ww[k]); 7970 } 7971 xfree (mp->theta); 7972 xfree (mp->uu); 7973 xfree (mp->vv); 7974 xfree (mp->ww); 7975} 7976 7977@ @<Declarations@>= 7978static void mp_reallocate_paths (MP mp, int l); 7979 7980@ @c 7981void mp_reallocate_paths (MP mp, int l) { 7982 int k; 7983 XREALLOC (mp->delta_x, l, mp_number); 7984 XREALLOC (mp->delta_y, l, mp_number); 7985 XREALLOC (mp->delta, l, mp_number); 7986 XREALLOC (mp->psi, l, mp_number); 7987 XREALLOC (mp->theta, l, mp_number); 7988 XREALLOC (mp->uu, l, mp_number); 7989 XREALLOC (mp->vv, l, mp_number); 7990 XREALLOC (mp->ww, l, mp_number); 7991 for (k = mp->path_size; k<l; k++) { 7992 new_number (mp->delta_x[k]); 7993 new_number (mp->delta_y[k]); 7994 new_number (mp->delta[k]); 7995 new_angle (mp->psi[k]); 7996 new_angle (mp->theta[k]); 7997 new_fraction (mp->uu[k]); 7998 new_angle (mp->vv[k]); 7999 new_fraction (mp->ww[k]); 8000 } 8001 mp->path_size = l; 8002} 8003 8004 8005@ Our immediate problem is to get the ball rolling by setting up the 8006first equation or by realizing that no equations are needed, and to fit 8007this initialization into a framework suitable for the overall computation. 8008 8009@<Declarations@>= 8010static void mp_solve_choices (MP mp, mp_knot p, mp_knot q, halfword n); 8011 8012@ @c 8013void mp_solve_choices (MP mp, mp_knot p, mp_knot q, halfword n) { 8014 int k; /* current knot number */ 8015 mp_knot r, s, t; /* registers for list traversal */ 8016 mp_number ff; 8017 new_fraction (ff); 8018 FUNCTION_TRACE2 ("solve_choices(%d)\n", n); 8019 k = 0; 8020 s = p; 8021 r = 0; 8022 while (1) { 8023 t = mp_next_knot (s); 8024 if (k == 0) { 8025 @<Get the linear equations started; or |return| 8026 with the control points in place, if linear equations 8027 needn't be solved@> 8028 } else { 8029 switch (mp_left_type (s)) { 8030 case mp_end_cycle: 8031 case mp_open: 8032 @<Set up equation to match mock curvatures 8033 at $z_k$; then |goto found| with $\theta_n$ 8034 adjusted to equal $\theta_0$, if a cycle has ended@>; 8035 break; 8036 case mp_curl: 8037 @<Set up equation for a curl at $\theta_n$ 8038 and |goto found|@>; 8039 break; 8040 case mp_given: 8041 @<Calculate the given value of $\theta_n$ 8042 and |goto found|@>; 8043 break; 8044 } /* there are no other cases */ 8045 } 8046 r = s; 8047 s = t; 8048 incr (k); 8049 } 8050FOUND: 8051 @<Finish choosing angles and assigning control points@>; 8052 free_number (ff); 8053} 8054 8055 8056@ On the first time through the loop, we have |k=0| and |r| is not yet 8057defined. The first linear equation, if any, will have $A_0=B_0=0$. 8058 8059@<Get the linear equations started...@>= 8060switch (mp_right_type (s)) { 8061case mp_given: 8062 if (mp_left_type (t) == mp_given) { 8063 @<Reduce to simple case of two givens and |return|@> 8064 } else { 8065 @<Set up the equation for a given value of $\theta_0$@>; 8066 } 8067 break; 8068case mp_curl: 8069 if (mp_left_type (t) == mp_curl) { 8070 @<Reduce to simple case of straight line and |return|@> 8071 } else { 8072 @<Set up the equation for a curl at $\theta_0$@>; 8073 } 8074 break; 8075case mp_open: 8076 set_number_to_zero(mp->uu[0]); 8077 set_number_to_zero(mp->vv[0]); 8078 number_clone(mp->ww[0], fraction_one_t); 8079 /* this begins a cycle */ 8080 break; 8081} /* there are no other cases */ 8082 8083 8084@ The general equation that specifies equality of mock curvature at $z_k$ is 8085$$A_k\theta_{k-1}+(B_k+C_k)\theta_k+D_k\theta\k=-B_k\psi_k-D_k\psi\k,$$ 8086as derived above. We want to combine this with the already-derived equation 8087$\theta_{k-1}+u_{k-1}\theta_k=v_{k-1}+w_{k-1}\theta_0$ in order to obtain 8088a new equation 8089$\theta_k+u_k\theta\k=v_k+w_k\theta_0$. This can be done by dividing the 8090equation 8091$$(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} 8092 -A_kw_{k-1}\theta_0$$ 8093by $B_k-u_{k-1}A_k+C_k$. The trick is to do this carefully with 8094fixed-point arithmetic, avoiding the chance of overflow while retaining 8095suitable precision. 8096 8097The calculations will be performed in several registers that 8098provide temporary storage for intermediate quantities. 8099 8100@ @<Set up equation to match mock curvatures...@>= 8101{ 8102 mp_number aa, bb, cc, acc; /* temporary registers */ 8103 mp_number dd, ee; /* likewise, but |scaled| */ 8104 new_fraction (aa); 8105 new_fraction (bb); 8106 new_fraction (cc); 8107 new_fraction (acc); 8108 new_number (dd); 8109 new_number (ee); 8110 @<Calculate the values $\\{aa}=A_k/B_k$, $\\{bb}=D_k/C_k$, 8111 $\\{dd}=(3-\alpha_{k-1})d_{k,k+1}$, $\\{ee}=(3-\beta\k)d_{k-1,k}$, 8112 and $\\{cc}=(B_k-u_{k-1}A_k)/B_k$@>; 8113 @<Calculate the ratio $\\{ff}=C_k/(C_k+B_k-u_{k-1}A_k)$@>; 8114 take_fraction (mp->uu[k], ff, bb); 8115 @<Calculate the values of $v_k$ and $w_k$@>; 8116 if (mp_left_type (s) == mp_end_cycle) { 8117 @<Adjust $\theta_n$ to equal $\theta_0$ and |goto found|@>; 8118 } 8119 free_number(aa); 8120 free_number(bb); 8121 free_number(cc); 8122 free_number(acc); 8123 free_number(dd); 8124 free_number(ee); 8125} 8126 8127 8128@ Since tension values are never less than 3/4, the values |aa| and 8129|bb| computed here are never more than 4/5. 8130 8131@<Calculate the values $\\{aa}=...@>= 8132{ 8133 mp_number absval; 8134 new_number (absval); 8135 number_clone (absval, r->right_tension); 8136 number_abs (absval); 8137if (number_equal (absval, unity_t)) { 8138 number_clone (aa, fraction_half_t); 8139 number_clone (dd, mp->delta[k]); 8140 number_double (dd); 8141} else { 8142 mp_number arg1, arg2, ret; 8143 new_number (arg2); 8144 new_number (arg1); 8145 number_clone (arg2, r->right_tension); 8146 number_abs (arg2); 8147 number_multiply_int (arg2, 3); 8148 number_substract (arg2, unity_t); 8149 make_fraction (aa, unity_t, arg2); 8150 number_clone (arg2, r->right_tension); 8151 number_abs (arg2); 8152 new_fraction (ret); 8153 make_fraction (ret, unity_t, arg2); 8154 set_number_from_substraction (arg1, fraction_three_t, ret); 8155 take_fraction (arg2, mp->delta[k], arg1); 8156 number_clone (dd, arg2); 8157 free_number (ret); 8158 free_number (arg1); 8159 free_number (arg2); 8160} 8161 number_clone (absval, t->left_tension); 8162 number_abs (absval); 8163if (number_equal (absval, unity_t)) { 8164 number_clone (bb, fraction_half_t); 8165 number_clone (ee, mp->delta[k - 1]); 8166 number_double (ee); 8167} else { 8168 mp_number arg1, arg2, ret; 8169 new_number (arg1); 8170 new_number (arg2); 8171 number_clone (arg2, t->left_tension); 8172 number_abs (arg2); 8173 number_multiply_int (arg2, 3); 8174 number_substract (arg2, unity_t); 8175 make_fraction (bb, unity_t, arg2); 8176 number_clone (arg2, t->left_tension); 8177 number_abs (arg2); 8178 new_fraction(ret); 8179 make_fraction (ret, unity_t, arg2); 8180 set_number_from_substraction (arg1,fraction_three_t, ret); 8181 take_fraction (ee, mp->delta[k - 1], arg1); 8182 free_number (ret); 8183 free_number (arg1); 8184 free_number (arg2); 8185} 8186free_number (absval); 8187} 8188{ 8189 mp_number r1; 8190 new_number (r1); 8191 take_fraction (r1, mp->uu[k - 1], aa); 8192 set_number_from_substraction (cc, fraction_one_t, r1); 8193 free_number (r1); 8194} 8195 8196@ The ratio to be calculated in this step can be written in the form 8197$$\beta_k^2\cdot\\{ee}\over\beta_k^2\cdot\\{ee}+\alpha_k^2\cdot 8198 \\{cc}\cdot\\{dd},$$ 8199because of the quantities just calculated. The values of |dd| and |ee| 8200will not be needed after this step has been performed. 8201 8202@<Calculate the ratio $\\{ff}=C_k/(C_k+B_k-u_{k-1}A_k)$@>= 8203{ 8204 mp_number rt, lt; 8205 mp_number arg2; 8206 new_number (arg2); 8207 number_clone (arg2, dd); 8208 take_fraction (dd, arg2, cc); 8209 new_number (lt); 8210 new_number (rt); 8211 number_clone (lt, s->left_tension); 8212 number_abs (lt); 8213 number_clone (rt, s->right_tension); 8214 number_abs (rt); 8215 if (!number_equal(lt, rt)) { /* $\beta_k^{-1}\ne\alpha_k^{-1}$ */ 8216 mp_number r1; 8217 new_number (r1); 8218 if (number_less(lt, rt)) { 8219 make_fraction (r1, lt, rt); /* $\alpha_k^2/\beta_k^2$ */ 8220 take_fraction (ff, r1, r1); 8221 number_clone (r1, dd); 8222 take_fraction (dd, r1, ff); 8223 } else { 8224 make_fraction (r1, rt, lt); /* $\beta_k^2/\alpha_k^2$ */ 8225 take_fraction (ff, r1, r1); 8226 number_clone (r1, ee); 8227 take_fraction (ee, r1, ff); 8228 } 8229 free_number (r1); 8230 } 8231 free_number (rt); 8232 free_number (lt); 8233 set_number_from_addition (arg2, dd, ee); 8234 make_fraction (ff, ee, arg2); 8235 free_number (arg2); 8236} 8237 8238 8239@ The value of $u_{k-1}$ will be |<=1| except when $k=1$ and the previous 8240equation was specified by a curl. In that case we must use a special 8241method of computation to prevent overflow. 8242 8243Fortunately, the calculations turn out to be even simpler in this ``hard'' 8244case. The curl equation makes $w_0=0$ and $v_0=-u_0\psi_1$, hence 8245$-B_1\psi_1-A_1v_0=-(B_1-u_0A_1)\psi_1=-\\{cc}\cdot B_1\psi_1$. 8246 8247@<Calculate the values of $v_k$ and $w_k$@>= 8248take_fraction (acc, mp->psi[k + 1], mp->uu[k]); 8249number_negate (acc); 8250if (mp_right_type (r) == mp_curl) { 8251 mp_number r1, arg2; 8252 new_fraction (r1); 8253 new_number (arg2); 8254 set_number_from_substraction (arg2, fraction_one_t, ff); 8255 take_fraction (r1, mp->psi[1], arg2); 8256 set_number_to_zero(mp->ww[k]); 8257 set_number_from_substraction(mp->vv[k], acc, r1); 8258 free_number (r1); 8259 free_number (arg2); 8260} else { 8261 mp_number arg1, r1; 8262 new_fraction (r1); 8263 new_number (arg1); 8264 set_number_from_substraction (arg1, fraction_one_t, ff); 8265 make_fraction (ff, arg1, cc); /* this is $B_k/(C_k+B_k-u_{k-1}A_k)<5$ */ 8266 free_number (arg1); 8267 take_fraction (r1, mp->psi[k], ff); 8268 number_substract (acc, r1); 8269 number_clone (r1, ff); 8270 take_fraction (ff, r1, aa); /* this is $A_k/(C_k+B_k-u_{k-1}A_k)$ */ 8271 take_fraction (r1, mp->vv[k - 1], ff); 8272 set_number_from_substraction(mp->vv[k], acc, r1 ); 8273 if (number_zero(mp->ww[k - 1])) { 8274 set_number_to_zero(mp->ww[k]); 8275 } else { 8276 take_fraction (mp->ww[k], mp->ww[k - 1], ff); 8277 number_negate(mp->ww[k]); 8278 } 8279 free_number (r1); 8280} 8281 8282 8283@ When a complete cycle has been traversed, we have $\theta_k+u_k\theta\k= 8284v_k+w_k\theta_0$, for |1<=k<=n|. We would like to determine the value of 8285$\theta_n$ and reduce the system to the form $\theta_k+u_k\theta\k=v_k$ 8286for |0<=k<n|, so that the cyclic case can be finished up just as if there 8287were no cycle. 8288 8289The idea in the following code is to observe that 8290$$\eqalign{\theta_n&=v_n+w_n\theta_0-u_n\theta_1=\cdots\cr 8291&=v_n+w_n\theta_0-u_n\bigl(v_1+w_1\theta_0-u_1(v_2+\cdots 8292 -u_{n-2}(v_{n-1}+w_{n-1}\theta_0-u_{n-1}\theta_0))\bigr),\cr}$$ 8293so we can solve for $\theta_n=\theta_0$. 8294 8295@<Adjust $\theta_n$ to equal $\theta_0$ and |goto found|@>= 8296{ 8297 mp_number arg2, r1; 8298 new_number (arg2); 8299 new_number (r1); 8300 set_number_to_zero (aa); 8301 number_clone (bb, fraction_one_t); /* we have |k=n| */ 8302 do { 8303 decr (k); 8304 if (k == 0) 8305 k = n; 8306 take_fraction (r1, aa, mp->uu[k]); 8307 set_number_from_substraction (aa, mp->vv[k], r1); 8308 take_fraction (r1, bb, mp->uu[k]); 8309 set_number_from_substraction (bb, mp->ww[k], r1); 8310 } while (k != n); /* now $\theta_n=\\{aa}+\\{bb}\cdot\theta_n$ */ 8311 set_number_from_substraction (arg2, fraction_one_t, bb); 8312 make_fraction (r1, aa, arg2); 8313 number_clone (aa, r1); 8314 number_clone(mp->theta[n], aa); 8315 number_clone(mp->vv[0], aa); 8316 for (k = 1; k < n; k++) { 8317 take_fraction (r1, aa, mp->ww[k]); 8318 number_add(mp->vv[k], r1); 8319 } 8320 free_number(arg2); 8321 free_number(r1); 8322 free_number(aa); 8323 free_number(bb); 8324 free_number(cc); 8325 free_number(acc); 8326 free_number(dd); 8327 free_number(ee); 8328 goto FOUND; 8329} 8330 8331 8332@ @c 8333void mp_reduce_angle (MP mp, mp_number *a) { 8334 mp_number abs_a; 8335 FUNCTION_TRACE2 ("reduce_angle(%f)\n", number_to_double(*a)); 8336 new_number(abs_a); 8337 number_clone(abs_a, *a); 8338 number_abs(abs_a); 8339 if ( number_greater(abs_a, one_eighty_deg_t)) { 8340 if (number_positive(*a)) { 8341 number_substract(*a, three_sixty_deg_t); 8342 } else { 8343 number_add(*a, three_sixty_deg_t); 8344 } 8345 } 8346 free_number(abs_a); 8347} 8348 8349@ @<Declarations@>= 8350void mp_reduce_angle (MP mp, mp_number *a); 8351 8352 8353@ @<Calculate the given value of $\theta_n$...@>= 8354{ 8355 mp_number narg; 8356 new_angle (narg); 8357 n_arg (narg, mp->delta_x[n - 1], mp->delta_y[n - 1]); 8358 set_number_from_substraction(mp->theta[n], s->left_given, narg); 8359 free_number (narg); 8360 mp_reduce_angle (mp, &mp->theta[n]); 8361 goto FOUND; 8362} 8363 8364 8365@ @<Set up the equation for a given value of $\theta_0$@>= 8366{ 8367 mp_number narg; 8368 new_angle (narg); 8369 n_arg (narg, mp->delta_x[0], mp->delta_y[0]); 8370 set_number_from_substraction(mp->vv[0], s->right_given, narg); 8371 free_number (narg); 8372 mp_reduce_angle (mp, &mp->vv[0]); 8373 set_number_to_zero(mp->uu[0]); 8374 set_number_to_zero(mp->ww[0]); 8375} 8376 8377 8378@ @<Set up the equation for a curl at $\theta_0$@>= 8379{ 8380 mp_number lt, rt, cc; /* tension values */ 8381 new_number (lt); 8382 new_number (rt); 8383 new_number (cc); 8384 number_clone (cc, s->right_curl); 8385 number_clone (lt, t->left_tension); 8386 number_abs(lt); 8387 number_clone (rt, s->right_tension); 8388 number_abs(rt); 8389 if (number_unity(rt) && number_unity(lt)) { 8390 mp_number arg1, arg2; 8391 new_number (arg1); 8392 new_number (arg2); 8393 number_clone (arg1, cc); 8394 number_double (arg1); 8395 number_add (arg1, unity_t); 8396 number_clone (arg2, cc); 8397 number_add (arg2, two_t); 8398 make_fraction (mp->uu[0], arg1, arg2); 8399 free_number (arg1); 8400 free_number (arg2); 8401 } else { 8402 mp_curl_ratio (mp, &mp->uu[0], cc, rt, lt); 8403 } 8404 take_fraction (mp->vv[0], mp->psi[1], mp->uu[0]); 8405 number_negate(mp->vv[0]); 8406 set_number_to_zero(mp->ww[0]); 8407 free_number (rt); 8408 free_number (lt); 8409 free_number (cc); 8410} 8411 8412 8413@ @<Set up equation for a curl at $\theta_n$...@>= 8414{ 8415 mp_number lt, rt, cc; /* tension values */ 8416 new_number (lt); 8417 new_number (rt); 8418 new_number (cc); 8419 number_clone (cc, s->left_curl); 8420 number_clone (lt, s->left_tension); 8421 number_abs(lt); 8422 number_clone (rt, r->right_tension); 8423 number_abs(rt); 8424 if (number_unity(rt) && number_unity(lt)) { 8425 mp_number arg1, arg2; 8426 new_number (arg1); 8427 new_number (arg2); 8428 number_clone (arg1, cc); 8429 number_double (arg1); 8430 number_add (arg1, unity_t); 8431 number_clone (arg2, cc); 8432 number_add (arg2, two_t); 8433 make_fraction (ff, arg1, arg2); 8434 free_number (arg1); 8435 free_number (arg2); 8436 } else { 8437 mp_curl_ratio (mp, &ff, cc, lt, rt); 8438 } 8439 { 8440 mp_number arg1, arg2, r1; 8441 new_fraction (r1); 8442 new_fraction (arg1); 8443 new_number (arg2); 8444 take_fraction (arg1, mp->vv[n - 1], ff); 8445 take_fraction (r1, ff, mp->uu[n - 1]); 8446 set_number_from_substraction (arg2, fraction_one_t, r1); 8447 make_fraction (mp->theta[n], arg1, arg2); 8448 number_negate(mp->theta[n]); 8449 free_number (r1); 8450 free_number (arg1); 8451 free_number (arg2); 8452 } 8453 free_number (rt); 8454 free_number (lt); 8455 free_number (cc); 8456 goto FOUND; 8457} 8458 8459 8460@ The |curl_ratio| subroutine has three arguments, which our previous notation 8461encourages us to call $\gamma$, $\alpha^{-1}$, and $\beta^{-1}$. It is 8462a somewhat tedious program to calculate 8463$${(3-\alpha)\alpha^2\gamma+\beta^3\over 8464 \alpha^3\gamma+(3-\beta)\beta^2},$$ 8465with the result reduced to 4 if it exceeds 4. (This reduction of curl 8466is necessary only if the curl and tension are both large.) 8467The values of $\alpha$ and $\beta$ will be at most~4/3. 8468 8469@<Declarations@>= 8470static void mp_curl_ratio (MP mp, mp_number *ret, mp_number gamma, mp_number a_tension, 8471 mp_number b_tension); 8472 8473@ @c 8474void mp_curl_ratio (MP mp, mp_number *ret, mp_number gamma_orig, mp_number a_tension, mp_number b_tension) { 8475 mp_number alpha, beta, gamma, num, denom, ff; /* registers */ 8476 mp_number arg1; 8477 new_number (arg1); 8478 new_fraction (alpha); 8479 new_fraction (beta); 8480 new_fraction (gamma); 8481 new_fraction (ff); 8482 new_fraction (denom); 8483 new_fraction (num); 8484 make_fraction (alpha, unity_t, a_tension); 8485 make_fraction (beta, unity_t, b_tension); 8486 number_clone (gamma, gamma_orig); 8487 if (number_lessequal(alpha, beta)) { 8488 make_fraction (ff, alpha, beta); 8489 number_clone (arg1, ff); 8490 take_fraction (ff, arg1, arg1); 8491 number_clone (arg1, gamma); 8492 take_fraction (gamma, arg1, ff); 8493 convert_fraction_to_scaled (beta); 8494 take_fraction (denom, gamma, alpha); 8495 number_add (denom, three_t); 8496 } else { 8497 make_fraction (ff, beta, alpha); 8498 number_clone (arg1, ff); 8499 take_fraction (ff, arg1, arg1); 8500 take_fraction (arg1, beta, ff); 8501 convert_fraction_to_scaled (arg1); 8502 number_clone (beta, arg1); 8503 take_fraction (denom, gamma, alpha); 8504 set_number_from_div (arg1, ff, twelvebits_3); 8505 number_add (denom, arg1); 8506 } 8507 number_substract (denom, beta); 8508 set_number_from_substraction (arg1, fraction_three_t, alpha); 8509 take_fraction (num, gamma, arg1); 8510 number_add (num, beta); 8511 number_clone (arg1, denom); 8512 number_double (arg1); 8513 number_double (arg1); /* arg1 = 4*denom */ 8514 if (number_greaterequal(num, arg1)) { 8515 number_clone(*ret, fraction_four_t); 8516 } else { 8517 make_fraction (*ret, num, denom); 8518 } 8519 free_number (alpha); 8520 free_number (beta); 8521 free_number (gamma); 8522 free_number (num); 8523 free_number (denom); 8524 free_number (ff); 8525 free_number (arg1); 8526} 8527 8528 8529@ We're in the home stretch now. 8530 8531@<Finish choosing angles and assigning control points@>= 8532{ 8533 mp_number r1; 8534 new_number (r1); 8535 for (k = n - 1; k >= 0; k--) { 8536 take_fraction (r1, mp->theta[k + 1], mp->uu[k]); 8537 set_number_from_substraction(mp->theta[k], mp->vv[k], r1); 8538 } 8539 free_number (r1); 8540} 8541s = p; 8542k = 0; 8543{ 8544mp_number arg; 8545new_number (arg); 8546do { 8547 t = mp_next_knot (s); 8548 n_sin_cos (mp->theta[k], mp->ct, mp->st); 8549 number_clone (arg, mp->psi[k + 1]); 8550 number_negate (arg); 8551 number_substract (arg, mp->theta[k + 1]); 8552 n_sin_cos (arg, mp->cf, mp->sf); 8553 mp_set_controls (mp, s, t, k); 8554 incr (k); 8555 s = t; 8556} while (k != n); 8557free_number (arg); 8558} 8559 8560 8561@ The |set_controls| routine actually puts the control points into 8562a pair of consecutive nodes |p| and~|q|. Global variables are used to 8563record the values of $\sin\theta$, $\cos\theta$, $\sin\phi$, and 8564$\cos\phi$ needed in this calculation. 8565 8566@<Glob...@>= 8567mp_number st; 8568mp_number ct; 8569mp_number sf; 8570mp_number cf; /* sines and cosines */ 8571 8572@ @<Initialize table...@>= 8573new_fraction (mp->st); 8574new_fraction (mp->ct); 8575new_fraction (mp->sf); 8576new_fraction (mp->cf); 8577 8578@ @<Dealloc ...@>= 8579free_number (mp->st); 8580free_number (mp->ct); 8581free_number (mp->sf); 8582free_number (mp->cf); 8583 8584 8585@ @<Declarations@>= 8586static void mp_set_controls (MP mp, mp_knot p, mp_knot q, integer k); 8587 8588@ @c 8589void mp_set_controls (MP mp, mp_knot p, mp_knot q, integer k) { 8590 mp_number rr, ss; /* velocities, divided by thrice the tension */ 8591 mp_number lt, rt; /* tensions */ 8592 mp_number sine; /* $\sin(\theta+\phi)$ */ 8593 mp_number tmp; 8594 mp_number r1, r2; 8595 new_number(tmp); 8596 new_number (lt); 8597 new_number (rt); 8598 new_number (r1); 8599 new_number (r2); 8600 number_clone(lt, q->left_tension); 8601 number_abs(lt); 8602 number_clone(rt, p->right_tension); 8603 number_abs(rt); 8604 new_fraction (sine); 8605 new_fraction (rr); 8606 new_fraction (ss); 8607 velocity (rr, mp->st, mp->ct, mp->sf, mp->cf, rt); 8608 velocity (ss, mp->sf, mp->cf, mp->st, mp->ct, lt); 8609 if (number_negative(p->right_tension) || number_negative(q->left_tension)) { 8610 @<Decrease the velocities, 8611 if necessary, to stay inside the bounding triangle@>; 8612 } 8613 take_fraction (r1, mp->delta_x [k], mp->ct); 8614 take_fraction (r2, mp->delta_y [k], mp->st); 8615 number_substract (r1, r2); 8616 take_fraction (tmp, r1, rr); 8617 set_number_from_addition (p->right_x, p->x_coord, tmp); 8618 take_fraction (r1, mp->delta_y[k], mp->ct); 8619 take_fraction (r2, mp->delta_x[k], mp->st); 8620 number_add (r1, r2); 8621 take_fraction (tmp, r1, rr); 8622 set_number_from_addition (p->right_y, p->y_coord, tmp); 8623 take_fraction (r1, mp->delta_x[k], mp->cf); 8624 take_fraction (r2, mp->delta_y[k], mp->sf); 8625 number_add (r1, r2); 8626 take_fraction (tmp, r1, ss); 8627 set_number_from_substraction (q->left_x, q->x_coord, tmp); 8628 take_fraction (r1, mp->delta_y[k], mp->cf); 8629 take_fraction (r2, mp->delta_x[k], mp->sf); 8630 number_substract (r1, r2); 8631 take_fraction (tmp, r1, ss); 8632 set_number_from_substraction(q->left_y, q->y_coord, tmp); 8633 mp_right_type (p) = mp_explicit; 8634 mp_left_type (q) = mp_explicit; 8635 free_number (tmp); 8636 free_number (r1); 8637 free_number (r2); 8638 free_number (lt); 8639 free_number (rt); 8640 free_number (rr); 8641 free_number (ss); 8642 free_number (sine); 8643} 8644 8645 8646@ The boundedness conditions $\\{rr}\L\sin\phi\,/\sin(\theta+\phi)$ and 8647$\\{ss}\L\sin\theta\,/\sin(\theta+\phi)$ are to be enforced if $\sin\theta$, 8648$\sin\phi$, and $\sin(\theta+\phi)$ all have the same sign. Otherwise 8649there is no ``bounding triangle.'' 8650 8651@<Decrease the velocities, if necessary...@>= 8652if ((number_nonnegative(mp->st) && number_nonnegative(mp->sf)) || (number_nonpositive(mp->st) && number_nonpositive(mp->sf))) { 8653 mp_number r1, r2, arg1; 8654 mp_number ab_vs_cd; 8655 new_number (ab_vs_cd); 8656 new_fraction (r1); 8657 new_fraction (r2); 8658 new_number (arg1); 8659 number_clone (arg1, mp->st); 8660 number_abs (arg1); 8661 take_fraction (r1, arg1, mp->cf); 8662 number_clone (arg1, mp->sf); 8663 number_abs (arg1); 8664 take_fraction (r2, arg1, mp->ct); 8665 set_number_from_addition (sine, r1, r2); 8666 if (number_positive(sine)) { 8667 set_number_from_addition (arg1, fraction_one_t, unity_t); /* safety factor */ 8668 number_clone (r1, sine); 8669 take_fraction (sine, r1, arg1); 8670 if (number_negative(p->right_tension)) { 8671 number_clone (arg1, mp->sf); 8672 number_abs (arg1); 8673 ab_vs_cd (ab_vs_cd, arg1, fraction_one_t, rr, sine); 8674 if (number_negative(ab_vs_cd)) { 8675 number_clone (arg1, mp->sf); 8676 number_abs (arg1); 8677 make_fraction (rr, arg1, sine); 8678 } 8679 } 8680 if (number_negative(q->left_tension)) { 8681 number_clone (arg1, mp->st); 8682 number_abs (arg1); 8683 ab_vs_cd (ab_vs_cd, arg1, fraction_one_t, ss, sine); 8684 if (number_negative(ab_vs_cd)) { 8685 number_clone (arg1, mp->st); 8686 number_abs (arg1); 8687 make_fraction (ss, arg1, sine); 8688 } 8689 } 8690 } 8691 free_number (arg1); 8692 free_number (r1); 8693 free_number (r2); 8694 free_number (ab_vs_cd); 8695} 8696 8697@ Only the simple cases remain to be handled. 8698 8699@<Reduce to simple case of two givens and |return|@>= 8700{ 8701 mp_number arg1; 8702 mp_number narg; 8703 new_angle (narg); 8704 n_arg (narg, mp->delta_x[0], mp->delta_y[0]); 8705 new_number (arg1); 8706 set_number_from_substraction (arg1, p->right_given, narg); 8707 n_sin_cos (arg1, mp->ct, mp->st); 8708 set_number_from_substraction (arg1, q->left_given, narg); 8709 n_sin_cos (arg1, mp->cf, mp->sf); 8710 number_negate (mp->sf); 8711 mp_set_controls (mp, p, q, 0); 8712 free_number (narg); 8713 free_number (arg1); 8714 free_number (ff); 8715 return; 8716} 8717 8718 8719@ @<Reduce to simple case of straight line and |return|@>= 8720{ 8721 mp_number lt, rt; /* tension values */ 8722 mp_right_type (p) = mp_explicit; 8723 mp_left_type (q) = mp_explicit; 8724 new_number (lt); 8725 new_number (rt); 8726 number_clone (lt, q->left_tension); 8727 number_abs(lt); 8728 number_clone (rt, p->right_tension); 8729 number_abs(rt); 8730 if (number_unity(rt)) { 8731 mp_number arg2; 8732 new_number (arg2); 8733 if (number_nonnegative(mp->delta_x[0])) { 8734 set_number_from_addition (arg2, mp->delta_x[0], epsilon_t); 8735 } else { 8736 set_number_from_substraction (arg2, mp->delta_x[0], epsilon_t); 8737 } 8738 number_int_div (arg2, 3); 8739 set_number_from_addition (p->right_x, p->x_coord, arg2); 8740 if (number_nonnegative(mp->delta_y[0])) { 8741 set_number_from_addition (arg2, mp->delta_y[0], epsilon_t); 8742 } else { 8743 set_number_from_substraction (arg2, mp->delta_y[0], epsilon_t); 8744 } 8745 number_int_div (arg2, 3); 8746 set_number_from_addition (p->right_y, p->y_coord, arg2); 8747 free_number (arg2); 8748 } else { 8749 mp_number arg2, r1; 8750 new_fraction (r1); 8751 new_number (arg2); 8752 number_clone (arg2, rt); 8753 number_multiply_int (arg2, 3); 8754 make_fraction (ff, unity_t, arg2); /* $\alpha/3$ */ 8755 free_number (arg2); 8756 take_fraction (r1, mp->delta_x[0], ff); 8757 set_number_from_addition (p->right_x, p->x_coord, r1); 8758 take_fraction (r1, mp->delta_y[0], ff); 8759 set_number_from_addition (p->right_y, p->y_coord, r1); 8760 } 8761 if (number_unity(lt)) { 8762 mp_number arg2; 8763 new_number (arg2); 8764 if (number_nonnegative(mp->delta_x[0])) { 8765 set_number_from_addition (arg2, mp->delta_x[0], epsilon_t); 8766 } else { 8767 set_number_from_substraction (arg2, mp->delta_x[0], epsilon_t); 8768 } 8769 number_int_div (arg2, 3); 8770 set_number_from_substraction (q->left_x, q->x_coord, arg2); 8771 if (number_nonnegative(mp->delta_y[0])) { 8772 set_number_from_addition (arg2, mp->delta_y[0], epsilon_t); 8773 } else { 8774 set_number_from_substraction (arg2, mp->delta_y[0], epsilon_t); 8775 } 8776 number_int_div (arg2, 3); 8777 set_number_from_substraction (q->left_y, q->y_coord, arg2); 8778 free_number (arg2); 8779 } else { 8780 mp_number arg2, r1; 8781 new_fraction (r1); 8782 new_number (arg2); 8783 number_clone (arg2, lt); 8784 number_multiply_int (arg2, 3); 8785 make_fraction (ff, unity_t, arg2); /* $\beta/3$ */ 8786 free_number (arg2); 8787 take_fraction (r1, mp->delta_x[0], ff); 8788 set_number_from_substraction(q->left_x, q->x_coord, r1); 8789 take_fraction (r1, mp->delta_y[0], ff); 8790 set_number_from_substraction(q->left_y, q->y_coord, r1); 8791 free_number (r1); 8792 } 8793 free_number (ff); 8794 free_number (lt); 8795 free_number (rt); 8796 return; 8797} 8798 8799@ Various subroutines that are useful for the new (1.770) exported 8800api for solving path choices 8801 8802@c 8803#define TOO_LARGE(a) (fabs((a))>4096.0) 8804#define PI 3.1415926535897932384626433832795028841971 8805 8806static int out_of_range(MP mp, double a) 8807{ 8808 mp_number t; 8809 new_number (t); 8810 set_number_from_double(t,fabs(a)); 8811 if (number_greaterequal(t,inf_t)) { 8812 free_number (t); 8813 return 1; 8814 } 8815 free_number (t); 8816 return 0; 8817} 8818 8819static int mp_link_knotpair (MP mp, mp_knot p, mp_knot q); 8820static int mp_link_knotpair (MP mp, mp_knot p, mp_knot q) 8821{ 8822 if (p==NULL ||q==NULL) return 0; 8823 p->next = q; 8824 set_number_from_double(p->right_tension, 1.0); 8825 if (mp_right_type(p)==mp_endpoint) { 8826 mp_right_type(p) = mp_open; 8827 } 8828 set_number_from_double(q->left_tension, 1.0); 8829 if (mp_left_type(q) == mp_endpoint) { 8830 mp_left_type(q) = mp_open; 8831 } 8832 return 1; 8833} 8834 8835int mp_close_path_cycle (MP mp, mp_knot p, mp_knot q) 8836{ 8837 return mp_link_knotpair(mp,p,q); 8838} 8839 8840int mp_close_path (MP mp, mp_knot q, mp_knot first) 8841{ 8842 if (q==NULL || first==NULL) return 0; 8843 q->next = first; 8844 mp_right_type(q) = mp_endpoint; 8845 set_number_from_double(q->right_tension, 1.0); 8846 mp_left_type(first) = mp_endpoint; 8847 set_number_from_double(first->left_tension, 1.0); 8848 return 1; 8849} 8850 8851mp_knot mp_create_knot (MP mp) 8852{ 8853 mp_knot q = mp_new_knot(mp); 8854 mp_left_type(q) = mp_endpoint; 8855 mp_right_type(q) = mp_endpoint; 8856 return q; 8857} 8858 8859int mp_set_knot (MP mp, mp_knot p, double x, double y) 8860{ 8861 if (out_of_range(mp, x)) return 0; 8862 if (out_of_range(mp, y)) return 0; 8863 if (p==NULL) return 0; 8864 set_number_from_double(p->x_coord, x); 8865 set_number_from_double(p->y_coord, y); 8866 return 1; 8867} 8868 8869mp_knot mp_append_knot (MP mp, mp_knot p, double x, double y) 8870{ 8871 mp_knot q = mp_create_knot(mp); 8872 if (q==NULL) return NULL; 8873 if (!mp_set_knot(mp, q, x, y)) { 8874 free(q); 8875 return NULL; 8876 } 8877 if (p == NULL) return q; 8878 if (!mp_link_knotpair(mp, p,q)) { 8879 free(q); 8880 return NULL; 8881 } 8882 return q; 8883} 8884 8885int mp_set_knot_curl (MP mp, mp_knot q, double value) { 8886 if (q==NULL) return 0; 8887 if (TOO_LARGE(value)) return 0; 8888 mp_right_type(q)=mp_curl; 8889 set_number_from_double(q->right_curl, value); 8890 if (mp_left_type(q)==mp_open) { 8891 mp_left_type(q)=mp_curl; 8892 set_number_from_double(q->left_curl, value); 8893 } 8894 return 1; 8895} 8896 8897int mp_set_knot_left_curl (MP mp, mp_knot q, double value) { 8898 if (q==NULL) return 0; 8899 if (TOO_LARGE(value)) return 0; 8900 mp_left_type(q)=mp_curl; 8901 set_number_from_double(q->left_curl, value); 8902 if (mp_right_type(q)==mp_open) { 8903 mp_right_type(q)=mp_curl; 8904 set_number_from_double(q->right_curl, value); 8905 } 8906 return 1; 8907} 8908 8909int mp_set_knot_right_curl (MP mp, mp_knot q, double value) { 8910 if (q==NULL) return 0; 8911 if (TOO_LARGE(value)) return 0; 8912 mp_right_type(q)=mp_curl; 8913 set_number_from_double(q->right_curl, value); 8914 if (mp_left_type(q)==mp_open) { 8915 mp_left_type(q)=mp_curl; 8916 set_number_from_double(q->left_curl, value); 8917 } 8918 return 1; 8919} 8920 8921int mp_set_knotpair_curls (MP mp, mp_knot p, mp_knot q, double t1, double t2) { 8922 if (p==NULL || q==NULL) return 0; 8923 if (mp_set_knot_curl(mp, p, t1)) 8924 return mp_set_knot_curl(mp, q, t2); 8925 return 0; 8926} 8927 8928int mp_set_knotpair_tensions (MP mp, mp_knot p, mp_knot q, double t1, double t2) { 8929 if (p==NULL || q==NULL) return 0; 8930 if (TOO_LARGE(t1)) return 0; 8931 if (TOO_LARGE(t2)) return 0; 8932 if ((fabs(t1)<0.75)) return 0; 8933 if ((fabs(t2)<0.75)) return 0; 8934 set_number_from_double(p->right_tension, t1); 8935 set_number_from_double(q->left_tension, t2); 8936 return 1; 8937} 8938 8939int mp_set_knot_left_tension (MP mp, mp_knot p, double t1) { 8940 if (p==NULL) return 0; 8941 if (TOO_LARGE(t1)) return 0; 8942 if ((fabs(t1)<0.75)) return 0; 8943 set_number_from_double(p->left_tension, t1); 8944 return 1; 8945} 8946 8947int mp_set_knot_right_tension (MP mp, mp_knot p, double t1) { 8948 if (p==NULL) return 0; 8949 if (TOO_LARGE(t1)) return 0; 8950 if ((fabs(t1)<0.75)) return 0; 8951 set_number_from_double(p->right_tension, t1); 8952 return 1; 8953} 8954 8955int mp_set_knotpair_controls (MP mp, mp_knot p, mp_knot q, double x1, double y1, double x2, double y2) { 8956 if (p==NULL || q==NULL) return 0; 8957 if (out_of_range(mp, x1)) return 0; 8958 if (out_of_range(mp, y1)) return 0; 8959 if (out_of_range(mp, x2)) return 0; 8960 if (out_of_range(mp, y2)) return 0; 8961 mp_right_type(p)=mp_explicit; 8962 set_number_from_double(p->right_x, x1); 8963 set_number_from_double(p->right_y, y1); 8964 mp_left_type(q)=mp_explicit; 8965 set_number_from_double(q->left_x, x2); 8966 set_number_from_double(q->left_y, y2); 8967 return 1; 8968} 8969 8970int mp_set_knot_left_control (MP mp, mp_knot p, double x1, double y1) { 8971 if (p==NULL) return 0; 8972 if (out_of_range(mp, x1)) return 0; 8973 if (out_of_range(mp, y1)) return 0; 8974 mp_left_type(p)=mp_explicit; 8975 set_number_from_double(p->left_x, x1); 8976 set_number_from_double(p->left_y, y1); 8977 return 1; 8978} 8979 8980int mp_set_knot_right_control (MP mp, mp_knot p, double x1, double y1) { 8981 if (p==NULL) return 0; 8982 if (out_of_range(mp, x1)) return 0; 8983 if (out_of_range(mp, y1)) return 0; 8984 mp_right_type(p)=mp_explicit; 8985 set_number_from_double(p->right_x, x1); 8986 set_number_from_double(p->right_y, y1); 8987 return 1; 8988} 8989 8990int mp_set_knot_direction (MP mp, mp_knot q, double x, double y) { 8991 double value = 0; 8992 if (q==NULL) return 0; 8993 if (TOO_LARGE(x)) return 0; 8994 if (TOO_LARGE(y)) return 0; 8995 if (!(x==0 && y == 0)) 8996 value = atan2 (y, x) * (180.0 / PI) * 16.0; 8997 mp_right_type(q)=mp_given; 8998 set_number_from_double(q->right_curl, value); 8999 if (mp_left_type(q)==mp_open) { 9000 mp_left_type(q)=mp_given; 9001 set_number_from_double(q->left_curl, value); 9002 } 9003 return 1; 9004} 9005 9006int mp_set_knotpair_directions (MP mp, mp_knot p, mp_knot q, double x1, double y1, double x2, double y2) { 9007 if (p==NULL || q==NULL) return 0; 9008 if (mp_set_knot_direction(mp,p, x1, y1)) 9009 return mp_set_knot_direction(mp,q, x2, y2); 9010 return 0; 9011} 9012 9013@ 9014@c 9015static int path_needs_fixing (mp_knot source); 9016static int path_needs_fixing (mp_knot source) { 9017 mp_knot sourcehead = source; 9018 do { 9019 source = source->next; 9020 } while (source && source != sourcehead); 9021 if (!source) { 9022 return 1; 9023 } 9024 return 0; 9025} 9026 9027int mp_solve_path (MP mp, mp_knot first) 9028{ 9029 int saved_arith_error = mp->arith_error; 9030 jmp_buf *saved_jump_buf = mp->jump_buf; 9031 int retval = 1; 9032 if (first==NULL) return 0; 9033 if (path_needs_fixing(first)) return 0; 9034 mp->jump_buf = malloc(sizeof(jmp_buf)); 9035 if (mp->jump_buf == NULL || setjmp(*(mp->jump_buf)) != 0) { 9036 return 0; 9037 } 9038 mp->arith_error = 0; 9039 mp_make_choices(mp, first); 9040 if (mp->arith_error) 9041 retval = 0; 9042 mp->arith_error = saved_arith_error; 9043 free(mp->jump_buf); 9044 mp->jump_buf = saved_jump_buf; 9045 return retval; 9046} 9047 9048void mp_free_path (MP mp, mp_knot p) { 9049 mp_toss_knot_list(mp, p); 9050} 9051 9052@ @<Exported function headers@>= 9053int mp_close_path_cycle (MP mp, mp_knot p, mp_knot q); 9054int mp_close_path (MP mp, mp_knot q, mp_knot first); 9055mp_knot mp_create_knot (MP mp); 9056int mp_set_knot (MP mp, mp_knot p, double x, double y); 9057mp_knot mp_append_knot (MP mp, mp_knot p, double x, double y); 9058int mp_set_knot_curl (MP mp, mp_knot q, double value); 9059int mp_set_knot_left_curl (MP mp, mp_knot q, double value); 9060int mp_set_knot_right_curl (MP mp, mp_knot q, double value); 9061int mp_set_knotpair_curls (MP mp, mp_knot p, mp_knot q, double t1, double t2) ; 9062int mp_set_knotpair_tensions (MP mp, mp_knot p, mp_knot q, double t1, double t2) ; 9063int mp_set_knot_left_tension (MP mp, mp_knot p, double t1); 9064int mp_set_knot_right_tension (MP mp, mp_knot p, double t1); 9065int mp_set_knot_left_control (MP mp, mp_knot p, double t1, double t2); 9066int mp_set_knot_right_control (MP mp, mp_knot p, double t1, double t2); 9067int mp_set_knotpair_controls (MP mp, mp_knot p, mp_knot q, double x1, double y1, double x2, double y2) ; 9068int mp_set_knot_direction (MP mp, mp_knot q, double x, double y) ; 9069int mp_set_knotpair_directions (MP mp, mp_knot p, mp_knot q, double x1, double y1, double x2, double y2) ; 9070int mp_solve_path (MP mp, mp_knot first); 9071void mp_free_path (MP mp, mp_knot p); 9072 9073@ Simple accessors for |mp_knot|. 9074 9075@c 9076mp_number mp_knot_x_coord(MP mp, mp_knot p) { return p->x_coord; } 9077mp_number mp_knot_y_coord(MP mp, mp_knot p) { return p->y_coord; } 9078mp_number mp_knot_left_x (MP mp, mp_knot p) { return p->left_x; } 9079mp_number mp_knot_left_y (MP mp, mp_knot p) { return p->left_y; } 9080mp_number mp_knot_right_x(MP mp, mp_knot p) { return p->right_x; } 9081mp_number mp_knot_right_y(MP mp, mp_knot p) { return p->right_y; } 9082int mp_knot_right_type(MP mp, mp_knot p) { return mp_right_type(p);} 9083int mp_knot_left_type (MP mp, mp_knot p) { return mp_left_type(p);} 9084mp_knot mp_knot_next (MP mp, mp_knot p) { return p->next; } 9085double mp_number_as_double(MP mp, mp_number n) { 9086 return number_to_double(n); 9087} 9088 9089@ @<Exported function headers@>= 9090#define mp_knot_left_curl mp_knot_left_x 9091#define mp_knot_left_given mp_knot_left_x 9092#define mp_knot_left_tension mp_knot_left_y 9093#define mp_knot_right_curl mp_knot_right_x 9094#define mp_knot_right_given mp_knot_right_x 9095#define mp_knot_right_tension mp_knot_right_y 9096mp_number mp_knot_x_coord(MP mp, mp_knot p); 9097mp_number mp_knot_y_coord(MP mp, mp_knot p); 9098mp_number mp_knot_left_x(MP mp, mp_knot p); 9099mp_number mp_knot_left_y(MP mp, mp_knot p); 9100mp_number mp_knot_right_x(MP mp, mp_knot p); 9101mp_number mp_knot_right_y(MP mp, mp_knot p); 9102int mp_knot_right_type(MP mp, mp_knot p); 9103int mp_knot_left_type(MP mp, mp_knot p); 9104mp_knot mp_knot_next(MP mp, mp_knot p); 9105double mp_number_as_double(MP mp, mp_number n); 9106 9107 9108@* Measuring paths. 9109\MP's \&{llcorner}, \&{lrcorner}, \&{ulcorner}, and \&{urcorner} operators 9110allow the user to measure the bounding box of anything that can go into a 9111picture. It's easy to get rough bounds on the $x$ and $y$ extent of a path 9112by just finding the bounding box of the knots and the control points. We 9113need a more accurate version of the bounding box, but we can still use the 9114easy estimate to save time by focusing on the interesting parts of the path. 9115 9116@ Computing an accurate bounding box involves a theme that will come up again 9117and again. Given a Bernshte{\u\i}n polynomial 9118@^Bernshte{\u\i}n, Serge{\u\i} Natanovich@> 9119$$B(z_0,z_1,\ldots,z_n;t)=\sum_k{n\choose k}t^k(1-t)^{n-k}z_k,$$ 9120we can conveniently bisect its range as follows: 9121 9122\smallskip 9123\textindent{1)} Let $z_k^{(0)}=z_k$, for |0<=k<=n|. 9124 9125\smallskip 9126\textindent{2)} Let $z_k^{(j+1)}={1\over2}(z_k^{(j)}+z\k^{(j)})$, for 9127|0<=k<n-j|, for |0<=j<n|. 9128 9129\smallskip\noindent 9130Then 9131$$B(z_0,z_1,\ldots,z_n;t)=B(z_0^{(0)},z_0^{(1)},\ldots,z_0^{(n)};2t) 9132 =B(z_0^{(n)},z_1^{(n-1)},\ldots,z_n^{(0)};2t-1).$$ 9133This formula gives us the coefficients of polynomials to use over the ranges 9134$0\L t\L{1\over2}$ and ${1\over2}\L t\L1$. 9135 9136@ Here is a routine that computes the $x$ or $y$ coordinate of the point on 9137a cubic corresponding to the |fraction| value~|t|. 9138 9139@c 9140static void mp_eval_cubic (MP mp, mp_number *r, mp_knot p, mp_knot q, quarterword c, 9141 mp_number t) { 9142 mp_number x1, x2, x3; /* intermediate values */ 9143 new_number(x1); 9144 new_number(x2); 9145 new_number(x3); 9146 if (c == mp_x_code) { 9147 set_number_from_of_the_way(x1, t, p->x_coord, p->right_x); 9148 set_number_from_of_the_way(x2, t, p->right_x, q->left_x); 9149 set_number_from_of_the_way(x3, t, q->left_x, q->x_coord); 9150 } else { 9151 set_number_from_of_the_way(x1, t, p->y_coord, p->right_y); 9152 set_number_from_of_the_way(x2, t, p->right_y, q->left_y); 9153 set_number_from_of_the_way(x3, t, q->left_y, q->y_coord); 9154 } 9155 set_number_from_of_the_way(x1, t, x1, x2); 9156 set_number_from_of_the_way(x2, t, x2, x3); 9157 set_number_from_of_the_way(*r, t, x1, x2); 9158 free_number (x1); 9159 free_number (x2); 9160 free_number (x3); 9161} 9162 9163 9164@ The actual bounding box information is stored in global variables. 9165Since it is convenient to address the $x$ and $y$ information 9166separately, we define arrays indexed by |x_code..y_code| and use 9167macros to give them more convenient names. 9168 9169@<Types...@>= 9170enum mp_bb_code { 9171 mp_x_code = 0, /* index for |minx| and |maxx| */ 9172 mp_y_code /* index for |miny| and |maxy| */ 9173}; 9174 9175@ 9176@d mp_minx mp->bbmin[mp_x_code] 9177@d mp_maxx mp->bbmax[mp_x_code] 9178@d mp_miny mp->bbmin[mp_y_code] 9179@d mp_maxy mp->bbmax[mp_y_code] 9180 9181@<Glob...@>= 9182mp_number bbmin[mp_y_code + 1]; 9183mp_number bbmax[mp_y_code + 1]; 9184/* the result of procedures that compute bounding box information */ 9185 9186@ @<Initialize table ...@>= 9187{ 9188 int i; 9189 for (i=0;i<=mp_y_code;i++) { 9190 new_number(mp->bbmin[i]); 9191 new_number(mp->bbmax[i]); 9192 } 9193} 9194 9195@ @<Dealloc...@>= 9196{ 9197 int i; 9198 for (i=0;i<=mp_y_code;i++) { 9199 free_number(mp->bbmin[i]); 9200 free_number(mp->bbmax[i]); 9201 } 9202} 9203 9204 9205@ Now we're ready for the key part of the bounding box computation. 9206The |bound_cubic| procedure updates |bbmin[c]| and |bbmax[c]| based on 9207$$B(\hbox{|knot_coord(p)|}, \hbox{|right_coord(p)|}, 9208 \hbox{|left_coord(q)|}, \hbox{|knot_coord(q)|};t) 9209$$ 9210for $0<t\le1$. In other words, the procedure adjusts the bounds to 9211accommodate |knot_coord(q)| and any extremes over the range $0<t<1$. 9212The |c| parameter is |x_code| or |y_code|. 9213 9214@c 9215static void mp_bound_cubic (MP mp, mp_knot p, mp_knot q, quarterword c) { 9216 boolean wavy; /* whether we need to look for extremes */ 9217 mp_number del1, del2, del3, del, dmax; /* proportional to the control 9218 points of a quadratic derived from a cubic */ 9219 mp_number t, tt; /* where a quadratic crosses zero */ 9220 mp_number x; /* a value that |bbmin[c]| and |bbmax[c]| must accommodate */ 9221 new_number (x); 9222 new_fraction (t); 9223 new_fraction (tt); 9224 if (c == mp_x_code) { 9225 number_clone(x, q->x_coord); 9226 } else { 9227 number_clone(x, q->y_coord); 9228 } 9229 new_number(del1); 9230 new_number(del2); 9231 new_number(del3); 9232 new_number(del); 9233 new_number(dmax); 9234 @<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>; 9235 @<Check the control points against the bounding box and set |wavy:=true| 9236 if any of them lie outside@>; 9237 if (wavy) { 9238 if (c == mp_x_code) { 9239 set_number_from_substraction(del1, p->right_x, p->x_coord); 9240 set_number_from_substraction(del2, q->left_x, p->right_x); 9241 set_number_from_substraction(del3, q->x_coord, q->left_x); 9242 } else { 9243 set_number_from_substraction(del1, p->right_y, p->y_coord); 9244 set_number_from_substraction(del2, q->left_y, p->right_y); 9245 set_number_from_substraction(del3, q->y_coord, q->left_y); 9246 } 9247 @<Scale up |del1|, |del2|, and |del3| for greater accuracy; 9248 also set |del| to the first nonzero element of |(del1,del2,del3)|@>; 9249 if (number_negative(del)) { 9250 number_negate (del1); 9251 number_negate (del2); 9252 number_negate (del3); 9253 } 9254 crossing_point (t, del1, del2, del3); 9255 if (number_less(t, fraction_one_t)) { 9256 @<Test the extremes of the cubic against the bounding box@>; 9257 } 9258 } 9259 free_number (del3); 9260 free_number (del2); 9261 free_number (del1); 9262 free_number (del); 9263 free_number (dmax); 9264 free_number (x); 9265 free_number (t); 9266 free_number (tt); 9267} 9268 9269 9270@ @<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>= 9271if (number_less(x, mp->bbmin[c])) 9272 number_clone(mp->bbmin[c], x); 9273if (number_greater(x, mp->bbmax[c])) 9274 number_clone(mp->bbmax[c], x) 9275 9276@ @<Check the control points against the bounding box and set...@>= 9277wavy = true; 9278if (c == mp_x_code) { 9279 if (number_lessequal(mp->bbmin[c], p->right_x)) 9280 if (number_lessequal (p->right_x, mp->bbmax[c])) 9281 if (number_lessequal(mp->bbmin[c], q->left_x)) 9282 if (number_lessequal (q->left_x, mp->bbmax[c])) 9283 wavy = false; 9284} else { 9285 if (number_lessequal(mp->bbmin[c], p->right_y)) 9286 if (number_lessequal (p->right_y, mp->bbmax[c])) 9287 if (number_lessequal(mp->bbmin[c], q->left_y)) 9288 if (number_lessequal (q->left_y, mp->bbmax[c])) 9289 wavy = false; 9290} 9291 9292 9293@ If |del1=del2=del3=0|, it's impossible to obey the title of this 9294section. We just set |del=0| in that case. 9295 9296@<Scale up |del1|, |del2|, and |del3| for greater accuracy...@>= 9297if (number_nonzero(del1)) { 9298 number_clone (del, del1); 9299} else if (number_nonzero(del2)) { 9300 number_clone (del, del2); 9301} else { 9302 number_clone (del, del3); 9303} 9304if (number_nonzero(del)) { 9305 mp_number absval1; 9306 new_number(absval1); 9307 number_clone (dmax, del1); 9308 number_abs (dmax); 9309 number_clone (absval1, del2); 9310 number_abs(absval1); 9311 if (number_greater(absval1, dmax)) { 9312 number_clone(dmax, absval1); 9313 } 9314 number_clone (absval1, del3); 9315 number_abs(absval1); 9316 if (number_greater(absval1, dmax)) { 9317 number_clone(dmax, absval1); 9318 } 9319 while (number_less(dmax, fraction_half_t)) { 9320 number_double(dmax); 9321 number_double(del1); 9322 number_double(del2); 9323 number_double(del3); 9324 } 9325 free_number (absval1); 9326} 9327 9328@ Since |crossing_point| has tried to choose |t| so that 9329$B(|del1|,|del2|,|del3|;\tau)$ crosses zero at $\tau=|t|$ with negative 9330slope, the value of |del2| computed below should not be positive. 9331But rounding error could make it slightly positive in which case we 9332must cut it to zero to avoid confusion. 9333 9334@<Test the extremes of the cubic against the bounding box@>= 9335{ 9336 mp_eval_cubic (mp, &x, p, q, c, t); 9337 @<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>; 9338 set_number_from_of_the_way(del2, t, del2, del3); 9339 /* now |0,del2,del3| represent the derivative on the remaining interval */ 9340 if (number_positive(del2)) 9341 set_number_to_zero(del2); 9342 { 9343 mp_number arg2, arg3; 9344 new_number(arg2); 9345 new_number(arg3); 9346 number_clone(arg2, del2); 9347 number_negate(arg2); 9348 number_clone(arg3, del3); 9349 number_negate(arg3); 9350 crossing_point (tt, zero_t, arg2, arg3); 9351 free_number (arg2); 9352 free_number (arg3); 9353 } 9354 if (number_less(tt, fraction_one_t)) { 9355 @<Test the second extreme against the bounding box@>; 9356 } 9357} 9358 9359 9360@ @<Test the second extreme against the bounding box@>= 9361{ 9362 mp_number arg; 9363 new_number (arg); 9364 set_number_from_of_the_way (arg, t, tt, fraction_one_t); 9365 mp_eval_cubic (mp, &x, p, q, c, arg); 9366 free_number (arg); 9367 @<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>; 9368} 9369 9370 9371@ Finding the bounding box of a path is basically a matter of applying 9372|bound_cubic| twice for each pair of adjacent knots. 9373 9374@c 9375static void mp_path_bbox (MP mp, mp_knot h) { 9376 mp_knot p, q; /* a pair of adjacent knots */ 9377 number_clone(mp_minx, h->x_coord); 9378 number_clone(mp_miny, h->y_coord); 9379 number_clone (mp_maxx, mp_minx); 9380 number_clone (mp_maxy, mp_miny); 9381 p = h; 9382 do { 9383 if (mp_right_type (p) == mp_endpoint) 9384 return; 9385 q = mp_next_knot (p); 9386 mp_bound_cubic (mp, p, q, mp_x_code); 9387 mp_bound_cubic (mp, p, q, mp_y_code); 9388 p = q; 9389 } while (p != h); 9390} 9391 9392 9393@ Another important way to measure a path is to find its arc length. This 9394is best done by using the general bisection algorithm to subdivide the path 9395until obtaining ``well behaved'' subpaths whose arc lengths can be approximated 9396by simple means. 9397 9398Since the arc length is the integral with respect to time of the magnitude of 9399the velocity, it is natural to use Simpson's rule for the approximation. 9400@^Simpson's rule@> 9401If $\dot B(t)$ is the spline velocity, Simpson's rule gives 9402$$ \vb\dot B(0)\vb + 4\vb\dot B({1\over2})\vb + \vb\dot B(1)\vb \over 6 $$ 9403for the arc length of a path of length~1. For a cubic spline 9404$B(z_0,z_1,z_2,z_3;t)$, the time derivative $\dot B(t)$ is 9405$3B(dz_0,dz_1,dz_2;t)$, where $dz_i=z_{i+1}-z_i$. Hence the arc length 9406approximation is 9407$$ {\vb dz_0\vb \over 2} + 2\vb dz_{02}\vb + {\vb dz_2\vb \over 2}, $$ 9408where 9409$$ dz_{02}={1\over2}\left({dz_0+dz_1\over 2}+{dz_1+dz_2\over 2}\right)$$ 9410is the result of the bisection algorithm. 9411 9412@ The remaining problem is how to decide when a subpath is ``well behaved.'' 9413This could be done via the theoretical error bound for Simpson's rule, 9414@^Simpson's rule@> 9415but this is impractical because it requires an estimate of the fourth 9416derivative of the quantity being integrated. It is much easier to just perform 9417a bisection step and see how much the arc length estimate changes. Since the 9418error for Simpson's rule is proportional to the fourth power of the sample 9419spacing, the remaining error is typically about $1\over16$ of the amount of 9420the change. We say ``typically'' because the error has a pseudo-random behavior 9421that could cause the two estimates to agree when each contain large errors. 9422 9423To protect against disasters such as undetected cusps, the bisection process 9424should always continue until all the $dz_i$ vectors belong to a single 9425$90^\circ$ sector. This ensures that no point on the spline can have velocity 9426less than 70\% of the minimum of $\vb dz_0\vb$, $\vb dz_1\vb$ and $\vb dz_2\vb$. 9427If such a spline happens to produce an erroneous arc length estimate that 9428is little changed by bisection, the amount of the error is likely to be fairly 9429small. We will try to arrange things so that freak accidents of this type do 9430not destroy the inverse relationship between the \&{arclength} and 9431\&{arctime} operations. 9432@:arclength_}{\&{arclength} primitive@> 9433@:arctime_}{\&{arctime} primitive@> 9434 9435@ The \&{arclength} and \&{arctime} operations are both based on a recursive 9436@^recursion@> 9437function that finds the arc length of a cubic spline given $dz_0$, $dz_1$, 9438$dz_2$. This |arc_test| routine also takes an arc length goal |a_goal| and 9439returns the time when the arc length reaches |a_goal| if there is such a time. 9440Thus the return value is either an arc length less than |a_goal| or, if the 9441arc length would be at least |a_goal|, it returns a time value decreased by 9442|two|. This allows the caller to use the sign of the result to distinguish 9443between arc lengths and time values. On certain types of overflow, it is 9444possible for |a_goal| and the result of |arc_test| both to be |EL_GORDO|. 9445Otherwise, the result is always less than |a_goal|. 9446 9447Rather than halving the control point coordinates on each recursive call to 9448|arc_test|, it is better to keep them proportional to velocity on the original 9449curve and halve the results instead. This means that recursive calls can 9450potentially use larger error tolerances in their arc length estimates. How 9451much larger depends on to what extent the errors behave as though they are 9452independent of each other. To save computing time, we use optimistic assumptions 9453and increase the tolerance by a factor of about $\sqrt2$ for each recursive 9454call. 9455 9456In addition to the tolerance parameter, |arc_test| should also have parameters 9457for ${1\over3}\vb\dot B(0)\vb$, ${2\over3}\vb\dot B({1\over2})\vb$, and 9458${1\over3}\vb\dot B(1)\vb$. These quantities are relatively expensive to compute 9459and they are needed in different instances of |arc_test|. 9460 9461@c 9462static void mp_arc_test (MP mp, mp_number *ret, mp_number dx0, mp_number dy0, mp_number dx1, 9463 mp_number dy1, mp_number dx2, mp_number dy2, mp_number v0, 9464 mp_number v02, mp_number v2, mp_number a_goal, mp_number tol_orig) { 9465 boolean simple; /* are the control points confined to a $90^\circ$ sector? */ 9466 mp_number dx01, dy01, dx12, dy12, dx02, dy02; /* bisection results */ 9467 mp_number v002, v022; /* twice the velocity magnitudes at $t={1\over4}$ and $t={3\over4}$ */ 9468 mp_number arc; /* best arc length estimate before recursion */ 9469 mp_number arc1; /* arc length estimate for the first half */ 9470 mp_number simply; 9471 mp_number tol; 9472 new_number (arc ); 9473 new_number (arc1); 9474 new_number (dx01); 9475 new_number (dy01); 9476 new_number (dx12); 9477 new_number (dy12); 9478 new_number (dx02); 9479 new_number (dy02); 9480 new_number (v002); 9481 new_number (v022); 9482 new_number (simply); 9483 new_number (tol); 9484 number_clone(tol, tol_orig); 9485 @<Bisect the B\'ezier quadratic given by |dx0|, |dy0|, |dx1|, |dy1|, 9486 |dx2|, |dy2|@>; 9487 @<Initialize |v002|, |v022|, and the arc length estimate |arc|; if it overflows 9488 set |arc_test| and |return|@>; 9489 @<Test if the control points are confined to one quadrant or rotating them 9490 $45^\circ$ would put them in one quadrant. Then set |simple| appropriately@>; 9491 9492 set_number_from_addition(simply, v0, v2); 9493 number_halfp (simply); 9494 number_negate (simply); 9495 number_add (simply, arc); 9496 number_substract (simply, v02); 9497 number_abs (simply); 9498 9499 if (simple && number_lessequal(simply, tol)) { 9500 if (number_less(arc, a_goal)){ 9501 number_clone(*ret, arc); 9502 } else { 9503 @<Estimate when the arc length reaches |a_goal| and set |arc_test| to 9504 that time minus |two|@>; 9505 } 9506 } else { 9507 @<Use one or two recursive calls to compute the |arc_test| function@>; 9508 } 9509DONE: 9510 free_number (arc); 9511 free_number (arc1); 9512 free_number (dx01); 9513 free_number (dy01); 9514 free_number (dx12); 9515 free_number (dy12); 9516 free_number (dx02); 9517 free_number (dy02); 9518 free_number (v002); 9519 free_number (v022); 9520 free_number (simply); 9521 free_number (tol); 9522} 9523 9524 9525@ The |tol| value should by multiplied by $\sqrt 2$ before making recursive 9526calls, but $1.5$ is an adequate approximation. It is best to avoid using 9527|make_fraction| in this inner loop. 9528@^inner loop@> 9529 9530@<Use one or two recursive calls to compute the |arc_test| function@>= 9531{ 9532 mp_number a_new, a_aux; /* the sum of these gives the |a_goal| */ 9533 mp_number a, b; /* results of recursive calls */ 9534 mp_number half_v02; /* |halfp(v02)|, a recursion argument */ 9535 new_number(a_new); 9536 new_number(a_aux); 9537 new_number(half_v02); 9538 @<Set |a_new| and |a_aux| so their sum is |2*a_goal| and |a_new| is as 9539 large as possible@>; 9540 { 9541 mp_number halfp_tol; 9542 new_number(halfp_tol); 9543 number_clone (halfp_tol, tol); 9544 number_halfp (halfp_tol); 9545 number_add(tol, halfp_tol); 9546 free_number (halfp_tol); 9547 } 9548 number_clone(half_v02, v02); 9549 number_halfp(half_v02); 9550 new_number (a); 9551 mp_arc_test (mp, &a, dx0, dy0, dx01, dy01, dx02, dy02, 9552 v0, v002, half_v02, a_new, tol); 9553 if (number_negative(a)) { 9554 set_number_to_unity(*ret); 9555 number_double(*ret); /* two */ 9556 number_substract(*ret, a); /* two - a */ 9557 number_halfp(*ret); 9558 number_negate(*ret); /* -halfp(two - a) */ 9559 } else { 9560 @<Update |a_new| to reduce |a_new+a_aux| by |a|@>; 9561 new_number (b); 9562 mp_arc_test (mp, &b, dx02, dy02, dx12, dy12, dx2, dy2, 9563 half_v02, v022, v2, a_new, tol); 9564 if (number_negative(b)) { 9565 mp_number tmp ; 9566 new_number (tmp); 9567 number_clone(tmp, b); 9568 number_negate(tmp); 9569 number_halfp(tmp); 9570 number_negate(tmp); 9571 number_clone(*ret, tmp); 9572 set_number_to_unity(tmp); 9573 number_halfp(tmp); 9574 number_substract(*ret, tmp); /* (-(halfp(-b)) - 1/2) */ 9575 free_number (tmp); 9576 } else { 9577 set_number_from_substraction(*ret, b, a); 9578 number_half(*ret); 9579 set_number_from_addition(*ret, a, *ret); /* (a + half(b - a)) */ 9580 } 9581 free_number (b); 9582 } 9583 free_number (half_v02); 9584 free_number (a_aux); 9585 free_number (a_new); 9586 free_number (a); 9587 goto DONE; 9588} 9589 9590 9591@ @<Set |a_new| and |a_aux| so their sum is |2*a_goal| and |a_new| is...@>= 9592set_number_to_inf(a_aux); 9593number_substract(a_aux, a_goal); 9594if (number_greater(a_goal, a_aux)) { 9595 set_number_from_substraction(a_aux, a_goal, a_aux); 9596 set_number_to_inf(a_new); 9597} else { 9598 set_number_from_addition(a_new, a_goal, a_goal); 9599 set_number_to_zero(a_aux); 9600} 9601 9602 9603@ There is no need to maintain |a_aux| at this point so we use it as a temporary 9604to force the additions and subtractions to be done in an order that avoids 9605overflow. 9606 9607@<Update |a_new| to reduce |a_new+a_aux| by |a|@>= 9608if (number_greater(a, a_aux)) { 9609 number_substract(a_aux, a); 9610 number_add(a_new, a_aux); 9611} 9612 9613@ This code assumes all {\it dx} and {\it dy} variables have magnitude less than 9614|fraction_four|. To simplify the rest of the |arc_test| routine, we strengthen 9615this assumption by requiring the norm of each $({\it dx},{\it dy})$ pair to obey 9616this bound. Note that recursive calls will maintain this invariant. 9617 9618@<Bisect the B\'ezier quadratic given by |dx0|, |dy0|, |dx1|, |dy1|,...@>= 9619set_number_from_addition(dx01, dx0, dx1); 9620number_half(dx01); 9621set_number_from_addition(dx12, dx1, dx2); 9622number_half(dx12); 9623set_number_from_addition(dx02, dx01, dx12); 9624number_half(dx02); 9625set_number_from_addition(dy01, dy0, dy1); 9626number_half(dy01); 9627set_number_from_addition(dy12, dy1, dy2); 9628number_half(dy12); 9629set_number_from_addition(dy02, dy01, dy12); 9630number_half(dy02); 9631 9632@ We should be careful to keep |arc<EL_GORDO| so that calling |arc_test| with 9633|a_goal=EL_GORDO| is guaranteed to yield the arc length. 9634 9635@<Initialize |v002|, |v022|, and the arc length estimate |arc|;...@>= 9636{ 9637 mp_number tmp, arg1, arg2 ; 9638 new_number (tmp); 9639 new_number (arg1); 9640 new_number (arg2); 9641 set_number_from_addition(arg1, dx0, dx02); 9642 number_half(arg1); 9643 number_add(arg1, dx01); 9644 set_number_from_addition(arg2, dy0, dy02); 9645 number_half(arg2); 9646 number_add(arg2, dy01); 9647 pyth_add (v002, arg1, arg2); 9648 9649 set_number_from_addition(arg1, dx02, dx2); 9650 number_half(arg1); 9651 number_add(arg1, dx12); 9652 set_number_from_addition(arg2, dy02, dy2); 9653 number_half(arg2); 9654 number_add(arg2, dy12); 9655 pyth_add (v022, arg1, arg2); 9656 free_number(arg1); 9657 free_number(arg2); 9658 9659 number_clone (tmp, v02); 9660 number_add_scaled (tmp, 2); 9661 number_halfp (tmp); 9662 9663 set_number_from_addition(arc1, v0, tmp); 9664 number_halfp (arc1); 9665 number_substract (arc1, v002); 9666 number_half (arc1); 9667 set_number_from_addition(arc1, v002, arc1); 9668 9669 set_number_from_addition(arc, v2, tmp); 9670 number_halfp (arc); 9671 number_substract (arc, v022); 9672 number_half (arc); 9673 set_number_from_addition(arc, v022, arc); 9674 9675 /* reuse |tmp| for the next |if| test: */ 9676 set_number_to_inf(tmp); 9677 number_substract(tmp,arc1); 9678 if (number_less(arc, tmp)) { 9679 free_number (tmp); 9680 number_add(arc, arc1); 9681 } else { 9682 free_number (tmp); 9683 mp->arith_error = true; 9684 if (number_infinite(a_goal)) { 9685 set_number_to_inf(*ret); 9686 } else { 9687 set_number_to_unity(*ret); 9688 number_double(*ret); 9689 number_negate(*ret); /* -two */ 9690 } 9691 goto DONE; 9692 } 9693} 9694 9695 9696@ @<Test if the control points are confined to one quadrant or rotating...@>= 9697simple = ((number_nonnegative(dx0) && number_nonnegative(dx1) && number_nonnegative(dx2)) || 9698 (number_nonpositive(dx0) && number_nonpositive(dx1) && number_nonpositive(dx2))); 9699if (simple) { 9700 simple = (number_nonnegative(dy0) && number_nonnegative(dy1) && number_nonnegative(dy2)) || 9701 (number_nonpositive(dy0) && number_nonpositive(dy1) && number_nonpositive(dy2)); 9702} 9703if (!simple) { 9704 simple = (number_greaterequal(dx0, dy0) && number_greaterequal(dx1, dy1) && number_greaterequal(dx2, dy2)) || 9705 (number_lessequal(dx0, dy0) && number_lessequal(dx1, dy1) && number_lessequal(dx2, dy2)); 9706 if (simple) { 9707 mp_number neg_dx0, neg_dx1, neg_dx2; 9708 new_number(neg_dx0); 9709 new_number(neg_dx1); 9710 new_number(neg_dx2); 9711 number_clone(neg_dx0, dx0); 9712 number_clone(neg_dx1, dx1); 9713 number_clone(neg_dx2, dx2); 9714 number_negate(neg_dx0); 9715 number_negate(neg_dx1); 9716 number_negate(neg_dx2); 9717 simple = 9718 (number_greaterequal(neg_dx0, dy0) && number_greaterequal(neg_dx1, dy1) && number_greaterequal(neg_dx2, dy2)) || 9719 (number_lessequal(neg_dx0, dy0) && number_lessequal(neg_dx1, dy1) && number_lessequal(neg_dx2, dy2)); 9720 free_number (neg_dx0); 9721 free_number (neg_dx1); 9722 free_number (neg_dx2); 9723 } 9724} 9725 9726@ Since Simpson's rule is based on approximating the integrand by a parabola, 9727@^Simpson's rule@> 9728it is appropriate to use the same approximation to decide when the integral 9729reaches the intermediate value |a_goal|. At this point 9730$$\eqalign{ 9731 {\vb\dot B(0)\vb\over 3} &= \hbox{|v0|}, \qquad 9732 {\vb\dot B({1\over4})\vb\over 3} = {\hbox{|v002|}\over 2}, \qquad 9733 {\vb\dot B({1\over2})\vb\over 3} = {\hbox{|v02|}\over 2}, \cr 9734 {\vb\dot B({3\over4})\vb\over 3} &= {\hbox{|v022|}\over 2}, \qquad 9735 {\vb\dot B(1)\vb\over 3} = \hbox{|v2|} \cr 9736} 9737$$ 9738and 9739$$ {\vb\dot B(t)\vb\over 3} \approx 9740 \cases{B\left(\hbox{|v0|}, 9741 \hbox{|v002|}-{1\over 2}\hbox{|v0|}-{1\over 4}\hbox{|v02|}, 9742 {1\over 2}\hbox{|v02|}; 2t \right)& 9743 if $t\le{1\over 2}$\cr 9744 B\left({1\over 2}\hbox{|v02|}, 9745 \hbox{|v022|}-{1\over 4}\hbox{|v02|}-{1\over 2}\hbox{|v2|}, 9746 \hbox{|v2|}; 2t-1 \right)& 9747 if $t\ge{1\over 2}$.\cr} 9748 \eqno (*) 9749$$ 9750We can integrate $\vb\dot B(t)\vb$ by using 9751$$\int 3B(a,b,c;\tau)\,dt = 9752 {B(0,a,a+b,a+b+c;\tau) + {\rm constant} \over {d\tau\over dt}}. 9753$$ 9754 9755This construction allows us to find the time when the arc length reaches 9756|a_goal| by solving a cubic equation of the form 9757$$ B(0,a,a+b,a+b+c;\tau) = x, $$ 9758where $\tau$ is $2t$ or $2t+1$, $x$ is |a_goal| or |a_goal-arc1|, and $a$, $b$, 9759and $c$ are the Bernshte{\u\i}n coefficients from $(*)$ divided by 9760@^Bernshte{\u\i}n, Serge{\u\i} Natanovich@> 9761$d\tau\over dt$. We shall define a function |solve_rising_cubic| that finds 9762$\tau$ given $a$, $b$, $c$, and $x$. 9763 9764@<Estimate when the arc length reaches |a_goal| and set |arc_test| to...@>= 9765{ 9766 mp_number tmp; 9767 mp_number tmp2; 9768 mp_number tmp3; 9769 mp_number tmp4; 9770 mp_number tmp5; 9771 new_number (tmp); 9772 new_number (tmp2); 9773 new_number (tmp3); 9774 new_number (tmp4); 9775 new_number (tmp5); 9776 number_clone(tmp, v02); 9777 number_add_scaled(tmp, 2); 9778 number_half(tmp); 9779 number_half(tmp); /* (v02+2) / 4 */ 9780 if (number_lessequal(a_goal, arc1)) { 9781 number_clone(tmp2, v0); 9782 number_halfp(tmp2); 9783 set_number_from_substraction(tmp3, arc1, tmp2); 9784 number_substract(tmp3, tmp); 9785 mp_solve_rising_cubic (mp, &tmp5, tmp2, tmp3, tmp, a_goal); 9786 number_halfp (tmp5); 9787 set_number_to_unity(tmp3); 9788 number_substract(tmp5, tmp3); 9789 number_substract(tmp5, tmp3); 9790 number_clone(*ret, tmp5); 9791 } else { 9792 number_clone(tmp2, v2); 9793 number_halfp(tmp2); 9794 set_number_from_substraction(tmp3, arc, arc1); 9795 number_substract(tmp3, tmp); 9796 number_substract(tmp3, tmp2); 9797 set_number_from_substraction(tmp4, a_goal, arc1); 9798 mp_solve_rising_cubic (mp, &tmp5, tmp, tmp3, tmp2, tmp4); 9799 number_halfp(tmp5); 9800 set_number_to_unity(tmp2); 9801 set_number_to_unity(tmp3); 9802 number_half(tmp2); 9803 number_substract(tmp2, tmp3); 9804 number_substract(tmp2, tmp3); 9805 set_number_from_addition(*ret, tmp2, tmp5); 9806 } 9807 free_number (tmp); 9808 free_number (tmp2); 9809 free_number (tmp3); 9810 free_number (tmp4); 9811 free_number (tmp5); 9812 goto DONE; 9813} 9814 9815 9816@ Here is the |solve_rising_cubic| routine that finds the time~$t$ when 9817$$ B(0, a, a+b, a+b+c; t) = x. $$ 9818This routine is based on |crossing_point| but is simplified by the 9819assumptions that $B(a,b,c;t)\ge0$ for $0\le t\le1$ and that |0<=x<=a+b+c|. 9820If rounding error causes this condition to be violated slightly, we just ignore 9821it and proceed with binary search. This finds a time when the function value 9822reaches |x| and the slope is positive. 9823 9824@<Declarations@>= 9825static void mp_solve_rising_cubic (MP mp, mp_number *ret, mp_number a, mp_number b, mp_number c, mp_number x); 9826 9827@ @c 9828void mp_solve_rising_cubic (MP mp, mp_number *ret, mp_number a_orig, mp_number b_orig, mp_number c_orig, mp_number x_orig) { 9829 mp_number abc; 9830 mp_number a, b, c, x; /* local versions of arguments */ 9831 mp_number ab, bc, ac; /* bisection results */ 9832 mp_number t; /* $2^k+q$ where unscaled answer is in $[q2^{-k},(q+1)2^{-k})$ */ 9833 mp_number xx; /* temporary for updating |x| */ 9834 mp_number neg_x; /* temporary for an |if| */ 9835 if (number_negative(a_orig) || number_negative(c_orig)) 9836 mp_confusion (mp, "rising?"); 9837@:this can't happen rising?}{\quad rising?@>; 9838 new_number (t); 9839 new_number (abc); 9840 new_number (a); 9841 new_number (b); 9842 new_number (c); 9843 new_number (x); 9844 number_clone(a, a_orig); 9845 number_clone(b, b_orig); 9846 number_clone(c, c_orig); 9847 number_clone(x, x_orig); 9848 new_number (ab); 9849 new_number (bc); 9850 new_number (ac); 9851 new_number (xx); 9852 new_number (neg_x); 9853 set_number_from_addition(abc, a, b); 9854 number_add(abc, c); 9855 if (number_nonpositive(x)) { 9856 set_number_to_zero(*ret); 9857 } else if (number_greaterequal(x, abc)) { 9858 set_number_to_unity(*ret); 9859 } else { 9860 number_clone (t, epsilon_t); 9861 @<Rescale if necessary to make sure |a|, |b|, and |c| are all less than 9862 |EL_GORDO div 3|@>; 9863 do { 9864 number_add (t, t); 9865 @<Subdivide the B\'ezier quadratic defined by |a|, |b|, |c|@>; 9866 number_clone(xx,x); 9867 number_substract(xx, a); 9868 number_substract(xx, ab); 9869 number_substract(xx, ac); 9870 number_clone(neg_x, x); 9871 number_negate(neg_x); 9872 if (number_less(xx, neg_x)) { 9873 number_double(x); 9874 number_clone(b, ab); 9875 number_clone(c, ac); 9876 } else { 9877 number_add(x, xx); 9878 number_clone(a, ac); 9879 number_clone(b, bc); 9880 number_add (t, epsilon_t); 9881 } 9882 } while (number_less (t, unity_t)); 9883 set_number_from_substraction(*ret, t, unity_t); 9884 } 9885 free_number (abc); 9886 free_number (t); 9887 free_number (a); 9888 free_number (b); 9889 free_number (c); 9890 free_number (ab); 9891 free_number (bc); 9892 free_number (ac); 9893 free_number (xx); 9894 free_number (x); 9895 free_number (neg_x); 9896} 9897 9898 9899@ @<Subdivide the B\'ezier quadratic defined by |a|, |b|, |c|@>= 9900set_number_from_addition(ab, a, b); 9901number_half(ab); 9902set_number_from_addition(bc, b, c); 9903number_half(bc); 9904set_number_from_addition(ac, ab, bc); 9905number_half(ac); 9906 9907@ The upper bound on |a|, |b|, and |c|: 9908 9909@d one_third_inf_t ((math_data *)mp->math)->one_third_inf_t 9910 9911@<Rescale if necessary to make sure |a|, |b|, and |c| are all less than...@>= 9912while (number_greater(a, one_third_inf_t) || 9913 number_greater(b, one_third_inf_t) || 9914 number_greater(c, one_third_inf_t)) { 9915 number_halfp(a); 9916 number_half(b); 9917 number_halfp(c); 9918 number_halfp(x); 9919} 9920 9921 9922@ It is convenient to have a simpler interface to |arc_test| that requires no 9923unnecessary arguments and ensures that each $({\it dx},{\it dy})$ pair has 9924length less than |fraction_four|. 9925 9926@c 9927static void mp_do_arc_test (MP mp, mp_number *ret, mp_number dx0, mp_number dy0, mp_number dx1, 9928 mp_number dy1, mp_number dx2, mp_number dy2, mp_number a_goal) { 9929 mp_number v0, v1, v2; /* length of each $({\it dx},{\it dy})$ pair */ 9930 mp_number v02; /* twice the norm of the quadratic at $t={1\over2}$ */ 9931 new_number (v0); 9932 new_number (v1); 9933 new_number (v2); 9934 pyth_add (v0, dx0, dy0); 9935 pyth_add (v1, dx1, dy1); 9936 pyth_add (v2, dx2, dy2); 9937 if ((number_greaterequal(v0, fraction_four_t)) || 9938 (number_greaterequal(v1, fraction_four_t)) || 9939 (number_greaterequal(v2, fraction_four_t))) { 9940 mp->arith_error = true; 9941 if (number_infinite(a_goal)) { 9942 set_number_to_inf(*ret); 9943 } else { 9944 set_number_to_unity(*ret); 9945 number_double(*ret); 9946 number_negate(*ret); 9947 } 9948 } else { 9949 mp_number arg1, arg2; 9950 new_number (v02); 9951 new_number (arg1); 9952 new_number (arg2); 9953 set_number_from_addition(arg1, dx0, dx2); 9954 number_half(arg1); 9955 number_add(arg1, dx1); 9956 set_number_from_addition(arg2, dy0, dy2); 9957 number_half(arg2); 9958 number_add(arg2, dy1); 9959 pyth_add (v02, arg1, arg2); 9960 free_number(arg1); 9961 free_number(arg2); 9962 mp_arc_test (mp, ret, dx0, dy0, dx1, dy1, dx2, dy2, v0, v02, v2, a_goal, arc_tol_k); 9963 free_number (v02); 9964 } 9965 free_number (v0); 9966 free_number (v1); 9967 free_number (v2); 9968} 9969 9970 9971@ Now it is easy to find the arc length of an entire path. 9972 9973@c 9974static void mp_get_arc_length (MP mp, mp_number *ret, mp_knot h) { 9975 mp_knot p, q; /* for traversing the path */ 9976 mp_number a; /* current arc length */ 9977 mp_number a_tot; /* total arc length */ 9978 mp_number arg1, arg2, arg3, arg4, arg5, arg6; 9979 mp_number arcgoal; 9980 p = h; 9981 new_number (a_tot); 9982 new_number (arg1); 9983 new_number (arg2); 9984 new_number (arg3); 9985 new_number (arg4); 9986 new_number (arg5); 9987 new_number (arg6); 9988 new_number (a); 9989 new_number(arcgoal); 9990 set_number_to_inf(arcgoal); 9991 while (mp_right_type (p) != mp_endpoint) { 9992 q = mp_next_knot (p); 9993 set_number_from_substraction(arg1, p->right_x, p->x_coord); 9994 set_number_from_substraction(arg2, p->right_y, p->y_coord); 9995 set_number_from_substraction(arg3, q->left_x, p->right_x); 9996 set_number_from_substraction(arg4, q->left_y, p->right_y); 9997 set_number_from_substraction(arg5, q->x_coord, q->left_x); 9998 set_number_from_substraction(arg6, q->y_coord, q->left_y); 9999 mp_do_arc_test (mp, &a, arg1, arg2, arg3, arg4, arg5, arg6, arcgoal); 10000 slow_add (a_tot, a, a_tot); 10001 if (q == h) 10002 break; 10003 else 10004 p = q; 10005 } 10006 free_number (arcgoal); 10007 free_number (a); 10008 free_number (arg1); 10009 free_number (arg2); 10010 free_number (arg3); 10011 free_number (arg4); 10012 free_number (arg5); 10013 free_number (arg6); 10014 check_arith(); 10015 number_clone (*ret, a_tot); 10016 free_number (a_tot); 10017} 10018 10019 10020@ The inverse operation of finding the time on a path~|h| when the arc length 10021reaches some value |arc0| can also be accomplished via |do_arc_test|. Some care 10022is required to handle very large times or negative times on cyclic paths. For 10023non-cyclic paths, |arc0| values that are negative or too large cause 10024|get_arc_time| to return 0 or the length of path~|h|. 10025 10026If |arc0| is greater than the arc length of a cyclic path~|h|, the result is a 10027time value greater than the length of the path. Since it could be much greater, 10028we must be prepared to compute the arc length of path~|h| and divide this into 10029|arc0| to find how many multiples of the length of path~|h| to add. 10030 10031@c 10032static void mp_get_arc_time (MP mp, mp_number *ret, mp_knot h, mp_number arc0_orig) { 10033 mp_knot p, q; /* for traversing the path */ 10034 mp_number t_tot; /* accumulator for the result */ 10035 mp_number t; /* the result of |do_arc_test| */ 10036 mp_number arc, arc0; /* portion of |arc0| not used up so far */ 10037 mp_number arg1, arg2, arg3, arg4, arg5, arg6; /* |do_arc_test| arguments */ 10038 if (number_negative(arc0_orig)) { 10039 @<Deal with a negative |arc0_orig| value and |return|@>; 10040 } 10041 new_number (t_tot); 10042 new_number (arc0); 10043 number_clone(arc0, arc0_orig); 10044 if (number_infinite(arc0)) { 10045 number_add_scaled (arc0, -1); 10046 } 10047 new_number (arc); 10048 number_clone(arc, arc0); 10049 p = h; 10050 new_number (arg1); 10051 new_number (arg2); 10052 new_number (arg3); 10053 new_number (arg4); 10054 new_number (arg5); 10055 new_number (arg6); 10056 new_number (t); 10057 while ((mp_right_type (p) != mp_endpoint) && number_positive(arc)) { 10058 q = mp_next_knot (p); 10059 set_number_from_substraction(arg1, p->right_x, p->x_coord); 10060 set_number_from_substraction(arg2, p->right_y, p->y_coord); 10061 set_number_from_substraction(arg3, q->left_x, p->right_x); 10062 set_number_from_substraction(arg4, q->left_y, p->right_y); 10063 set_number_from_substraction(arg5, q->x_coord, q->left_x); 10064 set_number_from_substraction(arg6, q->y_coord, q->left_y); 10065 mp_do_arc_test (mp, &t, arg1, arg2, arg3, arg4, arg5, arg6, arc); 10066 @<Update |arc| and |t_tot| after |do_arc_test| has just returned |t|@>; 10067 if (q == h) { 10068 @<Update |t_tot| and |arc| to avoid going around the cyclic 10069 path too many times but set |arith_error:=true| and |goto done| on 10070 overflow@>; 10071 } 10072 p = q; 10073 } 10074 check_arith(); 10075 number_clone (*ret, t_tot); 10076RETURN: 10077 free_number (t_tot); 10078 free_number (t); 10079 free_number (arc); 10080 free_number (arc0); 10081 free_number (arg1); 10082 free_number (arg2); 10083 free_number (arg3); 10084 free_number (arg4); 10085 free_number (arg5); 10086 free_number (arg6); 10087} 10088 10089 10090@ @<Update |arc| and |t_tot| after |do_arc_test| has just returned |t|@>= 10091if (number_negative(t)) { 10092 number_add (t_tot, t); 10093 number_add (t_tot, two_t); 10094 set_number_to_zero(arc); 10095} else { 10096 number_add (t_tot, unity_t); 10097 number_substract(arc, t); 10098} 10099 10100 10101@ @<Deal with a negative |arc0_orig| value and |return|@>= 10102{ 10103 if (mp_left_type (h) == mp_endpoint) { 10104 set_number_to_zero (*ret); 10105 } else { 10106 mp_number neg_arc0; 10107 p = mp_htap_ypoc (mp, h); 10108 new_number(neg_arc0); 10109 number_clone(neg_arc0, arc0_orig); 10110 number_negate(neg_arc0); 10111 mp_get_arc_time (mp, ret, p, neg_arc0); 10112 number_negate(*ret); 10113 mp_toss_knot_list (mp, p); 10114 free_number (neg_arc0); 10115 } 10116 check_arith(); 10117 return; 10118} 10119 10120 10121@ @<Update |t_tot| and |arc| to avoid going around the cyclic...@>= 10122if (number_positive(arc)) { 10123 mp_number n, n1, d1, v1; 10124 new_number (n); 10125 new_number (n1); 10126 new_number (d1); 10127 new_number (v1); 10128 10129 set_number_from_substraction (d1, arc0, arc); /* d1 = arc0 - arc */ 10130 set_number_from_div (n1, arc, d1); /* n1 = (arc / d1) */ 10131 number_clone (n, n1); 10132 set_number_from_mul (n1, n1, d1); /* n1 = (n1 * d1) */ 10133 number_substract (arc, n1); /* arc = arc - n1 */ 10134 10135 number_clone (d1, inf_t); /* reuse d1 */ 10136 number_clone (v1, n); /* v1 = n */ 10137 number_add (v1, epsilon_t); /* v1 = n1+1 */ 10138 set_number_from_div (d1, d1, v1); /* |d1 = EL_GORDO / v1| */ 10139 if (number_greater (t_tot, d1)) { 10140 mp->arith_error = true; 10141 check_arith(); 10142 set_number_to_inf(*ret); 10143 free_number (n); 10144 free_number (n1); 10145 free_number (d1); 10146 free_number (v1); 10147 goto RETURN; 10148 } 10149 set_number_from_mul (t_tot, t_tot, v1); 10150 free_number (n); 10151 free_number (n1); 10152 free_number (d1); 10153 free_number (v1); 10154} 10155 10156@* Data structures for pens. 10157A Pen in \MP\ can be either elliptical or polygonal. Elliptical pens result 10158in \ps\ \&{stroke} commands, while anything drawn with a polygonal pen is 10159@:stroke}{\&{stroke} command@> 10160converted into an area fill as described in the next part of this program. 10161The mathematics behind this process is based on simple aspects of the theory 10162of tracings developed by Leo Guibas, Lyle Ramshaw, and Jorge Stolfi 10163[``A kinematic framework for computational geometry,'' Proc.\ IEEE Symp.\ 10164Foundations of Computer Science {\bf 24} (1983), 100--111]. 10165 10166Polygonal pens are created from paths via \MP's \&{makepen} primitive. 10167@:makepen_}{\&{makepen} primitive@> 10168This path representation is almost sufficient for our purposes except that 10169a pen path should always be a convex polygon with the vertices in 10170counter-clockwise order. 10171Since we will need to scan pen polygons both forward and backward, a pen 10172should be represented as a doubly linked ring of knot nodes. There is 10173room for the extra back pointer because we do not need the 10174|mp_left_type| or |mp_right_type| fields. In fact, we don't need the |left_x|, 10175|left_y|, |right_x|, or |right_y| fields either but we leave these alone 10176so that certain procedures can operate on both pens and paths. In particular, 10177pens can be copied using |copy_path| and recycled using |toss_knot_list|. 10178 10179@ The |make_pen| procedure turns a path into a pen by initializing 10180the |prev_knot| pointers and making sure the knots form a convex polygon. 10181Thus each cubic in the given path becomes a straight line and the control 10182points are ignored. If the path is not cyclic, the ends are connected by a 10183straight line. 10184 10185@d copy_pen(A) mp_make_pen(mp, mp_copy_path(mp, (A)),false) 10186 10187@c 10188static mp_knot mp_make_pen (MP mp, mp_knot h, boolean need_hull) { 10189 mp_knot p, q; /* two consecutive knots */ 10190 q = h; 10191 do { 10192 p = q; 10193 q = mp_next_knot (q); 10194 mp_prev_knot (q) = p; 10195 } while (q != h); 10196 if (need_hull) { 10197 h = mp_convex_hull (mp, h); 10198 @<Make sure |h| isn't confused with an elliptical pen@>; 10199 } 10200 return h; 10201} 10202 10203 10204@ The only information required about an elliptical pen is the overall 10205transformation that has been applied to the original \&{pencircle}. 10206@:pencircle_}{\&{pencircle} primitive@> 10207Since it suffices to keep track of how the three points $(0,0)$, $(1,0)$, 10208and $(0,1)$ are transformed, an elliptical pen can be stored in a single 10209knot node and transformed as if it were a path. 10210 10211@d pen_is_elliptical(A) ((A)==mp_next_knot((A))) 10212 10213@c 10214static mp_knot mp_get_pen_circle (MP mp, mp_number diam) { 10215 mp_knot h; /* the knot node to return */ 10216 h = mp_new_knot (mp); 10217 mp_next_knot (h) = h; 10218 mp_prev_knot (h) = h; 10219 mp_originator (h) = mp_program_code; 10220 set_number_to_zero(h->x_coord); 10221 set_number_to_zero(h->y_coord); 10222 number_clone(h->left_x, diam); 10223 set_number_to_zero(h->left_y); 10224 set_number_to_zero(h->right_x); 10225 number_clone(h->right_y, diam); 10226 return h; 10227} 10228 10229 10230@ If the polygon being returned by |make_pen| has only one vertex, it will 10231be interpreted as an elliptical pen. This is no problem since a degenerate 10232polygon can equally well be thought of as a degenerate ellipse. We need only 10233initialize the |left_x|, |left_y|, |right_x|, and |right_y| fields. 10234 10235@<Make sure |h| isn't confused with an elliptical pen@>= 10236if (pen_is_elliptical (h)) { 10237 number_clone(h->left_x, h->x_coord); 10238 number_clone(h->left_y, h->y_coord); 10239 number_clone(h->right_x, h->x_coord); 10240 number_clone(h->right_y, h->y_coord); 10241} 10242 10243@ Printing a polygonal pen is very much like printing a path 10244 10245@<Declarations@>= 10246static void mp_pr_pen (MP mp, mp_knot h); 10247 10248@ @c 10249void mp_pr_pen (MP mp, mp_knot h) { 10250 mp_knot p, q; /* for list traversal */ 10251 if (pen_is_elliptical (h)) { 10252 @<Print the elliptical pen |h|@>; 10253 } else { 10254 p = h; 10255 do { 10256 mp_print_two (mp, p->x_coord, p->y_coord); 10257 mp_print_nl (mp, " .. "); 10258 @<Advance |p| making sure the links are OK and |return| if there is 10259 a problem@>; 10260 } while (p != h); 10261 mp_print (mp, "cycle"); 10262 } 10263} 10264 10265 10266@ @<Advance |p| making sure the links are OK and |return| if there is...@>= 10267q = mp_next_knot (p); 10268if ((q == NULL) || (mp_prev_knot (q) != p)) { 10269 mp_print_nl (mp, "???"); 10270 return; /* this won't happen */ 10271@.???@> 10272} 10273p = q 10274 10275@ @<Print the elliptical pen |h|@>= 10276{ 10277 mp_number v1; 10278 new_number (v1); 10279 mp_print (mp, "pencircle transformed ("); 10280 print_number (h->x_coord); 10281 mp_print_char (mp, xord (',')); 10282 print_number (h->y_coord); 10283 mp_print_char (mp, xord (',')); 10284 set_number_from_substraction (v1, h->left_x, h->x_coord); 10285 print_number (v1); 10286 mp_print_char (mp, xord (',')); 10287 set_number_from_substraction (v1, h->right_x, h->x_coord); 10288 print_number (v1); 10289 mp_print_char (mp, xord (',')); 10290 set_number_from_substraction (v1, h->left_y, h->y_coord); 10291 print_number (v1); 10292 mp_print_char (mp, xord (',')); 10293 set_number_from_substraction (v1, h->right_y, h->y_coord); 10294 print_number (v1); 10295 mp_print_char (mp, xord (')')); 10296 free_number (v1); 10297} 10298 10299 10300@ Here us another version of |pr_pen| that prints the pen as a diagnostic 10301message. 10302 10303@<Declarations@>= 10304static void mp_print_pen (MP mp, mp_knot h, const char *s, boolean nuline); 10305 10306@ @c 10307void mp_print_pen (MP mp, mp_knot h, const char *s, boolean nuline) { 10308 mp_print_diagnostic (mp, "Pen", s, nuline); 10309 mp_print_ln (mp); 10310@.Pen at line...@>; 10311 mp_pr_pen (mp, h); 10312 mp_end_diagnostic (mp, true); 10313} 10314 10315 10316@ Making a polygonal pen into a path involves restoring the |mp_left_type| and 10317|mp_right_type| fields and setting the control points so as to make a polygonal 10318path. 10319 10320@c 10321static void mp_make_path (MP mp, mp_knot h) { 10322 mp_knot p; /* for traversing the knot list */ 10323 quarterword k; /* a loop counter */ 10324 @<Other local variables in |make_path|@>; 10325 FUNCTION_TRACE1 ("make_path()\n"); 10326 if (pen_is_elliptical (h)) { 10327 FUNCTION_TRACE1 ("make_path(elliptical)\n"); 10328 @<Make the elliptical pen |h| into a path@>; 10329 } else { 10330 p = h; 10331 do { 10332 mp_left_type (p) = mp_explicit; 10333 mp_right_type (p) = mp_explicit; 10334 @<copy the coordinates of knot |p| into its control points@>; 10335 p = mp_next_knot (p); 10336 } while (p != h); 10337 } 10338} 10339 10340 10341@ @<copy the coordinates of knot |p| into its control points@>= 10342number_clone (p->left_x, p->x_coord); 10343number_clone (p->left_y, p->y_coord); 10344number_clone (p->right_x, p->x_coord); 10345number_clone (p->right_y, p->y_coord) 10346 10347 10348@ We need an eight knot path to get a good approximation to an ellipse. 10349 10350@<Make the elliptical pen |h| into a path@>= 10351{ 10352 mp_number center_x, center_y; /* translation parameters for an elliptical pen */ 10353 mp_number width_x, width_y; /* the effect of a unit change in $x$ */ 10354 mp_number height_x, height_y; /* the effect of a unit change in $y$ */ 10355 mp_number dx, dy; /* the vector from knot |p| to its right control point */ 10356 new_number (center_x); 10357 new_number (center_y); 10358 new_number (width_x); 10359 new_number (width_y); 10360 new_number (height_x); 10361 new_number (height_y); 10362 new_number (dx); 10363 new_number (dy); 10364 @<Extract the transformation parameters from the elliptical pen~|h|@>; 10365 p = h; 10366 for (k = 0; k <= 7; k++) { 10367 @<Initialize |p| as the |k|th knot of a circle of unit diameter, 10368 transforming it appropriately@>; 10369 if (k == 7) 10370 mp_next_knot (p) = h; 10371 else 10372 mp_next_knot (p) = mp_new_knot (mp); 10373 p = mp_next_knot (p); 10374 } 10375 free_number (dx); 10376 free_number (dy); 10377 free_number (center_x); 10378 free_number (center_y); 10379 free_number (width_x); 10380 free_number (width_y); 10381 free_number (height_x); 10382 free_number (height_y); 10383} 10384 10385 10386@ @<Extract the transformation parameters from the elliptical pen~|h|@>= 10387number_clone (center_x, h->x_coord); 10388number_clone (center_y, h->y_coord); 10389set_number_from_substraction (width_x, h->left_x, center_x); 10390set_number_from_substraction (width_y, h->left_y, center_y); 10391set_number_from_substraction (height_x, h->right_x, center_x); 10392set_number_from_substraction (height_y, h->right_y, center_y); 10393 10394@ @<Other local variables in |make_path|@>= 10395integer kk; 10396 /* |k| advanced $270^\circ$ around the ring (cf. $\sin\theta=\cos(\theta+270)$) */ 10397 10398@ The only tricky thing here are the tables |half_cos| and |d_cos| used to 10399find the point $k/8$ of the way around the circle and the direction vector 10400to use there. 10401 10402@<Initialize |p| as the |k|th knot of a circle of unit diameter,...@>= 10403kk = (k + 6) % 8; 10404{ 10405 mp_number r1, r2; 10406 new_fraction (r1); 10407 new_fraction (r2); 10408 take_fraction (r1, mp->half_cos[k], width_x); 10409 take_fraction (r2, mp->half_cos[kk], height_x); 10410 number_add (r1, r2); 10411 set_number_from_addition (p->x_coord, center_x, r1); 10412 take_fraction (r1, mp->half_cos[k], width_y); 10413 take_fraction (r2, mp->half_cos[kk], height_y); 10414 number_add (r1, r2); 10415 set_number_from_addition (p->y_coord, center_y, r1); 10416 take_fraction (r1, mp->d_cos[kk], width_x); 10417 take_fraction (r2, mp->d_cos[k], height_x); 10418 number_clone (dx, r1); 10419 number_negate (dx); 10420 number_add (dx, r2); 10421 take_fraction (r1, mp->d_cos[kk], width_y); 10422 take_fraction (r2, mp->d_cos[k], height_y); 10423 number_clone (dy, r1); 10424 number_negate (dy); 10425 number_add (dy, r2); 10426 set_number_from_addition (p->right_x, p->x_coord, dx); 10427 set_number_from_addition (p->right_y, p->y_coord, dy); 10428 set_number_from_substraction (p->left_x, p->x_coord, dx); 10429 set_number_from_substraction (p->left_y, p->y_coord, dy); 10430 free_number (r1); 10431 free_number (r2); 10432} 10433mp_left_type (p) = mp_explicit; 10434mp_right_type (p) = mp_explicit; 10435mp_originator (p) = mp_program_code 10436 10437@ @<Glob...@>= 10438mp_number half_cos[8]; /* ${1\over2}\cos(45k)$ */ 10439mp_number d_cos[8]; /* a magic constant times $\cos(45k)$ */ 10440 10441@ The magic constant for |d_cos| is the distance between $({1\over2},0)$ and 10442$({1\over4}\sqrt2,{1\over4}\sqrt2)$ times the result of the |velocity| 10443function for $\theta=\phi=22.5^\circ$. This comes out to be 10444$$ d = {\sqrt{2-\sqrt2}\over 3+3\cos22.5^\circ} 10445 \approx 0.132608244919772. 10446$$ 10447 10448@<Set init...@>= 10449for (k = 0; k <= 7; k++) { 10450 new_fraction (mp->half_cos[k]); 10451 new_fraction (mp->d_cos[k]); 10452} 10453number_clone (mp->half_cos[0], fraction_half_t); 10454number_clone (mp->half_cos[1], twentysixbits_sqrt2_t); 10455number_clone (mp->half_cos[2], zero_t); 10456number_clone (mp->d_cos[0], twentyeightbits_d_t); 10457number_clone (mp->d_cos[1], twentysevenbits_sqrt2_d_t); 10458number_clone (mp->d_cos[2], zero_t); 10459for (k = 3; k <= 4; k++) { 10460 number_clone (mp->half_cos[k], mp->half_cos[4 - k]); 10461 number_negate (mp->half_cos[k]); 10462 number_clone (mp->d_cos[k], mp->d_cos[4 - k]); 10463 number_negate (mp->d_cos[k]); 10464} 10465for (k = 5; k <= 7; k++) { 10466 number_clone (mp->half_cos[k], mp->half_cos[8 - k]); 10467 number_clone (mp->d_cos[k], mp->d_cos[8 - k]); 10468} 10469 10470@ @<Dealloc...@>= 10471for (k = 0; k <= 7; k++) { 10472 free_number (mp->half_cos[k]); 10473 free_number (mp->d_cos[k]); 10474} 10475 10476 10477@ The |convex_hull| function forces a pen polygon to be convex when it is 10478returned by |make_pen| and after any subsequent transformation where rounding 10479error might allow the convexity to be lost. 10480The convex hull algorithm used here is described by F.~P. Preparata and 10481M.~I. Shamos [{\sl Computational Geometry}, Springer-Verlag, 1985]. 10482 10483@<Declarations@>= 10484static mp_knot mp_convex_hull (MP mp, mp_knot h); 10485 10486@ @c 10487mp_knot mp_convex_hull (MP mp, mp_knot h) { /* Make a polygonal pen convex */ 10488 mp_knot l, r; /* the leftmost and rightmost knots */ 10489 mp_knot p, q; /* knots being scanned */ 10490 mp_knot s; /* the starting point for an upcoming scan */ 10491 mp_number dx, dy; /* a temporary pointer */ 10492 mp_knot ret; 10493 new_number (dx); 10494 new_number (dy); 10495 if (pen_is_elliptical (h)) { 10496 ret = h; 10497 } else { 10498 @<Set |l| to the leftmost knot in polygon~|h|@>; 10499 @<Set |r| to the rightmost knot in polygon~|h|@>; 10500 if (l != r) { 10501 s = mp_next_knot (r); 10502 @<Find any knots on the path from |l| to |r| above the |l|-|r| line and 10503 move them past~|r|@>; 10504 @<Find any knots on the path from |s| to |l| below the |l|-|r| line and 10505 move them past~|l|@>; 10506 @<Sort the path from |l| to |r| by increasing $x$@>; 10507 @<Sort the path from |r| to |l| by decreasing $x$@>; 10508 } 10509 if (l != mp_next_knot (l)) { 10510 @<Do a Gramm scan and remove vertices where there is no left turn@>; 10511 } 10512 ret = l; 10513 } 10514 free_number (dx); 10515 free_number (dy); 10516 return ret; 10517} 10518 10519 10520@ All comparisons are done primarily on $x$ and secondarily on $y$. 10521 10522@<Set |l| to the leftmost knot in polygon~|h|@>= 10523l = h; 10524p = mp_next_knot (h); 10525while (p != h) { 10526 if (number_lessequal (p->x_coord, l->x_coord)) 10527 if ((number_less (p->x_coord, l->x_coord)) || 10528 (number_less (p->y_coord, l->y_coord))) 10529 l = p; 10530 p = mp_next_knot (p); 10531} 10532 10533 10534@ @<Set |r| to the rightmost knot in polygon~|h|@>= 10535r = h; 10536p = mp_next_knot (h); 10537while (p != h) { 10538 if (number_greaterequal(p->x_coord, r->x_coord)) 10539 if (number_greater (p->x_coord, r->x_coord) || 10540 number_greater (p->y_coord, r->y_coord)) 10541 r = p; 10542 p = mp_next_knot (p); 10543} 10544 10545 10546@ @<Find any knots on the path from |l| to |r| above the |l|-|r| line...@>= 10547{ 10548 mp_number ab_vs_cd; 10549 mp_number arg1, arg2; 10550 new_number (arg1); 10551 new_number (arg2); 10552 new_number (ab_vs_cd); 10553 set_number_from_substraction (dx, r->x_coord, l->x_coord); 10554 set_number_from_substraction (dy, r->y_coord, l->y_coord); 10555 p = mp_next_knot (l); 10556 while (p != r) { 10557 q = mp_next_knot (p); 10558 set_number_from_substraction (arg1, p->y_coord, l->y_coord); 10559 set_number_from_substraction (arg2, p->x_coord, l->x_coord); 10560 ab_vs_cd (ab_vs_cd, dx, arg1, dy, arg2); 10561 if (number_positive(ab_vs_cd)) 10562 mp_move_knot (mp, p, r); 10563 p = q; 10564 } 10565 free_number (ab_vs_cd); 10566 free_number (arg1); 10567 free_number (arg2); 10568} 10569 10570 10571@ The |move_knot| procedure removes |p| from a doubly linked list and inserts 10572it after |q|. 10573 10574@ @<Declarations@>= 10575static void mp_move_knot (MP mp, mp_knot p, mp_knot q); 10576 10577@ @c 10578void mp_move_knot (MP mp, mp_knot p, mp_knot q) { 10579 (void) mp; 10580 mp_next_knot (mp_prev_knot (p)) = mp_next_knot (p); 10581 mp_prev_knot (mp_next_knot (p)) = mp_prev_knot (p); 10582 mp_prev_knot (p) = q; 10583 mp_next_knot (p) = mp_next_knot (q); 10584 mp_next_knot (q) = p; 10585 mp_prev_knot (mp_next_knot (p)) = p; 10586} 10587 10588 10589@ @<Find any knots on the path from |s| to |l| below the |l|-|r| line...@>= 10590{ 10591 mp_number ab_vs_cd; 10592 mp_number arg1, arg2; 10593 new_number (ab_vs_cd); 10594 new_number (arg1); 10595 new_number (arg2); 10596 p = s; 10597 while (p != l) { 10598 q = mp_next_knot (p); 10599 set_number_from_substraction (arg1, p->y_coord, l->y_coord); 10600 set_number_from_substraction (arg2, p->x_coord, l->x_coord); 10601 ab_vs_cd (ab_vs_cd, dx, arg1, dy, arg2); 10602 if (number_negative(ab_vs_cd)) 10603 mp_move_knot (mp, p, l); 10604 p = q; 10605 } 10606 free_number (ab_vs_cd); 10607 free_number (arg1); 10608 free_number (arg2); 10609} 10610 10611 10612@ The list is likely to be in order already so we just do linear insertions. 10613Secondary comparisons on $y$ ensure that the sort is consistent with the 10614choice of |l| and |r|. 10615 10616@<Sort the path from |l| to |r| by increasing $x$@>= 10617p = mp_next_knot (l); 10618while (p != r) { 10619 q = mp_prev_knot (p); 10620 while (number_greater(q->x_coord, p->x_coord)) 10621 q = mp_prev_knot (q); 10622 while (number_equal(q->x_coord, p->x_coord)) { 10623 if (number_greater(q->y_coord, p->y_coord)) 10624 q = mp_prev_knot (q); 10625 else 10626 break; 10627 } 10628 if (q == mp_prev_knot (p)) { 10629 p = mp_next_knot (p); 10630 } else { 10631 p = mp_next_knot (p); 10632 mp_move_knot (mp, mp_prev_knot (p), q); 10633 } 10634} 10635 10636 10637@ @<Sort the path from |r| to |l| by decreasing $x$@>= 10638p = mp_next_knot (r); 10639while (p != l) { 10640 q = mp_prev_knot (p); 10641 while (number_less(q->x_coord, p->x_coord)) 10642 q = mp_prev_knot (q); 10643 while (number_equal(q->x_coord, p->x_coord)) { 10644 if (number_less (q->y_coord, p->y_coord)) 10645 q = mp_prev_knot (q); 10646 else 10647 break; 10648 } 10649 if (q == mp_prev_knot (p)) { 10650 p = mp_next_knot (p); 10651 } else { 10652 p = mp_next_knot (p); 10653 mp_move_knot (mp, mp_prev_knot (p), q); 10654 } 10655} 10656 10657 10658@ The condition involving |ab_vs_cd| tests if there is not a left turn 10659at knot |q|. There usually will be a left turn so we streamline the case 10660where the |then| clause is not executed. 10661 10662@<Do a Gramm scan and remove vertices where there...@>= 10663{ 10664 mp_number ab_vs_cd; 10665 mp_number arg1, arg2; 10666 new_number (arg1); 10667 new_number (arg2); 10668 new_number (ab_vs_cd); 10669 p = l; 10670 q = mp_next_knot (l); 10671 while (1) { 10672 set_number_from_substraction (dx, q->x_coord, p->x_coord); 10673 set_number_from_substraction (dy, q->y_coord, p->y_coord); 10674 p = q; 10675 q = mp_next_knot (q); 10676 if (p == l) 10677 break; 10678 if (p != r) { 10679 set_number_from_substraction (arg1, q->y_coord, p->y_coord); 10680 set_number_from_substraction (arg2, q->x_coord, p->x_coord); 10681 ab_vs_cd (ab_vs_cd, dx, arg1, dy, arg2); 10682 if (number_nonpositive(ab_vs_cd)) { 10683 @<Remove knot |p| and back up |p| and |q| but don't go past |l|@>; 10684 } 10685 } 10686 } 10687 free_number (ab_vs_cd); 10688 free_number (arg1); 10689 free_number (arg2); 10690} 10691 10692 10693@ @<Remove knot |p| and back up |p| and |q| but don't go past |l|@>= 10694{ 10695 s = mp_prev_knot (p); 10696 mp_xfree (p); 10697 mp_next_knot (s) = q; 10698 mp_prev_knot (q) = s; 10699 if (s == l) { 10700 p = s; 10701 } else { 10702 p = mp_prev_knot (s); 10703 q = s; 10704 } 10705} 10706 10707 10708@ The |find_offset| procedure sets global variables |(cur_x,cur_y)| to the 10709offset associated with the given direction |(x,y)|. If two different offsets 10710apply, it chooses one of them. 10711 10712@c 10713static void mp_find_offset (MP mp, mp_number x_orig, mp_number y_orig, mp_knot h) { 10714 mp_knot p, q; /* consecutive knots */ 10715 if (pen_is_elliptical (h)) { 10716 mp_fraction xx, yy; /* untransformed offset for an elliptical pen */ 10717 mp_number wx, wy, hx, hy; /* the transformation matrix for an elliptical pen */ 10718 mp_fraction d; /* a temporary register */ 10719 new_fraction(xx); 10720 new_fraction(yy); 10721 new_number(wx); 10722 new_number(wy); 10723 new_number(hx); 10724 new_number(hy); 10725 new_fraction(d); 10726 @<Find the offset for |(x,y)| on the elliptical pen~|h|@> 10727 free_number (xx); 10728 free_number (yy); 10729 free_number (wx); 10730 free_number (wy); 10731 free_number (hx); 10732 free_number (hy); 10733 free_number (d); 10734 } else { 10735 mp_number ab_vs_cd; 10736 mp_number arg1, arg2; 10737 new_number (arg1); 10738 new_number (arg2); 10739 new_number (ab_vs_cd); 10740 q = h; 10741 do { 10742 p = q; 10743 q = mp_next_knot (q); 10744 set_number_from_substraction (arg1, q->x_coord, p->x_coord); 10745 set_number_from_substraction (arg2, q->y_coord, p->y_coord); 10746 ab_vs_cd (ab_vs_cd, arg1, y_orig, arg2, x_orig); 10747 } while (number_negative(ab_vs_cd)); 10748 do { 10749 p = q; 10750 q = mp_next_knot (q); 10751 set_number_from_substraction (arg1, q->x_coord, p->x_coord); 10752 set_number_from_substraction (arg2, q->y_coord, p->y_coord); 10753 ab_vs_cd (ab_vs_cd, arg1, y_orig, arg2, x_orig); 10754 } while (number_positive(ab_vs_cd)); 10755 number_clone (mp->cur_x, p->x_coord); 10756 number_clone (mp->cur_y, p->y_coord); 10757 free_number (ab_vs_cd); 10758 free_number (arg1); 10759 free_number (arg2); 10760 } 10761} 10762 10763 10764@ @<Glob...@>= 10765mp_number cur_x; 10766mp_number cur_y; /* all-purpose return value registers */ 10767 10768@ @<Initialize table entries@>= 10769new_number (mp->cur_x); 10770new_number (mp->cur_y); 10771 10772@ @<Dealloc...@>= 10773free_number (mp->cur_x); 10774free_number (mp->cur_y); 10775 10776@ @<Find the offset for |(x,y)| on the elliptical pen~|h|@>= 10777if (number_zero(x_orig) && number_zero(y_orig)) { 10778 number_clone(mp->cur_x, h->x_coord); 10779 number_clone(mp->cur_y, h->y_coord); 10780} else { 10781 mp_number x, y, abs_x, abs_y; 10782 new_number(x); 10783 new_number(y); 10784 new_number(abs_x); 10785 new_number(abs_y); 10786 number_clone(x, x_orig); 10787 number_clone(y, y_orig); 10788 @<Find the non-constant part of the transformation for |h|@>; 10789 number_clone(abs_x, x); 10790 number_clone(abs_y, y); 10791 number_abs(abs_x); 10792 number_abs(abs_y); 10793 while (number_less(abs_x, fraction_half_t) && number_less(abs_y, fraction_half_t)) { 10794 number_double(x); 10795 number_double(y); 10796 number_clone(abs_x, x); 10797 number_clone(abs_y, y); 10798 number_abs(abs_x); 10799 number_abs(abs_y); 10800 } 10801 @<Make |(xx,yy)| the offset on the untransformed \&{pencircle} for the 10802 untransformed version of |(x,y)|@>; 10803 { 10804 mp_number r1, r2; 10805 new_fraction (r1); 10806 new_fraction (r2); 10807 take_fraction (r1, xx, wx); 10808 take_fraction (r2, yy, hx); 10809 number_add(r1, r2); 10810 set_number_from_addition(mp->cur_x, h->x_coord, r1); 10811 take_fraction (r1, xx, wy); 10812 take_fraction (r2, yy, hy); 10813 number_add(r1, r2); 10814 set_number_from_addition(mp->cur_y, h->y_coord, r1); 10815 free_number (r1); 10816 free_number (r2); 10817 } 10818 free_number(abs_x); 10819 free_number(abs_y); 10820 free_number(x); 10821 free_number(y); 10822} 10823 10824 10825@ @<Find the non-constant part of the transformation for |h|@>= 10826{ 10827 set_number_from_substraction(wx, h->left_x, h->x_coord); 10828 set_number_from_substraction(wy, h->left_y, h->y_coord); 10829 set_number_from_substraction(hx, h->right_x, h->x_coord); 10830 set_number_from_substraction(hy, h->right_y, h->y_coord); 10831} 10832 10833 10834@ @<Make |(xx,yy)| the offset on the untransformed \&{pencircle} for the...@>= 10835{ 10836 mp_number r1, r2, arg1; 10837 new_number (arg1); 10838 new_fraction (r1); 10839 new_fraction (r2); 10840 take_fraction (r1, x, hy); 10841 number_clone (arg1, hx); 10842 number_negate (arg1); 10843 take_fraction (r2, y, arg1); 10844 number_add (r1, r2); 10845 number_negate (r1); 10846 number_clone(yy, r1); 10847 number_clone (arg1, wy); 10848 number_negate (arg1); 10849 take_fraction (r1, x, arg1); 10850 take_fraction (r2, y, wx); 10851 number_add (r1, r2); 10852 number_clone(xx, r1); 10853 free_number (arg1); 10854 free_number (r1); 10855 free_number (r2); 10856} 10857pyth_add (d, xx, yy); 10858if (number_positive(d)) { 10859 mp_number ret; 10860 new_fraction (ret); 10861 make_fraction (ret, xx, d); 10862 number_half(ret); 10863 number_clone(xx, ret); 10864 make_fraction (ret, yy, d); 10865 number_half(ret); 10866 number_clone(yy, ret); 10867 free_number (ret); 10868} 10869 10870@ Finding the bounding box of a pen is easy except if the pen is elliptical. 10871But we can handle that case by just calling |find_offset| twice. The answer 10872is stored in the global variables |minx|, |maxx|, |miny|, and |maxy|. 10873 10874@c 10875static void mp_pen_bbox (MP mp, mp_knot h) { 10876 mp_knot p; /* for scanning the knot list */ 10877 if (pen_is_elliptical (h)) { 10878 @<Find the bounding box of an elliptical pen@>; 10879 } else { 10880 number_clone (mp_minx, h->x_coord); 10881 number_clone (mp_maxx, mp_minx); 10882 number_clone (mp_miny, h->y_coord); 10883 number_clone (mp_maxy, mp_miny); 10884 p = mp_next_knot (h); 10885 while (p != h) { 10886 if (number_less (p->x_coord, mp_minx)) 10887 number_clone (mp_minx, p->x_coord); 10888 if (number_less (p->y_coord, mp_miny)) 10889 number_clone (mp_miny, p->y_coord); 10890 if (number_greater (p->x_coord, mp_maxx)) 10891 number_clone (mp_maxx, p->x_coord); 10892 if (number_greater (p->y_coord, mp_maxy)) 10893 number_clone (mp_maxy, p->y_coord); 10894 p = mp_next_knot (p); 10895 } 10896 } 10897} 10898 10899 10900@ @<Find the bounding box of an elliptical pen@>= 10901{ 10902 mp_number arg1, arg2; 10903 new_number(arg1); 10904 new_fraction (arg2); 10905 number_clone(arg2, fraction_one_t); 10906 mp_find_offset (mp, arg1, arg2, h); 10907 number_clone (mp_maxx, mp->cur_x); 10908 number_clone (mp_minx, h->x_coord); 10909 number_double (mp_minx); 10910 number_substract (mp_minx, mp->cur_x); 10911 number_negate (arg2); 10912 mp_find_offset (mp, arg2, arg1, h); 10913 number_clone (mp_maxy, mp->cur_y); 10914 number_clone (mp_miny, h->y_coord); 10915 number_double (mp_miny); 10916 number_substract (mp_miny, mp->cur_y); 10917 free_number(arg1); 10918 free_number(arg2); 10919} 10920 10921 10922@* Numerical values. 10923 10924This first set goes into the header 10925 10926@<MPlib internal header stuff@>= 10927#define mp_fraction mp_number 10928#define mp_angle mp_number 10929#define new_number(A) (((math_data *)(mp->math))->allocate)(mp, &(A), mp_scaled_type) 10930#define new_fraction(A) (((math_data *)(mp->math))->allocate)(mp, &(A), mp_fraction_type) 10931#define new_angle(A) (((math_data *)(mp->math))->allocate)(mp, &(A), mp_angle_type) 10932#define free_number(A) (((math_data *)(mp->math))->free)(mp, &(A)) 10933 10934@ 10935@d set_precision() (((math_data *)(mp->math))->set_precision)(mp) 10936@d free_math() (((math_data *)(mp->math))->free_math)(mp) 10937@d scan_numeric_token(A) (((math_data *)(mp->math))->scan_numeric)(mp, A) 10938@d scan_fractional_token(A) (((math_data *)(mp->math))->scan_fractional)(mp, A) 10939@d set_number_from_of_the_way(A,t,B,C) (((math_data *)(mp->math))->from_oftheway)(mp, &(A),t,B,C) 10940@d set_number_from_int(A,B) (((math_data *)(mp->math))->from_int)(&(A),B) 10941@d set_number_from_scaled(A,B) (((math_data *)(mp->math))->from_scaled)(&(A),B) 10942@d set_number_from_boolean(A,B) (((math_data *)(mp->math))->from_boolean)(&(A),B) 10943@d set_number_from_double(A,B) (((math_data *)(mp->math))->from_double)(&(A),B) 10944@d set_number_from_addition(A,B,C) (((math_data *)(mp->math))->from_addition)(&(A),B,C) 10945@d set_number_from_substraction(A,B,C) (((math_data *)(mp->math))->from_substraction)(&(A),B,C) 10946@d set_number_from_div(A,B,C) (((math_data *)(mp->math))->from_div)(&(A),B,C) 10947@d set_number_from_mul(A,B,C) (((math_data *)(mp->math))->from_mul)(&(A),B,C) 10948@d number_int_div(A,C) (((math_data *)(mp->math))->from_int_div)(&(A),A,C) 10949@d set_number_from_int_mul(A,B,C) (((math_data *)(mp->math))->from_int_mul)(&(A),B,C) 10950@# 10951@d set_number_to_unity(A) (((math_data *)(mp->math))->clone)(&(A), unity_t) 10952@d set_number_to_zero(A) (((math_data *)(mp->math))->clone)(&(A), zero_t) 10953@d set_number_to_inf(A) (((math_data *)(mp->math))->clone)(&(A), inf_t) 10954@d set_number_to_neg_inf(A) do { set_number_to_inf(A); number_negate (A); } while (0) 10955@# 10956@d init_randoms(A) (((math_data *)(mp->math))->init_randoms)(mp,A) 10957@d print_number(A) (((math_data *)(mp->math))->print)(mp,A) 10958@d number_tostring(A) (((math_data *)(mp->math))->tostring)(mp,A) 10959@d make_scaled(R,A,B) (((math_data *)(mp->math))->make_scaled)(mp,&(R),A,B) 10960@d take_scaled(R,A,B) (((math_data *)(mp->math))->take_scaled)(mp,&(R),A,B) 10961@d make_fraction(R,A,B) (((math_data *)(mp->math))->make_fraction)(mp,&(R),A,B) 10962@d take_fraction(R,A,B) (((math_data *)(mp->math))->take_fraction)(mp,&(R),A,B) 10963@d pyth_add(R,A,B) (((math_data *)(mp->math))->pyth_add)(mp,&(R),A,B) 10964@d pyth_sub(R,A,B) (((math_data *)(mp->math))->pyth_sub)(mp,&(R),A,B) 10965@d n_arg(R,A,B) (((math_data *)(mp->math))->n_arg)(mp,&(R),A,B) 10966@d m_log(R,A) (((math_data *)(mp->math))->m_log)(mp,&(R),A) 10967@d m_exp(R,A) (((math_data *)(mp->math))->m_exp)(mp,&(R),A) 10968@d m_norm_rand(R) (((math_data *)(mp->math))->m_norm_rand)(mp,&(R)) 10969@d velocity(R,A,B,C,D,E) (((math_data *)(mp->math))->velocity)(mp,&(R),A,B,C,D,E) 10970@d ab_vs_cd(R,A,B,C,D) (((math_data *)(mp->math))->ab_vs_cd)(mp,&(R),A,B,C,D) 10971@d crossing_point(R,A,B,C) (((math_data *)(mp->math))->crossing_point)(mp,&(R),A,B,C) 10972@d n_sin_cos(A,S,C) (((math_data *)(mp->math))->sin_cos)(mp,A,&(S),&(C)) 10973@d square_rt(A,S) (((math_data *)(mp->math))->sqrt)(mp,&(A),S) 10974@d slow_add(R,A,B) (((math_data *)(mp->math))->slow_add)(mp,&(R),A,B) 10975@d round_unscaled(A) (((math_data *)(mp->math))->round_unscaled)(A) 10976@d floor_scaled(A) (((math_data *)(mp->math))->floor_scaled)(&(A)) 10977@d fraction_to_round_scaled(A) (((math_data *)(mp->math))->fraction_to_round_scaled)(&(A)) 10978@d number_to_int(A) (((math_data *)(mp->math))->to_int)(A) 10979@d number_to_boolean(A) (((math_data *)(mp->math))->to_boolean)(A) 10980@d number_to_scaled(A) (((math_data *)(mp->math))->to_scaled)(A) 10981@d number_to_double(A) (((math_data *)(mp->math))->to_double)(A) 10982@d number_negate(A) (((math_data *)(mp->math))->negate)(&(A)) 10983@d number_add(A,B) (((math_data *)(mp->math))->add)(&(A),B) 10984@d number_substract(A,B) (((math_data *)(mp->math))->substract)(&(A),B) 10985@d number_half(A) (((math_data *)(mp->math))->half)(&(A)) 10986@d number_halfp(A) (((math_data *)(mp->math))->halfp)(&(A)) 10987@d number_double(A) (((math_data *)(mp->math))->do_double)(&(A)) 10988@d number_add_scaled(A,B) (((math_data *)(mp->math))->add_scaled)(&(A),B) 10989@d number_multiply_int(A,B) (((math_data *)(mp->math))->multiply_int)(&(A),B) 10990@d number_divide_int(A,B) (((math_data *)(mp->math))->divide_int)(&(A),B) 10991@d number_abs(A) (((math_data *)(mp->math))->abs)(&(A)) 10992@d number_modulo(A,B) (((math_data *)(mp->math))->modulo)(&(A), B) 10993@d number_nonequalabs(A,B) (((math_data *)(mp->math))->nonequalabs)(A,B) 10994@d number_odd(A) (((math_data *)(mp->math))->odd)(A) 10995@d number_equal(A,B) (((math_data *)(mp->math))->equal)(A,B) 10996@d number_greater(A,B) (((math_data *)(mp->math))->greater)(A,B) 10997@d number_less(A,B) (((math_data *)(mp->math))->less)(A,B) 10998@d number_clone(A,B) (((math_data *)(mp->math))->clone)(&(A),B) 10999@d number_swap(A,B) (((math_data *)(mp->math))->swap)(&(A),&(B)); 11000@d convert_scaled_to_angle(A) (((math_data *)(mp->math))->scaled_to_angle)(&(A)); 11001@d convert_angle_to_scaled(A) (((math_data *)(mp->math))->angle_to_scaled)(&(A)); 11002@d convert_fraction_to_scaled(A) (((math_data *)(mp->math))->fraction_to_scaled)(&(A)); 11003@d convert_scaled_to_fraction(A) (((math_data *)(mp->math))->scaled_to_fraction)(&(A)); 11004@# 11005@d number_zero(A) number_equal(A, zero_t) 11006@d number_infinite(A) number_equal(A, inf_t) 11007@d number_unity(A) number_equal(A, unity_t) 11008@d number_negative(A) number_less(A, zero_t) 11009@d number_nonnegative(A) (!number_negative(A)) 11010@d number_positive(A) number_greater(A, zero_t) 11011@d number_nonpositive(A) (!number_positive(A)) 11012@d number_nonzero(A) (!number_zero(A)) 11013@d number_greaterequal(A,B) (!number_less(A,B)) 11014@d number_lessequal(A,B) (!number_greater(A,B)) 11015 11016@* Edge structures. 11017Now we come to \MP's internal scheme for representing pictures. 11018The representation is very different from \MF's edge structures 11019because \MP\ pictures contain \ps\ graphics objects instead of pixel 11020images. However, the basic idea is somewhat similar in that shapes 11021are represented via their boundaries. 11022 11023The main purpose of edge structures is to keep track of graphical objects 11024until it is time to translate them into \ps. Since \MP\ does not need to 11025know anything about an edge structure other than how to translate it into 11026\ps\ and how to find its bounding box, edge structures can be just linked 11027lists of graphical objects. \MP\ has no easy way to determine whether 11028two such objects overlap, but it suffices to draw the first one first and 11029let the second one overwrite it if necessary. 11030 11031@<MPlib header stuff@>= 11032enum mp_graphical_object_code { 11033 @<Graphical object codes@> 11034 mp_final_graphic 11035}; 11036 11037@ Let's consider the types of graphical objects one at a time. 11038First of all, a filled contour is represented by a eight-word node. The first 11039word contains |type| and |link| fields, and the next six words contain a 11040pointer to a cyclic path and the value to use for \ps' \&{currentrgbcolor} 11041parameter. If a pen is used for filling |pen_p|, |ljoin| and |miterlim| 11042give the relevant information. 11043 11044@d mp_path_p(A) (A)->path_p_ /* a pointer to the path that needs filling */ 11045@d mp_pen_p(A) (A)->pen_p_ /* a pointer to the pen to fill or stroke with */ 11046@d mp_color_model(A) ((mp_fill_node)(A))->color_model_ /* the color model */ 11047@d cyan red 11048@d grey red 11049@d magenta green 11050@d yellow blue 11051@d mp_pre_script(A) ((mp_fill_node)(A))->pre_script_ 11052@d mp_post_script(A) ((mp_fill_node)(A))->post_script_ 11053 11054@<MPlib internal header stuff@>= 11055typedef struct mp_fill_node_data { 11056 NODE_BODY; 11057 halfword color_model_; 11058 mp_number red; 11059 mp_number green; 11060 mp_number blue; 11061 mp_number black; 11062 mp_string pre_script_; 11063 mp_string post_script_; 11064 mp_knot path_p_; 11065 mp_knot pen_p_; 11066 unsigned char ljoin; 11067 mp_number miterlim; 11068} mp_fill_node_data; 11069typedef struct mp_fill_node_data *mp_fill_node; 11070 11071@ @<Graphical object codes@>= 11072mp_fill_code = 1, 11073 11074@ Make a fill node for cyclic path |p| and color black. 11075 11076@d fill_node_size sizeof(struct mp_fill_node_data) 11077 11078@c 11079static mp_node mp_new_fill_node (MP mp, mp_knot p) { 11080 mp_fill_node t = malloc_node (fill_node_size); 11081 mp_type (t) = mp_fill_node_type; 11082 mp_path_p (t) = p; 11083 mp_pen_p (t) = NULL; /* |NULL| means don't use a pen */ 11084 new_number(t->red); 11085 new_number(t->green); 11086 new_number(t->blue); 11087 new_number(t->black); 11088 new_number(t->miterlim); 11089 clear_color (t); 11090 mp_color_model (t) = mp_uninitialized_model; 11091 mp_pre_script (t) = NULL; 11092 mp_post_script (t) = NULL; 11093 /* Set the |ljoin| and |miterlim| fields in object |t| */ 11094 if (number_greater(internal_value (mp_linejoin), unity_t)) 11095 t->ljoin = 2; 11096 else if (number_positive(internal_value (mp_linejoin))) 11097 t->ljoin = 1; 11098 else 11099 t->ljoin = 0; 11100 if (number_less(internal_value (mp_miterlimit), unity_t)) { 11101 set_number_to_unity(t->miterlim); 11102 } else { 11103 number_clone(t->miterlim,internal_value (mp_miterlimit)); 11104 } 11105 return (mp_node) t; 11106} 11107 11108@ @c 11109static void mp_free_fill_node (MP mp, mp_fill_node p) { 11110 mp_toss_knot_list (mp, mp_path_p (p)); 11111 if (mp_pen_p (p) != NULL) 11112 mp_toss_knot_list (mp, mp_pen_p (p)); 11113 if (mp_pre_script (p) != NULL) 11114 delete_str_ref (mp_pre_script (p)); 11115 if (mp_post_script (p) != NULL) 11116 delete_str_ref (mp_post_script (p)); 11117 free_number(p->red); 11118 free_number(p->green); 11119 free_number(p->blue); 11120 free_number(p->black); 11121 free_number(p->miterlim); 11122 mp_free_node (mp, (mp_node)p, fill_node_size); 11123} 11124 11125 11126 11127@ A stroked path is represented by an eight-word node that is like a filled 11128contour node except that it contains the current \&{linecap} value, a scale 11129factor for the dash pattern, and a pointer that is non-NULL if the stroke 11130is to be dashed. The purpose of the scale factor is to allow a picture to 11131be transformed without touching the picture that |dash_p| points to. 11132 11133@d mp_dash_p(A) ((mp_stroked_node)(A))->dash_p_ /* a pointer to the edge structure that gives the dash pattern */ 11134 11135@<MPlib internal header stuff@>= 11136typedef struct mp_stroked_node_data { 11137 NODE_BODY; 11138 halfword color_model_; 11139 mp_number red; 11140 mp_number green; 11141 mp_number blue; 11142 mp_number black; 11143 mp_string pre_script_; 11144 mp_string post_script_; 11145 mp_knot path_p_; 11146 mp_knot pen_p_; 11147 unsigned char ljoin; 11148 mp_number miterlim; 11149 unsigned char lcap; 11150 mp_node dash_p_; 11151 mp_number dash_scale; 11152} mp_stroked_node_data; 11153typedef struct mp_stroked_node_data *mp_stroked_node; 11154 11155 11156@ @<Graphical object codes@>= 11157mp_stroked_code = 2, 11158 11159@ Make a stroked node for path |p| with |mp_pen_p(p)| temporarily |NULL|. 11160 11161@d stroked_node_size sizeof(struct mp_stroked_node_data) 11162 11163@c 11164static mp_node mp_new_stroked_node (MP mp, mp_knot p) { 11165 mp_stroked_node t = malloc_node (stroked_node_size); 11166 mp_type (t) = mp_stroked_node_type; 11167 mp_path_p (t) = p; 11168 mp_pen_p (t) = NULL; 11169 mp_dash_p (t) = NULL; 11170 new_number(t->dash_scale); 11171 set_number_to_unity(t->dash_scale); 11172 new_number(t->red); 11173 new_number(t->green); 11174 new_number(t->blue); 11175 new_number(t->black); 11176 new_number(t->miterlim); 11177 clear_color(t); 11178 mp_pre_script (t) = NULL; 11179 mp_post_script (t) = NULL; 11180 /* Set the |ljoin| and |miterlim| fields in object |t| */ 11181 if (number_greater(internal_value (mp_linejoin), unity_t)) 11182 t->ljoin = 2; 11183 else if (number_positive(internal_value (mp_linejoin))) 11184 t->ljoin = 1; 11185 else 11186 t->ljoin = 0; 11187 if (number_less(internal_value (mp_miterlimit), unity_t)) { 11188 set_number_to_unity(t->miterlim); 11189 } else { 11190 number_clone(t->miterlim,internal_value (mp_miterlimit)); 11191 } 11192 if (number_greater(internal_value (mp_linecap), unity_t)) 11193 t->lcap = 2; 11194 else if (number_positive(internal_value (mp_linecap))) 11195 t->lcap = 1; 11196 else 11197 t->lcap = 0; 11198 return (mp_node) t; 11199} 11200 11201@ @c 11202static mp_edge_header_node mp_free_stroked_node (MP mp, mp_stroked_node p) { 11203 mp_edge_header_node e = NULL; 11204 mp_toss_knot_list (mp, mp_path_p (p)); 11205 if (mp_pen_p (p) != NULL) 11206 mp_toss_knot_list (mp, mp_pen_p (p)); 11207 if (mp_pre_script (p) != NULL) 11208 delete_str_ref (mp_pre_script (p)); 11209 if (mp_post_script (p) != NULL) 11210 delete_str_ref (mp_post_script (p)); 11211 e = (mp_edge_header_node)mp_dash_p (p); 11212 free_number(p->dash_scale); 11213 free_number(p->red); 11214 free_number(p->green); 11215 free_number(p->blue); 11216 free_number(p->black); 11217 free_number(p->miterlim); 11218 mp_free_node (mp, (mp_node)p, stroked_node_size); 11219 return e; 11220} 11221 11222@ When a dashed line is computed in a transformed coordinate system, the dash 11223lengths get scaled like the pen shape and we need to compensate for this. Since 11224there is no unique scale factor for an arbitrary transformation, we use the 11225the square root of the determinant. The properties of the determinant make it 11226easier to maintain the |dash_scale|. The computation is fairly straight-forward 11227except for the initialization of the scale factor |s|. The factor of 64 is 11228needed because |square_rt| scales its result by $2^8$ while we need $2^{14}$ 11229to counteract the effect of |take_fraction|. 11230 11231@ @c 11232void mp_sqrt_det (MP mp, mp_number *ret, mp_number a_orig, mp_number b_orig, mp_number c_orig, mp_number d_orig) { 11233 mp_number a,b,c,d; 11234 mp_number maxabs; /* $max(|a|,|b|,|c|,|d|)$ */ 11235 unsigned s; /* amount by which the result of |square_rt| needs to be scaled */ 11236 new_number(a); 11237 new_number(b); 11238 new_number(c); 11239 new_number(d); 11240 new_number(maxabs); 11241 number_clone(a, a_orig); 11242 number_clone(b, b_orig); 11243 number_clone(c, c_orig); 11244 number_clone(d, d_orig); 11245 /* Initialize |maxabs| */ 11246 { 11247 mp_number tmp; 11248 new_number (tmp); 11249 number_clone(maxabs, a); 11250 number_abs(maxabs); 11251 number_clone(tmp, b); 11252 number_abs(tmp); 11253 if (number_greater(tmp, maxabs)) 11254 number_clone(maxabs, tmp); 11255 number_clone(tmp, c); 11256 number_abs(tmp); 11257 if (number_greater(tmp, maxabs)) 11258 number_clone(maxabs, tmp); 11259 number_clone(tmp, d); 11260 number_abs(tmp); 11261 if (number_greater(tmp, maxabs)) 11262 number_clone(maxabs, tmp); 11263 free_number(tmp); 11264 } 11265 11266 11267 s = 64; 11268 while ((number_less(maxabs, fraction_one_t)) && (s > 1)) { 11269 number_double(a); 11270 number_double(b); 11271 number_double(c); 11272 number_double(d); 11273 number_double(maxabs); 11274 s = s/2; 11275 } 11276 { 11277 mp_number r1, r2; 11278 new_fraction (r1); 11279 new_fraction (r2); 11280 take_fraction (r1, a, d); 11281 take_fraction (r2, b, c); 11282 number_substract (r1, r2); 11283 number_abs (r1); 11284 square_rt(*ret, r1); 11285 number_multiply_int(*ret, s); 11286 free_number (r1); 11287 free_number (r2); 11288 } 11289 free_number(a); 11290 free_number(b); 11291 free_number(c); 11292 free_number(d); 11293 free_number(maxabs); 11294} 11295@# 11296static void mp_get_pen_scale (MP mp, mp_number *ret, mp_knot p) { 11297 if (p == NULL) { 11298 set_number_to_zero(*ret); 11299 } else { 11300 mp_number a,b,c,d; 11301 new_number(a); 11302 new_number(b); 11303 new_number(c); 11304 new_number(d); 11305 set_number_from_substraction(a, p->left_x, p->x_coord); 11306 set_number_from_substraction(b, p->right_x, p->x_coord); 11307 set_number_from_substraction(c, p->left_y, p->y_coord); 11308 set_number_from_substraction(d, p->right_y, p->y_coord); 11309 mp_sqrt_det (mp, ret, a, b, c, d); 11310 free_number(a); 11311 free_number(b); 11312 free_number(c); 11313 free_number(d); 11314 } 11315} 11316 11317 11318@ @<Declarations@>= 11319static void mp_sqrt_det (MP mp, mp_number *ret, mp_number a, mp_number b, mp_number c, mp_number d); 11320 11321@ When a picture contains text, this is represented by a fourteen-word node 11322where the color information and |type| and |link| fields are augmented by 11323additional fields that describe the text and how it is transformed. 11324The |path_p| and |mp_pen_p| pointers are replaced by a number that identifies 11325the font and a string number that gives the text to be displayed. 11326The |width|, |height|, and |depth| fields 11327give the dimensions of the text at its design size, and the remaining six 11328words give a transformation to be applied to the text. The |new_text_node| 11329function initializes everything to default values so that the text comes out 11330black with its reference point at the origin. 11331 11332@d mp_text_p(A) ((mp_text_node)(A))->text_p_ /* a string pointer for the text to display */ 11333@d mp_font_n(A) ((mp_text_node)(A))->font_n_ /* the font number */ 11334 11335@<MPlib internal header stuff@>= 11336typedef struct mp_text_node_data { 11337 NODE_BODY; 11338 halfword color_model_; 11339 mp_number red; 11340 mp_number green; 11341 mp_number blue; 11342 mp_number black; 11343 mp_string pre_script_; 11344 mp_string post_script_; 11345 mp_string text_p_; 11346 halfword font_n_; 11347 mp_number width; 11348 mp_number height; 11349 mp_number depth; 11350 mp_number tx; 11351 mp_number ty; 11352 mp_number txx; 11353 mp_number txy; 11354 mp_number tyx; 11355 mp_number tyy; 11356} mp_text_node_data; 11357typedef struct mp_text_node_data *mp_text_node; 11358 11359@ @<Graphical object codes@>= 11360mp_text_code = 3, 11361 11362@ Make a text node for font |f| and text string |s|. 11363 11364@d text_node_size sizeof(struct mp_text_node_data) 11365 11366@c 11367static mp_node mp_new_text_node (MP mp, char *f, mp_string s) { 11368 mp_text_node t = malloc_node (text_node_size); 11369 mp_type (t) = mp_text_node_type; 11370 mp_text_p (t) = s; 11371 add_str_ref(s); 11372 mp_font_n (t) = (halfword) mp_find_font (mp, f); /* this identifies the font */ 11373 new_number(t->red); 11374 new_number(t->green); 11375 new_number(t->blue); 11376 new_number(t->black); 11377 new_number(t->width); 11378 new_number(t->height); 11379 new_number(t->depth); 11380 clear_color (t); 11381 mp_pre_script (t) = NULL; 11382 mp_post_script (t) = NULL; 11383 new_number(t->tx); 11384 new_number(t->ty); 11385 new_number(t->txx); 11386 new_number(t->txy); 11387 new_number(t->tyx); 11388 new_number(t->tyy); 11389 /* |tx_val (t) = 0; ty_val (t) = 0;| */ 11390 /* |txy_val (t) = 0; tyx_val (t) = 0;| */ 11391 set_number_to_unity(t->txx); 11392 set_number_to_unity(t->tyy); 11393 mp_set_text_box (mp, t); /* this finds the bounding box */ 11394 return (mp_node) t; 11395} 11396 11397@ @c 11398static void mp_free_text_node (MP mp, mp_text_node p) { 11399 /* |delete_str_ref (mp_text_p (p));| */ /* gives errors */ 11400 if (mp_pre_script (p) != NULL) 11401 delete_str_ref (mp_pre_script (p)); 11402 if (mp_post_script (p) != NULL) 11403 delete_str_ref (mp_post_script (p)); 11404 free_number(p->red); 11405 free_number(p->green); 11406 free_number(p->blue); 11407 free_number(p->black); 11408 free_number(p->width); 11409 free_number(p->height); 11410 free_number(p->depth); 11411 free_number(p->tx); 11412 free_number(p->ty); 11413 free_number(p->txx); 11414 free_number(p->txy); 11415 free_number(p->tyx); 11416 free_number(p->tyy); 11417 mp_free_node (mp, (mp_node)p, text_node_size); 11418} 11419 11420@ The last two types of graphical objects that can occur in an edge structure 11421are clipping paths and \&{setbounds} paths. These are slightly more difficult 11422@:set_bounds_}{\&{setbounds} primitive@> 11423to implement because we must keep track of exactly what is being clipped or 11424bounded when pictures get merged together. For this reason, each clipping or 11425\&{setbounds} operation is represented by a pair of nodes: first comes a 11426node whose |path_p| gives the relevant path, then there is the list 11427of objects to clip or bound followed by a closing node. 11428 11429@d has_color(A) (mp_type((A))<mp_start_clip_node_type) 11430 /* does a graphical object have color fields? */ 11431@d has_pen(A) (mp_type((A))<mp_text_node_type) 11432 /* does a graphical object have a |mp_pen_p| field? */ 11433@d is_start_or_stop(A) (mp_type((A))>=mp_start_clip_node_type) 11434@d is_stop(A) (mp_type((A))>=mp_stop_clip_node_type) 11435 11436@<MPlib internal header stuff@>= 11437typedef struct mp_start_clip_node_data { 11438 NODE_BODY; 11439 mp_knot path_p_; 11440} mp_start_clip_node_data; 11441typedef struct mp_start_clip_node_data *mp_start_clip_node; 11442typedef struct mp_start_bounds_node_data { 11443 NODE_BODY; 11444 mp_knot path_p_; 11445} mp_start_bounds_node_data; 11446typedef struct mp_start_bounds_node_data *mp_start_bounds_node; 11447typedef struct mp_stop_clip_node_data { 11448 NODE_BODY; 11449} mp_stop_clip_node_data; 11450typedef struct mp_stop_clip_node_data *mp_stop_clip_node; 11451typedef struct mp_stop_bounds_node_data { 11452 NODE_BODY; 11453} mp_stop_bounds_node_data; 11454typedef struct mp_stop_bounds_node_data *mp_stop_bounds_node; 11455 11456 11457@ @<Graphical object codes@>= 11458mp_start_clip_code = 4, /* |type| of a node that starts clipping */ 11459 mp_start_bounds_code = 5, /* |type| of a node that gives a \&{setbounds} path */ 11460 mp_stop_clip_code = 6, /* |type| of a node that stops clipping */ 11461 mp_stop_bounds_code = 7, /* |type| of a node that stops \&{setbounds} */ 11462 11463 11464@ 11465 11466@d start_clip_size sizeof(struct mp_start_clip_node_data) 11467@d stop_clip_size sizeof(struct mp_stop_clip_node_data) 11468@d start_bounds_size sizeof(struct mp_start_bounds_node_data) 11469@d stop_bounds_size sizeof(struct mp_stop_bounds_node_data) 11470 11471@c 11472static mp_node mp_new_bounds_node (MP mp, mp_knot p, quarterword c) { 11473 /* make a node of type |c| where |p| is the clipping or \&{setbounds} path */ 11474 if (c == mp_start_clip_node_type) { 11475 mp_start_clip_node t; /* the new node */ 11476 t = (mp_start_clip_node) malloc_node (start_clip_size); 11477 t->path_p_ = p; 11478 mp_type (t) = c; 11479 t->link = NULL; 11480 return (mp_node) t; 11481 } else if (c == mp_start_bounds_node_type) { 11482 mp_start_bounds_node t; /* the new node */ 11483 t = (mp_start_bounds_node) malloc_node (start_bounds_size); 11484 t->path_p_ = p; 11485 mp_type (t) = c; 11486 t->link = NULL; 11487 return (mp_node) t; 11488 } else if (c == mp_stop_clip_node_type) { 11489 mp_stop_clip_node t; /* the new node */ 11490 t = (mp_stop_clip_node) malloc_node (stop_clip_size); 11491 mp_type (t) = c; 11492 t->link = NULL; 11493 return (mp_node) t; 11494 } else if (c == mp_stop_bounds_node_type) { 11495 mp_stop_bounds_node t; /* the new node */ 11496 t = (mp_stop_bounds_node) malloc_node (stop_bounds_size); 11497 mp_type (t) = c; 11498 t->link = NULL; 11499 return (mp_node) t; 11500 } else { 11501 assert (0); 11502 } 11503 return NULL; 11504} 11505 11506 11507@ @c 11508static void mp_free_start_clip_node (MP mp, mp_start_clip_node p) { 11509 mp_toss_knot_list (mp, mp_path_p (p)); 11510 mp_free_node (mp, (mp_node)p, start_clip_size); 11511} 11512static void mp_free_start_bounds_node (MP mp, mp_start_bounds_node p) { 11513 mp_toss_knot_list (mp, mp_path_p (p)); 11514 mp_free_node (mp, (mp_node)p, start_bounds_size); 11515} 11516static void mp_free_stop_clip_node (MP mp, mp_stop_clip_node p) { 11517 mp_free_node (mp, (mp_node)p, stop_clip_size); 11518} 11519static void mp_free_stop_bounds_node (MP mp, mp_stop_bounds_node p) { 11520 mp_free_node (mp, (mp_node)p, stop_bounds_size); 11521} 11522 11523 11524@ All the essential information in an edge structure is encoded as a linked list 11525of graphical objects as we have just seen, but it is helpful to add some 11526redundant information. A single edge structure might be used as a dash pattern 11527many times, and it would be nice to avoid scanning the same structure 11528repeatedly. Thus, an edge structure known to be a suitable dash pattern 11529has a header that gives a list of dashes in a sorted order designed for rapid 11530translation into \ps. 11531 11532Each dash is represented by a three-word node containing the initial and final 11533$x$~coordinates as well as the usual |link| field. The |link| fields points to 11534the dash node with the next higher $x$-coordinates and the final link points 11535to a special location called |null_dash|. (There should be no overlap between 11536dashes). Since the $y$~coordinate of the dash pattern is needed to determine 11537the period of repetition, this needs to be stored in the edge header along 11538with a pointer to the list of dash nodes. 11539 11540The |dash_info| is explained below. 11541 11542@d dash_list(A) (mp_dash_node)(((mp_dash_node)(A))->link) /* in an edge header this points to the first dash node */ 11543@d set_dash_list(A,B) ((mp_dash_node)(A))->link=(mp_node)((B)) /* in an edge header this points to the first dash node */ 11544 11545@<MPlib internal header stuff@>= 11546typedef struct mp_dash_node_data { 11547 NODE_BODY; 11548 mp_number start_x; /* the starting $x$~coordinate in a dash node */ 11549 mp_number stop_x; /* the ending $x$~coordinate in a dash node */ 11550 mp_number dash_y; /* $y$ value for the dash list in an edge header */ 11551 mp_node dash_info_; 11552} mp_dash_node_data; 11553 11554@ @<Types...@>= 11555typedef struct mp_dash_node_data *mp_dash_node; 11556 11557@ @<Initialize table entries@>= 11558mp->null_dash = mp_get_dash_node (mp); 11559 11560@ @<Free table entries@>= 11561mp_free_node (mp, (mp_node)mp->null_dash, dash_node_size); 11562 11563@ 11564@d dash_node_size sizeof(struct mp_dash_node_data) 11565 11566@c 11567static mp_dash_node mp_get_dash_node (MP mp) { 11568 mp_dash_node p = (mp_dash_node) malloc_node (dash_node_size); 11569 p->has_number = 0; 11570 new_number(p->start_x); 11571 new_number(p->stop_x); 11572 new_number(p->dash_y); 11573 mp_type (p) = mp_dash_node_type; 11574 return p; 11575} 11576 11577 11578@ It is also convenient for an edge header to contain the bounding 11579box information needed by the \&{llcorner} and \&{urcorner} operators 11580so that this does not have to be recomputed unnecessarily. This is done by 11581adding fields for the $x$~and $y$ extremes as well as a pointer that indicates 11582how far the bounding box computation has gotten. Thus if the user asks for 11583the bounding box and then adds some more text to the picture before asking 11584for more bounding box information, the second computation need only look at 11585the additional text. 11586 11587When the bounding box has not been computed, the |bblast| pointer points 11588to a dummy link at the head of the graphical object list while the |minx_val| 11589and |miny_val| fields contain |EL_GORDO| and the |maxx_val| and |maxy_val| 11590fields contain |-EL_GORDO|. 11591 11592Since the bounding box of pictures containing objects of type 11593|mp_start_bounds_node| depends on the value of \&{truecorners}, the bounding box 11594@:mp_true_corners_}{\&{truecorners} primitive@> 11595data might not be valid for all values of this parameter. Hence, the |bbtype| 11596field is needed to keep track of this. 11597 11598@d bblast(A) ((mp_edge_header_node)(A))->bblast_ /* last item considered in bounding box computation */ 11599@d edge_list(A) ((mp_edge_header_node)(A))->list_ /* where the object list begins in an edge header */ 11600 11601@<MPlib internal header stuff@>= 11602typedef struct mp_edge_header_node_data { 11603 NODE_BODY; 11604 mp_number start_x; 11605 mp_number stop_x; 11606 mp_number dash_y; 11607 mp_node dash_info_; 11608 mp_number minx; 11609 mp_number miny; 11610 mp_number maxx; 11611 mp_number maxy; 11612 mp_node bblast_; 11613 int bbtype; /* tells how bounding box data depends on \&{truecorners} */ 11614 mp_node list_; 11615 mp_node obj_tail_; /* explained below */ 11616 halfword ref_count_; /* explained below */ 11617} mp_edge_header_node_data; 11618typedef struct mp_edge_header_node_data *mp_edge_header_node; 11619 11620@ 11621@d no_bounds 0 /* |bbtype| value when bounding box data is valid for all \&{truecorners} values */ 11622@d bounds_set 1 /* |bbtype| value when bounding box data is for \&{truecorners}${}\le 0$ */ 11623@d bounds_unset 2 /* |bbtype| value when bounding box data is for \&{truecorners}${}>0$ */ 11624@c 11625static void mp_init_bbox (MP mp, mp_edge_header_node h) { 11626 /* Initialize the bounding box information in edge structure |h| */ 11627 (void) mp; 11628 bblast (h) = edge_list (h); 11629 h->bbtype = no_bounds; 11630 set_number_to_inf(h->minx); 11631 set_number_to_inf(h->miny); 11632 set_number_to_neg_inf(h->maxx); 11633 set_number_to_neg_inf(h->maxy); 11634} 11635 11636 11637@ The only other entries in an edge header are a reference count in the first 11638word and a pointer to the tail of the object list in the last word. 11639 11640@d obj_tail(A) ((mp_edge_header_node)(A))->obj_tail_ /* points to the last entry in the object list */ 11641@d edge_ref_count(A) ((mp_edge_header_node)(A))->ref_count_ 11642 11643@d edge_header_size sizeof(struct mp_edge_header_node_data) 11644 11645@c 11646static mp_edge_header_node mp_get_edge_header_node (MP mp) { 11647 mp_edge_header_node p = (mp_edge_header_node) malloc_node (edge_header_size); 11648 mp_type (p) = mp_edge_header_node_type; 11649 new_number(p->start_x); 11650 new_number(p->stop_x); 11651 new_number(p->dash_y); 11652 new_number(p->minx); 11653 new_number(p->miny); 11654 new_number(p->maxx); 11655 new_number(p->maxy); 11656 p->list_ = mp_get_token_node (mp); /* or whatever, just a need a link handle */ 11657 return p; 11658} 11659static void mp_init_edges (MP mp, mp_edge_header_node h) { 11660 /* initialize an edge header to NULL values */ 11661 set_dash_list (h, mp->null_dash); 11662 obj_tail (h) = edge_list (h); 11663 mp_link (edge_list (h)) = NULL; 11664 edge_ref_count (h) = 0; 11665 mp_init_bbox (mp, h); 11666} 11667 11668 11669@ Here is how edge structures are deleted. The process can be recursive because 11670of the need to dereference edge structures that are used as dash patterns. 11671@^recursion@> 11672 11673@d add_edge_ref(A) incr(edge_ref_count((A))) 11674@d delete_edge_ref(A) { 11675 if ( edge_ref_count((A))==0 ) 11676 mp_toss_edges(mp, (mp_edge_header_node)(A)); 11677 else 11678 decr(edge_ref_count((A))); 11679 } 11680 11681@<Declarations@>= 11682static void mp_flush_dash_list (MP mp, mp_edge_header_node h); 11683static mp_edge_header_node mp_toss_gr_object (MP mp, mp_node p); 11684static void mp_toss_edges (MP mp, mp_edge_header_node h); 11685 11686@ @c 11687void mp_toss_edges (MP mp, mp_edge_header_node h) { 11688 mp_node p, q; /* pointers that scan the list being recycled */ 11689 mp_edge_header_node r; /* an edge structure that object |p| refers to */ 11690 mp_flush_dash_list (mp, h); 11691 q = mp_link (edge_list (h)); 11692 while ((q != NULL)) { 11693 p = q; 11694 q = mp_link (q); 11695 r = mp_toss_gr_object (mp, p); 11696 if (r != NULL) 11697 delete_edge_ref (r); 11698 } 11699 free_number(h->start_x); 11700 free_number(h->stop_x); 11701 free_number(h->dash_y); 11702 free_number(h->minx); 11703 free_number(h->miny); 11704 free_number(h->maxx); 11705 free_number(h->maxy); 11706 mp_free_token_node (mp, h->list_); 11707 mp_free_node (mp, (mp_node)h, edge_header_size); 11708} 11709void mp_flush_dash_list (MP mp, mp_edge_header_node h) { 11710 mp_dash_node p, q; /* pointers that scan the list being recycled */ 11711 q = dash_list (h); 11712 while (q != mp->null_dash) { /* todo: NULL check should not be needed */ 11713 p = q; 11714 q = (mp_dash_node)mp_link (q); 11715 mp_free_node (mp, (mp_node)p, dash_node_size); 11716 } 11717 set_dash_list (h,mp->null_dash); 11718} 11719mp_edge_header_node mp_toss_gr_object (MP mp, mp_node p) { 11720 /* returns an edge structure that needs to be dereferenced */ 11721 mp_edge_header_node e = NULL; /* the edge structure to return */ 11722 switch (mp_type (p)) { 11723 case mp_fill_node_type: 11724 mp_free_fill_node (mp, (mp_fill_node)p); 11725 break; 11726 case mp_stroked_node_type: 11727 e = mp_free_stroked_node (mp, (mp_stroked_node)p); 11728 break; 11729 case mp_text_node_type: 11730 mp_free_text_node(mp, (mp_text_node)p); 11731 break; 11732 case mp_start_clip_node_type: 11733 mp_free_start_clip_node(mp, (mp_start_clip_node)p); 11734 break; 11735 case mp_start_bounds_node_type: 11736 mp_free_start_bounds_node(mp, (mp_start_bounds_node)p); 11737 break; 11738 case mp_stop_clip_node_type: 11739 mp_free_stop_clip_node(mp, (mp_stop_clip_node)p); 11740 break; 11741 case mp_stop_bounds_node_type: 11742 mp_free_stop_bounds_node(mp, (mp_stop_bounds_node)p); 11743 break; 11744 default: /* there are no other valid cases, but please the compiler */ 11745 break; 11746 } 11747 return e; 11748} 11749 11750 11751@ If we use |add_edge_ref| to ``copy'' edge structures, the real copying needs 11752to be done before making a significant change to an edge structure. Much of 11753the work is done in a separate routine |copy_objects| that copies a list of 11754graphical objects into a new edge header. 11755 11756@c 11757static mp_edge_header_node mp_private_edges (MP mp, mp_edge_header_node h) { 11758 /* make a private copy of the edge structure headed by |h| */ 11759 mp_edge_header_node hh; /* the edge header for the new copy */ 11760 mp_dash_node p, pp; /* pointers for copying the dash list */ 11761 assert (mp_type (h) == mp_edge_header_node_type); 11762 if (edge_ref_count (h) == 0) { 11763 return h; 11764 } else { 11765 decr (edge_ref_count (h)); 11766 hh = (mp_edge_header_node)mp_copy_objects (mp, mp_link (edge_list (h)), NULL); 11767 @<Copy the dash list from |h| to |hh|@>; 11768 @<Copy the bounding box information from |h| to |hh| and make |bblast(hh)| 11769 point into the new object list@>; 11770 return hh; 11771 } 11772} 11773 11774 11775@ Here we use the fact that |dash_list(hh)=mp_link(hh)|. 11776@^data structure assumptions@> 11777 11778@<Copy the dash list from |h| to |hh|@>= 11779pp = (mp_dash_node)hh; 11780p = dash_list (h); 11781while ((p != mp->null_dash)) { 11782 mp_link (pp) = (mp_node)mp_get_dash_node (mp); 11783 pp = (mp_dash_node)mp_link (pp); 11784 number_clone(pp->start_x, p->start_x); 11785 number_clone(pp->stop_x, p->stop_x); 11786 p = (mp_dash_node)mp_link (p); 11787} 11788mp_link (pp) = (mp_node)mp->null_dash; 11789number_clone(hh->dash_y, h->dash_y ) 11790 11791 11792@ |h| is an edge structure 11793 11794@c 11795static mp_dash_object *mp_export_dashes (MP mp, mp_stroked_node q, mp_number w) { 11796 mp_dash_object *d; 11797 mp_dash_node p, h; 11798 mp_number scf; /* scale factor */ 11799 mp_number dashoff; 11800 double *dashes = NULL; 11801 int num_dashes = 1; 11802 h = (mp_dash_node)mp_dash_p (q); 11803 if (h == NULL || dash_list (h) == mp->null_dash) 11804 return NULL; 11805 new_number (scf); 11806 p = dash_list (h); 11807 mp_get_pen_scale (mp, &scf, mp_pen_p (q)); 11808 if (number_zero(scf)) { 11809 if (number_zero(w)) { 11810 number_clone(scf, q->dash_scale); 11811 } else { 11812 free_number(scf); 11813 return NULL; 11814 } 11815 } else { 11816 mp_number ret; 11817 new_number (ret); 11818 make_scaled (ret, w, scf); 11819 take_scaled (scf, ret, q->dash_scale); 11820 free_number (ret); 11821 } 11822 number_clone(w, scf); 11823 d = xmalloc (1, sizeof (mp_dash_object)); 11824 add_var_used (sizeof (mp_dash_object)); 11825 set_number_from_addition(mp->null_dash->start_x, p->start_x, h->dash_y); 11826 { 11827 mp_number ret, arg1; 11828 new_number (ret); 11829 new_number (arg1); 11830 new_number (dashoff); 11831 while (p != mp->null_dash) { 11832 dashes = xrealloc (dashes, (num_dashes + 2), sizeof (double)); 11833 set_number_from_substraction (arg1, p->stop_x, p->start_x); 11834 take_scaled (ret, arg1, scf); 11835 dashes[(num_dashes - 1)] = number_to_double (ret); 11836 set_number_from_substraction (arg1, ((mp_dash_node)mp_link (p))->start_x, p->stop_x); 11837 take_scaled (ret, arg1, scf); 11838 dashes[(num_dashes)] = number_to_double (ret); 11839 dashes[(num_dashes + 1)] = -1.0; /* terminus */ 11840 num_dashes += 2; 11841 p = (mp_dash_node)mp_link (p); 11842 } 11843 d->array = dashes; 11844 mp_dash_offset (mp, &dashoff, h); 11845 take_scaled (ret, dashoff, scf); 11846 d->offset = number_to_double(ret); 11847 free_number (ret); 11848 free_number (arg1); 11849 } 11850 free_number (dashoff); 11851 free_number(scf); 11852 return d; 11853} 11854 11855 11856@ @<Copy the bounding box information from |h| to |hh|...@>= 11857number_clone(hh->minx, h->minx); 11858number_clone(hh->miny, h->miny); 11859number_clone(hh->maxx, h->maxx); 11860number_clone(hh->maxy, h->maxy); 11861hh->bbtype = h->bbtype; 11862p = (mp_dash_node)edge_list (h); 11863pp = (mp_dash_node)edge_list (hh); 11864while ((p != (mp_dash_node)bblast (h))) { 11865 if (p == NULL) 11866 mp_confusion (mp, "bblast"); 11867@:this can't happen bblast}{\quad bblast@>; 11868 p = (mp_dash_node)mp_link (p); 11869 pp = (mp_dash_node)mp_link (pp); 11870} 11871bblast (hh) = (mp_node)pp 11872 11873@ Here is the promised routine for copying graphical objects into a new edge 11874structure. It starts copying at object~|p| and stops just before object~|q|. 11875If |q| is NULL, it copies the entire sublist headed at |p|. The resulting edge 11876structure requires further initialization by |init_bbox|. 11877 11878@<Declarations@>= 11879static mp_edge_header_node mp_copy_objects (MP mp, mp_node p, mp_node q); 11880 11881@ @c 11882mp_edge_header_node mp_copy_objects (MP mp, mp_node p, mp_node q) { 11883 mp_edge_header_node hh; /* the new edge header */ 11884 mp_node pp; /* the last newly copied object */ 11885 quarterword k = 0; /* temporary register */ 11886 hh = mp_get_edge_header_node (mp); 11887 set_dash_list (hh, mp->null_dash); 11888 edge_ref_count (hh) = 0; 11889 pp = edge_list (hh); 11890 while (p != q) { 11891 @<Make |mp_link(pp)| point to a copy of object |p|, and update |p| and |pp|@>; 11892 } 11893 obj_tail (hh) = pp; 11894 mp_link (pp) = NULL; 11895 return hh; 11896} 11897 11898 11899@ @<Make |mp_link(pp)| point to a copy of object |p|, and update |p| and |pp|@>= 11900{ 11901 switch (mp_type (p)) { 11902 case mp_start_clip_node_type: 11903 k = start_clip_size; 11904 break; 11905 case mp_start_bounds_node_type: 11906 k = start_bounds_size; 11907 break; 11908 case mp_fill_node_type: 11909 k = fill_node_size; 11910 break; 11911 case mp_stroked_node_type: 11912 k = stroked_node_size; 11913 break; 11914 case mp_text_node_type: 11915 k = text_node_size; 11916 break; 11917 case mp_stop_clip_node_type: 11918 k = stop_clip_size; 11919 break; 11920 case mp_stop_bounds_node_type: 11921 k = stop_bounds_size; 11922 break; 11923 default: /* there are no other valid cases, but please the compiler */ 11924 break; 11925 } 11926 mp_link (pp) = malloc_node ((size_t) k); /* |gr_object| */ 11927 pp = mp_link (pp); 11928 memcpy (pp, p, (size_t) k); 11929 pp->link = NULL; 11930 @<Fix anything in graphical object |pp| that should differ from the 11931 corresponding field in |p|@>; 11932 p = mp_link (p); 11933} 11934 11935 11936@ @<Fix anything in graphical object |pp| that should differ from the...@>= 11937switch (mp_type (p)) { 11938case mp_start_clip_node_type: 11939 { 11940 mp_start_clip_node tt = (mp_start_clip_node)pp; 11941 mp_start_clip_node t = (mp_start_clip_node)p; 11942 mp_path_p (tt) = mp_copy_path (mp, mp_path_p (t)); 11943 } 11944 break; 11945case mp_start_bounds_node_type: 11946 { 11947 mp_start_bounds_node tt = (mp_start_bounds_node)pp; 11948 mp_start_bounds_node t = (mp_start_bounds_node)p; 11949 mp_path_p (tt) = mp_copy_path (mp, mp_path_p (t)); 11950 } 11951 break; 11952case mp_fill_node_type: 11953 { 11954 mp_fill_node tt = (mp_fill_node)pp; 11955 mp_fill_node t = (mp_fill_node)p; 11956 new_number(tt->red); number_clone(tt->red, t->red); 11957 new_number(tt->green); number_clone(tt->green, t->green); 11958 new_number(tt->blue); number_clone(tt->blue, t->blue); 11959 new_number(tt->black); number_clone(tt->black, t->black); 11960 new_number(tt->miterlim); number_clone(tt->miterlim,t->miterlim); 11961 mp_path_p (tt) = mp_copy_path (mp, mp_path_p (t)); 11962 if (mp_pre_script (p) != NULL) 11963 add_str_ref (mp_pre_script (p)); 11964 if (mp_post_script (p) != NULL) 11965 add_str_ref (mp_post_script (p)); 11966 if (mp_pen_p (t) != NULL) 11967 mp_pen_p (tt) = copy_pen (mp_pen_p (t)); 11968 } 11969 break; 11970case mp_stroked_node_type: 11971 { 11972 mp_stroked_node tt = (mp_stroked_node)pp; 11973 mp_stroked_node t = (mp_stroked_node)p; 11974 new_number(tt->red); number_clone(tt->red, t->red); 11975 new_number(tt->green); number_clone(tt->green, t->green); 11976 new_number(tt->blue); number_clone(tt->blue, t->blue); 11977 new_number(tt->black); number_clone(tt->black, t->black); 11978 new_number(tt->miterlim); number_clone(tt->miterlim,t->miterlim); 11979 new_number(tt->dash_scale); number_clone(tt->dash_scale,t->dash_scale); 11980 if (mp_pre_script (p) != NULL) 11981 add_str_ref (mp_pre_script (p)); 11982 if (mp_post_script (p) != NULL) 11983 add_str_ref (mp_post_script (p)); 11984 mp_path_p (tt) = mp_copy_path (mp, mp_path_p (t)); 11985 mp_pen_p (tt) = copy_pen (mp_pen_p (t)); 11986 if (mp_dash_p (p) != NULL) 11987 add_edge_ref (mp_dash_p (pp)); 11988 } 11989 break; 11990case mp_text_node_type: 11991 { 11992 mp_text_node tt = (mp_text_node)pp; 11993 mp_text_node t = (mp_text_node)p; 11994 new_number(tt->red); number_clone(tt->red, t->red); 11995 new_number(tt->green); number_clone(tt->green, t->green); 11996 new_number(tt->blue); number_clone(tt->blue, t->blue); 11997 new_number(tt->black); number_clone(tt->black, t->black); 11998 new_number(tt->width); number_clone(tt->width, t->width); 11999 new_number(tt->height); number_clone(tt->height, t->height); 12000 new_number(tt->depth); number_clone(tt->depth, t->depth); 12001 new_number(tt->tx); number_clone(tt->tx, t->tx); 12002 new_number(tt->ty); number_clone(tt->ty, t->ty); 12003 new_number(tt->txx); number_clone(tt->txx, t->txx); 12004 new_number(tt->tyx); number_clone(tt->tyx, t->tyx); 12005 new_number(tt->txy); number_clone(tt->txy, t->txy); 12006 new_number(tt->tyy); number_clone(tt->tyy, t->tyy); 12007 if (mp_pre_script (p) != NULL) 12008 add_str_ref (mp_pre_script (p)); 12009 if (mp_post_script (p) != NULL) 12010 add_str_ref (mp_post_script (p)); 12011 add_str_ref (mp_text_p (pp)); 12012 } 12013 break; 12014case mp_stop_clip_node_type: 12015case mp_stop_bounds_node_type: 12016 break; 12017default: /* there are no other valid cases, but please the compiler */ 12018 break; 12019} 12020 12021 12022@ Here is one way to find an acceptable value for the second argument to 12023|copy_objects|. Given a non-NULL graphical object list, |skip_1component| 12024skips past one picture component, where a ``picture component'' is a single 12025graphical object, or a start bounds or start clip object and everything up 12026through the matching stop bounds or stop clip object. 12027 12028@c 12029static mp_node mp_skip_1component (MP mp, mp_node p) { 12030 integer lev; /* current nesting level */ 12031 lev = 0; 12032 (void) mp; 12033 do { 12034 if (is_start_or_stop (p)) { 12035 if (is_stop (p)) 12036 decr (lev); 12037 else 12038 incr (lev); 12039 } 12040 p = mp_link (p); 12041 } while (lev != 0); 12042 return p; 12043} 12044 12045 12046@ Here is a diagnostic routine for printing an edge structure in symbolic form. 12047 12048@<Declarations@>= 12049static void mp_print_edges (MP mp, mp_node h, const char *s, boolean nuline); 12050 12051@ @c 12052void mp_print_edges (MP mp, mp_node h, const char *s, boolean nuline) { 12053 mp_node p; /* a graphical object to be printed */ 12054 mp_number scf; /* a scale factor for the dash pattern */ 12055 boolean ok_to_dash; /* |false| for polygonal pen strokes */ 12056 new_number (scf); 12057 mp_print_diagnostic (mp, "Edge structure", s, nuline); 12058 p = edge_list (h); 12059 while (mp_link (p) != NULL) { 12060 p = mp_link (p); 12061 mp_print_ln (mp); 12062 switch (mp_type (p)) { 12063 @<Cases for printing graphical object node |p|@>; 12064 default: 12065 mp_print (mp, "[unknown object type!]"); 12066 break; 12067 } 12068 } 12069 mp_print_nl (mp, "End edges"); 12070 if (p != obj_tail (h)) 12071 mp_print (mp, "?"); 12072@.End edges?@>; 12073 mp_end_diagnostic (mp, true); 12074 free_number (scf); 12075} 12076 12077 12078@ @<Cases for printing graphical object node |p|@>= 12079case mp_fill_node_type: 12080mp_print (mp, "Filled contour "); 12081mp_print_obj_color (mp, p); 12082mp_print_char (mp, xord (':')); 12083mp_print_ln (mp); 12084mp_pr_path (mp, mp_path_p ((mp_fill_node) p)); 12085mp_print_ln (mp); 12086if ((mp_pen_p ((mp_fill_node) p) != NULL)) { 12087 @<Print join type for graphical object |p|@>; 12088 mp_print (mp, " with pen"); 12089 mp_print_ln (mp); 12090 mp_pr_pen (mp, mp_pen_p ((mp_fill_node) p)); 12091} 12092break; 12093 12094@ @<Print join type for graphical object |p|@>= 12095switch (((mp_stroked_node)p)->ljoin) { 12096case 0: 12097 mp_print (mp, "mitered joins limited "); 12098 print_number (((mp_stroked_node)p)->miterlim); 12099 break; 12100case 1: 12101 mp_print (mp, "round joins"); 12102 break; 12103case 2: 12104 mp_print (mp, "beveled joins"); 12105 break; 12106default: 12107 mp_print (mp, "?? joins"); 12108@.??@>; 12109 break; 12110} 12111 12112 12113@ For stroked nodes, we need to print |lcap_val(p)| as well. 12114 12115@<Print join and cap types for stroked node |p|@>= 12116switch (((mp_stroked_node)p)->lcap ) { 12117case 0: 12118 mp_print (mp, "butt"); 12119 break; 12120case 1: 12121 mp_print (mp, "round"); 12122 break; 12123case 2: 12124 mp_print (mp, "square"); 12125 break; 12126default: 12127 mp_print (mp, "??"); 12128 break; 12129@.??@> 12130} 12131mp_print (mp, " ends, "); 12132@<Print join type for graphical object |p|@> 12133 12134 12135@ Here is a routine that prints the color of a graphical object if it isn't 12136black (the default color). 12137 12138@<Declarations@>= 12139static void mp_print_obj_color (MP mp, mp_node p); 12140 12141@ @c 12142void mp_print_obj_color (MP mp, mp_node p) { 12143 mp_stroked_node p0 = (mp_stroked_node) p; 12144 if (mp_color_model (p) == mp_grey_model) { 12145 if (number_positive(p0->grey)) { 12146 mp_print (mp, "greyed "); 12147 mp_print_char (mp, xord ('(')); 12148 print_number (p0->grey); 12149 mp_print_char (mp, xord (')')); 12150 }; 12151 } else if (mp_color_model (p) == mp_cmyk_model) { 12152 if (number_positive(p0->cyan) || number_positive(p0->magenta) || 12153 number_positive(p0->yellow) || number_positive(p0->black)) { 12154 mp_print (mp, "processcolored "); 12155 mp_print_char (mp, xord ('(')); 12156 print_number (p0->cyan); 12157 mp_print_char (mp, xord (',')); 12158 print_number (p0->magenta); 12159 mp_print_char (mp, xord (',')); 12160 print_number (p0->yellow); 12161 mp_print_char (mp, xord (',')); 12162 print_number (p0->black); 12163 mp_print_char (mp, xord (')')); 12164 }; 12165 } else if (mp_color_model (p) == mp_rgb_model) { 12166 if (number_positive(p0->red) || number_positive(p0->green) || 12167 number_positive(p0->blue)) { 12168 mp_print (mp, "colored "); 12169 mp_print_char (mp, xord ('(')); 12170 print_number (p0->red); 12171 mp_print_char (mp, xord (',')); 12172 print_number (p0->green); 12173 mp_print_char (mp, xord (',')); 12174 print_number (p0->blue); 12175 mp_print_char (mp, xord (')')); 12176 }; 12177 } 12178} 12179 12180 12181@ @<Cases for printing graphical object node |p|@>= 12182case mp_stroked_node_type: 12183mp_print (mp, "Filled pen stroke "); 12184mp_print_obj_color (mp, p); 12185mp_print_char (mp, xord (':')); 12186mp_print_ln (mp); 12187mp_pr_path (mp, mp_path_p ((mp_stroked_node) p)); 12188if (mp_dash_p (p) != NULL) { 12189 mp_print_nl (mp, "dashed ("); 12190 @<Finish printing the dash pattern that |p| refers to@>; 12191} 12192mp_print_ln (mp); 12193@<Print join and cap types for stroked node |p|@>; 12194mp_print (mp, " with pen"); 12195mp_print_ln (mp); 12196if (mp_pen_p ((mp_stroked_node) p) == NULL) { 12197 mp_print (mp, "???"); /* shouldn't happen */ 12198@.???@> 12199} else { 12200 mp_pr_pen (mp, mp_pen_p ((mp_stroked_node) p)); 12201} 12202break; 12203 12204@ Normally, the |dash_list| field in an edge header is set to |null_dash| 12205when it is not known to define a suitable dash pattern. This is disallowed 12206here because the |mp_dash_p| field should never point to such an edge header. 12207Note that memory is allocated for |start_x(null_dash)| and we are free to 12208give it any convenient value. 12209 12210@<Finish printing the dash pattern that |p| refers to@>= 12211{ 12212mp_dash_node ppd, hhd; 12213ok_to_dash = pen_is_elliptical (mp_pen_p ((mp_stroked_node) p)); 12214if (!ok_to_dash) 12215 set_number_to_unity (scf); 12216else 12217 number_clone(scf, ((mp_stroked_node) p)->dash_scale); 12218hhd = (mp_dash_node)mp_dash_p (p); 12219ppd = dash_list (hhd); 12220if ((ppd == mp->null_dash) || number_negative(hhd->dash_y)) { 12221 mp_print (mp, " ??"); 12222} else { 12223 mp_number dashoff; 12224 mp_number ret, arg1; 12225 new_number (ret); 12226 new_number (arg1); 12227 new_number (dashoff); 12228 set_number_from_addition(mp->null_dash->start_x, ppd->start_x, hhd->dash_y ); 12229 while (ppd != mp->null_dash) { 12230 mp_print (mp, "on "); 12231 set_number_from_substraction (arg1, ppd->stop_x, ppd->start_x); 12232 take_scaled (ret, arg1, scf); 12233 print_number ( ret); 12234 mp_print (mp, " off "); 12235 set_number_from_substraction (arg1, ((mp_dash_node)mp_link (ppd))->start_x, ppd->stop_x); 12236 take_scaled (ret, arg1, scf); 12237 print_number (ret); 12238 ppd = (mp_dash_node)mp_link (ppd); 12239 if (ppd != mp->null_dash) 12240 mp_print_char (mp, xord (' ')); 12241 } 12242 mp_print (mp, ") shifted "); 12243 mp_dash_offset (mp, &dashoff, hhd); 12244 take_scaled (ret, dashoff, scf); 12245 number_negate (ret); 12246 print_number (ret); 12247 free_number (dashoff); 12248 free_number (ret); 12249 free_number (arg1); 12250 if (!ok_to_dash || number_zero(hhd->dash_y) ) 12251 mp_print (mp, " (this will be ignored)"); 12252} 12253} 12254 12255@ @<Declarations@>= 12256static void mp_dash_offset (MP mp, mp_number *x, mp_dash_node h); 12257 12258@ @c 12259void mp_dash_offset (MP mp, mp_number *x, mp_dash_node h) { 12260 if (dash_list (h) == mp->null_dash || number_negative(h->dash_y )) 12261 mp_confusion (mp, "dash0"); 12262@:this can't happen dash0}{\quad dash0@>; 12263 if (number_zero(h->dash_y)) { 12264 set_number_to_zero(*x); 12265 } else { 12266 number_clone (*x, (dash_list (h))->start_x ); 12267 number_modulo (*x, h->dash_y); 12268 number_negate (*x); 12269 if (number_negative(*x)) 12270 number_add(*x, h->dash_y); 12271 } 12272} 12273 12274 12275@ @<Cases for printing graphical object node |p|@>= 12276case mp_text_node_type: 12277{ 12278mp_text_node p0 = (mp_text_node)p; 12279mp_print_char (mp, xord ('"')); 12280mp_print_str (mp, mp_text_p (p)); 12281mp_print (mp, "\" infont \""); 12282mp_print (mp, mp->font_name[mp_font_n (p)]); 12283mp_print_char (mp, xord ('"')); 12284mp_print_ln (mp); 12285mp_print_obj_color (mp, p); 12286mp_print (mp, "transformed "); 12287mp_print_char (mp, xord ('(')); 12288print_number (p0->tx); 12289mp_print_char (mp, xord (',')); 12290print_number (p0->ty); 12291mp_print_char (mp, xord (',')); 12292print_number (p0->txx); 12293mp_print_char (mp, xord (',')); 12294print_number (p0->txy); 12295mp_print_char (mp, xord (',')); 12296print_number (p0->tyx); 12297mp_print_char (mp, xord (',')); 12298print_number (p0->tyy); 12299mp_print_char (mp, xord (')')); 12300} 12301break; 12302 12303@ @<Cases for printing graphical object node |p|@>= 12304case mp_start_clip_node_type: 12305mp_print (mp, "clipping path:"); 12306mp_print_ln (mp); 12307mp_pr_path (mp, mp_path_p ((mp_start_clip_node) p)); 12308break; 12309case mp_stop_clip_node_type: 12310mp_print (mp, "stop clipping"); 12311break; 12312 12313@ @<Cases for printing graphical object node |p|@>= 12314case mp_start_bounds_node_type: 12315mp_print (mp, "setbounds path:"); 12316mp_print_ln (mp); 12317mp_pr_path (mp, mp_path_p ((mp_start_bounds_node) p)); 12318break; 12319case mp_stop_bounds_node_type: 12320mp_print (mp, "end of setbounds"); 12321break; 12322 12323@ To initialize the |dash_list| field in an edge header~|h|, we need a 12324subroutine that scans an edge structure and tries to interpret it as a dash 12325pattern. This can only be done when there are no filled regions or clipping 12326paths and all the pen strokes have the same color. The first step is to let 12327$y_0$ be the initial $y$~coordinate of the first pen stroke. Then we implicitly 12328project all the pen stroke paths onto the line $y=y_0$ and require that there 12329be no retracing. If the resulting paths cover a range of $x$~coordinates of 12330length $\Delta x$, we set |dash_y(h)| to the length of the dash pattern by 12331finding the maximum of $\Delta x$ and the absolute value of~$y_0$. 12332 12333@c 12334static mp_edge_header_node mp_make_dashes (MP mp, mp_edge_header_node h) { /* returns |h| or |NULL| */ 12335 mp_node p; /* this scans the stroked nodes in the object list */ 12336 mp_node p0; /* if not |NULL| this points to the first stroked node */ 12337 mp_knot pp, qq, rr; /* pointers into |mp_path_p(p)| */ 12338 mp_dash_node d, dd; /* pointers used to create the dash list */ 12339 mp_number y0; 12340 @<Other local variables in |make_dashes|@>; 12341 if (dash_list (h) != mp->null_dash) 12342 return h; 12343 new_number (y0); /* the initial $y$ coordinate */ 12344 p0 = NULL; 12345 p = mp_link (edge_list (h)); 12346 while (p != NULL) { 12347 if (mp_type (p) != mp_stroked_node_type) { 12348 @<Compain that the edge structure contains a node of the wrong type 12349 and |goto not_found|@>; 12350 } 12351 pp = mp_path_p ((mp_stroked_node) p); 12352 if (p0 == NULL) { 12353 p0 = p; 12354 number_clone(y0, pp->y_coord); 12355 } 12356 @<Make |d| point to a new dash node created from stroke |p| and path |pp| 12357 or |goto not_found| if there is an error@>; 12358 @<Insert |d| into the dash list and |goto not_found| if there is an error@>; 12359 p = mp_link (p); 12360 } 12361 if (dash_list (h) == mp->null_dash) 12362 goto NOT_FOUND; /* No error message */ 12363 @<Scan |dash_list(h)| and deal with any dashes that are themselves dashed@>; 12364 @<Set |dash_y(h)| and merge the first and last dashes if necessary@>; 12365 free_number (y0); 12366 return h; 12367NOT_FOUND: 12368 free_number (y0); 12369 @<Flush the dash list, recycle |h| and return |NULL|@>; 12370} 12371 12372 12373@ @<Compain that the edge structure contains a node of the wrong type...@>= 12374{ 12375 const char *hlp[] = { 12376 "When you say `dashed p', picture p should not contain any", 12377 "text, filled regions, or clipping paths. This time it did", 12378 "so I'll just make it a solid line instead.", 12379 NULL }; 12380 mp_back_error (mp, "Picture is too complicated to use as a dash pattern", hlp, true); 12381 mp_get_x_next (mp); 12382 goto NOT_FOUND; 12383} 12384 12385 12386@ A similar error occurs when monotonicity fails. 12387 12388@<Declarations@>= 12389static void mp_x_retrace_error (MP mp); 12390 12391@ @c 12392void mp_x_retrace_error (MP mp) { 12393 const char *hlp[] = { 12394 "When you say `dashed p', every path in p should be monotone", 12395 "in x and there must be no overlapping. This failed", 12396 "so I'll just make it a solid line instead.", 12397 NULL }; 12398 mp_back_error (mp, "Picture is too complicated to use as a dash pattern", hlp, true); 12399 mp_get_x_next (mp); 12400} 12401 12402 12403@ We stash |p| in |dash_info(d)| if |mp_dash_p(p)<>0| so that subsequent processing can 12404handle the case where the pen stroke |p| is itself dashed. 12405 12406@d dash_info(A) ((mp_dash_node)(A))->dash_info_ /* in an edge header this points to the first dash node */ 12407 12408@<Make |d| point to a new dash node created from stroke |p| and path...@>= 12409@<Make sure |p| and |p0| are the same color and |goto not_found| if there is 12410 an error@>; 12411rr = pp; 12412if (mp_next_knot (pp) != pp) { 12413 do { 12414 qq = rr; 12415 rr = mp_next_knot (rr); 12416 @<Check for retracing between knots |qq| and |rr| and |goto not_found| 12417 if there is a problem@>; 12418 } while (mp_right_type (rr) != mp_endpoint); 12419} 12420d = (mp_dash_node)mp_get_dash_node (mp); 12421if (mp_dash_p (p) == NULL) 12422 dash_info (d) = NULL; 12423else 12424 dash_info (d) = p; 12425if (number_less (pp->x_coord, rr->x_coord)) { 12426 number_clone(d->start_x, pp->x_coord); 12427 number_clone(d->stop_x, rr->x_coord); 12428} else { 12429 number_clone(d->start_x, rr->x_coord); 12430 number_clone(d->stop_x, pp->x_coord); 12431} 12432 12433 12434@ We also need to check for the case where the segment from |qq| to |rr| is 12435monotone in $x$ but is reversed relative to the path from |pp| to |qq|. 12436 12437@<Check for retracing between knots |qq| and |rr| and |goto not_found|...@>= 12438{ 12439 mp_number x0, x1, x2, x3; /* $x$ coordinates of the segment from |qq| to |rr| */ 12440 new_number(x0); 12441 new_number(x1); 12442 new_number(x2); 12443 new_number(x3); 12444 number_clone(x0, qq->x_coord); 12445 number_clone(x1, qq->right_x); 12446 number_clone(x2, rr->left_x); 12447 number_clone(x3, rr->x_coord); 12448 if (number_greater(x0, x1) || number_greater(x1, x2) || number_greater(x2, x3)) { 12449 if (number_less(x0, x1) || number_less(x1, x2) || number_less(x2, x3)) { 12450 mp_number a1, a2, a3, a4; 12451 mp_number test; 12452 new_number(test); 12453 new_number(a1); 12454 new_number(a2); 12455 new_number(a3); 12456 new_number(a4); 12457 set_number_from_substraction(a1, x2, x1); 12458 set_number_from_substraction(a2, x2, x1); 12459 set_number_from_substraction(a3, x1, x0); 12460 set_number_from_substraction(a4, x3, x2); 12461 ab_vs_cd (test, a1, a2, a3, a4); 12462 free_number(a1); 12463 free_number(a2); 12464 free_number(a3); 12465 free_number(a4); 12466 if (number_positive(test)) { 12467 mp_x_retrace_error (mp); 12468 free_number(x0); 12469 free_number(x1); 12470 free_number(x2); 12471 free_number(x3); 12472 free_number(test); 12473 goto NOT_FOUND; 12474 } 12475 free_number(test); 12476 } 12477 } 12478 if (number_greater(pp->x_coord, x0) || number_greater(x0, x3)) { 12479 if (number_less (pp->x_coord, x0) || number_less(x0, x3)) { 12480 mp_x_retrace_error (mp); 12481 free_number(x0); 12482 free_number(x1); 12483 free_number(x2); 12484 free_number(x3); 12485 goto NOT_FOUND; 12486 } 12487 } 12488 free_number(x0); 12489 free_number(x1); 12490 free_number(x2); 12491 free_number(x3); 12492} 12493 12494@ @<Make sure |p| and |p0| are the same color and |goto not_found|...@>= 12495if (!number_equal(((mp_stroked_node)p)->red, ((mp_stroked_node)p0)->red) || 12496 !number_equal(((mp_stroked_node)p)->black, ((mp_stroked_node)p0)->black) || 12497 !number_equal(((mp_stroked_node)p)->green, ((mp_stroked_node)p0)->green) || 12498 !number_equal(((mp_stroked_node)p)->blue, ((mp_stroked_node)p0)->blue) 12499 ) { 12500 const char *hlp[] = { 12501 "When you say `dashed p', everything in picture p should", 12502 "be the same color. I can\'t handle your color changes", 12503 "so I'll just make it a solid line instead.", 12504 NULL }; 12505 mp_back_error (mp, "Picture is too complicated to use as a dash pattern", hlp, true); 12506 mp_get_x_next (mp); 12507 goto NOT_FOUND; 12508} 12509 12510@ @<Insert |d| into the dash list and |goto not_found| if there is an error@>= 12511number_clone(mp->null_dash->start_x, d->stop_x); 12512dd = (mp_dash_node)h; /* this makes |mp_link(dd)=dash_list(h)| */ 12513while (number_less(((mp_dash_node)mp_link (dd))->start_x, d->stop_x )) 12514 dd = (mp_dash_node)mp_link (dd); 12515if (dd != (mp_dash_node)h) { 12516 if (number_greater(dd->stop_x, d->start_x)) { 12517 mp_x_retrace_error (mp); 12518 goto NOT_FOUND; 12519 }; 12520} 12521mp_link (d) = mp_link (dd); 12522mp_link (dd) = (mp_node)d 12523 12524@ @<Set |dash_y(h)| and merge the first and last dashes if necessary@>= 12525d = dash_list (h); 12526while ((mp_link (d) != (mp_node)mp->null_dash)) 12527 d = (mp_dash_node)mp_link (d); 12528dd = dash_list (h); 12529set_number_from_substraction(h->dash_y, d->stop_x, dd->start_x); 12530{ 12531 mp_number absval; 12532 new_number (absval); 12533 number_clone (absval, y0); 12534 number_abs (absval); 12535 if (number_greater (absval, h->dash_y) ) { 12536 number_clone(h->dash_y, absval); 12537 } else if (d != dd) { 12538 set_dash_list (h, mp_link (dd)); 12539 set_number_from_addition(d->stop_x, dd->stop_x, h->dash_y); 12540 mp_free_node (mp, (mp_node)dd, dash_node_size); 12541 } 12542 free_number (absval); 12543} 12544@ We get here when the argument is a NULL picture or when there is an error. 12545Recovering from an error involves making |dash_list(h)| empty to indicate 12546that |h| is not known to be a valid dash pattern. We also dereference |h| 12547since it is not being used for the return value. 12548 12549@<Flush the dash list, recycle |h| and return |NULL|@>= 12550mp_flush_dash_list (mp, h); 12551delete_edge_ref (h); 12552return NULL 12553 12554@ Having carefully saved the dashed stroked nodes in the 12555corresponding dash nodes, we must be prepared to break up these dashes into 12556smaller dashes. 12557 12558@<Scan |dash_list(h)| and deal with any dashes that are themselves dashed@>= 12559{ 12560mp_number hsf; /* the dash pattern from |hh| gets scaled by this */ 12561new_number (hsf); 12562d = (mp_dash_node)h; /* now |mp_link(d)=dash_list(h)| */ 12563while (mp_link (d) != (mp_node)mp->null_dash) { 12564 ds = dash_info (mp_link (d)); 12565 if (ds == NULL) { 12566 d = (mp_dash_node)mp_link (d); 12567 } else { 12568 hh = (mp_edge_header_node)mp_dash_p (ds); 12569 number_clone(hsf, ((mp_stroked_node)ds)->dash_scale); 12570 if (hh == NULL) 12571 mp_confusion (mp, "dash1"); 12572@:this can't happen dash0}{\quad dash1@>; 12573 /* clang: dereference null pointer 'hh' */ assert(hh); 12574 if (number_zero(((mp_dash_node)hh)->dash_y )) { 12575 d = (mp_dash_node)mp_link (d); 12576 } else { 12577 if (dash_list (hh) == NULL) 12578 mp_confusion (mp, "dash1"); 12579@:this can't happen dash0}{\quad dash1@>; 12580 @<Replace |mp_link(d)| by a dashed version as determined by edge header 12581 |hh| and scale factor |ds|@>; 12582 } 12583 } 12584} 12585free_number (hsf); 12586} 12587 12588@ @<Other local variables in |make_dashes|@>= 12589mp_dash_node dln; /* |mp_link(d)| */ 12590mp_edge_header_node hh; /* an edge header that tells how to break up |dln| */ 12591mp_node ds; /* the stroked node from which |hh| and |hsf| are derived */ 12592 12593@ @<Replace |mp_link(d)| by a dashed version as determined by edge header...@>= 12594{ 12595 mp_number xoff; /* added to $x$ values in |dash_list(hh)| to match |dln| */ 12596 mp_number dashoff; 12597 mp_number r1, r2; 12598 new_number (r1); 12599 new_number (r2); 12600 dln = (mp_dash_node)mp_link (d); 12601 dd = dash_list (hh); 12602 /* clang: dereference null pointer 'dd' */ assert(dd); 12603 new_number (xoff); 12604 new_number (dashoff); 12605 mp_dash_offset (mp, &dashoff, (mp_dash_node)hh); 12606 take_scaled (r1, hsf, dd->start_x); 12607 take_scaled (r2, hsf, dashoff); 12608 number_add (r1, r2); 12609 set_number_from_substraction(xoff, dln->start_x, r1); 12610 free_number (dashoff); 12611 take_scaled (r1, hsf, dd->start_x); 12612 take_scaled (r2, hsf, hh->dash_y); 12613 set_number_from_addition(mp->null_dash->start_x, r1, r2); 12614 number_clone(mp->null_dash->stop_x, mp->null_dash->start_x); 12615 @<Advance |dd| until finding the first dash that overlaps |dln| when 12616 offset by |xoff|@>; 12617 while (number_lessequal(dln->start_x, dln->stop_x)) { 12618 @<If |dd| has `fallen off the end', back up to the beginning and fix |xoff|@>; 12619 @<Insert a dash between |d| and |dln| for the overlap with the offset version 12620 of |dd|@>; 12621 dd = (mp_dash_node)mp_link (dd); 12622 take_scaled (r1, hsf, dd->start_x); 12623 set_number_from_addition(dln->start_x , xoff, r1); 12624 } 12625 free_number(xoff); 12626 free_number (r1); 12627 free_number (r2); 12628 mp_link (d) = mp_link (dln); 12629 mp_free_node (mp, (mp_node)dln, dash_node_size); 12630} 12631 12632 12633@ The name of this module is a bit of a lie because we just find the 12634first |dd| where |take_scaled (hsf, stop_x(dd))| is large enough to make an 12635overlap possible. It could be that the unoffset version of dash |dln| falls 12636in the gap between |dd| and its predecessor. 12637 12638@<Advance |dd| until finding the first dash that overlaps |dln| when...@>= 12639{ 12640 mp_number r1; 12641 new_number (r1); 12642 take_scaled (r1, hsf, dd->stop_x); 12643 number_add (r1, xoff); 12644 while (number_less(r1, dln->start_x)) { 12645 dd = (mp_dash_node)mp_link (dd); 12646 take_scaled (r1, hsf, dd->stop_x); 12647 number_add (r1, xoff); 12648 } 12649 free_number (r1); 12650} 12651 12652@ @<If |dd| has `fallen off the end', back up to the beginning and fix...@>= 12653if (dd == mp->null_dash) { 12654 mp_number ret; 12655 new_number (ret); 12656 dd = dash_list (hh); 12657 take_scaled (ret, hsf, hh->dash_y); 12658 number_add(xoff, ret); 12659 free_number (ret); 12660} 12661 12662@ At this point we already know that |start_x(dln)<=xoff+take_scaled(hsf,stop_x(dd))|. 12663 12664@<Insert a dash between |d| and |dln| for the overlap with the offset...@>= 12665{ 12666 mp_number r1; 12667 new_number (r1); 12668 take_scaled (r1, hsf, dd->start_x); 12669 number_add (r1, xoff); 12670 if (number_lessequal(r1, dln->stop_x)) { 12671 mp_link (d) = (mp_node)mp_get_dash_node (mp); 12672 d = (mp_dash_node)mp_link (d); 12673 mp_link (d) = (mp_node)dln; 12674 take_scaled (r1, hsf, dd->start_x ); 12675 number_add (r1, xoff); 12676 if (number_greater(dln->start_x, r1)) 12677 number_clone(d->start_x, dln->start_x); 12678 else { 12679 number_clone(d->start_x, r1); 12680 } 12681 take_scaled (r1, hsf, dd->stop_x); 12682 number_add (r1, xoff); 12683 if (number_less(dln->stop_x, r1)) 12684 number_clone(d->stop_x, dln->stop_x ); 12685 else { 12686 number_clone(d->stop_x, r1); 12687 } 12688 } 12689 free_number (r1); 12690} 12691 12692@ The next major task is to update the bounding box information in an edge 12693header~|h|. This is done via a procedure |adjust_bbox| that enlarges an edge 12694header's bounding box to accommodate the box computed by |path_bbox| or 12695|pen_bbox|. (This is stored in global variables |minx|, |miny|, |maxx|, and 12696|maxy|.) 12697 12698@c 12699static void mp_adjust_bbox (MP mp, mp_edge_header_node h) { 12700 if (number_less (mp_minx, h->minx)) 12701 number_clone(h->minx, mp_minx); 12702 if (number_less (mp_miny, h->miny)) 12703 number_clone(h->miny, mp_miny); 12704 if (number_greater (mp_maxx, h->maxx)) 12705 number_clone(h->maxx, mp_maxx); 12706 if (number_greater (mp_maxy, h->maxy)) 12707 number_clone(h->maxy, mp_maxy); 12708} 12709 12710 12711@ Here is a special routine for updating the bounding box information in 12712edge header~|h| to account for the squared-off ends of a non-cyclic path~|p| 12713that is to be stroked with the pen~|pp|. 12714 12715@c 12716static void mp_box_ends (MP mp, mp_knot p, mp_knot pp, mp_edge_header_node h) { 12717 mp_knot q; /* a knot node adjacent to knot |p| */ 12718 mp_fraction dx, dy; /* a unit vector in the direction out of the path at~|p| */ 12719 mp_number d; /* a factor for adjusting the length of |(dx,dy)| */ 12720 mp_number z; /* a coordinate being tested against the bounding box */ 12721 mp_number xx, yy; /* the extreme pen vertex in the |(dx,dy)| direction */ 12722 integer i; /* a loop counter */ 12723 new_fraction(dx); 12724 new_fraction(dy); 12725 new_number(xx); 12726 new_number(yy); 12727 new_number(z); 12728 new_number(d); 12729 if (mp_right_type (p) != mp_endpoint) { 12730 q = mp_next_knot (p); 12731 while (1) { 12732 @<Make |(dx,dy)| the final direction for the path segment from 12733 |q| to~|p|; set~|d|@>; 12734 pyth_add (d, dx, dy); 12735 if (number_positive(d)) { 12736 @<Normalize the direction |(dx,dy)| and find the pen offset |(xx,yy)|@>; 12737 for (i = 1; i <= 2; i++) { 12738 @<Use |(dx,dy)| to generate a vertex of the square end cap and 12739 update the bounding box to accommodate it@>; 12740 number_negate(dx); 12741 number_negate(dy); 12742 } 12743 } 12744 if (mp_right_type (p) == mp_endpoint) { 12745 goto DONE; 12746 } else { 12747 @<Advance |p| to the end of the path and make |q| the previous knot@>; 12748 } 12749 } 12750 } 12751DONE: 12752 free_number (dx); 12753 free_number (dy); 12754 free_number (xx); 12755 free_number (yy); 12756 free_number (z); 12757 free_number (d); 12758} 12759 12760 12761@ @<Make |(dx,dy)| the final direction for the path segment from...@>= 12762if (q == mp_next_knot (p)) { 12763 set_number_from_substraction(dx, p->x_coord, p->right_x); 12764 set_number_from_substraction(dy, p->y_coord, p->right_y); 12765 if (number_zero(dx) && number_zero(dy)) { 12766 set_number_from_substraction(dx, p->x_coord, q->left_x); 12767 set_number_from_substraction(dy, p->y_coord, q->left_y); 12768 } 12769} else { 12770 set_number_from_substraction(dx, p->x_coord, p->left_x); 12771 set_number_from_substraction(dy, p->y_coord, p->left_y); 12772 if (number_zero(dx) && number_zero(dy)) { 12773 set_number_from_substraction(dx, p->x_coord, q->right_x); 12774 set_number_from_substraction(dy, p->y_coord, q->right_y); 12775 } 12776} 12777set_number_from_substraction(dx, p->x_coord, q->x_coord); 12778set_number_from_substraction(dy, p->y_coord, q->y_coord); 12779 12780 12781@ @<Normalize the direction |(dx,dy)| and find the pen offset |(xx,yy)|@>= 12782{ 12783 mp_number arg1, r; 12784 new_fraction (r); 12785 new_number(arg1); 12786 make_fraction (r, dx, d); 12787 number_clone(dx, r); 12788 make_fraction (r, dy, d); 12789 number_clone(dy, r); 12790 free_number (r); 12791 number_clone(arg1, dy); 12792 number_negate(arg1); 12793 mp_find_offset (mp, arg1, dx, pp); 12794 free_number(arg1); 12795 number_clone(xx, mp->cur_x); 12796 number_clone(yy, mp->cur_y); 12797} 12798 12799@ @<Use |(dx,dy)| to generate a vertex of the square end cap and...@>= 12800{ 12801 mp_number r1, r2, arg1; 12802 new_number (arg1); 12803 new_fraction (r1); 12804 new_fraction (r2); 12805 mp_find_offset (mp, dx, dy, pp); 12806 set_number_from_substraction (arg1, xx, mp->cur_x); 12807 take_fraction (r1, arg1, dx); 12808 set_number_from_substraction (arg1, yy, mp->cur_y); 12809 take_fraction (r2, arg1, dy); 12810 set_number_from_addition(d, r1, r2); 12811 if ((number_negative(d) && (i == 1)) || (number_positive(d) && (i == 2))) 12812 mp_confusion (mp, "box_ends"); 12813@:this can't happen box ends}{\quad\\{box\_ends}@>; 12814 take_fraction (r1, d, dx); 12815 set_number_from_addition(z, p->x_coord, mp->cur_x); 12816 number_add (z, r1); 12817 if (number_less(z, h->minx)) 12818 number_clone(h->minx, z); 12819 if (number_greater(z, h->maxx)) 12820 number_clone(h->maxx, z); 12821 take_fraction (r1, d, dy); 12822 set_number_from_addition(z, p->y_coord, mp->cur_y); 12823 number_add (z, r1); 12824 if (number_less(z, h->miny)) 12825 number_clone(h->miny, z); 12826 if (number_greater(z, h->maxy)) 12827 number_clone(h->maxy, z); 12828 free_number (r1); 12829 free_number (r2); 12830 free_number (arg1); 12831} 12832 12833@ @<Advance |p| to the end of the path and make |q| the previous knot@>= 12834do { 12835 q = p; 12836 p = mp_next_knot (p); 12837} while (mp_right_type (p) != mp_endpoint) 12838 12839@ The major difficulty in finding the bounding box of an edge structure is the 12840effect of clipping paths. We treat them conservatively by only clipping to the 12841clipping path's bounding box, but this still 12842requires recursive calls to |set_bbox| in order to find the bounding box of 12843@^recursion@> 12844the objects to be clipped. Such calls are distinguished by the fact that the 12845boolean parameter |top_level| is false. 12846 12847@c 12848void mp_set_bbox (MP mp, mp_edge_header_node h, boolean top_level) { 12849 mp_node p; /* a graphical object being considered */ 12850 integer lev; /* nesting level for |mp_start_bounds_node| nodes */ 12851 /* Wipe out any existing bounding box information if |bbtype(h)| is 12852 incompatible with |internal[mp_true_corners]| */ 12853 switch (h->bbtype ) { 12854 case no_bounds: 12855 break; 12856 case bounds_set: 12857 if (number_positive(internal_value (mp_true_corners))) 12858 mp_init_bbox (mp, h); 12859 break; 12860 case bounds_unset: 12861 if (number_nonpositive(internal_value (mp_true_corners))) 12862 mp_init_bbox (mp, h); 12863 break; 12864 } /* there are no other cases */ 12865 12866 while (mp_link (bblast (h)) != NULL) { 12867 p = mp_link (bblast (h)); 12868 bblast (h) = p; 12869 switch (mp_type (p)) { 12870 case mp_stop_clip_node_type: 12871 if (top_level) 12872 mp_confusion (mp, "bbox"); 12873 else 12874 return; 12875@:this can't happen bbox}{\quad bbox@>; 12876 break; 12877 @<Other cases for updating the bounding box based on the type of object |p|@>; 12878 default: /* there are no other valid cases, but please the compiler */ 12879 break; 12880 } 12881 } 12882 if (!top_level) 12883 mp_confusion (mp, "bbox"); 12884} 12885 12886 12887@ @<Declarations@>= 12888static void mp_set_bbox (MP mp, mp_edge_header_node h, boolean top_level); 12889 12890 12891@ @<Other cases for updating the bounding box...@>= 12892case mp_fill_node_type: 12893 mp_path_bbox (mp, mp_path_p ((mp_fill_node) p)); 12894 if (mp_pen_p ((mp_fill_node) p) != NULL) { 12895 mp_number x0a, y0a, x1a, y1a; 12896 new_number (x0a); 12897 new_number (y0a); 12898 new_number (x1a); 12899 new_number (y1a); 12900 number_clone (x0a, mp_minx); 12901 number_clone (y0a, mp_miny); 12902 number_clone (x1a, mp_maxx); 12903 number_clone (y1a, mp_maxy); 12904 mp_pen_bbox (mp, mp_pen_p ((mp_fill_node) p)); 12905 number_add (mp_minx, x0a); 12906 number_add (mp_miny, y0a); 12907 number_add (mp_maxx, x1a); 12908 number_add (mp_maxy, y1a); 12909 free_number (x0a); 12910 free_number (y0a); 12911 free_number (x1a); 12912 free_number (y1a); 12913 } 12914 mp_adjust_bbox (mp, h); 12915break; 12916 12917@ @<Other cases for updating the bounding box...@>= 12918case mp_start_bounds_node_type: 12919 if (number_positive (internal_value (mp_true_corners))) { 12920 h->bbtype = bounds_unset; 12921 } else { 12922 h->bbtype = bounds_set; 12923 mp_path_bbox (mp, mp_path_p ((mp_start_bounds_node) p)); 12924 mp_adjust_bbox (mp, h); 12925 @<Scan to the matching |mp_stop_bounds_node| node and update |p| and 12926 |bblast(h)|@>; 12927 } 12928 break; 12929case mp_stop_bounds_node_type: 12930 if (number_nonpositive (internal_value (mp_true_corners))) 12931 mp_confusion (mp, "bbox2"); 12932@:this can't happen bbox2}{\quad bbox2@>; 12933 break; 12934 12935@ @<Scan to the matching |mp_stop_bounds_node| node and update |p| and...@>= 12936lev = 1; 12937while (lev != 0) { 12938 if (mp_link (p) == NULL) 12939 mp_confusion (mp, "bbox2"); 12940@:this can't happen bbox2}{\quad bbox2@>; 12941 /* clang: dereference null pointer */ assert(mp_link(p)); 12942 p = mp_link (p); 12943 if (mp_type (p) == mp_start_bounds_node_type) 12944 incr (lev); 12945 else if (mp_type (p) == mp_stop_bounds_node_type) 12946 decr (lev); 12947} 12948bblast (h) = p 12949 12950@ It saves a lot of grief here to be slightly conservative and not account for 12951omitted parts of dashed lines. We also don't worry about the material omitted 12952when using butt end caps. The basic computation is for round end caps and 12953|box_ends| augments it for square end caps. 12954 12955@<Other cases for updating the bounding box...@>= 12956case mp_stroked_node_type: 12957mp_path_bbox (mp, mp_path_p ((mp_stroked_node) p)); 12958{ 12959 mp_number x0a, y0a, x1a, y1a; 12960 new_number (x0a); 12961 new_number (y0a); 12962 new_number (x1a); 12963 new_number (y1a); 12964 number_clone (x0a, mp_minx); 12965 number_clone (y0a, mp_miny); 12966 number_clone (x1a, mp_maxx); 12967 number_clone (y1a, mp_maxy); 12968 mp_pen_bbox (mp, mp_pen_p ((mp_stroked_node) p)); 12969 number_add (mp_minx, x0a); 12970 number_add (mp_miny, y0a); 12971 number_add (mp_maxx, x1a); 12972 number_add (mp_maxy, y1a); 12973 free_number (x0a); 12974 free_number (y0a); 12975 free_number (x1a); 12976 free_number (y1a); 12977} 12978mp_adjust_bbox (mp, h); 12979if ((mp_left_type (mp_path_p ((mp_stroked_node) p)) == mp_endpoint) 12980 && (((mp_stroked_node) p)->lcap == 2)) 12981 mp_box_ends (mp, mp_path_p ((mp_stroked_node) p), 12982 mp_pen_p ((mp_stroked_node) p), h); 12983break; 12984 12985@ The height width and depth information stored in a text node determines a 12986rectangle that needs to be transformed according to the transformation 12987parameters stored in the text node. 12988 12989@<Other cases for updating the bounding box...@>= 12990case mp_text_node_type: 12991{ 12992 mp_number x0a, y0a, x1a, y1a, arg1; 12993 mp_text_node p0 = (mp_text_node)p; 12994 new_number (x0a); 12995 new_number (x1a); 12996 new_number (y0a); 12997 new_number (y1a); 12998 new_number (arg1); 12999 number_clone (arg1, p0->depth); 13000 number_negate (arg1); 13001 take_scaled (x1a, p0->txx, p0->width); 13002 take_scaled (y0a, p0->txy, arg1); 13003 take_scaled (y1a, p0->txy, p0->height); 13004 number_clone (mp_minx, p0->tx); 13005 number_clone (mp_maxx, mp_minx); 13006 if (number_less(y0a, y1a)) { 13007 number_add (mp_minx, y0a); 13008 number_add (mp_maxx, y1a); 13009 } else { 13010 number_add (mp_minx, y1a); 13011 number_add (mp_maxx, y0a); 13012 } 13013 if (number_negative(x1a)) 13014 number_add (mp_minx, x1a); 13015 else 13016 number_add (mp_maxx, x1a); 13017 take_scaled (x1a, p0->tyx, p0->width); 13018 number_clone (arg1, p0->depth); 13019 number_negate (arg1); 13020 take_scaled (y0a, p0->tyy, arg1); 13021 take_scaled (y1a, p0->tyy, p0->height); 13022 number_clone (mp_miny, p0->ty); 13023 number_clone (mp_maxy, mp_miny); 13024 if (number_less (y0a, y1a)) { 13025 number_add (mp_miny, y0a); 13026 number_add (mp_maxy, y1a); 13027 } else { 13028 number_add (mp_miny, y1a); 13029 number_add (mp_maxy, y0a); 13030 } 13031 if (number_negative(x1a)) 13032 number_add (mp_miny, x1a); 13033 else 13034 number_add (mp_maxy, x1a); 13035 mp_adjust_bbox (mp, h); 13036 free_number (x0a); 13037 free_number (y0a); 13038 free_number (x1a); 13039 free_number (y1a); 13040 free_number (arg1); 13041} 13042break; 13043 13044@ This case involves a recursive call that advances |bblast(h)| to the node of 13045type |mp_stop_clip_node| that matches |p|. 13046 13047@<Other cases for updating the bounding box...@>= 13048case mp_start_clip_node_type: 13049{ 13050 mp_number sminx, sminy, smaxx, smaxy; 13051 /* for saving the bounding box during recursive calls */ 13052 mp_number x0a, y0a, x1a, y1a; 13053 new_number (x0a); 13054 new_number (y0a); 13055 new_number (x1a); 13056 new_number (y1a); 13057 new_number (sminx); 13058 new_number (sminy); 13059 new_number (smaxx); 13060 new_number (smaxy); 13061mp_path_bbox (mp, mp_path_p ((mp_start_clip_node) p)); 13062number_clone (x0a, mp_minx); 13063number_clone (y0a, mp_miny); 13064number_clone (x1a, mp_maxx); 13065number_clone (y1a, mp_maxy); 13066number_clone (sminx, h->minx); 13067number_clone (sminy, h->miny); 13068number_clone (smaxx, h->maxx); 13069number_clone (smaxy, h->maxy); 13070@<Reinitialize the bounding box in header |h| and call |set_bbox| recursively 13071 starting at |mp_link(p)|@>; 13072@<Clip the bounding box in |h| to the rectangle given by |x0a|, |x1a|, 13073 |y0a|, |y1a|@>; 13074number_clone (mp_minx, sminx); 13075number_clone (mp_miny, sminy); 13076number_clone (mp_maxx, smaxx); 13077number_clone (mp_maxy, smaxy); 13078mp_adjust_bbox (mp, h); 13079 free_number (sminx); 13080 free_number (sminy); 13081 free_number (smaxx); 13082 free_number (smaxy); 13083 free_number (x0a); 13084 free_number (y0a); 13085 free_number (x1a); 13086 free_number (y1a); 13087} 13088break; 13089 13090@ @<Reinitialize the bounding box in header |h| and call |set_bbox|...@>= 13091set_number_to_inf(h->minx); 13092set_number_to_inf(h->miny); 13093set_number_to_neg_inf(h->maxx); 13094set_number_to_neg_inf(h->maxy); 13095mp_set_bbox (mp, h, false) 13096 13097 13098@ @<Clip the bounding box in |h| to the rectangle given by |x0a|, |x1a|,...@>= 13099if (number_less(h->minx, x0a)) 13100 number_clone(h->minx, x0a); 13101if (number_less(h->miny, y0a)) 13102 number_clone(h->miny, y0a); 13103if (number_greater(h->maxx, x1a)) 13104 number_clone(h->maxx, x1a); 13105if (number_greater(h->maxy, y1a)) 13106 number_clone(h->maxy, y1a); 13107 13108@* Finding an envelope. 13109When \MP\ has a path and a polygonal pen, it needs to express the desired 13110shape in terms of things \ps\ can understand. The present task is to compute 13111a new path that describes the region to be filled. It is convenient to 13112define this as a two step process where the first step is determining what 13113offset to use for each segment of the path. 13114 13115@ Given a pointer |c| to a cyclic path, 13116and a pointer~|h| to the first knot of a pen polygon, 13117the |offset_prep| routine changes the path into cubics that are 13118associated with particular pen offsets. Thus if the cubic between |p| 13119and~|q| is associated with the |k|th offset and the cubic between |q| and~|r| 13120has offset |l| then |mp_info(q)=zero_off+l-k|. (The constant |zero_off| is added 13121to because |l-k| could be negative.) 13122 13123After overwriting the type information with offset differences, we no longer 13124have a true path so we refer to the knot list returned by |offset_prep| as an 13125``envelope spec.'' 13126@^envelope spec@> 13127Since an envelope spec only determines relative changes in pen offsets, 13128|offset_prep| sets a global variable |spec_offset| to the relative change from 13129|h| to the first offset. 13130 13131@d zero_off 16384 /* added to offset changes to make them positive */ 13132 13133@<Glob...@>= 13134integer spec_offset; /* number of pen edges between |h| and the initial offset */ 13135 13136@ @c 13137static mp_knot mp_offset_prep (MP mp, mp_knot c, mp_knot h) { 13138 int n; /* the number of vertices in the pen polygon */ 13139 mp_knot c0, p, q, q0, r, w, ww; /* for list manipulation */ 13140 int k_needed; /* amount to be added to |mp_info(p)| when it is computed */ 13141 mp_knot w0; /* a pointer to pen offset to use just before |p| */ 13142 mp_number dxin, dyin; /* the direction into knot |p| */ 13143 int turn_amt; /* change in pen offsets for the current cubic */ 13144 mp_number max_coef; /* used while scaling */ 13145 mp_number ss; 13146 @<Other local variables for |offset_prep|@>; 13147 new_number(max_coef); 13148 new_number(dxin); 13149 new_number(dyin); 13150 new_number(dx0); 13151 new_number(dy0); 13152 new_number(x0); 13153 new_number(y0); 13154 new_number(x1); 13155 new_number(y1); 13156 new_number(x2); 13157 new_number(y2); 13158 new_number(du); 13159 new_number(dv); 13160 new_number(dx); 13161 new_number(dy); 13162 new_number(x0a); 13163 new_number(y0a); 13164 new_number(x1a); 13165 new_number(y1a); 13166 new_number(x2a); 13167 new_number(y2a); 13168 new_number(t0); 13169 new_number(t1); 13170 new_number(t2); 13171 new_number(u0); 13172 new_number(u1); 13173 new_number(v0); 13174 new_number(v1); 13175 new_fraction (ss); 13176 new_fraction (s); 13177 new_fraction (t); 13178 @<Initialize the pen size~|n|@>; 13179 @<Initialize the incoming direction and pen offset at |c|@>; 13180 p = c; 13181 c0 = c; 13182 k_needed = 0; 13183 do { 13184 q = mp_next_knot (p); 13185 @<Split the cubic between |p| and |q|, if necessary, into cubics 13186 associated with single offsets, after which |q| should 13187 point to the end of the final such cubic@>; 13188 NOT_FOUND: 13189 @<Advance |p| to node |q|, removing any ``dead'' cubics that 13190 might have been introduced by the splitting process@>; 13191 } while (q != c); 13192 @<Fix the offset change in |mp_knot_info(c)| and set |c| to the return value of 13193 |offset_prep|@>; 13194 free_number (ss); 13195 free_number (s); 13196 free_number (dxin); 13197 free_number (dyin); 13198 free_number (dx0); 13199 free_number (dy0); 13200 free_number (x0); 13201 free_number (y0); 13202 free_number (x1); 13203 free_number (y1); 13204 free_number (x2); 13205 free_number (y2); 13206 free_number (max_coef); 13207 free_number (du); 13208 free_number (dv); 13209 free_number (dx); 13210 free_number (dy); 13211 free_number (x0a); 13212 free_number (y0a); 13213 free_number (x1a); 13214 free_number (y1a); 13215 free_number (x2a); 13216 free_number (y2a); 13217 free_number (t0); 13218 free_number (t1); 13219 free_number (t2); 13220 free_number (u0); 13221 free_number (u1); 13222 free_number (v0); 13223 free_number (v1); 13224 free_number (t); 13225 return c; 13226} 13227 13228 13229@ We shall want to keep track of where certain knots on the cyclic path 13230wind up in the envelope spec. It doesn't suffice just to keep pointers to 13231knot nodes because some nodes are deleted while removing dead cubics. Thus 13232|offset_prep| updates the following pointers 13233 13234@<Glob...@>= 13235mp_knot spec_p1; 13236mp_knot spec_p2; /* pointers to distinguished knots */ 13237 13238@ @<Set init...@>= 13239mp->spec_p1 = NULL; 13240mp->spec_p2 = NULL; 13241 13242@ @<Initialize the pen size~|n|@>= 13243n = 0; 13244p = h; 13245do { 13246 incr (n); 13247 p = mp_next_knot (p); 13248} while (p != h) 13249 13250@ Since the true incoming direction isn't known yet, we just pick a direction 13251consistent with the pen offset~|h|. If this is wrong, it can be corrected 13252later. 13253 13254@<Initialize the incoming direction and pen offset at |c|@>= 13255{ 13256 mp_knot hn = mp_next_knot (h); 13257 mp_knot hp = mp_prev_knot (h); 13258 set_number_from_substraction(dxin, hn->x_coord, hp->x_coord); 13259 set_number_from_substraction(dyin, hn->y_coord, hp->y_coord); 13260 if (number_zero(dxin) && number_zero(dyin)) { 13261 set_number_from_substraction(dxin, hp->y_coord, h->y_coord); 13262 set_number_from_substraction(dyin, h->x_coord, hp->x_coord); 13263 } 13264} 13265w0 = h 13266 13267@ We must be careful not to remove the only cubic in a cycle. 13268 13269But we must also be careful for another reason. If the user-supplied 13270path starts with a set of degenerate cubics, the target node |q| can 13271be collapsed to the initial node |p| which might be the same as the 13272initial node |c| of the curve. This would cause the |offset_prep| routine 13273to bail out too early, causing distress later on. (See for example 13274the testcase reported by Bogus\l{}aw Jackowski in tracker id 267, case 52c 13275on Sarovar.) 13276 13277@<Advance |p| to node |q|, removing any ``dead'' cubics...@>= 13278q0 = q; 13279do { 13280 r = mp_next_knot (p); 13281 if (number_equal (p->x_coord, p->right_x) && 13282 number_equal (p->y_coord, p->right_y) && 13283 number_equal (p->x_coord, r->left_x) && 13284 number_equal (p->y_coord, r->left_y) && 13285 number_equal (p->x_coord, r->x_coord) && 13286 number_equal (p->y_coord, r->y_coord) && 13287 r != p && r != q) { 13288 @<Remove the cubic following |p| and update the data structures 13289 to merge |r| into |p|@>; 13290 } 13291 p = r; 13292} while (p != q); 13293/* Check if we removed too much */ 13294if ((q != q0) && (q != c || c == c0)) 13295 q = mp_next_knot (q) 13296 13297 13298@ @<Remove the cubic following |p| and update the data structures...@>= 13299{ 13300 k_needed = mp_knot_info (p) - zero_off; 13301 if (r == q) { 13302 q = p; 13303 } else { 13304 mp_knot_info (p) = k_needed + mp_knot_info (r); 13305 k_needed = 0; 13306 } 13307 if (r == c) { 13308 mp_knot_info (p) = mp_knot_info (c); 13309 c = p; 13310 } 13311 if (r == mp->spec_p1) 13312 mp->spec_p1 = p; 13313 if (r == mp->spec_p2) 13314 mp->spec_p2 = p; 13315 r = p; 13316 mp_remove_cubic (mp, p); 13317} 13318 13319 13320@ Not setting the |info| field of the newly created knot allows the splitting 13321routine to work for paths. 13322 13323@<Declarations@>= 13324static void mp_split_cubic (MP mp, mp_knot p, mp_number t); 13325 13326@ @c 13327void mp_split_cubic (MP mp, mp_knot p, mp_number t) { /* splits the cubic after |p| */ 13328 mp_number v; /* an intermediate value */ 13329 mp_knot q, r; /* for list manipulation */ 13330 q = mp_next_knot (p); 13331 r = mp_new_knot (mp); 13332 mp_next_knot (p) = r; 13333 mp_next_knot (r) = q; 13334 mp_originator (r) = mp_program_code; 13335 mp_left_type (r) = mp_explicit; 13336 mp_right_type (r) = mp_explicit; 13337 new_number(v); 13338 set_number_from_of_the_way (v, t, p->right_x, q->left_x); 13339 set_number_from_of_the_way (p->right_x, t, p->x_coord, p->right_x); 13340 set_number_from_of_the_way (q->left_x, t, q->left_x, q->x_coord); 13341 set_number_from_of_the_way (r->left_x, t, p->right_x, v); 13342 set_number_from_of_the_way (r->right_x, t, v, q->left_x); 13343 set_number_from_of_the_way (r->x_coord, t, r->left_x, r->right_x); 13344 set_number_from_of_the_way (v, t, p->right_y, q->left_y); 13345 set_number_from_of_the_way (p->right_y, t, p->y_coord, p->right_y); 13346 set_number_from_of_the_way (q->left_y, t, q->left_y, q->y_coord); 13347 set_number_from_of_the_way (r->left_y, t, p->right_y, v); 13348 set_number_from_of_the_way (r->right_y, t, v, q->left_y); 13349 set_number_from_of_the_way (r->y_coord, t, r->left_y, r->right_y); 13350 free_number (v); 13351} 13352 13353 13354@ This does not set |mp_knot_info(p)| or |mp_right_type(p)|. 13355 13356@<Declarations@>= 13357static void mp_remove_cubic (MP mp, mp_knot p); 13358 13359@ @c 13360void mp_remove_cubic (MP mp, mp_knot p) { /* removes the dead cubic following~|p| */ 13361 mp_knot q; /* the node that disappears */ 13362 (void) mp; 13363 q = mp_next_knot (p); 13364 mp_next_knot (p) = mp_next_knot (q); 13365 number_clone (p->right_x, q->right_x); 13366 number_clone (p->right_y, q->right_y); 13367 mp_xfree (q); 13368} 13369 13370 13371@ Let $d\prec d'$ mean that the counter-clockwise angle from $d$ to~$d'$ is 13372strictly between zero and $180^\circ$. Then we can define $d\preceq d'$ to 13373mean that the angle could be zero or $180^\circ$. If $w_k=(u_k,v_k)$ is the 13374$k$th pen offset, the $k$th pen edge direction is defined by the formula 13375$$d_k=(u\k-u_k,\,v\k-v_k).$$ 13376When listed by increasing $k$, these directions occur in counter-clockwise 13377order so that $d_k\preceq d\k$ for all~$k$. 13378The goal of |offset_prep| is to find an offset index~|k| to associate with 13379each cubic, such that the direction $d(t)$ of the cubic satisfies 13380$$d_{k-1}\preceq d(t)\preceq d_k\qquad\hbox{for $0\le t\le 1$.}\eqno(*)$$ 13381We may have to split a cubic into many pieces before each 13382piece corresponds to a unique offset. 13383 13384@<Split the cubic between |p| and |q|, if necessary, into cubics...@>= 13385mp_knot_info (p) = zero_off + k_needed; 13386k_needed = 0; 13387@<Prepare for derivative computations; 13388 |goto not_found| if the current cubic is dead@>; 13389@<Find the initial direction |(dx,dy)|@>; 13390@<Update |mp_knot_info(p)| and find the offset $w_k$ such that 13391 $d_{k-1}\preceq(\\{dx},\\{dy})\prec d_k$; also advance |w0| for 13392 the direction change at |p|@>; 13393@<Find the final direction |(dxin,dyin)|@>; 13394@<Decide on the net change in pen offsets and set |turn_amt|@>; 13395@<Complete the offset splitting process@>; 13396w0 = mp_pen_walk (mp, w0, turn_amt) 13397 13398 13399@ @<Declarations@>= 13400static mp_knot mp_pen_walk (MP mp, mp_knot w, integer k); 13401 13402@ @c 13403mp_knot mp_pen_walk (MP mp, mp_knot w, integer k) { 13404 /* walk |k| steps around a pen from |w| */ 13405 (void) mp; 13406 while (k > 0) { 13407 w = mp_next_knot (w); 13408 decr (k); 13409 } 13410 while (k < 0) { 13411 w = mp_prev_knot (w); 13412 incr (k); 13413 } 13414 return w; 13415} 13416 13417 13418@ The direction of a cubic $B(z_0,z_1,z_2,z_3;t)=\bigl(x(t),y(t)\bigr)$ can be 13419calculated from the quadratic polynomials 13420${1\over3}x'(t)=B(x_1-x_0,x_2-x_1,x_3-x_2;t)$ and 13421${1\over3}y'(t)=B(y_1-y_0,y_2-y_1,y_3-y_2;t)$. 13422Since we may be calculating directions from several cubics 13423split from the current one, it is desirable to do these calculations 13424without losing too much precision. ``Scaled up'' values of the 13425derivatives, which will be less tainted by accumulated errors than 13426derivatives found from the cubics themselves, are maintained in 13427local variables |x0|, |x1|, and |x2|, representing $X_0=2^l(x_1-x_0)$, 13428$X_1=2^l(x_2-x_1)$, and $X_2=2^l(x_3-x_2)$; similarly |y0|, |y1|, and~|y2| 13429represent $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)$. 13430 13431@<Other local variables for |offset_prep|@>= 13432mp_number x0, x1, x2, y0, y1, y2; /* representatives of derivatives */ 13433mp_number t0, t1, t2; /* coefficients of polynomial for slope testing */ 13434mp_number du, dv, dx, dy; /* for directions of the pen and the curve */ 13435mp_number dx0, dy0; /* initial direction for the first cubic in the curve */ 13436mp_number x0a, x1a, x2a, y0a, y1a, y2a; /* intermediate values */ 13437mp_number t; /* where the derivative passes through zero */ 13438mp_number s; /* a temporary value */ 13439 13440@ @<Prepare for derivative computations...@>= 13441set_number_from_substraction(x0, p->right_x, p->x_coord); 13442set_number_from_substraction(x2, q->x_coord, q->left_x); 13443set_number_from_substraction(x1, q->left_x, p->right_x); 13444set_number_from_substraction(y0, p->right_y, p->y_coord); 13445set_number_from_substraction(y2, q->y_coord, q->left_y); 13446set_number_from_substraction(y1, q->left_y, p->right_y); 13447{ 13448 mp_number absval; 13449 new_number (absval); 13450 number_clone(absval, x1); 13451 number_abs(absval); 13452 number_clone(max_coef, x0); 13453 number_abs (max_coef); 13454 if (number_greater(absval, max_coef)) { 13455 number_clone(max_coef, absval); 13456 } 13457 number_clone(absval, x2); 13458 number_abs(absval); 13459 if (number_greater(absval, max_coef)) { 13460 number_clone(max_coef, absval); 13461 } 13462 number_clone(absval, y0); 13463 number_abs(absval); 13464 if (number_greater(absval, max_coef)) { 13465 number_clone(max_coef, absval); 13466 } 13467 number_clone(absval, y1); 13468 number_abs(absval); 13469 if (number_greater(absval, max_coef)) { 13470 number_clone(max_coef, absval); 13471 } 13472 number_clone(absval, y2); 13473 number_abs(absval); 13474 if (number_greater(absval, max_coef)) { 13475 number_clone(max_coef, absval); 13476 } 13477 if (number_zero(max_coef)) { 13478 goto NOT_FOUND; 13479 } 13480 free_number (absval); 13481} 13482while (number_less(max_coef, fraction_half_t)) { 13483 number_double (max_coef); 13484 number_double (x0); 13485 number_double (x1); 13486 number_double (x2); 13487 number_double (y0); 13488 number_double (y1); 13489 number_double (y2); 13490} 13491 13492 13493@ Let us first solve a special case of the problem: Suppose we 13494know an index~$k$ such that either (i)~$d(t)\succeq d_{k-1}$ for all~$t$ 13495and $d(0)\prec d_k$, or (ii)~$d(t)\preceq d_k$ for all~$t$ and 13496$d(0)\succ d_{k-1}$. 13497Then, in a sense, we're halfway done, since one of the two relations 13498in $(*)$ is satisfied, and the other couldn't be satisfied for 13499any other value of~|k|. 13500 13501Actually, the conditions can be relaxed somewhat since a relation such as 13502$d(t)\succeq d_{k-1}$ restricts $d(t)$ to a half plane when all that really 13503matters is whether $d(t)$ crosses the ray in the $d_{k-1}$ direction from 13504the origin. The condition for case~(i) becomes $d_{k-1}\preceq d(0)\prec d_k$ 13505and $d(t)$ never crosses the $d_{k-1}$ ray in the clockwise direction. 13506Case~(ii) is similar except $d(t)$ cannot cross the $d_k$ ray in the 13507counterclockwise direction. 13508 13509The |fin_offset_prep| subroutine solves the stated subproblem. 13510It has a parameter called |rise| that is |1| in 13511case~(i), |-1| in case~(ii). Parameters |x0| through |y2| represent 13512the derivative of the cubic following |p|. 13513The |w| parameter should point to offset~$w_k$ and |mp_info(p)| should already 13514be set properly. The |turn_amt| parameter gives the absolute value of the 13515overall net change in pen offsets. 13516 13517@<Declarations@>= 13518static void mp_fin_offset_prep (MP mp, mp_knot p, mp_knot w, mp_number 13519 x0, mp_number x1, mp_number x2, mp_number y0, 13520 mp_number y1, mp_number y2, integer rise, 13521 integer turn_amt); 13522 13523@ @c 13524void mp_fin_offset_prep (MP mp, mp_knot p, mp_knot w, mp_number 13525 x0, mp_number x1, mp_number x2, mp_number y0, mp_number y1, 13526 mp_number y2, integer rise, integer turn_amt) { 13527 mp_knot ww; /* for list manipulation */ 13528 mp_number du, dv; /* for slope calculation */ 13529 mp_number t0, t1, t2; /* test coefficients */ 13530 mp_number t; /* place where the derivative passes a critical slope */ 13531 mp_number s; /* slope or reciprocal slope */ 13532 mp_number v; /* intermediate value for updating |x0..y2| */ 13533 mp_knot q; /* original |mp_next_knot(p)| */ 13534 q = mp_next_knot (p); 13535 new_number(du); 13536 new_number(dv); 13537 new_number(v); 13538 new_number(t0); 13539 new_number(t1); 13540 new_number(t2); 13541 new_fraction(s); 13542 new_fraction(t); 13543 while (1) { 13544 if (rise > 0) 13545 ww = mp_next_knot (w); /* a pointer to $w\k$ */ 13546 else 13547 ww = mp_prev_knot (w); /* a pointer to $w_{k-1}$ */ 13548 @<Compute test coefficients |(t0,t1,t2)| 13549 for $d(t)$ versus $d_k$ or $d_{k-1}$@>; 13550 crossing_point (t, t0, t1, t2); 13551 if (number_greaterequal(t, fraction_one_t)) { 13552 if (turn_amt > 0) 13553 number_clone(t, fraction_one_t); 13554 else 13555 goto RETURN; 13556 } 13557 @<Split the cubic at $t$, 13558 and split off another cubic if the derivative crosses back@>; 13559 w = ww; 13560 } 13561RETURN: 13562 free_number (s); 13563 free_number (t); 13564 free_number (du); 13565 free_number (dv); 13566 free_number (v); 13567 free_number (t0); 13568 free_number (t1); 13569 free_number (t2); 13570} 13571 13572 13573@ We want $B(\\{t0},\\{t1},\\{t2};t)$ to be the dot product of $d(t)$ with a 13574$-90^\circ$ rotation of the vector from |w| to |ww|. This makes the resulting 13575function cross from positive to negative when $d_{k-1}\preceq d(t)\preceq d_k$ 13576begins to fail. 13577 13578@<Compute test coefficients |(t0,t1,t2)| for $d(t)$ versus...@>= 13579{ 13580 mp_number abs_du, abs_dv; 13581 new_number (abs_du); 13582 new_number (abs_dv); 13583 set_number_from_substraction(du, ww->x_coord, w->x_coord); 13584 set_number_from_substraction(dv, ww->y_coord, w->y_coord); 13585 number_clone(abs_du, du); 13586 number_abs(abs_du); 13587 number_clone(abs_dv, dv); 13588 number_abs(abs_dv); 13589 if (number_greaterequal(abs_du, abs_dv)) { 13590 mp_number r1; 13591 new_fraction (r1); 13592 make_fraction (s, dv, du); 13593 take_fraction (r1, x0, s); 13594 set_number_from_substraction(t0, r1, y0); 13595 take_fraction (r1, x1, s); 13596 set_number_from_substraction(t1, r1, y1); 13597 take_fraction (r1, x2, s); 13598 set_number_from_substraction(t2, r1, y2); 13599 if (number_negative(du)) { 13600 number_negate (t0); 13601 number_negate (t1); 13602 number_negate (t2); 13603 } 13604 free_number (r1); 13605 } else { 13606 mp_number r1; 13607 new_fraction (r1); 13608 make_fraction (s, du, dv); 13609 take_fraction (r1, y0, s); 13610 set_number_from_substraction(t0, x0, r1); 13611 take_fraction (r1, y1, s); 13612 set_number_from_substraction(t1, x1, r1); 13613 take_fraction (r1, y2, s); 13614 set_number_from_substraction(t2, x2, r1); 13615 if (number_negative(dv)) { 13616 number_negate (t0); 13617 number_negate (t1); 13618 number_negate (t2); 13619 } 13620 free_number (r1); 13621 } 13622 free_number (abs_du); 13623 free_number (abs_dv); 13624 if (number_negative(t0)) 13625 set_number_to_zero(t0); /* should be positive without rounding error */ 13626} 13627 13628 13629@ The curve has crossed $d_k$ or $d_{k-1}$; its initial segment satisfies 13630$(*)$, and it might cross again and return towards $s_{k-1}$ or $s_k$, 13631respectively, yielding another solution of $(*)$. 13632 13633@<Split the cubic at $t$, and split off another...@>= 13634{ 13635 mp_split_cubic (mp, p, t); 13636 p = mp_next_knot (p); 13637 mp_knot_info (p) = zero_off + rise; 13638 decr (turn_amt); 13639 set_number_from_of_the_way(v, t, x0, x1); 13640 set_number_from_of_the_way(x1, t, x1, x2); 13641 set_number_from_of_the_way(x0, t, v, x1); 13642 set_number_from_of_the_way(v, t, y0, y1); 13643 set_number_from_of_the_way(y1, t, y1, y2); 13644 set_number_from_of_the_way(y0, t, v, y1); 13645 if (turn_amt < 0) { 13646 mp_number arg1, arg2, arg3; 13647 new_number (arg1); 13648 new_number (arg2); 13649 new_number (arg3); 13650 set_number_from_of_the_way(t1, t, t1, t2); 13651 if (number_positive(t1)) 13652 set_number_to_zero(t1); /* without rounding error, |t1| would be |<=0| */ 13653 number_clone(arg2, t1); 13654 number_negate(arg2); 13655 number_clone(arg3, t2); 13656 number_negate(arg3); 13657 crossing_point (t, arg1, arg2, arg3); 13658 free_number (arg1); 13659 free_number (arg2); 13660 free_number (arg3); 13661 if (number_greater(t, fraction_one_t)) 13662 number_clone(t, fraction_one_t); 13663 incr (turn_amt); 13664 if (number_equal(t,fraction_one_t) && (mp_next_knot (p) != q)) { 13665 mp_knot_info (mp_next_knot (p)) = mp_knot_info (mp_next_knot (p)) - rise; 13666 } else { 13667 mp_split_cubic (mp, p, t); 13668 mp_knot_info (mp_next_knot (p)) = zero_off - rise; 13669 set_number_from_of_the_way(v, t, x1, x2); 13670 set_number_from_of_the_way(x1, t, x0, x1); 13671 set_number_from_of_the_way(x2, t, x1, v); 13672 set_number_from_of_the_way(v, t, y1, y2); 13673 set_number_from_of_the_way(y1, t, y0, y1); 13674 set_number_from_of_the_way(y2, t, y1, v); 13675 } 13676 } 13677} 13678 13679 13680@ Now we must consider the general problem of |offset_prep|, when 13681nothing is known about a given cubic. We start by finding its 13682direction in the vicinity of |t=0|. 13683 13684If $z'(t)=0$, the given cubic is numerically unstable but |offset_prep| 13685has not yet introduced any more numerical errors. Thus we can compute 13686the true initial direction for the given cubic, even if it is almost 13687degenerate. 13688 13689@<Find the initial direction |(dx,dy)|@>= 13690number_clone(dx, x0); 13691number_clone(dy, y0); 13692if (number_zero(dx) && number_zero(dy)) { 13693 number_clone(dx, x1); 13694 number_clone(dy, y1); 13695 if (number_zero(dx) && number_zero(dy)) { 13696 number_clone(dx, x2); 13697 number_clone(dy, y2); 13698 } 13699} 13700if (p == c) { 13701 number_clone(dx0, dx); 13702 number_clone(dy0, dy); 13703} 13704 13705@ @<Find the final direction |(dxin,dyin)|@>= 13706number_clone(dxin, x2); 13707number_clone(dyin, y2); 13708if (number_zero(dxin) && number_zero(dyin)) { 13709 number_clone(dxin, x1); 13710 number_clone(dyin, y1); 13711 if (number_zero(dxin) && number_zero(dyin)) { 13712 number_clone(dxin, x0); 13713 number_clone(dyin, y0); 13714 } 13715} 13716 13717@ The next step is to bracket the initial direction between consecutive 13718edges of the pen polygon. We must be careful to turn clockwise only if 13719this makes the turn less than $180^\circ$. (A $180^\circ$ turn must be 13720counter-clockwise in order to make \&{doublepath} envelopes come out 13721@:double_path_}{\&{doublepath} primitive@> 13722right.) This code depends on |w0| being the offset for |(dxin,dyin)|. 13723 13724@<Update |mp_knot_info(p)| and find the offset $w_k$ such that...@>= 13725{ 13726 mp_number ab_vs_cd; 13727 new_number (ab_vs_cd); 13728 ab_vs_cd (ab_vs_cd, dy, dxin, dx, dyin); 13729 turn_amt = mp_get_turn_amt (mp, w0, dx, dy, number_nonnegative(ab_vs_cd)); 13730 free_number (ab_vs_cd); 13731 w = mp_pen_walk (mp, w0, turn_amt); 13732 w0 = w; 13733 mp_knot_info (p) = mp_knot_info (p) + turn_amt; 13734} 13735 13736@ Decide how many pen offsets to go away from |w| in order to find the offset 13737for |(dx,dy)|, going counterclockwise if |ccw| is |true|. This assumes that 13738|w| is the offset for some direction $(x',y')$ from which the angle to |(dx,dy)| 13739in the sense determined by |ccw| is less than or equal to $180^\circ$. 13740 13741If the pen polygon has only two edges, they could both be parallel 13742to |(dx,dy)|. In this case, we must be careful to stop after crossing the first 13743such edge in order to avoid an infinite loop. 13744 13745@<Declarations@>= 13746static integer mp_get_turn_amt (MP mp, mp_knot w, mp_number dx, 13747 mp_number dy, boolean ccw); 13748 13749@ @c 13750integer mp_get_turn_amt (MP mp, mp_knot w, mp_number dx, mp_number dy, boolean ccw) { 13751 mp_knot ww; /* a neighbor of knot~|w| */ 13752 integer s; /* turn amount so far */ 13753 mp_number t; /* |ab_vs_cd| result */ 13754 mp_number arg1, arg2; 13755 s = 0; 13756 new_number (arg1); 13757 new_number (arg2); 13758 new_number (t); 13759 if (ccw) { 13760 ww = mp_next_knot (w); 13761 do { 13762 set_number_from_substraction (arg1, ww->x_coord, w->x_coord); 13763 set_number_from_substraction (arg2, ww->y_coord, w->y_coord); 13764 ab_vs_cd (t, dy, arg1, dx, arg2); 13765 if (number_negative(t)) 13766 break; 13767 incr (s); 13768 w = ww; 13769 ww = mp_next_knot (ww); 13770 } while (number_positive(t)); 13771 } else { 13772 ww = mp_prev_knot (w); 13773 set_number_from_substraction (arg1, w->x_coord, ww->x_coord); 13774 set_number_from_substraction (arg2, w->y_coord, ww->y_coord); 13775 ab_vs_cd (t, dy, arg1, dx, arg2); 13776 while (number_negative(t)) { 13777 decr (s); 13778 w = ww; 13779 ww = mp_prev_knot (ww); 13780 set_number_from_substraction (arg1, w->x_coord, ww->x_coord); 13781 set_number_from_substraction (arg2, w->y_coord, ww->y_coord); 13782 ab_vs_cd (t, dy, arg1, dx, arg2); 13783 } 13784 } 13785 free_number (t); 13786 free_number (arg1); 13787 free_number (arg2); 13788 return s; 13789} 13790 13791 13792@ When we're all done, the final offset is |w0| and the final curve direction 13793is |(dxin,dyin)|. With this knowledge of the incoming direction at |c|, we 13794can correct |mp_info(c)| which was erroneously based on an incoming offset 13795of~|h|. 13796 13797@d fix_by(A) mp_knot_info(c)=mp_knot_info(c)+(A) 13798 13799@<Fix the offset change in |mp_knot_info(c)| and set |c| to the return value of...@>= 13800mp->spec_offset = mp_knot_info (c) - zero_off; 13801if (mp_next_knot (c) == c) { 13802mp_knot_info (c) = zero_off + n; 13803} else { 13804 mp_number ab_vs_cd; 13805 new_number (ab_vs_cd); 13806 fix_by (k_needed); 13807 while (w0 != h) { 13808 fix_by (1); 13809 w0 = mp_next_knot (w0); 13810 } 13811 while (mp_knot_info (c) <= zero_off - n) 13812 fix_by (n); 13813 while (mp_knot_info (c) > zero_off) 13814 fix_by (-n); 13815 ab_vs_cd (ab_vs_cd, dy0, dxin, dx0, dyin); 13816 if ((mp_knot_info (c) != zero_off) && number_nonnegative(ab_vs_cd)) 13817 fix_by (n); 13818 free_number (ab_vs_cd); 13819} 13820 13821 13822@ Finally we want to reduce the general problem to situations that 13823|fin_offset_prep| can handle. We split the cubic into at most three parts 13824with respect to $d_{k-1}$, and apply |fin_offset_prep| to each part. 13825 13826@<Complete the offset splitting process@>= 13827ww = mp_prev_knot (w); 13828@<Compute test coeff...@>; 13829@<Find the first |t| where $d(t)$ crosses $d_{k-1}$ or set 13830 |t:=fraction_one+1|@>; 13831if (number_greater(t, fraction_one_t)) { 13832 mp_fin_offset_prep (mp, p, w, x0, x1, x2, y0, y1, y2, 1, turn_amt); 13833} else { 13834 mp_split_cubic (mp, p, t); 13835 r = mp_next_knot (p); 13836 set_number_from_of_the_way(x1a, t, x0, x1); 13837 set_number_from_of_the_way(x1, t, x1, x2); 13838 set_number_from_of_the_way(x2a, t, x1a, x1); 13839 set_number_from_of_the_way(y1a, t, y0, y1); 13840 set_number_from_of_the_way(y1, t, y1, y2); 13841 set_number_from_of_the_way(y2a, t, y1a, y1); 13842 mp_fin_offset_prep (mp, p, w, x0, x1a, x2a, y0, y1a, y2a, 1, 0); 13843 number_clone(x0, x2a); 13844 number_clone(y0, y2a); 13845 mp_knot_info (r) = zero_off - 1; 13846 if (turn_amt >= 0) { 13847 mp_number arg1, arg2, arg3; 13848 new_number(arg1); 13849 new_number(arg2); 13850 new_number(arg3); 13851 set_number_from_of_the_way(t1, t, t1, t2); 13852 if (number_positive(t1)) 13853 set_number_to_zero(t1); 13854 number_clone(arg2, t1); 13855 number_negate(arg2); 13856 number_clone(arg3, t2); 13857 number_negate(arg3); 13858 crossing_point (t, arg1, arg2, arg3); 13859 free_number (arg1); 13860 free_number (arg2); 13861 free_number (arg3); 13862 if (number_greater(t, fraction_one_t)) 13863 number_clone (t, fraction_one_t); 13864 @<Split off another rising cubic for |fin_offset_prep|@>; 13865 mp_fin_offset_prep (mp, r, ww, x0, x1, x2, y0, y1, y2, -1, 0); 13866 } else { 13867 mp_fin_offset_prep (mp, r, ww, x0, x1, x2, y0, y1, y2, -1, (-1 - turn_amt)); 13868 } 13869} 13870 13871 13872@ @<Split off another rising cubic for |fin_offset_prep|@>= 13873mp_split_cubic (mp, r, t); 13874mp_knot_info (mp_next_knot (r)) = zero_off + 1; 13875set_number_from_of_the_way(x1a, t, x1, x2); 13876set_number_from_of_the_way(x1, t, x0, x1); 13877set_number_from_of_the_way(x0a, t, x1, x1a); 13878set_number_from_of_the_way(y1a, t, y1, y2); 13879set_number_from_of_the_way(y1, t, y0, y1); 13880set_number_from_of_the_way(y0a, t, y1, y1a); 13881mp_fin_offset_prep (mp, mp_next_knot (r), w, x0a, x1a, x2, y0a, y1a, y2, 1, turn_amt); 13882number_clone(x2, x0a); 13883number_clone(y2, y0a) 13884 13885@ At this point, the direction of the incoming pen edge is |(-du,-dv)|. 13886When the component of $d(t)$ perpendicular to |(-du,-dv)| crosses zero, we 13887need to decide whether the directions are parallel or antiparallel. We 13888can test this by finding the dot product of $d(t)$ and |(-du,-dv)|, but this 13889should be avoided when the value of |turn_amt| already determines the 13890answer. If |t2<0|, there is one crossing and it is antiparallel only if 13891|turn_amt>=0|. If |turn_amt<0|, there should always be at least one 13892crossing and the first crossing cannot be antiparallel. 13893 13894@<Find the first |t| where $d(t)$ crosses $d_{k-1}$ or set...@>= 13895crossing_point (t, t0, t1, t2); 13896if (turn_amt >= 0) { 13897 if (number_negative(t2)) { 13898 number_clone(t, fraction_one_t); 13899 number_add_scaled (t, 1); 13900 } else { 13901 mp_number tmp, arg1, r1; 13902 new_fraction (r1); 13903 new_number(tmp); 13904 new_number(arg1); 13905 set_number_from_of_the_way(u0, t, x0, x1); 13906 set_number_from_of_the_way(u1, t, x1, x2); 13907 set_number_from_of_the_way(tmp, t, u0, u1); 13908 number_clone (arg1, du); 13909 number_abs (arg1); 13910 take_fraction (ss, arg1, tmp); 13911 set_number_from_of_the_way(v0, t, y0, y1); 13912 set_number_from_of_the_way(v1, t, y1, y2); 13913 set_number_from_of_the_way(tmp, t, v0, v1); 13914 number_clone (arg1, dv); 13915 number_abs (arg1); 13916 take_fraction (r1, arg1, tmp); 13917 number_add (ss, r1); 13918 free_number (tmp); 13919 if (number_negative(ss)) { 13920 number_clone(t, fraction_one_t); 13921 number_add_scaled (t, 1); 13922 } 13923 free_number(arg1); 13924 free_number(r1); 13925 } 13926} else if (number_greater(t, fraction_one_t)) { 13927 number_clone (t, fraction_one_t); 13928} 13929 13930@ @<Other local variables for |offset_prep|@>= 13931mp_number u0, u1, v0, v1; /* intermediate values for $d(t)$ calculation */ 13932int d_sign; /* sign of overall change in direction for this cubic */ 13933 13934@ If the cubic almost has a cusp, it is a numerically ill-conditioned 13935problem to decide which way it loops around but that's OK as long we're 13936consistent. To make \&{doublepath} envelopes work properly, reversing 13937the path should always change the sign of |turn_amt|. 13938 13939@<Decide on the net change in pen offsets and set |turn_amt|@>= 13940{ 13941 mp_number ab_vs_cd; 13942 new_number (ab_vs_cd); 13943 ab_vs_cd (ab_vs_cd, dx, dyin, dxin, dy); 13944 if (number_negative (ab_vs_cd)) 13945 d_sign = -1; 13946 else if (number_zero (ab_vs_cd)) 13947 d_sign = 0; 13948 else 13949 d_sign = 1; 13950 free_number (ab_vs_cd); 13951} 13952if (d_sign == 0) { 13953 @<Check rotation direction based on node position@> 13954} 13955if (d_sign == 0) { 13956 if (number_zero(dx)) { 13957 if (number_positive(dy)) 13958 d_sign = 1; 13959 else 13960 d_sign = -1; 13961 } else { 13962 if (number_positive(dx)) 13963 d_sign = 1; 13964 else 13965 d_sign = -1; 13966 } 13967} 13968@<Make |ss| negative if and only if the total change in direction is 13969 more than $180^\circ$@>; 13970turn_amt = mp_get_turn_amt (mp, w, dxin, dyin, (d_sign > 0)); 13971if (number_negative(ss)) 13972 turn_amt = turn_amt - d_sign * n 13973 13974@ We check rotation direction by looking at the vector connecting the current 13975node with the next. If its angle with incoming and outgoing tangents has the 13976same sign, we pick this as |d_sign|, since it means we have a flex, not a cusp. 13977Otherwise we proceed to the cusp code. 13978 13979@<Check rotation direction based on node position@>= 13980{ 13981 mp_number ab_vs_cd1, ab_vs_cd2, t; 13982 new_number (ab_vs_cd1); 13983 new_number (ab_vs_cd2); 13984 new_number (t); 13985 set_number_from_substraction(u0, q->x_coord, p->x_coord); 13986 set_number_from_substraction(u1, q->y_coord, p->y_coord); 13987 ab_vs_cd (ab_vs_cd1, dx, u1, u0, dy); 13988 ab_vs_cd (ab_vs_cd2, u0, dyin, dxin, u1); 13989 set_number_from_addition (t, ab_vs_cd1, ab_vs_cd2); 13990 number_half (t); 13991 if (number_negative (t)) 13992 d_sign = -1; 13993 else if (number_zero (t)) 13994 d_sign = 0; 13995 else 13996 d_sign = 1; 13997 free_number (t); 13998 free_number (ab_vs_cd1); 13999 free_number (ab_vs_cd2); 14000} 14001 14002@ In order to be invariant under path reversal, the result of this computation 14003should not change when |x0|, |y0|, $\ldots$ are all negated and |(x0,y0)| is 14004then swapped with |(x2,y2)|. We make use of the identities 14005|take_fraction(-a,-b)=take_fraction(a,b)| and 14006|t_of_the_way(-a,-b)=-(t_of_the_way(a,b))|. 14007 14008@<Make |ss| negative if and only if the total change in direction is...@>= 14009{ 14010 mp_number r1, r2, arg1; 14011 new_number (arg1); 14012 new_fraction (r1); 14013 new_fraction (r2); 14014 take_fraction (r1, x0, y2); 14015 take_fraction (r2, x2, y0); 14016 number_half (r1); 14017 number_half (r2); 14018 set_number_from_substraction(t0, r1, r2); 14019 set_number_from_addition (arg1, y0, y2); 14020 take_fraction (r1, x1, arg1); 14021 set_number_from_addition (arg1, x0, x2); 14022 take_fraction (r1, y1, arg1); 14023 number_half (r1); 14024 number_half (r2); 14025 set_number_from_substraction(t1, r1, r2); 14026 free_number (arg1); 14027 free_number (r1); 14028 free_number (r2); 14029} 14030if (number_zero(t0)) 14031 set_number_from_scaled(t0, d_sign); /* path reversal always negates |d_sign| */ 14032if (number_positive(t0)) { 14033 mp_number arg3; 14034 new_number(arg3); 14035 number_clone(arg3, t0); 14036 number_negate(arg3); 14037 crossing_point (t, t0, t1, arg3); 14038 free_number (arg3); 14039 set_number_from_of_the_way(u0, t, x0, x1); 14040 set_number_from_of_the_way(u1, t, x1, x2); 14041 set_number_from_of_the_way(v0, t, y0, y1); 14042 set_number_from_of_the_way(v1, t, y1, y2); 14043} else { 14044 mp_number arg1; 14045 new_number(arg1); 14046 number_clone(arg1, t0); 14047 number_negate(arg1); 14048 crossing_point (t, arg1, t1, t0); 14049 free_number (arg1); 14050 set_number_from_of_the_way(u0, t, x2, x1); 14051 set_number_from_of_the_way(u1, t, x1, x0); 14052 set_number_from_of_the_way(v0, t, y2, y1); 14053 set_number_from_of_the_way(v1, t, y1, y0); 14054} 14055{ 14056 mp_number tmp1, tmp2, r1, r2, arg1; 14057 new_fraction (r1); 14058 new_fraction (r2); 14059 new_number(arg1); 14060 new_number(tmp1); 14061 new_number(tmp2); 14062 set_number_from_of_the_way(tmp1, t, u0, u1); 14063 set_number_from_of_the_way(tmp2, t, v0, v1); 14064 set_number_from_addition(arg1, x0, x2); 14065 take_fraction (r1, arg1, tmp1); 14066 set_number_from_addition(arg1, y0, y2); 14067 take_fraction (r2, arg1, tmp2); 14068 set_number_from_addition (ss, r1, r2); 14069 free_number (arg1); 14070 free_number (r1); 14071 free_number (r2); 14072 free_number (tmp1); 14073 free_number (tmp2); 14074} 14075 14076 14077@ Here's a routine that prints an envelope spec in symbolic form. It assumes 14078that the |cur_pen| has not been walked around to the first offset. 14079 14080@c 14081static void mp_print_spec (MP mp, mp_knot cur_spec, mp_knot cur_pen, 14082 const char *s) { 14083 mp_knot p, q; /* list traversal */ 14084 mp_knot w; /* the current pen offset */ 14085 mp_print_diagnostic (mp, "Envelope spec", s, true); 14086 p = cur_spec; 14087 w = mp_pen_walk (mp, cur_pen, mp->spec_offset); 14088 mp_print_ln (mp); 14089 mp_print_two (mp, cur_spec->x_coord, cur_spec->y_coord); 14090 mp_print (mp, " % beginning with offset "); 14091 mp_print_two (mp, w->x_coord, w->y_coord); 14092 do { 14093 while (1) { 14094 q = mp_next_knot (p); 14095 @<Print the cubic between |p| and |q|@>; 14096 p = q; 14097 if ((p == cur_spec) || (mp_knot_info (p) != zero_off)) 14098 break; 14099 } 14100 if (mp_knot_info (p) != zero_off) { 14101 @<Update |w| as indicated by |mp_knot_info(p)| and print an explanation@>; 14102 } 14103 } while (p != cur_spec); 14104 mp_print_nl (mp, " & cycle"); 14105 mp_end_diagnostic (mp, true); 14106} 14107 14108 14109@ @<Update |w| as indicated by |mp_knot_info(p)| and print an explanation@>= 14110{ 14111 w = mp_pen_walk (mp, w, (mp_knot_info (p) - zero_off)); 14112 mp_print (mp, " % "); 14113 if (mp_knot_info (p) > zero_off) 14114 mp_print (mp, "counter"); 14115 mp_print (mp, "clockwise to offset "); 14116 mp_print_two (mp, w->x_coord, w->y_coord); 14117} 14118 14119 14120@ @<Print the cubic between |p| and |q|@>= 14121{ 14122 mp_print_nl (mp, " ..controls "); 14123 mp_print_two (mp, p->right_x, p->right_y); 14124 mp_print (mp, " and "); 14125 mp_print_two (mp, q->left_x, q->left_y); 14126 mp_print_nl (mp, " .."); 14127 mp_print_two (mp, q->x_coord, q->y_coord); 14128} 14129 14130 14131@ Once we have an envelope spec, the remaining task to construct the actual 14132envelope by offsetting each cubic as determined by the |info| fields in 14133the knots. First we use |offset_prep| to convert the |c| into an envelope 14134spec. Then we add the offsets so that |c| becomes a cyclic path that represents 14135the envelope. 14136 14137The |ljoin| and |miterlim| parameters control the treatment of points where the 14138pen offset changes, and |lcap| controls the endpoints of a \&{doublepath}. 14139The endpoints are easily located because |c| is given in undoubled form 14140and then doubled in this procedure. We use |spec_p1| and |spec_p2| to keep 14141track of the endpoints and treat them like very sharp corners. 14142Butt end caps are treated like beveled joins; round end caps are treated like 14143round joins; and square end caps are achieved by setting |join_type:=3|. 14144 14145None of these parameters apply to inside joins where the convolution tracing 14146has retrograde lines. In such cases we use a simple connect-the-endpoints 14147approach that is achieved by setting |join_type:=2|. 14148 14149@c 14150static mp_knot mp_make_envelope (MP mp, mp_knot c, mp_knot h, quarterword ljoin, 14151 quarterword lcap, mp_number miterlim) { 14152 mp_knot p, q, r, q0; /* for manipulating the path */ 14153 mp_knot w, w0; /* the pen knot for the current offset */ 14154 halfword k, k0; /* controls pen edge insertion */ 14155 mp_number qx, qy; /* unshifted coordinates of |q| */ 14156 mp_fraction dxin, dyin, dxout, dyout; /* directions at |q| when square or mitered */ 14157 int join_type = 0; /* codes |0..3| for mitered, round, beveled, or square */ 14158 @<Other local variables for |make_envelope|@>; 14159 new_number (max_ht); 14160 new_number (tmp); 14161 new_fraction(dxin); 14162 new_fraction(dyin); 14163 new_fraction(dxout); 14164 new_fraction(dyout); 14165 mp->spec_p1 = NULL; 14166 mp->spec_p2 = NULL; 14167 new_number(qx); 14168 new_number(qy); 14169 @<If endpoint, double the path |c|, and set |spec_p1| and |spec_p2|@>; 14170 @<Use |offset_prep| to compute the envelope spec then walk |h| around to 14171 the initial offset@>; 14172 w = h; 14173 p = c; 14174 do { 14175 q = mp_next_knot (p); 14176 q0 = q; 14177 number_clone (qx, q->x_coord); 14178 number_clone (qy, q->y_coord); 14179 k = mp_knot_info (q); 14180 k0 = k; 14181 w0 = w; 14182 if (k != zero_off) { 14183 @<Set |join_type| to indicate how to handle offset changes at~|q|@>; 14184 } 14185 @<Add offset |w| to the cubic from |p| to |q|@>; 14186 while (k != zero_off) { 14187 @<Step |w| and move |k| one step closer to |zero_off|@>; 14188 if ((join_type == 1) || (k == zero_off)) { 14189 mp_number xtot, ytot; 14190 new_number(xtot); 14191 new_number(ytot); 14192 set_number_from_addition (xtot, qx, w->x_coord); 14193 set_number_from_addition (ytot, qy, w->y_coord); 14194 q = mp_insert_knot (mp, q, xtot, ytot); 14195 } 14196 } 14197 if (q != mp_next_knot (p)) { 14198 @<Set |p=mp_link(p)| and add knots between |p| and |q| as 14199 required by |join_type|@>; 14200 } 14201 p = q; 14202 } while (q0 != c); 14203 free_number (max_ht); 14204 free_number (tmp); 14205 free_number (qx); 14206 free_number (qy); 14207 free_number (dxin); 14208 free_number (dyin); 14209 free_number (dxout); 14210 free_number (dyout); 14211 return c; 14212} 14213 14214 14215@ @<Use |offset_prep| to compute the envelope spec then walk |h| around to...@>= 14216c = mp_offset_prep (mp, c, h); 14217if (number_positive(internal_value (mp_tracing_specs))) 14218 mp_print_spec (mp, c, h, ""); 14219h = mp_pen_walk (mp, h, mp->spec_offset) 14220 14221 14222@ Mitered and squared-off joins depend on path directions that are difficult to 14223compute for degenerate cubics. The envelope spec computed by |offset_prep| can 14224have degenerate cubics only if the entire cycle collapses to a single 14225degenerate cubic. Setting |join_type:=2| in this case makes the computed 14226envelope degenerate as well. 14227 14228@<Set |join_type| to indicate how to handle offset changes at~|q|@>= 14229if (k < zero_off) { 14230 join_type = 2; 14231} else { 14232 if ((q != mp->spec_p1) && (q != mp->spec_p2)) 14233 join_type = ljoin; 14234 else if (lcap == 2) 14235 join_type = 3; 14236 else 14237 join_type = 2 - lcap; 14238 if ((join_type == 0) || (join_type == 3)) { 14239 @<Set the incoming and outgoing directions at |q|; in case of 14240 degeneracy set |join_type:=2|@>; 14241 if (join_type == 0) { 14242 @<If |miterlim| is less than the secant of half the angle at |q| 14243 then set |join_type:=2|@>; 14244 } 14245 } 14246} 14247 14248 14249@ @<If |miterlim| is less than the secant of half the angle at |q|...@>= 14250{ 14251 mp_number r1, r2; 14252 new_fraction (r1); 14253 new_fraction (r2); 14254 take_fraction (r1, dxin, dxout); 14255 take_fraction (r2, dyin, dyout); 14256 number_add (r1, r2); 14257 number_half (r1); 14258 number_add (r1, fraction_half_t); 14259 take_fraction (tmp, miterlim, r1); 14260 if (number_less(tmp, unity_t)) { 14261 mp_number ret; 14262 new_number (ret); 14263 take_scaled (ret, miterlim, tmp); 14264 if (number_less(ret, unity_t)) 14265 join_type = 2; 14266 free_number (ret); 14267 } 14268 free_number (r1); 14269 free_number (r2); 14270} 14271 14272 14273@ @<Other local variables for |make_envelope|@>= 14274mp_number tmp; /* a temporary value */ 14275 14276@ The coordinates of |p| have already been shifted unless |p| is the first 14277knot in which case they get shifted at the very end. 14278 14279@<Add offset |w| to the cubic from |p| to |q|@>= 14280number_add (p->right_x, w->x_coord); 14281number_add (p->right_y, w->y_coord); 14282number_add (q->left_x, w->x_coord); 14283number_add (q->left_y, w->y_coord); 14284number_add (q->x_coord, w->x_coord); 14285number_add (q->y_coord, w->y_coord); 14286mp_left_type (q) = mp_explicit; 14287mp_right_type (q) = mp_explicit 14288 14289@ @<Step |w| and move |k| one step closer to |zero_off|@>= 14290if (k > zero_off) { 14291 w = mp_next_knot (w); 14292 decr (k); 14293} else { 14294 w = mp_prev_knot (w); 14295 incr (k); 14296} 14297 14298 14299@ The cubic from |q| to the new knot at |(x,y)| becomes a line segment and 14300the |mp_right_x| and |mp_right_y| fields of |r| are set from |q|. This is done in 14301case the cubic containing these control points is ``yet to be examined.'' 14302 14303@<Declarations@>= 14304static mp_knot mp_insert_knot (MP mp, mp_knot q, mp_number x, mp_number y); 14305 14306@ @c 14307mp_knot mp_insert_knot (MP mp, mp_knot q, mp_number x, mp_number y) { 14308 /* returns the inserted knot */ 14309 mp_knot r; /* the new knot */ 14310 r = mp_new_knot (mp); 14311 mp_next_knot (r) = mp_next_knot (q); 14312 mp_next_knot (q) = r; 14313 number_clone (r->right_x, q->right_x); 14314 number_clone (r->right_y, q->right_y); 14315 number_clone (r->x_coord, x); 14316 number_clone (r->y_coord, y); 14317 number_clone (q->right_x, q->x_coord); 14318 number_clone (q->right_y, q->y_coord); 14319 number_clone (r->left_x, r->x_coord); 14320 number_clone (r->left_y, r->y_coord); 14321 mp_left_type (r) = mp_explicit; 14322 mp_right_type (r) = mp_explicit; 14323 mp_originator (r) = mp_program_code; 14324 return r; 14325} 14326 14327 14328@ After setting |p:=mp_link(p)|, either |join_type=1| or |q=mp_link(p)|. 14329 14330@<Set |p=mp_link(p)| and add knots between |p| and |q| as...@>= 14331{ 14332 p = mp_next_knot (p); 14333 if ((join_type == 0) || (join_type == 3)) { 14334 if (join_type == 0) { 14335 @<Insert a new knot |r| between |p| and |q| as required for a mitered join@> 14336 } else { 14337 @<Make |r| the last of two knots inserted between |p| and |q| to form a 14338 squared join@>; 14339 } 14340 if (r != NULL) { 14341 number_clone (r->right_x, r->x_coord); 14342 number_clone (r->right_y, r->y_coord); 14343 } 14344 } 14345} 14346 14347 14348@ For very small angles, adding a knot is unnecessary and would cause numerical 14349problems, so we just set |r:=NULL| in that case. 14350 14351@d near_zero_angle_k ((math_data *)mp->math)->near_zero_angle_t 14352 14353@<Insert a new knot |r| between |p| and |q| as required for a mitered join@>= 14354{ 14355 mp_number det; /* a determinant used for mitered join calculations */ 14356 mp_number absdet; 14357 mp_number r1, r2; 14358 new_fraction (r1); 14359 new_fraction (r2); 14360 new_fraction (det); 14361 new_fraction (absdet); 14362 take_fraction (r1, dyout, dxin); 14363 take_fraction (r2, dxout, dyin); 14364 set_number_from_substraction(det, r1, r2); 14365 number_clone (absdet, det); 14366 number_abs (absdet); 14367 if (number_less (absdet, near_zero_angle_k)) { 14368 r = NULL; /* sine $<10^{-4}$ */ 14369 } else { 14370 mp_number xtot, ytot, xsub, ysub; 14371 new_fraction(xsub); 14372 new_fraction(ysub); 14373 new_number(xtot); 14374 new_number(ytot); 14375 set_number_from_substraction (tmp, q->x_coord, p->x_coord); 14376 take_fraction (r1, tmp, dyout); 14377 set_number_from_substraction (tmp, q->y_coord, p->y_coord); 14378 take_fraction (r2, tmp, dxout); 14379 set_number_from_substraction (tmp, r1, r2); 14380 make_fraction (r1, tmp, det); 14381 number_clone (tmp, r1); 14382 take_fraction (xsub, tmp, dxin); 14383 take_fraction (ysub, tmp, dyin); 14384 set_number_from_addition(xtot, p->x_coord, xsub); 14385 set_number_from_addition(ytot, p->y_coord, ysub); 14386 r = mp_insert_knot (mp, p, xtot, ytot); 14387 free_number (xtot); 14388 free_number (ytot); 14389 free_number (xsub); 14390 free_number (ysub); 14391 } 14392 free_number (r1); 14393 free_number (r2); 14394 free_number (det); 14395 free_number (absdet); 14396} 14397 14398 14399@ @<Make |r| the last of two knots inserted between |p| and |q| to form a...@>= 14400{ 14401 mp_number ht_x, ht_y; /* perpendicular to the segment from |p| to |q| */ 14402 mp_number ht_x_abs, ht_y_abs; /* absolutes */ 14403 mp_number xtot, ytot, xsub, ysub; 14404 new_fraction(xsub); 14405 new_fraction(ysub); 14406 new_number(xtot); 14407 new_number(ytot); 14408 new_fraction (ht_x); 14409 new_fraction (ht_y); 14410 new_fraction (ht_x_abs); 14411 new_fraction (ht_y_abs); 14412 set_number_from_substraction(ht_x, w->y_coord, w0->y_coord); 14413 set_number_from_substraction(ht_y, w0->x_coord, w->x_coord); 14414 number_clone (ht_x_abs, ht_x); 14415 number_clone (ht_y_abs, ht_y); 14416 number_abs (ht_x_abs); 14417 number_abs (ht_y_abs); 14418 while (number_less(ht_x_abs, fraction_half_t) && number_less(ht_y_abs, fraction_half_t)) { 14419 number_double(ht_x); 14420 number_double(ht_y); 14421 number_clone (ht_x_abs, ht_x); 14422 number_clone (ht_y_abs, ht_y); 14423 number_abs (ht_x_abs); 14424 number_abs (ht_y_abs); 14425 } 14426 @<Scan the pen polygon between |w0| and |w| and make |max_ht| the range dot 14427 product with |(ht_x,ht_y)|@>; 14428 { 14429 mp_number r1 ,r2; 14430 new_fraction (r1); 14431 new_fraction (r2); 14432 take_fraction (r1, dxin, ht_x); 14433 take_fraction (r2, dyin, ht_y); 14434 number_add (r1, r2); 14435 make_fraction (tmp, max_ht, r1); 14436 free_number (r1); 14437 free_number (r2); 14438 } 14439 take_fraction (xsub, tmp, dxin); 14440 take_fraction (ysub, tmp, dyin); 14441 set_number_from_addition(xtot, p->x_coord, xsub); 14442 set_number_from_addition(ytot, p->y_coord, ysub); 14443 r = mp_insert_knot (mp, p, xtot, ytot); 14444 /* clang: value never read */ assert(r); 14445 { 14446 mp_number r1 ,r2; 14447 new_fraction (r1); 14448 new_fraction (r2); 14449 take_fraction (r1, dxout, ht_x); 14450 take_fraction (r2, dyout, ht_y); 14451 number_add (r1, r2); 14452 make_fraction (tmp, max_ht, r1); 14453 free_number (r1); 14454 free_number (r2); 14455 } 14456 take_fraction (xsub, tmp, dxout); 14457 take_fraction (ysub, tmp, dyout); 14458 set_number_from_addition(xtot, q->x_coord, xsub); 14459 set_number_from_addition(ytot, q->y_coord, ysub); 14460 r = mp_insert_knot (mp, p, xtot, ytot); 14461 free_number (xsub); 14462 free_number (ysub); 14463 free_number (xtot); 14464 free_number (ytot); 14465 free_number (ht_x); 14466 free_number (ht_y); 14467 free_number (ht_x_abs); 14468 free_number (ht_y_abs); 14469} 14470 14471 14472@ @<Other local variables for |make_envelope|@>= 14473mp_number max_ht; /* maximum height of the pen polygon above the |w0|-|w| line */ 14474halfword kk; /* keeps track of the pen vertices being scanned */ 14475mp_knot ww; /* the pen vertex being tested */ 14476 14477@ The dot product of the vector from |w0| to |ww| with |(ht_x,ht_y)| ranges 14478from zero to |max_ht|. 14479 14480@<Scan the pen polygon between |w0| and |w| and make |max_ht| the range...@>= 14481set_number_to_zero (max_ht); 14482kk = zero_off; 14483ww = w; 14484while (1) { 14485 @<Step |ww| and move |kk| one step closer to |k0|@>; 14486 if (kk == k0) 14487 break; 14488 { 14489 mp_number r1, r2; 14490 new_fraction (r1); 14491 new_fraction (r2); 14492 set_number_from_substraction (tmp, ww->x_coord, w0->x_coord); 14493 take_fraction (r1, tmp, ht_x); 14494 set_number_from_substraction (tmp, ww->y_coord, w0->y_coord); 14495 take_fraction (r2, tmp, ht_y); 14496 set_number_from_addition (tmp, r1, r2); 14497 free_number (r1); 14498 free_number (r2); 14499 } 14500 if (number_greater(tmp, max_ht)) 14501 number_clone(max_ht, tmp); 14502} 14503 14504 14505@ @<Step |ww| and move |kk| one step closer to |k0|@>= 14506if (kk > k0) { 14507 ww = mp_next_knot (ww); 14508 decr (kk); 14509} else { 14510 ww = mp_prev_knot (ww); 14511 incr (kk); 14512} 14513 14514 14515@ @<If endpoint, double the path |c|, and set |spec_p1| and |spec_p2|@>= 14516if (mp_left_type (c) == mp_endpoint) { 14517 mp->spec_p1 = mp_htap_ypoc (mp, c); 14518 mp->spec_p2 = mp->path_tail; 14519 mp_originator (mp->spec_p1) = mp_program_code; 14520 mp_next_knot (mp->spec_p2) = mp_next_knot (mp->spec_p1); 14521 mp_next_knot (mp->spec_p1) = c; 14522 mp_remove_cubic (mp, mp->spec_p1); 14523 c = mp->spec_p1; 14524 if (c != mp_next_knot (c)) { 14525 mp_originator (mp->spec_p2) = mp_program_code; 14526 mp_remove_cubic (mp, mp->spec_p2); 14527 } else { 14528 @<Make |c| look like a cycle of length one@>; 14529 } 14530} 14531 14532@ @<Make |c| look like a cycle of length one@>= 14533{ 14534 mp_left_type (c) = mp_explicit; 14535 mp_right_type (c) = mp_explicit; 14536 number_clone(c->left_x, c->x_coord); 14537 number_clone(c->left_y, c->y_coord); 14538 number_clone(c->right_x, c->x_coord); 14539 number_clone(c->right_y, c->y_coord); 14540} 14541 14542 14543@ In degenerate situations we might have to look at the knot preceding~|q|. 14544That knot is |p| but if |p<>c|, its coordinates have already been offset by |w|. 14545 14546@<Set the incoming and outgoing directions at |q|; in case of...@>= 14547{ 14548 set_number_from_substraction(dxin, q->x_coord, q->left_x); 14549 set_number_from_substraction(dyin, q->y_coord, q->left_y); 14550 if (number_zero(dxin) && number_zero(dyin)) { 14551 set_number_from_substraction(dxin, q->x_coord, p->right_x); 14552 set_number_from_substraction(dyin, q->y_coord, p->right_y); 14553 if (number_zero(dxin) && number_zero(dyin)) { 14554 set_number_from_substraction(dxin, q->x_coord, p->x_coord); 14555 set_number_from_substraction(dyin, q->y_coord, p->y_coord); 14556 if (p != c) { /* the coordinates of |p| have been offset by |w| */ 14557 number_add(dxin, w->x_coord); 14558 number_add(dyin, w->y_coord); 14559 } 14560 } 14561 } 14562 pyth_add (tmp, dxin, dyin); 14563 if (number_zero(tmp)) { 14564 join_type = 2; 14565 } else { 14566 mp_number r1; 14567 new_fraction (r1); 14568 make_fraction (r1, dxin, tmp); 14569 number_clone(dxin, r1); 14570 make_fraction (r1, dyin, tmp); 14571 number_clone(dyin, r1); 14572 free_number (r1); 14573 @<Set the outgoing direction at |q|@>; 14574 } 14575} 14576 14577 14578@ If |q=c| then the coordinates of |r| and the control points between |q| 14579and~|r| have already been offset by |h|. 14580 14581@<Set the outgoing direction at |q|@>= 14582{ 14583 set_number_from_substraction(dxout, q->right_x, q->x_coord); 14584 set_number_from_substraction(dyout, q->right_y, q->y_coord); 14585 if (number_zero(dxout) && number_zero(dyout)) { 14586 r = mp_next_knot (q); 14587 set_number_from_substraction(dxout, r->left_x, q->x_coord); 14588 set_number_from_substraction(dyout, r->left_y, q->y_coord); 14589 if (number_zero(dxout) && number_zero(dyout)) { 14590 set_number_from_substraction(dxout, r->x_coord, q->x_coord); 14591 set_number_from_substraction(dyout, r->y_coord, q->y_coord); 14592 } 14593 } 14594 if (q == c) { 14595 number_substract(dxout, h->x_coord); 14596 number_substract(dyout, h->y_coord); 14597 } 14598 pyth_add (tmp, dxout, dyout); 14599 if (number_zero(tmp)) { 14600 /* |mp_confusion (mp, "degenerate spec");| */ 14601@:this can't happen degerate spec}{\quad degenerate spec@>; 14602 /* But apparently, it actually can happen. The test case is this: 14603 14604 path p; 14605 linejoin := mitered; 14606 p:= (10,0)..(0,10)..(-10,0)..(0,-10)..cycle; 14607 addto currentpicture contour p withpen pensquare; 14608 14609 The reason for failure here is the addition of |r != q| in revision 1757 14610 in ``Advance |p| to node |q|, removing any ``dead'' cubics'', which itself 14611 was needed to fix a bug with disappearing knots in a path that was rotated 14612 exactly 45 degrees (luatex.org bug 530). 14613 */ 14614 } else { 14615 mp_number r1; 14616 new_fraction (r1); 14617 make_fraction (r1, dxout, tmp); 14618 number_clone(dxout, r1); 14619 make_fraction (r1, dyout, tmp); 14620 number_clone(dyout, r1); 14621 free_number (r1); 14622 } 14623} 14624 14625 14626@* Direction and intersection times. 14627A path of length $n$ is defined parametrically by functions $x(t)$ and 14628$y(t)$, for |0<=t<=n|; we can regard $t$ as the ``time'' at which the path 14629reaches the point $\bigl(x(t),y(t)\bigr)$. In this section of the program 14630we shall consider operations that determine special times associated with 14631given paths: the first time that a path travels in a given direction, and 14632a pair of times at which two paths cross each other. 14633 14634@ Let's start with the easier task. The function |find_direction_time| is 14635given a direction |(x,y)| and a path starting at~|h|. If the path never 14636travels in direction |(x,y)|, the direction time will be~|-1|; otherwise 14637it will be nonnegative. 14638 14639Certain anomalous cases can arise: If |(x,y)=(0,0)|, so that the given 14640direction is undefined, the direction time will be~0. If $\bigl(x'(t), 14641y'(t)\bigr)=(0,0)$, so that the path direction is undefined, it will be 14642assumed to match any given direction at time~|t|. 14643 14644The routine solves this problem in nondegenerate cases by rotating the path 14645and the given direction so that |(x,y)=(1,0)|; i.e., the main task will be 14646to find when a given path first travels ``due east.'' 14647 14648@c 14649static void mp_find_direction_time (MP mp, mp_number *ret, mp_number x_orig, mp_number y_orig, mp_knot h) { 14650 mp_number max; /* $\max\bigl(\vert x\vert,\vert y\vert\bigr)$ */ 14651 mp_knot p, q; /* for list traversal */ 14652 mp_number n; /* the direction time at knot |p| */ 14653 mp_number tt; /* the direction time within a cubic */ 14654 mp_number x, y; 14655 mp_number abs_x, abs_y; 14656 /* Other local variables for |find_direction_time| */ 14657 mp_number x1, x2, x3, y1, y2, y3; /* multiples of rotated derivatives */ 14658 mp_number phi; /* angles of exit and entry at a knot */ 14659 mp_number t; /* temp storage */ 14660 mp_number ab_vs_cd; 14661 new_number(max); 14662 new_number(x1); 14663 new_number(x2); 14664 new_number(x3); 14665 new_number(y1); 14666 new_number(y2); 14667 new_number(y3); 14668 new_fraction(t); 14669 new_angle(phi); 14670 new_number (ab_vs_cd); 14671 set_number_to_zero (*ret); /* just in case */ 14672 new_number (x); 14673 new_number (y); 14674 new_number (abs_x); 14675 new_number (abs_y); 14676 new_number (n); 14677 new_fraction (tt); 14678 number_clone (x, x_orig); 14679 number_clone (y, y_orig); 14680 number_clone (abs_x, x_orig); 14681 number_clone (abs_y, y_orig); 14682 number_abs (abs_x); 14683 number_abs (abs_y); 14684 /* Normalize the given direction for better accuracy; 14685 but |return| with zero result if it's zero */ 14686 if (number_less(abs_x, abs_y)) { 14687 mp_number r1; 14688 new_fraction (r1); 14689 make_fraction (r1, x, abs_y); 14690 number_clone(x, r1); 14691 free_number (r1); 14692 if (number_positive(y)) { 14693 number_clone(y, fraction_one_t); 14694 } else { 14695 number_clone(y, fraction_one_t); 14696 number_negate(y); 14697 } 14698 } else if (number_zero(x)) { 14699 goto FREE; 14700 } else { 14701 mp_number r1; 14702 new_fraction (r1); 14703 make_fraction (r1, y, abs_x); 14704 number_clone(y, r1); 14705 free_number (r1); 14706 if (number_positive(x)) { 14707 number_clone(x, fraction_one_t); 14708 } else { 14709 number_clone(x, fraction_one_t); 14710 number_negate(x); 14711 } 14712 } 14713 14714 p = h; 14715 while (1) { 14716 if (mp_right_type (p) == mp_endpoint) 14717 break; 14718 q = mp_next_knot (p); 14719 @<Rotate the cubic between |p| and |q|; then 14720 |goto found| if the rotated cubic travels due east at some time |tt|; 14721 but |break| if an entire cyclic path has been traversed@>; 14722 p = q; 14723 number_add(n, unity_t); 14724 } 14725 set_number_to_unity (*ret); 14726 number_negate(*ret); 14727 goto FREE; 14728FOUND: 14729 set_number_from_addition (*ret, n, tt); 14730 goto FREE; 14731FREE: 14732 free_number (x); 14733 free_number (y); 14734 free_number (abs_x); 14735 free_number (abs_y); 14736 /* Free local variables for |find_direction_time| */ 14737 free_number (x1); 14738 free_number (x2); 14739 free_number (x3); 14740 free_number (y1); 14741 free_number (y2); 14742 free_number (y3); 14743 free_number (t); 14744 free_number (phi); 14745 free_number (ab_vs_cd); 14746 14747 free_number (n); 14748 free_number (max); 14749 free_number (tt); 14750} 14751 14752 14753 14754@ Since we're interested in the tangent directions, we work with the 14755derivative $${1\over3}B'(x_0,x_1,x_2,x_3;t)= 14756B(x_1-x_0,x_2-x_1,x_3-x_2;t)$$ instead of 14757$B(x_0,x_1,x_2,x_3;t)$ itself. The derived coefficients are also scale-d up 14758in order to achieve better accuracy. 14759 14760The given path may turn abruptly at a knot, and it might pass the critical 14761tangent direction at such a time. Therefore we remember the direction |phi| 14762in which the previous rotated cubic was traveling. (The value of |phi| will be 14763undefined on the first cubic, i.e., when |n=0|.) 14764 14765@d we_found_it { 14766 number_clone (tt, t); 14767 fraction_to_round_scaled (tt); 14768 goto FOUND; 14769} 14770 14771@<Rotate the cubic between |p| and |q|; then...@>= 14772set_number_to_zero(tt); 14773/* Set local variables |x1,x2,x3| and |y1,y2,y3| to multiples of the control 14774 points of the rotated derivatives */ 14775{ 14776 mp_number absval; 14777 new_number (absval); 14778 set_number_from_substraction(x1, p->right_x, p->x_coord); 14779 set_number_from_substraction(x2, q->left_x, p->right_x); 14780 set_number_from_substraction(x3, q->x_coord, q->left_x); 14781 set_number_from_substraction(y1, p->right_y, p->y_coord); 14782 set_number_from_substraction(y2, q->left_y, p->right_y); 14783 set_number_from_substraction(y3, q->y_coord, q->left_y); 14784 number_clone(absval, x2); 14785 number_abs(absval); 14786 number_clone(max, x1); 14787 number_abs(max); 14788 if (number_greater(absval, max)) { 14789 number_clone(max, absval); 14790 } 14791 number_clone(absval, x3); 14792 number_abs(absval); 14793 if (number_greater(absval, max)) { 14794 number_clone(max, absval); 14795 } 14796 number_clone(absval, y1); 14797 number_abs(absval); 14798 if (number_greater(absval, max)) { 14799 number_clone(max, absval); 14800 } 14801 number_clone(absval, y2); 14802 number_abs(absval); 14803 if (number_greater(absval, max)) { 14804 number_clone(max, absval); 14805 } 14806 number_clone(absval, y3); 14807 number_abs(absval); 14808 if (number_greater(absval, max)) { 14809 number_clone(max, absval); 14810 } 14811 free_number (absval); 14812 if (number_zero(max)) 14813 goto FOUND; 14814 while (number_less (max, fraction_half_t)) { 14815 number_double(max); 14816 number_double(x1); 14817 number_double(x2); 14818 number_double(x3); 14819 number_double(y1); 14820 number_double(y2); 14821 number_double(y3); 14822 } 14823 number_clone(t, x1); 14824 { 14825 mp_number r1, r2; 14826 new_fraction (r1); 14827 new_fraction (r2); 14828 take_fraction (r1, x1, x); 14829 take_fraction (r2, y1, y); 14830 set_number_from_addition(x1, r1, r2); 14831 take_fraction (r1, y1, x); 14832 take_fraction (r2, t, y); 14833 set_number_from_substraction(y1, r1, r2); 14834 number_clone(t, x2); 14835 take_fraction (r1, x2, x); 14836 take_fraction (r2, y2, y); 14837 set_number_from_addition(x2, r1, r2); 14838 take_fraction (r1, y2, x); 14839 take_fraction (r2, t, y); 14840 set_number_from_substraction(y2, r1, r2); 14841 number_clone(t, x3); 14842 take_fraction (r1, x3 ,x); 14843 take_fraction (r2, y3, y); 14844 set_number_from_addition(x3, r1, r2); 14845 take_fraction (r1, y3, x); 14846 take_fraction (r2, t, y); 14847 set_number_from_substraction(y3, r1, r2); 14848 free_number (r1); 14849 free_number (r2); 14850 } 14851} 14852if (number_zero(y1)) 14853 if (number_zero(x1) || number_positive(x1)) 14854 goto FOUND; 14855if (number_positive(n)) { 14856 /* Exit to |found| if an eastward direction occurs at knot |p| */ 14857 mp_number theta; 14858 mp_number tmp; 14859 new_angle (theta); 14860 n_arg (theta, x1, y1); 14861 new_angle (tmp); 14862 set_number_from_substraction (tmp, theta, one_eighty_deg_t); 14863 14864 if (number_nonnegative(theta) && number_nonpositive(phi) && number_greaterequal(phi, tmp)) { 14865 free_number (tmp); 14866 free_number (theta); 14867 goto FOUND; 14868 } 14869 set_number_from_addition (tmp, theta, one_eighty_deg_t); 14870 if (number_nonpositive(theta) && number_nonnegative(phi) && number_lessequal(phi, tmp)) { 14871 free_number (tmp); 14872 free_number (theta); 14873 goto FOUND; 14874 } 14875 free_number (tmp); 14876 free_number (theta); 14877 14878 if (p == h) 14879 break; 14880} 14881if (number_nonzero(x3) || number_nonzero(y3)) { 14882 n_arg (phi, x3, y3); 14883} 14884/* Exit to |found| if the curve whose derivatives are specified by 14885 |x1,x2,x3,y1,y2,y3| travels eastward at some time~|tt| */ 14886/* In this step we want to use the |crossing_point| routine to find the 14887roots of the quadratic equation $B(y_1,y_2,y_3;t)=0$. 14888Several complications arise: If the quadratic equation has a double root, 14889the curve never crosses zero, and |crossing_point| will find nothing; 14890this case occurs iff $y_1y_3=y_2^2$ and $y_1y_2<0$. If the quadratic 14891equation has simple roots, or only one root, we may have to negate it 14892so that $B(y_1,y_2,y_3;t)$ crosses from positive to negative at its first root. 14893And finally, we need to do special things if $B(y_1,y_2,y_3;t)$ is 14894identically zero. */ 14895if (number_negative(x1)) 14896 if (number_negative(x2)) 14897 if (number_negative(x3)) 14898 goto DONE; 14899{ 14900 ab_vs_cd (ab_vs_cd, y1, y3, y2, y2); 14901 if (number_zero(ab_vs_cd)) { 14902 /* Handle the test for eastward directions when $y_1y_3=y_2^2$; 14903 either |goto found| or |goto done| */ 14904{ 14905 ab_vs_cd (ab_vs_cd, y1, y2, zero_t, zero_t); 14906 if (number_negative(ab_vs_cd)) { 14907 mp_number tmp, arg2; 14908 new_number(tmp); 14909 new_number(arg2); 14910 set_number_from_substraction (arg2, y1, y2); 14911 make_fraction (t, y1, arg2); 14912 free_number (arg2); 14913 set_number_from_of_the_way(x1, t, x1, x2); 14914 set_number_from_of_the_way(x2, t, x2, x3); 14915 set_number_from_of_the_way(tmp, t, x1, x2); 14916 if (number_zero(tmp) || number_positive(tmp)) { 14917 free_number (tmp); 14918 we_found_it; 14919 } 14920 free_number (tmp); 14921 } else if (number_zero(y3)) { 14922 if (number_zero(y1)) { 14923 /* Exit to |found| if the derivative $B(x_1,x_2,x_3;t)$ becomes |>=0| */ 14924/* At this point we know that the derivative of |y(t)| is identically zero, 14925and that |x1<0|; but either |x2>=0| or |x3>=0|, so there's some hope of 14926traveling east. */ 14927{ 14928 mp_number arg1, arg2, arg3; 14929 new_number (arg1); 14930 new_number (arg2); 14931 new_number (arg3); 14932 number_clone(arg1, x1); 14933 number_negate(arg1); 14934 number_clone(arg2, x2); 14935 number_negate(arg2); 14936 number_clone(arg3, x3); 14937 number_negate(arg3); 14938 crossing_point (t, arg1, arg2, arg3); 14939 free_number (arg1); 14940 free_number (arg2); 14941 free_number (arg3); 14942 if (number_lessequal (t, fraction_one_t)) 14943 we_found_it; 14944 ab_vs_cd (ab_vs_cd, x1, x3, x2, x2); 14945 if (number_nonpositive(ab_vs_cd)) { 14946 mp_number arg2; 14947 new_number (arg2); 14948 set_number_from_substraction (arg2, x1, x2); 14949 make_fraction (t, x1, arg2); 14950 free_number (arg2); 14951 we_found_it; 14952 } 14953} 14954 14955 14956 14957 } else if (number_zero(x3) || number_positive(x3)) { 14958 set_number_to_unity(tt); 14959 goto FOUND; 14960 } 14961 } 14962 goto DONE; 14963} 14964 14965 14966 } 14967} 14968if (number_zero(y1) || number_negative(y1)) { 14969 if (number_negative(y1)) { 14970 number_negate(y1); 14971 number_negate(y2); 14972 number_negate(y3); 14973 } else if (number_positive(y2)) { 14974 number_negate(y2); 14975 number_negate(y3); 14976 } 14977} 14978/* Check the places where $B(y_1,y_2,y_3;t)=0$ to see if 14979 $B(x_1,x_2,x_3;t)\ge0$ */ 14980/* The quadratic polynomial $B(y_1,y_2,y_3;t)$ begins |>=0| and has at most 14981two roots, because we know that it isn't identically zero. 14982 14983It must be admitted that the |crossing_point| routine is not perfectly accurate; 14984rounding errors might cause it to find a root when $y_1y_3>y_2^2$, or to 14985miss the roots when $y_1y_3<y_2^2$. The rotation process is itself 14986subject to rounding errors. Yet this code optimistically tries to 14987do the right thing. 14988 14989*/ 14990crossing_point (t, y1, y2, y3); 14991if (number_greater (t, fraction_one_t)) 14992 goto DONE; 14993set_number_from_of_the_way(y2, t, y2, y3); 14994set_number_from_of_the_way(x1, t, x1, x2); 14995set_number_from_of_the_way(x2, t, x2, x3); 14996set_number_from_of_the_way(x1, t, x1, x2); 14997if (number_zero(x1) || number_positive(x1)) 14998 we_found_it; 14999if (number_positive(y2)) 15000 set_number_to_zero(y2); 15001number_clone(tt, t); 15002{ 15003 mp_number arg1, arg2, arg3; 15004 new_number (arg1); 15005 new_number (arg2); 15006 new_number (arg3); 15007 number_clone(arg2, y2); 15008 number_negate(arg2); 15009 number_clone(arg3, y3); 15010 number_negate(arg3); 15011 crossing_point (t, arg1, arg2, arg3); 15012 free_number (arg1); 15013 free_number (arg2); 15014 free_number (arg3); 15015} 15016if (number_greater (t, fraction_one_t)) 15017 goto DONE; 15018{ 15019 mp_number tmp; 15020 new_number(tmp); 15021 set_number_from_of_the_way(x1, t, x1, x2); 15022 set_number_from_of_the_way(x2, t, x2, x3); 15023 set_number_from_of_the_way(tmp, t, x1, x2); 15024 if (number_nonnegative(tmp)) { 15025 free_number (tmp); 15026 set_number_from_of_the_way (t, t, tt, fraction_one_t); 15027 we_found_it; 15028 } 15029 free_number (tmp); 15030} 15031DONE: 15032 15033 15034@ The intersection of two cubics can be found by an interesting variant 15035of the general bisection scheme described in the introduction to 15036|crossing_point|.\ 15037Given $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)$, 15038we wish to find a pair of times $(t_1,t_2)$ such that $w(t_1)=z(t_2)$, 15039if an intersection exists. First we find the smallest rectangle that 15040encloses the points $\{w_0,w_1,w_2,w_3\}$ and check that it overlaps 15041the smallest rectangle that encloses 15042$\{z_0,z_1,z_2,z_3\}$; if not, the cubics certainly don't intersect. 15043But if the rectangles do overlap, we bisect the intervals, getting 15044new cubics $w'$ and~$w''$, $z'$~and~$z''$; the intersection routine first 15045tries for an intersection between $w'$ and~$z'$, then (if unsuccessful) 15046between $w'$ and~$z''$, then (if still unsuccessful) between $w''$ and~$z'$, 15047finally (if thrice unsuccessful) between $w''$ and~$z''$. After $l$~successful 15048levels of bisection we will have determined the intersection times $t_1$ 15049and~$t_2$ to $l$~bits of accuracy. 15050 15051\def\submin{_{\rm min}} \def\submax{_{\rm max}} 15052As before, it is better to work with the numbers $W_k=2^l(w_k-w_{k-1})$ 15053and $Z_k=2^l(z_k-z_{k-1})$ rather than the coefficients $w_k$ and $z_k$ 15054themselves. We also need one other quantity, $\Delta=2^l(w_0-z_0)$, 15055to determine when the enclosing rectangles overlap. Here's why: 15056The $x$~coordinates of~$w(t)$ are between $u\submin$ and $u\submax$, 15057and the $x$~coordinates of~$z(t)$ are between $x\submin$ and $x\submax$, 15058if we write $w_k=(u_k,v_k)$ and $z_k=(x_k,y_k)$ and $u\submin= 15059\min(u_0,u_1,u_2,u_3)$, etc. These intervals of $x$~coordinates 15060overlap if and only if $u\submin\L x\submax$ and 15061$x\submin\L u\submax$. Letting 15062$$U\submin=\min(0,U_1,U_1+U_2,U_1+U_2+U_3),\; 15063 U\submax=\max(0,U_1,U_1+U_2,U_1+U_2+U_3),$$ 15064we have $2^lu\submin=2^lu_0+U\submin$, etc.; the condition for overlap 15065reduces to 15066$$X\submin-U\submax\L 2^l(u_0-x_0)\L X\submax-U\submin.$$ 15067Thus we want to maintain the quantity $2^l(u_0-x_0)$; similarly, 15068the quantity $2^l(v_0-y_0)$ accounts for the $y$~coordinates. The 15069coordinates of $\Delta=2^l(w_0-z_0)$ must stay bounded as $l$ increases, 15070because of the overlap condition; i.e., we know that $X\submin$, 15071$X\submax$, and their relatives are bounded, hence $X\submax- 15072U\submin$ and $X\submin-U\submax$ are bounded. 15073 15074@ Incidentally, if the given cubics intersect more than once, the process 15075just sketched will not necessarily find the lexicographically smallest pair 15076$(t_1,t_2)$. The solution actually obtained will be smallest in ``shuffled 15077order''; i.e., if $t_1=(.a_1a_2\ldots a_{16})_2$ and 15078$t_2=(.b_1b_2\ldots b_{16})_2$, then we will minimize 15079$a_1b_1a_2b_2\ldots a_{16}b_{16}$, not 15080$a_1a_2\ldots a_{16}b_1b_2\ldots b_{16}$. 15081Shuffled order agrees with lexicographic order if all pairs of solutions 15082$(t_1,t_2)$ and $(t_1',t_2')$ have the property that $t_1<t_1'$ iff 15083$t_2<t_2'$; but in general, lexicographic order can be quite different, 15084and the bisection algorithm would be substantially less efficient if it were 15085constrained by lexicographic order. 15086 15087For example, suppose that an overlap has been found for $l=3$ and 15088$(t_1,t_2)= (.101,.011)$ in binary, but that no overlap is produced by 15089either of the alternatives $(.1010,.0110)$, $(.1010,.0111)$ at level~4. 15090Then there is probably an intersection in one of the subintervals 15091$(.1011,.011x)$; but lexicographic order would require us to explore 15092$(.1010,.1xxx)$ and $(.1011,.00xx)$ and $(.1011,.010x)$ first. We wouldn't 15093want to store all of the subdivision data for the second path, so the 15094subdivisions would have to be regenerated many times. Such inefficiencies 15095would be associated with every `1' in the binary representation of~$t_1$. 15096 15097@ The subdivision process introduces rounding errors, hence we need to 15098make a more liberal test for overlap. It is not hard to show that the 15099computed values of $U_i$ differ from the truth by at most~$l$, on 15100level~$l$, hence $U\submin$ and $U\submax$ will be at most $3l$ in error. 15101If $\beta$ is an upper bound on the absolute error in the computed 15102components of $\Delta=(|delx|,|dely|)$ on level~$l$, we will replace 15103the test `$X\submin-U\submax\L|delx|$' by the more liberal test 15104`$X\submin-U\submax\L|delx|+|tol|$', where $|tol|=6l+\beta$. 15105 15106More accuracy is obtained if we try the algorithm first with |tol=0|; 15107the more liberal tolerance is used only if an exact approach fails. 15108It is convenient to do this double-take by letting `3' in the preceding 15109paragraph be a parameter, which is first 0, then 3. 15110 15111@<Glob...@>= 15112unsigned int tol_step; /* either 0 or 3, usually */ 15113 15114@ We shall use an explicit stack to implement the recursive bisection 15115method described above. The |bisect_stack| array will contain numerous 5-word 15116packets like $(U_1,U_2,U_3,U\submin,U\submax)$, as well as 20-word packets 15117comprising the 5-word packets for $U$, $V$, $X$, and~$Y$. 15118 15119The following macros define the allocation of stack positions to 15120the quantities needed for bisection-intersection. 15121 15122@d stack_1(A) mp->bisect_stack[(A)] /* $U_1$, $V_1$, $X_1$, or $Y_1$ */ 15123@d stack_2(A) mp->bisect_stack[(A)+1] /* $U_2$, $V_2$, $X_2$, or $Y_2$ */ 15124@d stack_3(A) mp->bisect_stack[(A)+2] /* $U_3$, $V_3$, $X_3$, or $Y_3$ */ 15125@d stack_min(A) mp->bisect_stack[(A)+3] 15126 /* $U\submin$, $V\submin$, $X\submin$, or $Y\submin$ */ 15127@d stack_max(A) mp->bisect_stack[(A)+4] 15128 /* $U\submax$, $V\submax$, $X\submax$, or $Y\submax$ */ 15129@d int_packets 20 /* number of words to represent $U_k$, $V_k$, $X_k$, and $Y_k$ */ 15130@# 15131@d u_packet(A) ((A)-5) 15132@d v_packet(A) ((A)-10) 15133@d x_packet(A) ((A)-15) 15134@d y_packet(A) ((A)-20) 15135@d l_packets (mp->bisect_ptr-int_packets) 15136@d r_packets mp->bisect_ptr 15137@d ul_packet u_packet(l_packets) /* base of $U'_k$ variables */ 15138@d vl_packet v_packet(l_packets) /* base of $V'_k$ variables */ 15139@d xl_packet x_packet(l_packets) /* base of $X'_k$ variables */ 15140@d yl_packet y_packet(l_packets) /* base of $Y'_k$ variables */ 15141@d ur_packet u_packet(r_packets) /* base of $U''_k$ variables */ 15142@d vr_packet v_packet(r_packets) /* base of $V''_k$ variables */ 15143@d xr_packet x_packet(r_packets) /* base of $X''_k$ variables */ 15144@d yr_packet y_packet(r_packets) /* base of $Y''_k$ variables */ 15145@# 15146@d u1l stack_1(ul_packet) /* $U'_1$ */ 15147@d u2l stack_2(ul_packet) /* $U'_2$ */ 15148@d u3l stack_3(ul_packet) /* $U'_3$ */ 15149@d v1l stack_1(vl_packet) /* $V'_1$ */ 15150@d v2l stack_2(vl_packet) /* $V'_2$ */ 15151@d v3l stack_3(vl_packet) /* $V'_3$ */ 15152@d x1l stack_1(xl_packet) /* $X'_1$ */ 15153@d x2l stack_2(xl_packet) /* $X'_2$ */ 15154@d x3l stack_3(xl_packet) /* $X'_3$ */ 15155@d y1l stack_1(yl_packet) /* $Y'_1$ */ 15156@d y2l stack_2(yl_packet) /* $Y'_2$ */ 15157@d y3l stack_3(yl_packet) /* $Y'_3$ */ 15158@d u1r stack_1(ur_packet) /* $U''_1$ */ 15159@d u2r stack_2(ur_packet) /* $U''_2$ */ 15160@d u3r stack_3(ur_packet) /* $U''_3$ */ 15161@d v1r stack_1(vr_packet) /* $V''_1$ */ 15162@d v2r stack_2(vr_packet) /* $V''_2$ */ 15163@d v3r stack_3(vr_packet) /* $V''_3$ */ 15164@d x1r stack_1(xr_packet) /* $X''_1$ */ 15165@d x2r stack_2(xr_packet) /* $X''_2$ */ 15166@d x3r stack_3(xr_packet) /* $X''_3$ */ 15167@d y1r stack_1(yr_packet) /* $Y''_1$ */ 15168@d y2r stack_2(yr_packet) /* $Y''_2$ */ 15169@d y3r stack_3(yr_packet) /* $Y''_3$ */ 15170@# 15171@d stack_dx mp->bisect_stack[mp->bisect_ptr] /* stacked value of |delx| */ 15172@d stack_dy mp->bisect_stack[mp->bisect_ptr+1] /* stacked value of |dely| */ 15173@d stack_tol mp->bisect_stack[mp->bisect_ptr+2] /* stacked value of |tol| */ 15174@d stack_uv mp->bisect_stack[mp->bisect_ptr+3] /* stacked value of |uv| */ 15175@d stack_xy mp->bisect_stack[mp->bisect_ptr+4] /* stacked value of |xy| */ 15176@d int_increment (int_packets+int_packets+5) /* number of stack words per level */ 15177 15178@<Glob...@>= 15179mp_number *bisect_stack; 15180integer bisect_ptr; 15181 15182@ @<Allocate or initialize ...@>= 15183mp->bisect_stack = xmalloc ((bistack_size + 1), sizeof (mp_number)); 15184{ 15185 int i; 15186 for (i=0;i<bistack_size + 1;i++) { 15187 new_number (mp->bisect_stack[i]); 15188 } 15189} 15190 15191@ @<Dealloc variables@>= 15192{ 15193 int i; 15194 for (i=0;i<bistack_size + 1;i++) { 15195 free_number (mp->bisect_stack[i]); 15196 } 15197} 15198xfree (mp->bisect_stack); 15199 15200@ @<Check the ``constant''...@>= 15201if (int_packets + 17 * int_increment > bistack_size) 15202 mp->bad = 19; 15203 15204@ Computation of the min and max is a tedious but fairly fast sequence of 15205instructions; exactly four comparisons are made in each branch. 15206 15207@d set_min_max(A) 15208 debug_number (stack_1(A)); 15209 debug_number (stack_3(A)); 15210 debug_number (stack_2(A)); 15211 debug_number (stack_min(A)); 15212 debug_number (stack_max(A)); 15213 if ( number_negative(stack_1((A))) ) { 15214 if ( number_nonnegative (stack_3((A))) ) { 15215 if ( number_negative (stack_2((A))) ) 15216 set_number_from_addition (stack_min((A)), stack_1((A)), stack_2((A))); 15217 else 15218 number_clone (stack_min((A)), stack_1((A))); 15219 set_number_from_addition (stack_max((A)), stack_1((A)), stack_2((A))); 15220 number_add (stack_max((A)), stack_3((A))); 15221 if ( number_negative (stack_max((A))) ) 15222 set_number_to_zero (stack_max((A))); 15223 } else { 15224 set_number_from_addition (stack_min((A)), stack_1((A)), stack_2((A))); 15225 number_add (stack_min((A)), stack_3((A))); 15226 if ( number_greater (stack_min((A)), stack_1((A)))) 15227 number_clone (stack_min((A)), stack_1((A))); 15228 set_number_from_addition (stack_max((A)), stack_1((A)), stack_2((A))); 15229 if ( number_negative (stack_max((A))) ) 15230 set_number_to_zero (stack_max((A))); 15231 } 15232 } else if ( number_nonpositive (stack_3((A)))) { 15233 if ( number_positive (stack_2((A))) ) 15234 set_number_from_addition (stack_max((A)), stack_1((A)), stack_2((A))); 15235 else 15236 number_clone (stack_max((A)), stack_1((A))); 15237 set_number_from_addition (stack_min((A)), stack_1((A)), stack_2((A))); 15238 number_add (stack_min((A)), stack_3((A))); 15239 if ( number_positive (stack_min((A))) ) 15240 set_number_to_zero (stack_min((A))); 15241 } else { 15242 set_number_from_addition (stack_max((A)), stack_1((A)), stack_2((A))); 15243 number_add (stack_max((A)), stack_3((A))); 15244 if ( number_less (stack_max((A)), stack_1((A)))) 15245 number_clone (stack_max((A)), stack_1((A))); 15246 set_number_from_addition (stack_min((A)), stack_1((A)), stack_2((A))); 15247 if ( number_positive (stack_min((A))) ) 15248 set_number_to_zero (stack_min((A))); 15249 } 15250 15251@ It's convenient to keep the current values of $l$, $t_1$, and $t_2$ in 15252the integer form $2^l+2^lt_1$ and $2^l+2^lt_2$. The |cubic_intersection| 15253routine uses global variables |cur_t| and |cur_tt| for this purpose; 15254after successful completion, |cur_t| and |cur_tt| will contain |unity| 15255plus the |scaled| values of $t_1$ and~$t_2$. 15256 15257The values of |cur_t| and |cur_tt| will be set to zero if |cubic_intersection| 15258finds no intersection. The routine gives up and gives an approximate answer 15259if it has backtracked 15260more than 5000 times (otherwise there are cases where several minutes 15261of fruitless computation would be possible). 15262 15263@d max_patience 5000 15264 15265@<Glob...@>= 15266mp_number cur_t; 15267mp_number cur_tt; /* controls and results of |cubic_intersection| */ 15268integer time_to_go; /* this many backtracks before giving up */ 15269mp_number max_t; /* maximum of $2^{l+1}$ so far achieved */ 15270 15271@ @<Initialize table ...@>= 15272new_number (mp->cur_t); 15273new_number (mp->cur_tt); 15274new_number (mp->max_t); 15275 15276@ @<Dealloc ...@>= 15277free_number (mp->cur_t); 15278free_number (mp->cur_tt); 15279free_number (mp->max_t); 15280 15281@ The given cubics $B(w_0,w_1,w_2,w_3;t)$ and 15282$B(z_0,z_1,z_2,z_3;t)$ are specified in adjacent knot nodes |(p,mp_link(p))| 15283and |(pp,mp_link(pp))|, respectively. 15284 15285@d half(A) ((A)/2) 15286 15287@c 15288static void mp_cubic_intersection (MP mp, mp_knot p, mp_knot pp) { 15289 mp_knot q, qq; /* |mp_link(p)|, |mp_link(pp)| */ 15290 mp->time_to_go = max_patience; 15291 set_number_from_scaled (mp->max_t, 2); 15292 @<Initialize for intersections at level zero@>; 15293CONTINUE: 15294 while (1) { 15295 if (number_to_scaled (mp->delx) - mp->tol <= 15296 number_to_scaled (stack_max (x_packet (mp->xy))) - number_to_scaled (stack_min (u_packet (mp->uv)))) 15297 if (number_to_scaled (mp->delx) + mp->tol >= 15298 number_to_scaled (stack_min (x_packet (mp->xy))) - number_to_scaled (stack_max (u_packet (mp->uv)))) 15299 if (number_to_scaled (mp->dely) - mp->tol <= 15300 number_to_scaled (stack_max (y_packet (mp->xy))) - number_to_scaled (stack_min (v_packet (mp->uv)))) 15301 if (number_to_scaled (mp->dely) + mp->tol >= 15302 number_to_scaled (stack_min (y_packet (mp->xy))) - number_to_scaled (stack_max (v_packet (mp->uv)))) { 15303 if (number_to_scaled (mp->cur_t) >= number_to_scaled (mp->max_t)) { 15304 if (number_equal(mp->max_t, two_t)) { /* we've done 17 bisections */ 15305 set_number_from_scaled (mp->cur_t, ((number_to_scaled (mp->cur_t) + 1)/2)); 15306 set_number_from_scaled (mp->cur_tt, ((number_to_scaled (mp->cur_tt) + 1)/2)); 15307 return; 15308 } 15309 number_double(mp->max_t); 15310 number_clone (mp->appr_t, mp->cur_t); 15311 number_clone (mp->appr_tt, mp->cur_tt); 15312 } 15313 @<Subdivide for a new level of intersection@>; 15314 goto CONTINUE; 15315 } 15316 if (mp->time_to_go > 0) { 15317 decr (mp->time_to_go); 15318 } else { 15319 while (number_less (mp->appr_t, unity_t)) { 15320 number_double(mp->appr_t); 15321 number_double(mp->appr_tt); 15322 } 15323 number_clone (mp->cur_t, mp->appr_t); 15324 number_clone (mp->cur_tt, mp->appr_tt); 15325 return; 15326 } 15327 NOT_FOUND: 15328 /* Advance to the next pair |(cur_t,cur_tt)| */ 15329 if (odd (number_to_scaled (mp->cur_tt))) { 15330 if (odd (number_to_scaled (mp->cur_t))) { 15331 /* Descend to the previous level and |goto not_found| */ 15332 { 15333 set_number_from_scaled (mp->cur_t, half (number_to_scaled (mp->cur_t))); 15334 set_number_from_scaled (mp->cur_tt, half (number_to_scaled (mp->cur_tt))); 15335 if (number_to_scaled (mp->cur_t) == 0) 15336 return; 15337 mp->bisect_ptr -= int_increment; 15338 mp->three_l -= (integer) mp->tol_step; 15339 number_clone (mp->delx, stack_dx); 15340 number_clone (mp->dely, stack_dy); 15341 mp->tol = number_to_scaled (stack_tol); 15342 mp->uv = number_to_scaled (stack_uv); 15343 mp->xy = number_to_scaled (stack_xy); 15344 goto NOT_FOUND; 15345 } 15346 15347 } else { 15348 set_number_from_scaled (mp->cur_t, number_to_scaled (mp->cur_t) + 1); 15349 number_add (mp->delx, stack_1 (u_packet (mp->uv))); 15350 number_add (mp->delx, stack_2 (u_packet (mp->uv))); 15351 number_add (mp->delx, stack_3 (u_packet (mp->uv))); 15352 number_add (mp->dely, stack_1 (v_packet (mp->uv))); 15353 number_add (mp->dely, stack_2 (v_packet (mp->uv))); 15354 number_add (mp->dely, stack_3 (v_packet (mp->uv))); 15355 mp->uv = mp->uv + int_packets; /* switch from |l_packets| to |r_packets| */ 15356 set_number_from_scaled (mp->cur_tt, number_to_scaled (mp->cur_tt) - 1); 15357 mp->xy = mp->xy - int_packets; 15358 number_add (mp->delx, stack_1 (x_packet (mp->xy))); 15359 number_add (mp->delx, stack_2 (x_packet (mp->xy))); 15360 number_add (mp->delx, stack_3 (x_packet (mp->xy))); 15361 number_add (mp->dely, stack_1 (y_packet (mp->xy))); 15362 number_add (mp->dely, stack_2 (y_packet (mp->xy))); 15363 number_add (mp->dely, stack_3 (y_packet (mp->xy))); 15364 } 15365 } else { 15366 set_number_from_scaled (mp->cur_tt, number_to_scaled (mp->cur_tt) + 1); 15367 mp->tol = mp->tol + mp->three_l; 15368 number_substract (mp->delx, stack_1 (x_packet (mp->xy))); 15369 number_substract (mp->delx, stack_2 (x_packet (mp->xy))); 15370 number_substract (mp->delx, stack_3 (x_packet (mp->xy))); 15371 number_substract (mp->dely, stack_1 (y_packet (mp->xy))); 15372 number_substract (mp->dely, stack_2 (y_packet (mp->xy))); 15373 number_substract (mp->dely, stack_3 (y_packet (mp->xy))); 15374 mp->xy = mp->xy + int_packets; /* switch from |l_packets| to |r_packets| */ 15375 } 15376 } 15377} 15378 15379 15380@ The following variables are global, although they are used only by 15381|cubic_intersection|, because it is necessary on some machines to 15382split |cubic_intersection| up into two procedures. 15383 15384@<Glob...@>= 15385mp_number delx; 15386mp_number dely; /* the components of $\Delta=2^l(w_0-z_0)$ */ 15387integer tol; /* bound on the uncertainty in the overlap test */ 15388integer uv; 15389integer xy; /* pointers to the current packets of interest */ 15390integer three_l; /* |tol_step| times the bisection level */ 15391mp_number appr_t; 15392mp_number appr_tt; /* best approximations known to the answers */ 15393 15394@ @<Initialize table ...@>= 15395new_number (mp->delx); 15396new_number (mp->dely); 15397new_number (mp->appr_t); 15398new_number (mp->appr_tt); 15399 15400@ @<Dealloc...@>= 15401free_number (mp->delx); 15402free_number (mp->dely); 15403free_number (mp->appr_t); 15404free_number (mp->appr_tt); 15405 15406@ We shall assume that the coordinates are sufficiently non-extreme that 15407integer overflow will not occur. 15408@^overflow in arithmetic@> 15409 15410@<Initialize for intersections at level zero@>= 15411q = mp_next_knot (p); 15412qq = mp_next_knot (pp); 15413mp->bisect_ptr = int_packets; 15414set_number_from_substraction (u1r, p->right_x, p->x_coord); 15415set_number_from_substraction (u2r, q->left_x, p->right_x); 15416set_number_from_substraction (u3r, q->x_coord, q->left_x); 15417set_min_max (ur_packet); 15418set_number_from_substraction (v1r, p->right_y, p->y_coord ); 15419set_number_from_substraction (v2r, q->left_y, p->right_y); 15420set_number_from_substraction (v3r, q->y_coord, q->left_y ); 15421set_min_max (vr_packet); 15422set_number_from_substraction (x1r, pp->right_x, pp->x_coord ); 15423set_number_from_substraction (x2r, qq->left_x, pp->right_x ); 15424set_number_from_substraction (x3r, qq->x_coord, qq->left_x ); 15425set_min_max (xr_packet); 15426set_number_from_substraction (y1r, pp->right_y, pp->y_coord ); 15427set_number_from_substraction (y2r, qq->left_y, pp->right_y); 15428set_number_from_substraction (y3r, qq->y_coord, qq->left_y); 15429set_min_max (yr_packet); 15430set_number_from_substraction (mp->delx, p->x_coord, pp->x_coord ); 15431set_number_from_substraction (mp->dely, p->y_coord, pp->y_coord ); 15432mp->tol = 0; 15433mp->uv = r_packets; 15434mp->xy = r_packets; 15435mp->three_l = 0; 15436set_number_from_scaled (mp->cur_t, 1); 15437set_number_from_scaled (mp->cur_tt, 1) 15438 15439@ 15440 15441@<Subdivide for a new level of intersection@>= 15442number_clone (stack_dx, mp->delx); 15443number_clone (stack_dy, mp->dely); 15444set_number_from_scaled (stack_tol, mp->tol); 15445set_number_from_scaled (stack_uv, mp->uv); 15446set_number_from_scaled (stack_xy, mp->xy); 15447mp->bisect_ptr = mp->bisect_ptr + int_increment; 15448number_double (mp->cur_t); 15449number_double (mp->cur_tt); 15450number_clone (u1l, stack_1 (u_packet (mp->uv))); 15451number_clone (u3r, stack_3 (u_packet (mp->uv))); 15452set_number_from_addition (u2l, u1l, stack_2 (u_packet (mp->uv))); number_half (u2l); 15453set_number_from_addition (u2r, u3r, stack_2 (u_packet (mp->uv))); number_half (u2r); 15454set_number_from_addition (u3l, u2l, u2r); number_half (u3l); 15455number_clone (u1r, u3l); 15456set_min_max (ul_packet); 15457set_min_max (ur_packet); 15458number_clone (v1l, stack_1 (v_packet (mp->uv))); 15459number_clone (v3r, stack_3 (v_packet (mp->uv))); 15460set_number_from_addition (v2l, v1l, stack_2 (v_packet (mp->uv))); number_half(v2l); 15461set_number_from_addition (v2r, v3r, stack_2 (v_packet (mp->uv))); number_half(v2r); 15462set_number_from_addition (v3l, v2l, v2r); number_half(v3l); 15463number_clone (v1r, v3l); 15464set_min_max (vl_packet); 15465set_min_max (vr_packet); 15466number_clone (x1l, stack_1 (x_packet (mp->xy))); 15467number_clone (x3r, stack_3 (x_packet (mp->xy))); 15468set_number_from_addition (x2l, x1l, stack_2 (x_packet (mp->xy))); number_half(x2l); 15469set_number_from_addition (x2r, x3r, stack_2 (x_packet (mp->xy))); number_half(x2r); 15470set_number_from_addition (x3l, x2l, x2r); number_half(x3l); 15471number_clone (x1r, x3l); 15472set_min_max (xl_packet); 15473set_min_max (xr_packet); 15474number_clone (y1l, stack_1 (y_packet (mp->xy))); 15475number_clone (y3r, stack_3 (y_packet (mp->xy))); 15476set_number_from_addition (y2l, y1l, stack_2 (y_packet (mp->xy))); number_half (y2l); 15477set_number_from_addition (y2r, y3r, stack_2 (y_packet (mp->xy))); number_half (y2r); 15478set_number_from_addition (y3l, y2l, y2r); number_half (y3l); 15479number_clone (y1r, y3l); 15480set_min_max (yl_packet); 15481set_min_max (yr_packet); 15482mp->uv = l_packets; 15483mp->xy = l_packets; 15484number_double(mp->delx); 15485number_double(mp->dely); 15486mp->tol = mp->tol - mp->three_l + (integer) mp->tol_step; 15487mp->tol += mp->tol; 15488mp->three_l = mp->three_l + (integer) mp->tol_step 15489 15490@ The |path_intersection| procedure is much simpler. 15491It invokes |cubic_intersection| in lexicographic order until finding a 15492pair of cubics that intersect. The final intersection times are placed in 15493|cur_t| and~|cur_tt|. 15494 15495@c 15496static void mp_path_intersection (MP mp, mp_knot h, mp_knot hh) { 15497 mp_knot p, pp; /* link registers that traverse the given paths */ 15498 mp_number n, nn; /* integer parts of intersection times, minus |unity| */ 15499 @<Change one-point paths into dead cycles@>; 15500 new_number (n); 15501 new_number (nn); 15502 mp->tol_step = 0; 15503 do { 15504 set_number_to_unity(n); 15505 number_negate (n); 15506 p = h; 15507 do { 15508 if (mp_right_type (p) != mp_endpoint) { 15509 set_number_to_unity(nn); 15510 number_negate (nn); 15511 pp = hh; 15512 do { 15513 if (mp_right_type (pp) != mp_endpoint) { 15514 mp_cubic_intersection (mp, p, pp); 15515 if (number_positive (mp->cur_t)) { 15516 number_add (mp->cur_t, n); 15517 number_add (mp->cur_tt, nn); 15518 goto DONE; 15519 } 15520 } 15521 number_add(nn, unity_t); 15522 pp = mp_next_knot (pp); 15523 } while (pp != hh); 15524 } 15525 number_add(n, unity_t); 15526 p = mp_next_knot (p); 15527 } while (p != h); 15528 mp->tol_step = mp->tol_step + 3; 15529 } while (mp->tol_step <= 3); 15530 number_clone (mp->cur_t, unity_t); 15531 number_negate (mp->cur_t); 15532 number_clone (mp->cur_tt, unity_t); 15533 number_negate (mp->cur_tt); 15534DONE: 15535 free_number (n); 15536 free_number (nn); 15537} 15538 15539 15540@ @<Change one-point paths...@>= 15541if (mp_right_type (h) == mp_endpoint) { 15542 number_clone (h->right_x, h->x_coord); 15543 number_clone (h->left_x, h->x_coord); 15544 number_clone (h->right_y, h->y_coord); 15545 number_clone (h->left_y, h->y_coord); 15546 mp_right_type (h) = mp_explicit; 15547} 15548if (mp_right_type (hh) == mp_endpoint) { 15549 number_clone (hh->right_x, hh->x_coord); 15550 number_clone (hh->left_x, hh->x_coord); 15551 number_clone (hh->right_y, hh->y_coord); 15552 number_clone (hh->left_y, hh->y_coord); 15553 mp_right_type (hh) = mp_explicit; 15554} 15555 15556@* Dynamic linear equations. 15557\MP\ users define variables implicitly by stating equations that should be 15558satisfied; the computer is supposed to be smart enough to solve those equations. 15559And indeed, the computer tries valiantly to do so, by distinguishing five 15560different types of numeric values: 15561 15562\smallskip\hang 15563|type(p)=mp_known| is the nice case, when |value(p)| is the |scaled| value 15564of the variable whose address is~|p|. 15565 15566\smallskip\hang 15567|type(p)=mp_dependent| means that |value(p)| is not present, but |dep_list(p)| 15568points to a {\sl dependency list\/} that expresses the value of variable~|p| 15569as a |scaled| number plus a sum of independent variables with |fraction| 15570coefficients. 15571 15572\smallskip\hang 15573|type(p)=mp_independent| means that |indep_value(p)=s|, where |s>0| is a ``serial 15574number'' reflecting the time this variable was first used in an equation; 15575and there is an extra field |indep_scale(p)=m|, with |0<=m<64|, each dependent 15576variable that refers to this one is actually referring to the future value of 15577this variable times~$2^m$. (Usually |m=0|, but higher degrees of 15578scaling are sometimes needed to keep the coefficients in dependency lists 15579from getting too large. The value of~|m| will always be even.) 15580 15581\smallskip\hang 15582|type(p)=mp_numeric_type| means that variable |p| hasn't appeared in an 15583equation before, but it has been explicitly declared to be numeric. 15584 15585\smallskip\hang 15586|type(p)=undefined| means that variable |p| hasn't appeared before. 15587 15588\smallskip\noindent 15589We have actually discussed these five types in the reverse order of their 15590history during a computation: Once |known|, a variable never again 15591becomes |dependent|; once |dependent|, it almost never again becomes 15592|mp_independent|; once |mp_independent|, it never again becomes |mp_numeric_type|; 15593and once |mp_numeric_type|, it never again becomes |undefined| (except 15594of course when the user specifically decides to scrap the old value 15595and start again). A backward step may, however, take place: Sometimes 15596a |dependent| variable becomes |mp_independent| again, when one of the 15597independent variables it depends on is reverting to |undefined|. 15598 15599@d indep_scale(A) ((mp_value_node)(A))->data.indep.scale 15600@d set_indep_scale(A,B) ((mp_value_node)(A))->data.indep.scale=(B) 15601@d indep_value(A) ((mp_value_node)(A))->data.indep.serial 15602@d set_indep_value(A,B) ((mp_value_node)(A))->data.indep.serial=(B) 15603 15604 15605@c 15606void mp_new_indep(MP mp, mp_node p) { /* create a new independent variable */ 15607 if ( mp->serial_no>=max_integer ) { 15608 mp_fatal_error(mp, "variable instance identifiers exhausted"); 15609 } 15610 mp_type(p)=mp_independent; 15611 mp->serial_no=mp->serial_no+1; 15612 set_indep_scale(p,0); 15613 set_indep_value(p,mp->serial_no); 15614} 15615 15616@ @<Declarations@>= 15617void mp_new_indep(MP mp, mp_node p); 15618 15619 15620@ @<Glob...@>= 15621integer serial_no; /* the most recent serial number */ 15622 15623@ But how are dependency lists represented? It's simple: The linear combination 15624$\alpha_1v_1+\cdots+\alpha_kv_k+\beta$ appears in |k+1| value nodes. If 15625|q=dep_list(p)| points to this list, and if |k>0|, then |dep_value(q)= 15626@t$\alpha_1$@>| (which is a |fraction|); |dep_info(q)| points to the location 15627of $\alpha_1$; and |mp_link(p)| points to the dependency list 15628$\alpha_2v_2+\cdots+\alpha_kv_k+\beta$. On the other hand if |k=0|, 15629then |dep_value(q)=@t$\beta$@>| (which is |scaled|) and |dep_info(q)=NULL|. 15630The independent variables $v_1$, \dots,~$v_k$ have been sorted so that 15631they appear in decreasing order of their |value| fields (i.e., of 15632their serial numbers). \ (It is convenient to use decreasing order, 15633since |value(NULL)=0|. If the independent variables were not sorted by 15634serial number but by some other criterion, such as their location in |mem|, 15635the equation-solving mechanism would be too system-dependent, because 15636the ordering can affect the computed results.) 15637 15638The |link| field in the node that contains the constant term $\beta$ is 15639called the {\sl final link\/} of the dependency list. \MP\ maintains 15640a doubly-linked master list of all dependency lists, in terms of a permanently 15641allocated node 15642in |mem| called |dep_head|. If there are no dependencies, we have 15643|mp_link(dep_head)=dep_head| and |prev_dep(dep_head)=dep_head|; 15644otherwise |mp_link(dep_head)| points to the first dependent variable, say~|p|, 15645and |prev_dep(p)=dep_head|. We have |type(p)=mp_dependent|, and |dep_list(p)| 15646points to its dependency list. If the final link of that dependency list 15647occurs in location~|q|, then |mp_link(q)| points to the next dependent 15648variable (say~|r|); and we have |prev_dep(r)=q|, etc. 15649 15650Dependency nodes sometimes mutate into value nodes and vice versa, so their 15651structures have to match. 15652 15653@d dep_value(A) ((mp_value_node)(A))->data.n 15654@d set_dep_value(A,B) do_set_dep_value(mp,(A),(B)) 15655@d dep_info(A) get_dep_info(mp, (A)) 15656@d set_dep_info(A,B) do { 15657 mp_value_node d = (mp_value_node)(B); 15658 FUNCTION_TRACE4("set_dep_info(%p,%p) on %d\n",(A),d,__LINE__); 15659 ((mp_value_node)(A))->parent_ = (mp_node)d; 15660} while (0) 15661@d dep_list(A) ((mp_value_node)(A))->attr_head_ /* half of the |value| field in a |dependent| variable */ 15662@d set_dep_list(A,B) do { 15663 mp_value_node d = (mp_value_node)(B); 15664 FUNCTION_TRACE4("set_dep_list(%p,%p) on %d\n",(A),d,__LINE__); 15665 dep_list((A)) = (mp_node)d; 15666} while (0) 15667@d prev_dep(A) ((mp_value_node)(A))->subscr_head_ /* the other half; makes a doubly linked list */ 15668@d set_prev_dep(A,B) do { 15669 mp_value_node d = (mp_value_node)(B); 15670 FUNCTION_TRACE4("set_prev_dep(%p,%p) on %d\n",(A),d,__LINE__); 15671 prev_dep((A)) = (mp_node)d; 15672} while (0) 15673 15674@c 15675static mp_node get_dep_info (MP mp, mp_value_node p) { 15676 mp_node d; 15677 d = p->parent_; /* half of the |value| field in a |dependent| variable */ 15678 FUNCTION_TRACE3 ("%p = dep_info(%p)\n", d, p); 15679 return d; 15680} 15681static void do_set_dep_value (MP mp, mp_value_node p, mp_number q) { 15682 number_clone (p->data.n, q); /* half of the |value| field in a |dependent| variable */ 15683 FUNCTION_TRACE3("set_dep_value(%p,%d)\n", p, q); 15684 p->attr_head_ = NULL; 15685 p->subscr_head_ = NULL; 15686} 15687 15688@ @<Declarations...@>= 15689static mp_node get_dep_info (MP mp, mp_value_node p); 15690 15691@ 15692 15693@c 15694static mp_value_node mp_get_dep_node (MP mp) { 15695 mp_value_node p = (mp_value_node) mp_get_value_node (mp); 15696 mp_type (p) = mp_dep_node_type; 15697 return p; 15698} 15699static void mp_free_dep_node (MP mp, mp_value_node p) { 15700 mp_free_value_node (mp, (mp_node) p); 15701} 15702 15703 15704@ @<Declarations...@>= 15705static void mp_free_dep_node (MP mp, mp_value_node p); 15706 15707@ @<Initialize table entries@>= 15708mp->serial_no = 0; 15709mp->dep_head = mp_get_dep_node (mp); 15710set_mp_link (mp->dep_head, (mp_node) mp->dep_head); 15711set_prev_dep (mp->dep_head, (mp_node) mp->dep_head); 15712set_dep_info (mp->dep_head, NULL); 15713set_dep_list (mp->dep_head, NULL); 15714 15715@ @<Free table entries@>= 15716mp_free_dep_node (mp, mp->dep_head); 15717 15718@ Actually the description above contains a little white lie. There's 15719another kind of variable called |mp_proto_dependent|, which is 15720just like a |dependent| one except that the $\alpha$ coefficients 15721in its dependency list are |scaled| instead of being fractions. 15722Proto-dependency lists are mixed with dependency lists in the 15723nodes reachable from |dep_head|. 15724 15725@ Here is a procedure that prints a dependency list in symbolic form. 15726The second parameter should be either |dependent| or |mp_proto_dependent|, 15727to indicate the scaling of the coefficients. 15728 15729@<Declarations@>= 15730static void mp_print_dependency (MP mp, mp_value_node p, quarterword t); 15731 15732@ @c 15733void mp_print_dependency (MP mp, mp_value_node p, quarterword t) { 15734 mp_number v; /* a coefficient */ 15735 mp_value_node pp; /* for list manipulation */ 15736 mp_node q; 15737 pp = p; 15738 new_number (v); 15739 while (true) { 15740 number_clone (v, dep_value (p)); 15741 number_abs (v); 15742 q = dep_info (p); 15743 if (q == NULL) { /* the constant term */ 15744 if (number_nonzero(v) || (p == pp)) { 15745 if (number_positive(dep_value (p))) 15746 if (p != pp) 15747 mp_print_char (mp, xord ('+')); 15748 print_number (dep_value (p)); 15749 } 15750 return; 15751 } 15752 /* Print the coefficient, unless it's $\pm1.0$ */ 15753 if (number_negative(dep_value (p))) 15754 mp_print_char (mp, xord ('-')); 15755 else if (p != pp) 15756 mp_print_char (mp, xord ('+')); 15757 if (t == mp_dependent) { 15758 fraction_to_round_scaled (v); 15759 } 15760 if (!number_equal (v, unity_t)) 15761 print_number (v); 15762 15763 if (mp_type (q) != mp_independent) 15764 mp_confusion (mp, "dep"); 15765 mp_print_variable_name (mp, q); 15766 set_number_from_scaled (v, indep_scale(q)); 15767 while (number_positive (v)) { 15768 mp_print (mp, "*4"); 15769 number_add_scaled (v, -2); 15770 } 15771 p = (mp_value_node) mp_link (p); 15772 } 15773} 15774 15775 15776 15777@ The maximum absolute value of a coefficient in a given dependency list 15778is returned by the following simple function. 15779 15780@c 15781static void mp_max_coef (MP mp, mp_number *x, mp_value_node p) { 15782 mp_number (absv); 15783 new_number (absv); 15784 set_number_to_zero (*x); 15785 while (dep_info (p) != NULL) { 15786 number_clone (absv, dep_value (p)); 15787 number_abs (absv); 15788 if (number_greater (absv, *x)) { 15789 number_clone (*x, absv); 15790 } 15791 p = (mp_value_node) mp_link (p); 15792 } 15793 free_number (absv); 15794} 15795 15796 15797@ One of the main operations needed on dependency lists is to add a multiple 15798of one list to the other; we call this |p_plus_fq|, where |p| and~|q| point 15799to dependency lists and |f| is a fraction. 15800 15801If the coefficient of any independent variable becomes |coef_bound| or 15802more, in absolute value, this procedure changes the type of that variable 15803to `|independent_needing_fix|', and sets the global variable |fix_needed| 15804to~|true|. The value of $|coef_bound|=\mu$ is chosen so that 15805$\mu^2+\mu<8$; this means that the numbers we deal with won't 15806get too large. (Instead of the ``optimum'' $\mu=(\sqrt{33}-1)/2\approx 158072.3723$, the safer value 7/3 is taken as the threshold.) 15808 15809The changes mentioned in the preceding paragraph are actually done only if 15810the global variable |watch_coefs| is |true|. But it usually is; in fact, 15811it is |false| only when \MP\ is making a dependency list that will soon 15812be equated to zero. 15813 15814Several procedures that act on dependency lists, including |p_plus_fq|, 15815set the global variable |dep_final| to the final (constant term) node of 15816the dependency list that they produce. 15817 15818@d independent_needing_fix 0 15819 15820@<Glob...@>= 15821boolean fix_needed; /* does at least one |independent| variable need scaling? */ 15822boolean watch_coefs; /* should we scale coefficients that exceed |coef_bound|? */ 15823mp_value_node dep_final; /* location of the constant term and final link */ 15824 15825@ @<Set init...@>= 15826mp->fix_needed = false; 15827mp->watch_coefs = true; 15828 15829@ The |p_plus_fq| procedure has a fourth parameter, |t|, that should be 15830set to |mp_proto_dependent| if |p| is a proto-dependency list. In this 15831case |f| will be |scaled|, not a |fraction|. Similarly, the fifth parameter~|tt| 15832should be |mp_proto_dependent| if |q| is a proto-dependency list. 15833 15834List |q| is unchanged by the operation; but list |p| is totally destroyed. 15835 15836The final link of the dependency list or proto-dependency list returned 15837by |p_plus_fq| is the same as the original final link of~|p|. Indeed, the 15838constant term of the result will be located in the same |mem| location 15839as the original constant term of~|p|. 15840 15841Coefficients of the result are assumed to be zero if they are less than 15842a certain threshold. This compensates for inevitable rounding errors, 15843and tends to make more variables `|known|'. The threshold is approximately 15844$10^{-5}$ in the case of normal dependency lists, $10^{-4}$ for 15845proto-dependencies. 15846 15847@d fraction_threshold_k ((math_data *)mp->math)->fraction_threshold_t 15848@d half_fraction_threshold_k ((math_data *)mp->math)->half_fraction_threshold_t 15849@d scaled_threshold_k ((math_data *)mp->math)->scaled_threshold_t 15850@d half_scaled_threshold_k ((math_data *)mp->math)->half_scaled_threshold_t 15851 15852@<Declarations@>= 15853static mp_value_node mp_p_plus_fq (MP mp, mp_value_node p, mp_number f, 15854 mp_value_node q, mp_variable_type t, 15855 mp_variable_type tt); 15856 15857@ @c 15858static mp_value_node mp_p_plus_fq (MP mp, mp_value_node p, mp_number f, 15859 mp_value_node q, mp_variable_type t, 15860 mp_variable_type tt) { 15861 mp_node pp, qq; /* |dep_info(p)| and |dep_info(q)|, respectively */ 15862 mp_value_node r, s; /* for list manipulation */ 15863 mp_number threshold, half_threshold; /* defines a neighborhood of zero */ 15864 mp_number v, vv; /* temporary registers */ 15865 new_number (v); 15866 new_number (vv); 15867 new_number (threshold); 15868 new_number (half_threshold); 15869 if (t == mp_dependent) { 15870 number_clone (threshold, fraction_threshold_k); 15871 number_clone (half_threshold, half_fraction_threshold_k); 15872 } else { 15873 number_clone (threshold, scaled_threshold_k); 15874 number_clone (half_threshold, half_scaled_threshold_k); 15875 } 15876 r = (mp_value_node) mp->temp_head; 15877 pp = dep_info (p); 15878 qq = dep_info (q); 15879 while (1) { 15880 if (pp == qq) { 15881 if (pp == NULL) { 15882 break; 15883 } else { 15884 /* Contribute a term from |p|, plus |f| times the 15885 corresponding term from |q| */ 15886 mp_number r1; 15887 mp_number absv; 15888 new_fraction (r1); 15889 new_number (absv); 15890 if (tt == mp_dependent) { 15891 take_fraction (r1, f, dep_value (q)); 15892 } else { 15893 take_scaled (r1, f, dep_value (q)); 15894 } 15895 set_number_from_addition (v, dep_value (p), r1); 15896 free_number (r1); 15897 set_dep_value (p, v); 15898 s = p; 15899 p = (mp_value_node) mp_link (p); 15900 number_clone (absv, v); 15901 number_abs(absv); 15902 if (number_less (absv, threshold)) { 15903 mp_free_dep_node (mp, s); 15904 } else { 15905 if (number_greaterequal (absv, coef_bound_k) && mp->watch_coefs) { 15906 mp_type (qq) = independent_needing_fix; 15907 /* If we set this , then we can drop (mp_type(pp) == independent_needing_fix && mp->fix_needed) later */ 15908 /* set_number_from_scaled (value_number (qq), indep_value(qq)); */ 15909 mp->fix_needed = true; 15910 } 15911 set_mp_link (r, (mp_node) s); 15912 r = s; 15913 } 15914 free_number (absv); 15915 pp = dep_info (p); 15916 q = (mp_value_node) mp_link (q); 15917 qq = dep_info (q); 15918 } 15919 15920 } else { 15921 if (pp == NULL) 15922 set_number_to_neg_inf(v); 15923 else if (mp_type(pp) == mp_independent || (mp_type(pp) == independent_needing_fix && mp->fix_needed)) 15924 set_number_from_scaled(v, indep_value(pp)); 15925 else 15926 number_clone (v, value_number (pp)); 15927 if (qq == NULL) 15928 set_number_to_neg_inf(vv); 15929 else if (mp_type(qq) == mp_independent || (mp_type(qq) == independent_needing_fix && mp->fix_needed)) 15930 set_number_from_scaled(vv, indep_value(qq)); 15931 else 15932 number_clone (vv, value_number (qq)); 15933 if (number_less (v, vv)) { 15934 /* Contribute a term from |q|, multiplied by~|f| */ 15935 mp_number absv; 15936 new_number (absv); 15937 { 15938 mp_number r1; 15939 mp_number arg1, arg2; 15940 new_fraction (r1); 15941 new_number (arg1); 15942 new_number (arg2); 15943 number_clone (arg1, f); 15944 number_clone (arg2, dep_value (q)); 15945 if (tt == mp_dependent) { 15946 take_fraction (r1, arg1, arg2); 15947 } else { 15948 take_scaled (r1, arg1, arg2); 15949 } 15950 number_clone (v, r1); 15951 free_number (r1); 15952 free_number (arg1); 15953 free_number (arg2); 15954 } 15955 number_clone (absv, v); 15956 number_abs(absv); 15957 if (number_greater (absv, half_threshold)) { 15958 s = mp_get_dep_node (mp); 15959 set_dep_info (s, qq); 15960 set_dep_value (s, v); 15961 if (number_greaterequal(absv, coef_bound_k) && mp->watch_coefs) { 15962 /* clang: dereference of a null pointer ('qq') */ assert(qq); 15963 mp_type (qq) = independent_needing_fix; 15964 mp->fix_needed = true; 15965 } 15966 set_mp_link (r, (mp_node) s); 15967 r = s; 15968 } 15969 q = (mp_value_node) mp_link (q); 15970 qq = dep_info (q); 15971 free_number (absv); 15972 15973 } else { 15974 set_mp_link (r, (mp_node) p); 15975 r = p; 15976 p = (mp_value_node) mp_link (p); 15977 pp = dep_info (p); 15978 } 15979 } 15980 } 15981 { 15982 mp_number r1; 15983 mp_number arg1, arg2; 15984 new_fraction (r1); 15985 new_number (arg1); 15986 new_number (arg2); 15987 number_clone (arg1, dep_value (q)); 15988 number_clone (arg2, f); 15989 if (t == mp_dependent) { 15990 take_fraction (r1, arg1, arg2); 15991 } else { 15992 take_scaled (r1, arg1, arg2); 15993 } 15994 slow_add (arg1, dep_value (p), r1); 15995 set_dep_value (p, arg1); 15996 free_number (r1); 15997 free_number (arg1); 15998 free_number (arg2); 15999 } 16000 set_mp_link (r, (mp_node) p); 16001 mp->dep_final = p; 16002 free_number (threshold); 16003 free_number (half_threshold); 16004 free_number (v); 16005 free_number (vv); 16006 return (mp_value_node) mp_link (mp->temp_head); 16007} 16008 16009 16010@ It is convenient to have another subroutine for the special case 16011of |p_plus_fq| when |f=1.0|. In this routine lists |p| and |q| are 16012both of the same type~|t| (either |dependent| or |mp_proto_dependent|). 16013 16014@c 16015static mp_value_node mp_p_plus_q (MP mp, mp_value_node p, mp_value_node q, 16016 mp_variable_type t) { 16017 mp_node pp, qq; /* |dep_info(p)| and |dep_info(q)|, respectively */ 16018 mp_value_node s; /* for list manipulation */ 16019 mp_value_node r; /* for list manipulation */ 16020 mp_number threshold; /* defines a neighborhood of zero */ 16021 mp_number v, vv; /* temporary register */ 16022 new_number (v); 16023 new_number (vv); 16024 new_number (threshold); 16025 if (t == mp_dependent) 16026 number_clone (threshold, fraction_threshold_k); 16027 else 16028 number_clone (threshold, scaled_threshold_k); 16029 r = (mp_value_node) mp->temp_head; 16030 pp = dep_info (p); 16031 qq = dep_info (q); 16032 while (1) { 16033 if (pp == qq) { 16034 if (pp == NULL) { 16035 break; 16036 } else { 16037 /* Contribute a term from |p|, plus the corresponding term from |q| */ 16038 mp_number test; 16039 new_number (test); 16040 set_number_from_addition (v, dep_value (p), dep_value (q)); 16041 set_dep_value (p, v); 16042 s = p; 16043 p = (mp_value_node) mp_link (p); 16044 pp = dep_info (p); 16045 number_clone (test, v); 16046 number_abs(test); 16047 if (number_less (test, threshold)) { 16048 mp_free_dep_node (mp, s); 16049 } else { 16050 if (number_greaterequal(test, coef_bound_k) && mp->watch_coefs) { 16051 mp_type (qq) = independent_needing_fix; 16052 /* If we set this , then we can drop (mp_type(pp) == independent_needing_fix && mp->fix_needed) later */ 16053 /* set_number_from_scaled (value_number (qq), indep_value(qq)); */ 16054 mp->fix_needed = true; 16055 } 16056 set_mp_link (r, (mp_node) s); 16057 r = s; 16058 } 16059 free_number (test); 16060 q = (mp_value_node) mp_link (q); 16061 qq = dep_info (q); 16062 } 16063 16064 } else { 16065 if (pp == NULL) 16066 set_number_to_zero (v); 16067 else if (mp_type(pp) == mp_independent || (mp_type(pp) == independent_needing_fix && mp->fix_needed)) 16068 set_number_from_scaled (v, indep_value(pp)); 16069 else 16070 number_clone (v, value_number (pp)); 16071 if (qq == NULL) 16072 set_number_to_zero (vv); 16073 else if (mp_type(qq) == mp_independent || (mp_type(qq) == independent_needing_fix && mp->fix_needed)) 16074 set_number_from_scaled (vv, indep_value(qq)); 16075 else 16076 number_clone (vv, value_number (qq)); 16077 if (number_less (v, vv)) { 16078 s = mp_get_dep_node (mp); 16079 set_dep_info (s, qq); 16080 set_dep_value (s, dep_value (q)); 16081 q = (mp_value_node) mp_link (q); 16082 qq = dep_info (q); 16083 set_mp_link (r, (mp_node) s); 16084 r = s; 16085 } else { 16086 set_mp_link (r, (mp_node) p); 16087 r = p; 16088 p = (mp_value_node) mp_link (p); 16089 pp = dep_info (p); 16090 } 16091 } 16092 } 16093 { 16094 mp_number r1; 16095 new_number (r1); 16096 slow_add (r1, dep_value (p), dep_value (q)); 16097 set_dep_value (p, r1); 16098 free_number (r1); 16099 } 16100 set_mp_link (r, (mp_node) p); 16101 mp->dep_final = p; 16102 free_number (v); 16103 free_number (vv); 16104 free_number (threshold); 16105 return (mp_value_node) mp_link (mp->temp_head); 16106} 16107 16108@ A somewhat simpler routine will multiply a dependency list 16109by a given constant~|v|. The constant is either a |fraction| less than 16110|fraction_one|, or it is |scaled|. In the latter case we might be forced to 16111convert a dependency list to a proto-dependency list. 16112Parameters |t0| and |t1| are the list types before and after; 16113they should agree unless |t0=mp_dependent| and |t1=mp_proto_dependent| 16114and |v_is_scaled=true|. 16115 16116@c 16117static mp_value_node mp_p_times_v (MP mp, mp_value_node p, mp_number v, 16118 quarterword t0, quarterword t1, 16119 boolean v_is_scaled) { 16120 mp_value_node r, s; /* for list manipulation */ 16121 mp_number w; /* tentative coefficient */ 16122 mp_number threshold; 16123 boolean scaling_down; 16124 new_number (threshold); 16125 new_number (w); 16126 if (t0 != t1) 16127 scaling_down = true; 16128 else 16129 scaling_down = (!v_is_scaled); 16130 if (t1 == mp_dependent) 16131 number_clone (threshold, half_fraction_threshold_k); 16132 else 16133 number_clone (threshold, half_scaled_threshold_k); 16134 r = (mp_value_node) mp->temp_head; 16135 while (dep_info (p) != NULL) { 16136 mp_number test; 16137 new_number (test); 16138 if (scaling_down) { 16139 take_fraction (w, v, dep_value (p)); 16140 } else { 16141 take_scaled (w, v, dep_value (p)); 16142 } 16143 number_clone (test, w); 16144 number_abs(test); 16145 if (number_lessequal (test, threshold)) { 16146 s = (mp_value_node) mp_link (p); 16147 mp_free_dep_node (mp, p); 16148 p = s; 16149 } else { 16150 if (number_greaterequal(test, coef_bound_k)) { 16151 mp->fix_needed = true; 16152 mp_type (dep_info (p)) = independent_needing_fix; 16153 } 16154 set_mp_link (r, (mp_node) p); 16155 r = p; 16156 set_dep_value (p, w); 16157 p = (mp_value_node) mp_link (p); 16158 } 16159 free_number (test); 16160 } 16161 set_mp_link (r, (mp_node) p); 16162 { 16163 mp_number r1; 16164 new_number (r1); 16165 if (v_is_scaled) { 16166 take_scaled (r1, dep_value (p), v); 16167 } else { 16168 take_fraction (r1, dep_value (p), v); 16169 } 16170 set_dep_value (p, r1); 16171 free_number (r1); 16172 } 16173 free_number (w); 16174 free_number (threshold); 16175 return (mp_value_node) mp_link (mp->temp_head); 16176} 16177 16178 16179@ Similarly, we sometimes need to divide a dependency list 16180by a given |scaled| constant. 16181 16182@<Declarations@>= 16183static mp_value_node mp_p_over_v (MP mp, mp_value_node p, mp_number v, quarterword 16184 t0, quarterword t1); 16185 16186@ 16187@d p_over_v_threshold_k ((math_data *)mp->math)->p_over_v_threshold_t 16188 16189@c 16190mp_value_node mp_p_over_v (MP mp, mp_value_node p, mp_number v_orig, quarterword 16191 t0, quarterword t1) { 16192 mp_value_node r, s; /* for list manipulation */ 16193 mp_number w; /* tentative coefficient */ 16194 mp_number threshold; 16195 mp_number v; 16196 boolean scaling_down; 16197 new_number (v); 16198 new_number (w); 16199 new_number (threshold); 16200 number_clone (v, v_orig); 16201 if (t0 != t1) 16202 scaling_down = true; 16203 else 16204 scaling_down = false; 16205 if (t1 == mp_dependent) 16206 number_clone (threshold, half_fraction_threshold_k); 16207 else 16208 number_clone (threshold, half_scaled_threshold_k); 16209 r = (mp_value_node) mp->temp_head; 16210 while (dep_info (p) != NULL) { 16211 if (scaling_down) { 16212 mp_number x, absv; 16213 new_number (x); 16214 new_number (absv); 16215 number_clone (absv, v); 16216 number_abs (absv); 16217 if (number_less (absv, p_over_v_threshold_k)) { 16218 number_clone (x, v); 16219 convert_scaled_to_fraction (x); 16220 make_scaled (w, dep_value (p), x); 16221 } else { 16222 number_clone (x, dep_value (p)); 16223 fraction_to_round_scaled (x); 16224 make_scaled (w, x, v); 16225 } 16226 free_number (x); 16227 free_number (absv); 16228 } else { 16229 make_scaled (w, dep_value (p), v); 16230 } 16231 { 16232 mp_number test; 16233 new_number (test); 16234 number_clone (test, w); 16235 number_abs(test); 16236 if (number_lessequal (test, threshold)) { 16237 s = (mp_value_node) mp_link (p); 16238 mp_free_dep_node (mp, p); 16239 p = s; 16240 } else { 16241 if (number_greaterequal (test, coef_bound_k)) { 16242 mp->fix_needed = true; 16243 mp_type (dep_info (p)) = independent_needing_fix; 16244 } 16245 set_mp_link (r, (mp_node) p); 16246 r = p; 16247 set_dep_value (p, w); 16248 p = (mp_value_node) mp_link (p); 16249 } 16250 free_number (test); 16251 } 16252 } 16253 set_mp_link (r, (mp_node) p); 16254 { 16255 mp_number ret; 16256 new_number (ret); 16257 make_scaled (ret, dep_value (p), v); 16258 set_dep_value (p, ret); 16259 free_number (ret); 16260 } 16261 free_number (v); 16262 free_number (w); 16263 free_number (threshold); 16264 return (mp_value_node) mp_link (mp->temp_head); 16265} 16266 16267 16268@ Here's another utility routine for dependency lists. When an independent 16269variable becomes dependent, we want to remove it from all existing 16270dependencies. The |p_with_x_becoming_q| function computes the 16271dependency list of~|p| after variable~|x| has been replaced by~|q|. 16272 16273This procedure has basically the same calling conventions as |p_plus_fq|: 16274List~|q| is unchanged; list~|p| is destroyed; the constant node and the 16275final link are inherited from~|p|; and the fourth parameter tells whether 16276or not |p| is |mp_proto_dependent|. However, the global variable |dep_final| 16277is not altered if |x| does not occur in list~|p|. 16278 16279@c 16280static mp_value_node mp_p_with_x_becoming_q (MP mp, mp_value_node p, 16281 mp_node x, mp_node q, 16282 quarterword t) { 16283 mp_value_node r, s; /* for list manipulation */ 16284 integer sx; /* serial number of |x| */ 16285 s = p; 16286 r = (mp_value_node) mp->temp_head; 16287 sx = indep_value (x); 16288 while (dep_info (s) != NULL && indep_value (dep_info (s)) > sx) { 16289 r = s; 16290 s = (mp_value_node) mp_link (s); 16291 } 16292 if (dep_info (s) == NULL || dep_info (s) != x) { 16293 return p; 16294 } else { 16295 mp_value_node ret; 16296 mp_number v1; 16297 new_number (v1); 16298 set_mp_link (mp->temp_head, (mp_node) p); 16299 set_mp_link (r, mp_link (s)); 16300 number_clone (v1, dep_value (s)); 16301 mp_free_dep_node (mp, s); 16302 ret = mp_p_plus_fq (mp, (mp_value_node) mp_link (mp->temp_head), v1, 16303 (mp_value_node) q, t, mp_dependent); 16304 free_number (v1); 16305 return ret; 16306 } 16307} 16308 16309 16310@ Here's a simple procedure that reports an error when a variable 16311has just received a known value that's out of the required range. 16312 16313@<Declarations@>= 16314static void mp_val_too_big (MP mp, mp_number x); 16315 16316@ @c 16317static void mp_val_too_big (MP mp, mp_number x) { 16318 if (number_positive (internal_value (mp_warning_check))) { 16319 char msg[256]; 16320 const char *hlp[] = { 16321 "The equation I just processed has given some variable a", 16322 "value outside of the safetyp range. Continue and I'll try", 16323 "to cope with that big value; but it might be dangerous.", 16324 "(Set warningcheck:=0 to suppress this message.)", 16325 NULL }; 16326 mp_snprintf (msg, 256, "Value is too large (%s)", number_tostring(x)); 16327 mp_error (mp, msg, hlp, true); 16328 } 16329} 16330 16331@ When a dependent variable becomes known, the following routine 16332removes its dependency list. Here |p| points to the variable, and 16333|q| points to the dependency list (which is one node long). 16334 16335@<Declarations@>= 16336static void mp_make_known (MP mp, mp_value_node p, mp_value_node q); 16337 16338@ @c 16339void mp_make_known (MP mp, mp_value_node p, mp_value_node q) { 16340 mp_variable_type t; /* the previous type */ 16341 mp_number absp; 16342 new_number (absp); 16343 set_prev_dep (mp_link (q), prev_dep (p)); 16344 set_mp_link (prev_dep (p), mp_link (q)); 16345 t = mp_type (p); 16346 mp_type (p) = mp_known; 16347 set_value_number (p, dep_value (q)); 16348 mp_free_dep_node (mp, q); 16349 number_clone (absp, value_number (p)); 16350 number_abs (absp); 16351 if (number_greaterequal (absp, warning_limit_t)) 16352 mp_val_too_big (mp, value_number (p)); 16353 if ((number_positive(internal_value (mp_tracing_equations))) 16354 && mp_interesting (mp, (mp_node) p)) { 16355 mp_begin_diagnostic (mp); 16356 mp_print_nl (mp, "#### "); 16357 mp_print_variable_name (mp, (mp_node) p); 16358 mp_print_char (mp, xord ('=')); 16359 print_number (value_number (p)); 16360 mp_end_diagnostic (mp, false); 16361 } 16362 if (cur_exp_node () == (mp_node) p && mp->cur_exp.type == t) { 16363 mp->cur_exp.type = mp_known; 16364 set_cur_exp_value_number (value_number (p)); 16365 mp_free_value_node (mp, (mp_node) p); 16366 } 16367 free_number (absp); 16368} 16369 16370 16371@ The |fix_dependencies| routine is called into action when |fix_needed| 16372has been triggered. The program keeps a list~|s| of independent variables 16373whose coefficients must be divided by~4. 16374 16375In unusual cases, this fixup process might reduce one or more coefficients 16376to zero, so that a variable will become known more or less by default. 16377 16378@<Declarations@>= 16379static void mp_fix_dependencies (MP mp); 16380 16381@ 16382@d independent_being_fixed 1 /* this variable already appears in |s| */ 16383@c 16384static void mp_fix_dependencies (MP mp) { 16385 mp_value_node p, q, r, s, t; /* list manipulation registers */ 16386 mp_node x; /* an independent variable */ 16387 r = (mp_value_node) mp_link (mp->dep_head); 16388 s = NULL; 16389 while (r != mp->dep_head) { 16390 t = r; 16391 /* Run through the dependency list for variable |t|, fixing 16392 all nodes, and ending with final link~|q| */ 16393 while (1) { 16394 if (t==r) { 16395 q = (mp_value_node) dep_list(t); 16396 } else { 16397 q = (mp_value_node) mp_link (r); 16398 } 16399 x = dep_info (q); 16400 if (x == NULL) 16401 break; 16402 if (mp_type (x) <= independent_being_fixed) { 16403 if (mp_type (x) < independent_being_fixed) { 16404 p = mp_get_dep_node (mp); 16405 set_mp_link (p, (mp_node) s); 16406 s = p; 16407 set_dep_info (s, x); 16408 mp_type (x) = independent_being_fixed; 16409 } 16410 set_dep_value (q, dep_value (q)); 16411 number_divide_int (dep_value (q), 4); 16412 if (number_zero(dep_value (q))) { 16413 set_mp_link (r, mp_link (q)); 16414 mp_free_dep_node (mp, q); 16415 q = r; 16416 } 16417 } 16418 r = q; 16419 } 16420 16421 r = (mp_value_node) mp_link (q); 16422 if (q == (mp_value_node) dep_list (t)) 16423 mp_make_known (mp, t, q); 16424 } 16425 while (s != NULL) { 16426 p = (mp_value_node) mp_link (s); 16427 x = dep_info (s); 16428 mp_free_dep_node (mp, s); 16429 s = p; 16430 mp_type (x) = mp_independent; 16431 set_indep_scale (x, indep_scale (x) + 2); 16432 } 16433 mp->fix_needed = false; 16434} 16435 16436 16437@ The |new_dep| routine installs a dependency list~|p| based on the value node~|q|, 16438linking it into the list of all known dependencies. It replaces |q| with the new 16439dependency node. We assume that |dep_final| points to the final node of list~|p|. 16440 16441@c 16442static void mp_new_dep (MP mp, mp_node q, mp_variable_type newtype, 16443 mp_value_node p) { 16444 mp_node r; /* what used to be the first dependency */ 16445 FUNCTION_TRACE4 ("mp_new_dep(%p,%d,%p)\n", q, newtype, p); 16446 mp_type (q) = newtype; 16447 set_dep_list (q, p); 16448 set_prev_dep (q, (mp_node) mp->dep_head); 16449 r = mp_link (mp->dep_head); 16450 set_mp_link (mp->dep_final, r); 16451 set_prev_dep (r, (mp_node) mp->dep_final); 16452 set_mp_link (mp->dep_head, q); 16453} 16454 16455 16456@ Here is one of the ways a dependency list gets started. 16457The |const_dependency| routine produces a list that has nothing but 16458a constant term. 16459 16460@c 16461static mp_value_node mp_const_dependency (MP mp, mp_number v) { 16462 mp->dep_final = mp_get_dep_node (mp); 16463 set_dep_value (mp->dep_final, v); 16464 set_dep_info (mp->dep_final, NULL); 16465 FUNCTION_TRACE3 ("%p = mp_const_dependency(%d)\n", mp->dep_final, number_to_scaled (v)); 16466 return mp->dep_final; 16467} 16468 16469 16470@ And here's a more interesting way to start a dependency list from scratch: 16471The parameter to |single_dependency| is the location of an 16472independent variable~|x|, and the result is the simple dependency list 16473`|x+0|'. 16474 16475In the unlikely event that the given independent variable has been doubled so 16476often that we can't refer to it with a nonzero coefficient, 16477|single_dependency| returns the simple list `0'. This case can be 16478recognized by testing that the returned list pointer is equal to 16479|dep_final|. 16480 16481@d two_to_the(A) (1<<(unsigned)(A)) 16482 16483@c 16484static mp_value_node mp_single_dependency (MP mp, mp_node p) { 16485 mp_value_node q, rr; /* the new dependency list */ 16486 integer m; /* the number of doublings */ 16487 m = indep_scale (p); 16488 if (m > 28) { 16489 q = mp_const_dependency (mp, zero_t); 16490 } else { 16491 q = mp_get_dep_node (mp); 16492 set_dep_value (q, zero_t); 16493 set_number_from_scaled (dep_value (q), (integer) two_to_the (28 - m)); 16494 set_dep_info (q, p); 16495 rr = mp_const_dependency (mp, zero_t); 16496 set_mp_link (q, (mp_node) rr); 16497 } 16498 FUNCTION_TRACE3 ("%p = mp_single_dependency(%p)\n", q, p); 16499 return q; 16500} 16501 16502 16503@ We sometimes need to make an exact copy of a dependency list. 16504 16505@c 16506static mp_value_node mp_copy_dep_list (MP mp, mp_value_node p) { 16507 mp_value_node q; /* the new dependency list */ 16508 FUNCTION_TRACE2 ("mp_copy_dep_list(%p)\n", p); 16509 q = mp_get_dep_node (mp); 16510 mp->dep_final = q; 16511 while (1) { 16512 set_dep_info (mp->dep_final, dep_info (p)); 16513 set_dep_value (mp->dep_final, dep_value (p)); 16514 if (dep_info (mp->dep_final) == NULL) 16515 break; 16516 set_mp_link (mp->dep_final, (mp_node) mp_get_dep_node (mp)); 16517 mp->dep_final = (mp_value_node) mp_link (mp->dep_final); 16518 p = (mp_value_node) mp_link (p); 16519 } 16520 return q; 16521} 16522 16523 16524@ But how do variables normally become known? Ah, now we get to the heart of the 16525equation-solving mechanism. The |linear_eq| procedure is given a |dependent| 16526or |mp_proto_dependent| list,~|p|, in which at least one independent variable 16527appears. It equates this list to zero, by choosing an independent variable 16528with the largest coefficient and making it dependent on the others. The 16529newly dependent variable is eliminated from all current dependencies, 16530thereby possibly making other dependent variables known. 16531 16532The given list |p| is, of course, totally destroyed by all this processing. 16533 16534@c 16535static mp_value_node find_node_with_largest_coefficient(MP mp, mp_value_node p, mp_number *v); 16536static void display_new_dependency (MP mp, mp_value_node p, mp_node x, integer n); 16537static void change_to_known (MP mp, mp_value_node p, mp_node x, mp_value_node final_node, integer n); 16538static mp_value_node divide_p_by_minusv_removing_q (MP mp, mp_value_node p, mp_value_node q, 16539 mp_value_node *final_node, mp_number v, quarterword t); 16540static mp_value_node divide_p_by_2_n (MP mp, mp_value_node p, integer n); 16541static void mp_linear_eq (MP mp, mp_value_node p, quarterword t) { 16542 mp_value_node r; /* for link manipulation */ 16543 mp_node x; /* the variable that loses its independence */ 16544 integer n; /* the number of times |x| had been halved */ 16545 mp_number v; /* the coefficient of |x| in list |p| */ 16546 mp_value_node prev_r; /* lags one step behind |r| */ 16547 mp_value_node final_node; /* the constant term of the new dependency list */ 16548 mp_value_node qq; 16549 new_number (v); 16550 FUNCTION_TRACE3 ("mp_linear_eq(%p,%d)\n", p, t); 16551 qq = find_node_with_largest_coefficient(mp, p, &v); 16552 x = dep_info (qq); 16553 n = indep_scale (x); 16554 p = divide_p_by_minusv_removing_q(mp, p, qq, &final_node, v, t); 16555 if (number_positive (internal_value (mp_tracing_equations))) { 16556 display_new_dependency(mp,p,(mp_node)x,n); 16557 } 16558 prev_r = (mp_value_node) mp->dep_head; 16559 r = (mp_value_node) mp_link (mp->dep_head); 16560 while (r != mp->dep_head) { 16561 mp_value_node s = (mp_value_node) dep_list (r); 16562 mp_value_node q = mp_p_with_x_becoming_q (mp, s, x, (mp_node) p, mp_type (r)); 16563 if (dep_info (q) == NULL) { 16564 mp_make_known (mp, r, q); 16565 } else { 16566 set_dep_list (r, q); 16567 do { 16568 q = (mp_value_node) mp_link (q); 16569 } while (dep_info (q) != NULL); 16570 prev_r = q; 16571 } 16572 r = (mp_value_node) mp_link (prev_r); 16573 } 16574 if (n > 0) { 16575 p = divide_p_by_2_n(mp, p, n); 16576 } 16577 change_to_known(mp,p,(mp_node)x,final_node,n); 16578 if (mp->fix_needed) 16579 mp_fix_dependencies (mp); 16580 free_number (v); 16581} 16582 16583 16584@ 16585@c 16586static mp_value_node find_node_with_largest_coefficient(MP mp, mp_value_node p, mp_number *v) { 16587 mp_number vabs; /* its absolute value of v*/ 16588 mp_number rabs; /* the absolute value of |dep_value(r)| */ 16589 mp_value_node q = p; 16590 mp_value_node r = (mp_value_node) mp_link (p); 16591 new_number (vabs); 16592 new_number (rabs); 16593 number_clone (*v, dep_value (q)); 16594 while (dep_info (r) != NULL) { 16595 number_clone (vabs, *v); 16596 number_abs (vabs); 16597 number_clone (rabs, dep_value (r)); 16598 number_abs (rabs); 16599 if (number_greater (rabs, vabs)) { 16600 q = r; 16601 number_clone (*v, dep_value (r)); 16602 } 16603 r = (mp_value_node) mp_link (r); 16604 } 16605 free_number (vabs); 16606 free_number (rabs); 16607 return q; 16608} 16609 16610 16611@ Here we want to change the coefficients from |scaled| to |fraction|, 16612except in the constant term. In the common case of a trivial equation 16613like `\.{x=3.14}', we will have |v=-fraction_one|, |q=p|, and |t=mp_dependent|. 16614 16615@c 16616static mp_value_node divide_p_by_minusv_removing_q (MP mp, mp_value_node p, mp_value_node q, 16617 mp_value_node *final_node, mp_number v, quarterword t) { 16618 mp_value_node r; /* for link manipulation */ 16619 mp_value_node s; 16620 s = (mp_value_node) mp->temp_head; 16621 set_mp_link (s, (mp_node) p); 16622 r = p; 16623 do { 16624 if (r == q) { 16625 set_mp_link (s, mp_link (r)); 16626 mp_free_dep_node (mp, r); 16627 } else { 16628 mp_number w; /* a tentative coefficient */ 16629 mp_number absw; 16630 new_number (w); 16631 new_number (absw); 16632 make_fraction (w, dep_value (r), v); 16633 number_clone (absw, w); 16634 number_abs (absw); 16635 if (number_lessequal (absw, half_fraction_threshold_k)) { 16636 set_mp_link (s, mp_link (r)); 16637 mp_free_dep_node (mp, r); 16638 } else { 16639 number_negate (w); 16640 set_dep_value (r, w); 16641 s = r; 16642 } 16643 free_number(w); 16644 free_number (absw); 16645 } 16646 r = (mp_value_node) mp_link (s); 16647 } while (dep_info (r) != NULL); 16648 16649 if (t == mp_proto_dependent) { 16650 mp_number ret; 16651 new_number (ret); 16652 make_scaled (ret, dep_value (r), v); 16653 number_negate (ret); 16654 set_dep_value (r, ret); 16655 free_number (ret); 16656 } else if (number_to_scaled (v) != -number_to_scaled (fraction_one_t)) { 16657 mp_number ret; 16658 new_fraction (ret); 16659 make_fraction (ret, dep_value (r), v); 16660 number_negate (ret); 16661 set_dep_value (r, ret); 16662 free_number (ret); 16663 } 16664 *final_node = r; 16665 return (mp_value_node) mp_link (mp->temp_head); 16666} 16667 16668 16669@ 16670@c 16671static void display_new_dependency (MP mp, mp_value_node p, mp_node x, integer n) { 16672 if (mp_interesting (mp, x)) { 16673 int w0; 16674 mp_begin_diagnostic (mp); 16675 mp_print_nl (mp, "## "); 16676 mp_print_variable_name (mp, x); 16677 w0 = n; 16678 while (w0 > 0) { 16679 mp_print (mp, "*4"); 16680 w0 = w0 - 2; 16681 } 16682 mp_print_char (mp, xord ('=')); 16683 mp_print_dependency (mp, p, mp_dependent); 16684 mp_end_diagnostic (mp, false); 16685 } 16686} 16687 16688@ The |n > 0| test is repeated here because it is of vital importance to the 16689function's functioning. 16690 16691@c 16692static mp_value_node divide_p_by_2_n (MP mp, mp_value_node p, integer n) { 16693 mp_value_node pp = NULL; 16694 if (n > 0) { 16695 /* Divide list |p| by $2^n$ */ 16696 mp_value_node r; 16697 mp_value_node s; 16698 mp_number absw; 16699 mp_number w; /* a tentative coefficient */ 16700 new_number (w); 16701 new_number (absw); 16702 s = (mp_value_node) mp->temp_head; 16703 set_mp_link (mp->temp_head, (mp_node) p); 16704 r = p; 16705 do { 16706 if (n > 30) { 16707 set_number_to_zero (w); 16708 } else { 16709 number_clone (w, dep_value (r)); 16710 number_divide_int (w, two_to_the (n)); 16711 } 16712 number_clone (absw, w); 16713 number_abs (absw); 16714 if (number_lessequal(absw, half_fraction_threshold_k) && (dep_info (r) != NULL)) { 16715 set_mp_link (s, mp_link (r)); 16716 mp_free_dep_node (mp, r); 16717 } else { 16718 set_dep_value (r, w); 16719 s = r; 16720 } 16721 r = (mp_value_node) mp_link (s); 16722 } while (dep_info (s) != NULL); 16723 pp = (mp_value_node) mp_link (mp->temp_head); 16724 free_number (absw); 16725 free_number (w); 16726 } 16727 return pp; 16728} 16729 16730@ 16731@c 16732static void change_to_known (MP mp, mp_value_node p, mp_node x, mp_value_node final_node, integer n) { 16733 if (dep_info (p) == NULL) { 16734 mp_number absx; 16735 new_number (absx); 16736 mp_type (x) = mp_known; 16737 set_value_number (x, dep_value (p)); 16738 number_clone (absx, value_number (x)); 16739 number_abs (absx); 16740 if (number_greaterequal (absx, warning_limit_t)) 16741 mp_val_too_big (mp, value_number (x)); 16742 free_number (absx); 16743 mp_free_dep_node (mp, p); 16744 if (cur_exp_node () == x && mp->cur_exp.type == mp_independent) { 16745 set_cur_exp_value_number (value_number (x)); 16746 mp->cur_exp.type = mp_known; 16747 mp_free_value_node (mp, x); 16748 } 16749 } else { 16750 mp->dep_final = final_node; 16751 mp_new_dep (mp, x, mp_dependent, p); 16752 if (cur_exp_node () == x && mp->cur_exp.type == mp_independent) { 16753 mp->cur_exp.type = mp_dependent; 16754 } 16755 } 16756} 16757 16758@* Dynamic nonlinear equations. 16759Variables of numeric type are maintained by the general scheme of 16760independent, dependent, and known values that we have just studied; 16761and the components of pair and transform variables are handled in the 16762same way. But \MP\ also has five other types of values: \&{boolean}, 16763\&{string}, \&{pen}, \&{path}, and \&{picture}; what about them? 16764 16765Equations are allowed between nonlinear quantities, but only in a 16766simple form. Two variables that haven't yet been assigned values are 16767either equal to each other, or they're not. 16768 16769Before a boolean variable has received a value, its type is |mp_unknown_boolean|; 16770similarly, there are variables whose type is |mp_unknown_string|, |mp_unknown_pen|, 16771|mp_unknown_path|, and |mp_unknown_picture|. In such cases the value is either 16772|NULL| (which means that no other variables are equivalent to this one), or 16773it points to another variable of the same undefined type. The pointers in the 16774latter case form a cycle of nodes, which we shall call a ``ring.'' 16775Rings of undefined variables may include capsules, which arise as 16776intermediate results within expressions or as \&{expr} parameters to macros. 16777 16778When one member of a ring receives a value, the same value is given to 16779all the other members. In the case of paths and pictures, this implies 16780making separate copies of a potentially large data structure; users should 16781restrain their enthusiasm for such generality, unless they have lots and 16782lots of memory space. 16783 16784@ The following procedure is called when a capsule node is being 16785added to a ring (e.g., when an unknown variable is mentioned in an expression). 16786 16787@c 16788static mp_node mp_new_ring_entry (MP mp, mp_node p) { 16789 mp_node q; /* the new capsule node */ 16790 q = mp_get_value_node (mp); 16791 mp_name_type (q) = mp_capsule; 16792 mp_type (q) = mp_type (p); 16793 if (value_node (p) == NULL) 16794 set_value_node (q, p); 16795 else 16796 set_value_node (q, value_node (p)); 16797 set_value_node (p, q); 16798 return q; 16799} 16800 16801 16802@ Conversely, we might delete a capsule or a variable before it becomes known. 16803The following procedure simply detaches a quantity from its ring, 16804without recycling the storage. 16805 16806@<Declarations@>= 16807static void mp_ring_delete (MP mp, mp_node p); 16808 16809@ @c 16810void mp_ring_delete (MP mp, mp_node p) { 16811 mp_node q; 16812 (void) mp; 16813 q = value_node (p); 16814 if (q != NULL && q != p) { 16815 while (value_node (q) != p) 16816 q = value_node (q); 16817 set_value_node (q, value_node (p)); 16818 } 16819} 16820 16821 16822@ Eventually there might be an equation that assigns values to all of the 16823variables in a ring. The |nonlinear_eq| subroutine does the necessary 16824propagation of values. 16825 16826If the parameter |flush_p| is |true|, node |p| itself needn't receive a 16827value, it will soon be recycled. 16828 16829@c 16830static void mp_nonlinear_eq (MP mp, mp_value v, mp_node p, boolean flush_p) { 16831 mp_variable_type t; /* the type of ring |p| */ 16832 mp_node q, r; /* link manipulation registers */ 16833 t = (mp_type (p) - unknown_tag); 16834 q = value_node (p); 16835 if (flush_p) 16836 mp_type (p) = mp_vacuous; 16837 else 16838 p = q; 16839 do { 16840 r = value_node (q); 16841 mp_type (q) = t; 16842 switch (t) { 16843 case mp_boolean_type: 16844 set_value_number (q, v.data.n); 16845 break; 16846 case mp_string_type: 16847 set_value_str (q, v.data.str); 16848 add_str_ref (v.data.str); 16849 break; 16850 case mp_pen_type: 16851 set_value_knot (q, copy_pen (v.data.p)); 16852 break; 16853 case mp_path_type: 16854 set_value_knot (q, mp_copy_path (mp, v.data.p)); 16855 break; 16856 case mp_picture_type: 16857 set_value_node (q, v.data.node); 16858 add_edge_ref (v.data.node); 16859 break; 16860 default: 16861 break; 16862 } /* there ain't no more cases */ 16863 q = r; 16864 } while (q != p); 16865} 16866 16867 16868@ If two members of rings are equated, and if they have the same type, 16869the |ring_merge| procedure is called on to make them equivalent. 16870 16871@c 16872static void mp_ring_merge (MP mp, mp_node p, mp_node q) { 16873 mp_node r; /* traverses one list */ 16874 r = value_node (p); 16875 while (r != p) { 16876 if (r == q) { 16877 exclaim_redundant_equation(mp); 16878 return; 16879 }; 16880 r = value_node (r); 16881 } 16882 r = value_node (p); 16883 set_value_node (p, value_node (q)); 16884 set_value_node (q, r); 16885} 16886 16887 16888@ @c 16889static void exclaim_redundant_equation (MP mp) { 16890 const char *hlp[] = { 16891 "I already knew that this equation was true.", 16892 "But perhaps no harm has been done; let's continue.", 16893 NULL }; 16894 mp_back_error (mp, "Redundant equation", hlp, true); 16895 mp_get_x_next (mp); 16896} 16897 16898@ @<Declarations@>= 16899static void exclaim_redundant_equation (MP mp); 16900 16901@* Introduction to the syntactic routines. 16902Let's pause a moment now and try to look at the Big Picture. 16903The \MP\ program consists of three main parts: syntactic routines, 16904semantic routines, and output routines. The chief purpose of the 16905syntactic routines is to deliver the user's input to the semantic routines, 16906while parsing expressions and locating operators and operands. The 16907semantic routines act as an interpreter responding to these operators, 16908which may be regarded as commands. And the output routines are 16909periodically called on to produce compact font descriptions that can be 16910used for typesetting or for making interim proof drawings. We have 16911discussed the basic data structures and many of the details of semantic 16912operations, so we are good and ready to plunge into the part of \MP\ that 16913actually controls the activities. 16914 16915Our current goal is to come to grips with the |get_next| procedure, 16916which is the keystone of \MP's input mechanism. Each call of |get_next| 16917sets the value of three variables |cur_cmd|, |cur_mod|, and |cur_sym|, 16918representing the next input token. 16919$$\vbox{\halign{#\hfil\cr 16920 \hbox{|cur_cmd| denotes a command code from the long list of codes 16921 given earlier;}\cr 16922 \hbox{|cur_mod| denotes a modifier or operand of the command code;}\cr 16923 \hbox{|cur_sym| is the hash address of the symbolic token that was 16924 just scanned,}\cr 16925 \hbox{\qquad or zero in the case of a numeric or string 16926 or capsule token.}\cr}}$$ 16927Underlying this external behavior of |get_next| is all the machinery 16928necessary to convert from character files to tokens. At a given time we 16929may be only partially finished with the reading of several files (for 16930which \&{input} was specified), and partially finished with the expansion 16931of some user-defined macros and/or some macro parameters, and partially 16932finished reading some text that the user has inserted online, 16933and so on. When reading a character file, the characters must be 16934converted to tokens; comments and blank spaces must 16935be removed, numeric and string tokens must be evaluated. 16936 16937To handle these situations, which might all be present simultaneously, 16938\MP\ uses various stacks that hold information about the incomplete 16939activities, and there is a finite state control for each level of the 16940input mechanism. These stacks record the current state of an implicitly 16941recursive process, but the |get_next| procedure is not recursive. 16942 16943@d cur_cmd() (unsigned)(mp->cur_mod_->type) 16944@d set_cur_cmd(A) mp->cur_mod_->type=(A) 16945@d cur_mod_int() number_to_int (mp->cur_mod_->data.n) /* operand of current command */ 16946@d cur_mod() number_to_scaled (mp->cur_mod_->data.n) /* operand of current command */ 16947@d cur_mod_number() mp->cur_mod_->data.n /* operand of current command */ 16948@d set_cur_mod(A) set_number_from_scaled (mp->cur_mod_->data.n, (A)) 16949@d set_cur_mod_number(A) number_clone (mp->cur_mod_->data.n, (A)) 16950@d cur_mod_node() mp->cur_mod_->data.node 16951@d set_cur_mod_node(A) mp->cur_mod_->data.node=(A) 16952@d cur_mod_str() mp->cur_mod_->data.str 16953@d set_cur_mod_str(A) mp->cur_mod_->data.str=(A) 16954@d cur_sym() mp->cur_mod_->data.sym 16955@d set_cur_sym(A) mp->cur_mod_->data.sym=(A) 16956@d cur_sym_mod() mp->cur_mod_->name_type 16957@d set_cur_sym_mod(A) mp->cur_mod_->name_type=(A) 16958 16959@<Glob...@>= 16960mp_node cur_mod_; /* current command, symbol, and its operands */ 16961 16962@ @<Initialize table...@>= 16963mp->cur_mod_ = mp_get_symbolic_node(mp); 16964 16965@ @<Free table...@>= 16966mp_free_symbolic_node(mp, mp->cur_mod_); 16967 16968@ The |print_cmd_mod| routine prints a symbolic interpretation of a 16969command code and its modifier. 16970It consists of a rather tedious sequence of print 16971commands, and most of it is essentially an inverse to the |primitive| 16972routine that enters a \MP\ primitive into |hash| and |eqtb|. Therefore almost 16973all of this procedure appears elsewhere in the program, together with the 16974corresponding |primitive| calls. 16975 16976@<Declarations@>= 16977static void mp_print_cmd_mod (MP mp, integer c, integer m); 16978 16979@ @c 16980void mp_print_cmd_mod (MP mp, integer c, integer m) { 16981 switch (c) { 16982 @<Cases of |print_cmd_mod| for symbolic printing of primitives@> 16983 default: 16984 mp_print (mp, "[unknown command code!]"); 16985 break; 16986 } 16987} 16988 16989 16990@ Here is a procedure that displays a given command in braces, in the 16991user's transcript file. 16992 16993@d show_cur_cmd_mod mp_show_cmd_mod(mp, cur_cmd(),cur_mod()) 16994 16995@c 16996static void mp_show_cmd_mod (MP mp, integer c, integer m) { 16997 mp_begin_diagnostic (mp); 16998 mp_print_nl (mp, "{"); 16999 mp_print_cmd_mod (mp, c, m); 17000 mp_print_char (mp, xord ('}')); 17001 mp_end_diagnostic (mp, false); 17002} 17003 17004 17005@* Input stacks and states. 17006The state of \MP's input mechanism appears in the input stack, whose 17007entries are records with five fields, called |index|, |start|, |loc|, 17008|limit|, and |name|. The top element of this stack is maintained in a 17009global variable for which no subscripting needs to be done; the other 17010elements of the stack appear in an array. Hence the stack is declared thus: 17011 17012@<Types...@>= 17013typedef struct { 17014 char *long_name_field; 17015 halfword start_field, loc_field, limit_field; 17016 mp_node nstart_field, nloc_field; 17017 mp_string name_field; 17018 quarterword index_field; 17019} in_state_record; 17020 17021@ @<Glob...@>= 17022in_state_record *input_stack; 17023integer input_ptr; /* first unused location of |input_stack| */ 17024integer max_in_stack; /* largest value of |input_ptr| when pushing */ 17025in_state_record cur_input; /* the ``top'' input state */ 17026int stack_size; /* maximum number of simultaneous input sources */ 17027 17028@ @<Allocate or initialize ...@>= 17029mp->stack_size = 16; 17030mp->input_stack = xmalloc ((mp->stack_size + 1), sizeof (in_state_record)); 17031 17032@ @<Dealloc variables@>= 17033xfree (mp->input_stack); 17034 17035@ We've already defined the special variable |loc==cur_input.loc_field| 17036in our discussion of basic input-output routines. The other components of 17037|cur_input| are defined in the same way: 17038 17039@d iindex mp->cur_input.index_field /* reference for buffer information */ 17040@d start mp->cur_input.start_field /* starting position in |buffer| */ 17041@d limit mp->cur_input.limit_field /* end of current line in |buffer| */ 17042@d name mp->cur_input.name_field /* name of the current file */ 17043 17044@ Let's look more closely now at the five control variables 17045(|index|,~|start|,~|loc|,~|limit|,~|name|), 17046assuming that \MP\ is reading a line of characters that have been input 17047from some file or from the user's terminal. There is an array called 17048|buffer| that acts as a stack of all lines of characters that are 17049currently being read from files, including all lines on subsidiary 17050levels of the input stack that are not yet completed. \MP\ will return to 17051the other lines when it is finished with the present input file. 17052 17053(Incidentally, on a machine with byte-oriented addressing, it would be 17054appropriate to combine |buffer| with the |str_pool| array, 17055letting the buffer entries grow downward from the top of the string pool 17056and checking that these two tables don't bump into each other.) 17057 17058The line we are currently working on begins in position |start| of the 17059buffer; the next character we are about to read is |buffer[loc]|; and 17060|limit| is the location of the last character present. We always have 17061|loc<=limit|. For convenience, |buffer[limit]| has been set to |"%"|, so 17062that the end of a line is easily sensed. 17063 17064The |name| variable is a string number that designates the name of 17065the current file, if we are reading an ordinary text file. Special codes 17066|is_term..max_spec_src| indicate other sources of input text. 17067 17068@d is_term (mp_string)0 /* |name| value when reading from the terminal for normal input */ 17069@d is_read (mp_string)1 /* |name| value when executing a \&{readstring} or \&{readfrom} */ 17070@d is_scantok (mp_string)2 /* |name| value when reading text generated by \&{scantokens} */ 17071@d max_spec_src is_scantok 17072 17073@ Additional information about the current line is available via the 17074|index| variable, which counts how many lines of characters are present 17075in the buffer below the current level. We have |index=0| when reading 17076from the terminal and prompting the user for each line; then if the user types, 17077e.g., `\.{input figs}', we will have |index=1| while reading 17078the file \.{figs.mp}. However, it does not follow that |index| is the 17079same as the input stack pointer, since many of the levels on the input 17080stack may come from token lists and some |index| values may correspond 17081to \.{MPX} files that are not currently on the stack. 17082 17083The global variable |in_open| is equal to the highest |index| value counting 17084\.{MPX} files but excluding token-list input levels. Thus, the number of 17085partially read lines in the buffer is |in_open+1| and we have |in_open>=index| 17086when we are not reading a token list. 17087 17088If we are not currently reading from the terminal, 17089we are reading from the file variable |input_file[index]|. We use 17090the notation |terminal_input| as a convenient abbreviation for |name=is_term|, 17091and |cur_file| as an abbreviation for |input_file[index]|. 17092 17093When \MP\ is not reading from the terminal, the global variable |line| contains 17094the line number in the current file, for use in error messages. More precisely, 17095|line| is a macro for |line_stack[index]| and the |line_stack| array gives 17096the line number for each file in the |input_file| array. 17097 17098When an \.{MPX} file is opened the file name is stored in the |mpx_name| 17099array so that the name doesn't get lost when the file is temporarily removed 17100from the input stack. 17101Thus when |input_file[k]| is an \.{MPX} file, its name is |mpx_name[k]| 17102and it contains translated \TeX\ pictures for |input_file[k-1]|. 17103Since this is not an \.{MPX} file, we have 17104$$ \hbox{|mpx_name[k-1]<=absent|}. $$ 17105This |name| field is set to |finished| when |input_file[k]| is completely 17106read. 17107 17108If more information about the input state is needed, it can be 17109included in small arrays like those shown here. For example, 17110the current page or segment number in the input file might be put 17111into a variable |page|, that is really a macro for the current entry 17112in `\ignorespaces|page_stack:array[0..max_in_open] of integer|\unskip' 17113by analogy with |line_stack|. 17114@^system dependencies@> 17115 17116@d terminal_input (name==is_term) /* are we reading from the terminal? */ 17117@d cur_file mp->input_file[iindex] /* the current |void *| variable */ 17118@d line mp->line_stack[iindex] /* current line number in the current source file */ 17119@d in_ext mp->inext_stack[iindex] /* a string used to construct \.{MPX} file names */ 17120@d in_name mp->iname_stack[iindex] /* a string used to construct \.{MPX} file names */ 17121@d in_area mp->iarea_stack[iindex] /* another string for naming \.{MPX} files */ 17122@d absent (mp_string)1 /* |name_field| value for unused |mpx_in_stack| entries */ 17123@d mpx_reading (mp->mpx_name[iindex]>absent) 17124 /* when reading a file, is it an \.{MPX} file? */ 17125@d mpx_finished 0 17126 /* |name_field| value when the corresponding \.{MPX} file is finished */ 17127 17128@<Glob...@>= 17129integer in_open; /* the number of lines in the buffer, less one */ 17130integer in_open_max; /* highest value of |in_open| ever seen */ 17131unsigned int open_parens; /* the number of open text files */ 17132void **input_file; 17133integer *line_stack; /* the line number for each file */ 17134char **inext_stack; /* used for naming \.{MPX} files */ 17135char **iname_stack; /* used for naming \.{MPX} files */ 17136char **iarea_stack; /* used for naming \.{MPX} files */ 17137mp_string *mpx_name; 17138 17139@ @<Declarations@>= 17140static void mp_reallocate_input_stack (MP mp, int newsize); 17141 17142@ @c 17143static void mp_reallocate_input_stack (MP mp, int newsize) { 17144 int k; 17145 int n = newsize +1; 17146 XREALLOC (mp->input_file, n, void *); 17147 XREALLOC (mp->line_stack, n, integer); 17148 XREALLOC (mp->inext_stack, n, char *); 17149 XREALLOC (mp->iname_stack, n, char *); 17150 XREALLOC (mp->iarea_stack, n, char *); 17151 XREALLOC (mp->mpx_name, n, mp_string); 17152 for (k = mp->max_in_open; k <= n; k++) { 17153 mp->input_file[k] = NULL; 17154 mp->line_stack[k] = 0; 17155 mp->inext_stack[k] = NULL; 17156 mp->iname_stack[k] = NULL; 17157 mp->iarea_stack[k] = NULL; 17158 mp->mpx_name[k] = NULL; 17159 } 17160 mp->max_in_open = newsize; 17161} 17162 17163 17164@ This has to be more than |file_bottom|, so: 17165@<Allocate or ...@>= 17166mp_reallocate_input_stack (mp, file_bottom+4); 17167 17168@ @<Dealloc variables@>= 17169{ 17170 int l; 17171 for (l = 0; l <= mp->max_in_open; l++) { 17172 xfree (mp->inext_stack[l]); 17173 xfree (mp->iname_stack[l]); 17174 xfree (mp->iarea_stack[l]); 17175 } 17176} 17177xfree (mp->input_file); 17178xfree (mp->line_stack); 17179xfree (mp->inext_stack); 17180xfree (mp->iname_stack); 17181xfree (mp->iarea_stack); 17182xfree (mp->mpx_name); 17183 17184 17185@ However, all this discussion about input state really applies only to the 17186case that we are inputting from a file. There is another important case, 17187namely when we are currently getting input from a token list. In this case 17188|iindex>max_in_open|, and the conventions about the other state variables 17189are different: 17190 17191\yskip\hang|nloc| is a pointer to the current node in the token list, i.e., 17192the node that will be read next. If |nloc=NULL|, the token list has been 17193fully read. 17194 17195\yskip\hang|start| points to the first node of the token list; this node 17196may or may not contain a reference count, depending on the type of token 17197list involved. 17198 17199\yskip\hang|token_type|, which takes the place of |iindex| in the 17200discussion above, is a code number that explains what kind of token list 17201is being scanned. 17202 17203\yskip\hang|name| points to the |eqtb| address of the control sequence 17204being expanded, if the current token list is a macro not defined by 17205\&{vardef}. Macros defined by \&{vardef} have |name=NULL|; their name 17206can be deduced by looking at their first two parameters. 17207 17208\yskip\hang|param_start|, which takes the place of |limit|, tells where 17209the parameters of the current macro or loop text begin in the |param_stack|. 17210 17211\yskip\noindent The |token_type| can take several values, depending on 17212where the current token list came from: 17213 17214\yskip 17215\indent|forever_text|, if the token list being scanned is the body of 17216a \&{forever} loop; 17217 17218\indent|loop_text|, if the token list being scanned is the body of 17219a \&{for} or \&{forsuffixes} loop; 17220 17221\indent|parameter|, if a \&{text} or \&{suffix} parameter is being scanned; 17222 17223\indent|backed_up|, if the token list being scanned has been inserted as 17224`to be read again'. 17225 17226\indent|inserted|, if the token list being scanned has been inserted as 17227part of error recovery; 17228 17229\indent|macro|, if the expansion of a user-defined symbolic token is being 17230scanned. 17231 17232\yskip\noindent 17233The token list begins with a reference count if and only if |token_type= 17234macro|. 17235@^reference counts@> 17236 17237@d nloc mp->cur_input.nloc_field /* location of next node node */ 17238@d nstart mp->cur_input.nstart_field /* location of next node node */ 17239 17240@d token_type iindex /* type of current token list */ 17241@d token_state (iindex<=macro) /* are we scanning a token list? */ 17242@d file_state (iindex>macro) /* are we scanning a file line? */ 17243@d param_start limit /* base of macro parameters in |param_stack| */ 17244@d forever_text 0 /* |token_type| code for loop texts */ 17245@d loop_text 1 /* |token_type| code for loop texts */ 17246@d parameter 2 /* |token_type| code for parameter texts */ 17247@d backed_up 3 /* |token_type| code for texts to be reread */ 17248@d inserted 4 /* |token_type| code for inserted texts */ 17249@d macro 5 /* |token_type| code for macro replacement texts */ 17250@d file_bottom 6 /* lowest file code */ 17251 17252@ The |param_stack| is an auxiliary array used to hold pointers to the token 17253lists for parameters at the current level and subsidiary levels of input. 17254This stack grows at a different rate from the others, and is dynamically reallocated 17255when needed. 17256 17257@<Glob...@>= 17258mp_node *param_stack; /* token list pointers for parameters */ 17259integer param_ptr; /* first unused entry in |param_stack| */ 17260integer max_param_stack; /* largest value of |param_ptr| */ 17261 17262@ @<Allocate or initialize ...@>= 17263mp->param_stack = xmalloc ((mp->param_size + 1), sizeof (mp_node)); 17264 17265@ @c 17266static void mp_check_param_size (MP mp, int k) { 17267 while (k >= mp->param_size) { 17268 XREALLOC (mp->param_stack, (k + k / 4), mp_node); 17269 mp->param_size = k + k / 4; 17270 } 17271} 17272 17273 17274@ @<Dealloc variables@>= 17275xfree (mp->param_stack); 17276 17277@ Notice that the |line| isn't valid when |token_state| is true because it 17278depends on |iindex|. If we really need to know the line number for the 17279topmost file in the iindex stack we use the following function. If a page 17280number or other information is needed, this routine should be modified to 17281compute it as well. 17282@^system dependencies@> 17283 17284@<Declarations@>= 17285static integer mp_true_line (MP mp); 17286 17287@ @c 17288integer mp_true_line (MP mp) { 17289 int k; /* an index into the input stack */ 17290 if (file_state && (name > max_spec_src)) { 17291 return line; 17292 } else { 17293 k = mp->input_ptr; 17294 while ((k > 0) && 17295 ((mp->input_stack[(k - 1)].index_field < file_bottom) || 17296 (mp->input_stack[(k - 1)].name_field <= max_spec_src))) { 17297 decr (k); 17298 } 17299 return (k > 0 ? mp->line_stack[(k - 1) + file_bottom] : 0); 17300 } 17301} 17302 17303 17304@ Thus, the ``current input state'' can be very complicated indeed; there 17305can be many levels and each level can arise in a variety of ways. The 17306|show_context| procedure, which is used by \MP's error-reporting routine to 17307print out the current input state on all levels down to the most recent 17308line of characters from an input file, illustrates most of these conventions. 17309The global variable |file_ptr| contains the lowest level that was 17310displayed by this procedure. 17311 17312@<Glob...@>= 17313integer file_ptr; /* shallowest level shown by |show_context| */ 17314 17315@ The status at each level is indicated by printing two lines, where the first 17316line indicates what was read so far and the second line shows what remains 17317to be read. The context is cropped, if necessary, so that the first line 17318contains at most |half_error_line| characters, and the second contains 17319at most |error_line|. Non-current input levels whose |token_type| is 17320`|backed_up|' are shown only if they have not been fully read. 17321 17322@c 17323void mp_show_context (MP mp) { /* prints where the scanner is */ 17324 unsigned old_setting; /* saved |selector| setting */ 17325 @<Local variables for formatting calculations@>; 17326 mp->file_ptr = mp->input_ptr; 17327 mp->input_stack[mp->file_ptr] = mp->cur_input; 17328 /* store current state */ 17329 while (1) { 17330 mp->cur_input = mp->input_stack[mp->file_ptr]; /* enter into the context */ 17331 @<Display the current context@>; 17332 if (file_state) 17333 if ((name > max_spec_src) || (mp->file_ptr == 0)) 17334 break; 17335 decr (mp->file_ptr); 17336 } 17337 mp->cur_input = mp->input_stack[mp->input_ptr]; /* restore original state */ 17338} 17339 17340 17341@ @<Display the current context@>= 17342if ((mp->file_ptr == mp->input_ptr) || file_state || 17343 (token_type != backed_up) || (nloc != NULL)) { 17344 /* we omit backed-up token lists that have already been read */ 17345 mp->tally = 0; /* get ready to count characters */ 17346 old_setting = mp->selector; 17347 if (file_state) { 17348 @<Print location of current line@>; 17349 @<Pseudoprint the line@>; 17350 } else { 17351 @<Print type of token list@>; 17352 @<Pseudoprint the token list@>; 17353 } 17354 mp->selector = old_setting; /* stop pseudoprinting */ 17355 @<Print two lines using the tricky pseudoprinted information@>; 17356} 17357 17358@ This routine should be changed, if necessary, to give the best possible 17359indication of where the current line resides in the input file. 17360For example, on some systems it is best to print both a page and line number. 17361@^system dependencies@> 17362 17363@<Print location of current line@>= 17364if (name > max_spec_src) { 17365 mp_print_nl (mp, "l."); 17366 mp_print_int (mp, mp_true_line (mp)); 17367} else if (terminal_input) { 17368 if (mp->file_ptr == 0) 17369 mp_print_nl (mp, "<*>"); 17370 else 17371 mp_print_nl (mp, "<insert>"); 17372} else if (name == is_scantok) { 17373 mp_print_nl (mp, "<scantokens>"); 17374} else { 17375 mp_print_nl (mp, "<read>"); 17376} 17377mp_print_char (mp, xord (' ')) 17378 17379 17380@ Can't use case statement here because the |token_type| is not 17381a constant expression. 17382 17383@<Print type of token list@>= 17384{ 17385 if (token_type == forever_text) { 17386 mp_print_nl (mp, "<forever> "); 17387 } else if (token_type == loop_text) { 17388 @<Print the current loop value@>; 17389 } else if (token_type == parameter) { 17390 mp_print_nl (mp, "<argument> "); 17391 } else if (token_type == backed_up) { 17392 if (nloc == NULL) 17393 mp_print_nl (mp, "<recently read> "); 17394 else 17395 mp_print_nl (mp, "<to be read again> "); 17396 } else if (token_type == inserted) { 17397 mp_print_nl (mp, "<inserted text> "); 17398 } else if (token_type == macro) { 17399 mp_print_ln (mp); 17400 if (name != NULL) 17401 mp_print_str (mp, name); 17402 else 17403 @<Print the name of a \&{vardef}'d macro@>; 17404 mp_print (mp, "->"); 17405 } else { 17406 mp_print_nl (mp, "?"); /* this should never happen */ 17407@.?\relax@> 17408 } 17409} 17410 17411 17412@ The parameter that corresponds to a loop text is either a token list 17413(in the case of \&{forsuffixes}) or a ``capsule'' (in the case of \&{for}). 17414We'll discuss capsules later; for now, all we need to know is that 17415the |link| field in a capsule parameter is |void| and that 17416|print_exp(p,0)| displays the value of capsule~|p| in abbreviated form. 17417 17418@<Print the current loop value@>= 17419{ 17420 mp_node pp; 17421 mp_print_nl (mp, "<for("); 17422 pp = mp->param_stack[param_start]; 17423 if (pp != NULL) { 17424 if (mp_link (pp) == MP_VOID) 17425 mp_print_exp (mp, pp, 0); /* we're in a \&{for} loop */ 17426 else 17427 mp_show_token_list (mp, pp, NULL, 20, mp->tally); 17428 } 17429 mp_print (mp, ")> "); 17430} 17431 17432 17433@ The first two parameters of a macro defined by \&{vardef} will be token 17434lists representing the macro's prefix and ``at point.'' By putting these 17435together, we get the macro's full name. 17436 17437@<Print the name of a \&{vardef}'d macro@>= 17438{ 17439 mp_node pp = mp->param_stack[param_start]; 17440 if (pp == NULL) { 17441 mp_show_token_list (mp, mp->param_stack[param_start + 1], NULL, 20, 17442 mp->tally); 17443 } else { 17444 mp_node qq = pp; 17445 while (mp_link (qq) != NULL) 17446 qq = mp_link (qq); 17447 mp_link (qq) = mp->param_stack[param_start + 1]; 17448 mp_show_token_list (mp, pp, NULL, 20, mp->tally); 17449 mp_link (qq) = NULL; 17450 } 17451} 17452 17453 17454@ Now it is necessary to explain a little trick. We don't want to store a long 17455string that corresponds to a token list, because that string might take up 17456lots of memory; and we are printing during a time when an error message is 17457being given, so we dare not do anything that might overflow one of \MP's 17458tables. So `pseudoprinting' is the answer: We enter a mode of printing 17459that stores characters into a buffer of length |error_line|, where character 17460$k+1$ is placed into \hbox{|trick_buf[k mod error_line]|} if 17461|k<trick_count|, otherwise character |k| is dropped. Initially we set 17462|tally:=0| and |trick_count:=1000000|; then when we reach the 17463point where transition from line 1 to line 2 should occur, we 17464set |first_count:=tally| and |trick_count:=@tmax@>(error_line, 17465tally+1+error_line-half_error_line)|. At the end of the 17466pseudoprinting, the values of |first_count|, |tally|, and 17467|trick_count| give us all the information we need to print the two lines, 17468and all of the necessary text is in |trick_buf|. 17469 17470Namely, let |l| be the length of the descriptive information that appears 17471on the first line. The length of the context information gathered for that 17472line is |k=first_count|, and the length of the context information 17473gathered for line~2 is $m=\min(|tally|, |trick_count|)-k$. If |l+k<=h|, 17474where |h=half_error_line|, we print |trick_buf[0..k-1]| after the 17475descriptive information on line~1, and set |n:=l+k|; here |n| is the 17476length of line~1. If $l+k>h$, some cropping is necessary, so we set |n:=h| 17477and print `\.{...}' followed by 17478$$\hbox{|trick_buf[(l+k-h+3)..k-1]|,}$$ 17479where subscripts of |trick_buf| are circular modulo |error_line|. The 17480second line consists of |n|~spaces followed by |trick_buf[k..(k+m-1)]|, 17481unless |n+m>error_line|; in the latter case, further cropping is done. 17482This is easier to program than to explain. 17483 17484@<Local variables for formatting...@>= 17485int i; /* index into |buffer| */ 17486integer l; /* length of descriptive information on line 1 */ 17487integer m; /* context information gathered for line 2 */ 17488int n; /* length of line 1 */ 17489integer p; /* starting or ending place in |trick_buf| */ 17490integer q; /* temporary index */ 17491 17492@ The following code tells the print routines to gather 17493the desired information. 17494 17495@d begin_pseudoprint { 17496 l=mp->tally; mp->tally=0; mp->selector=pseudo; 17497 mp->trick_count=1000000; 17498} 17499@d set_trick_count() { 17500 mp->first_count=mp->tally; 17501 mp->trick_count=mp->tally+1+mp->error_line-mp->half_error_line; 17502 if ( mp->trick_count<mp->error_line ) mp->trick_count=mp->error_line; 17503} 17504 17505@ And the following code uses the information after it has been gathered. 17506 17507@<Print two lines using the tricky pseudoprinted information@>= 17508if (mp->trick_count == 1000000) 17509 set_trick_count(); 17510 /* |set_trick_count| must be performed */ 17511if (mp->tally < mp->trick_count) 17512 m = mp->tally - mp->first_count; 17513else 17514 m = mp->trick_count - mp->first_count; /* context on line 2 */ 17515if (l + mp->first_count <= mp->half_error_line) { 17516 p = 0; 17517 n = l + mp->first_count; 17518} else { 17519 mp_print (mp, "..."); 17520 p = l + mp->first_count - mp->half_error_line + 3; 17521 n = mp->half_error_line; 17522} 17523for (q = p; q <= mp->first_count - 1; q++) { 17524 mp_print_char (mp, mp->trick_buf[q % mp->error_line]); 17525} 17526mp_print_ln (mp); 17527for (q = 1; q <= n; q++) { 17528 mp_print_char (mp, xord (' ')); /* print |n| spaces to begin line~2 */ 17529} 17530if (m + n <= mp->error_line) 17531 p = mp->first_count + m; 17532else 17533 p = mp->first_count + (mp->error_line - n - 3); 17534for (q = mp->first_count; q <= p - 1; q++) { 17535 mp_print_char (mp, mp->trick_buf[q % mp->error_line]); 17536} 17537if (m + n > mp->error_line) 17538 mp_print (mp, "...") 17539 17540 17541@ But the trick is distracting us from our current goal, which is to 17542understand the input state. So let's concentrate on the data structures that 17543are being pseudoprinted as we finish up the |show_context| procedure. 17544 17545@<Pseudoprint the line@>= 17546begin_pseudoprint; 17547if (limit > 0) { 17548 for (i = start; i <= limit - 1; i++) { 17549 if (i == loc) 17550 set_trick_count(); 17551 mp_print_char (mp, mp->buffer[i]); 17552 } 17553} 17554 17555@ @<Pseudoprint the token list@>= 17556begin_pseudoprint; 17557if (token_type != macro) 17558 mp_show_token_list (mp, nstart, nloc, 100000, 0); 17559else 17560 mp_show_macro (mp, nstart, nloc, 100000) 17561 17562 17563@* Maintaining the input stacks. 17564The following subroutines change the input status in commonly needed ways. 17565 17566First comes |push_input|, which stores the current state and creates a 17567new level (having, initially, the same properties as the old). 17568 17569@d push_input { /* enter a new input level, save the old */ 17570 if ( mp->input_ptr>mp->max_in_stack ) { 17571 mp->max_in_stack=mp->input_ptr; 17572 if ( mp->input_ptr==mp->stack_size ) { 17573 int l = (mp->stack_size+(mp->stack_size/4)); 17574 XREALLOC(mp->input_stack, l, in_state_record); 17575 mp->stack_size = l; 17576 } 17577 } 17578 mp->input_stack[mp->input_ptr]=mp->cur_input; /* stack the record */ 17579 incr(mp->input_ptr); 17580} 17581 17582@ And of course what goes up must come down. 17583 17584@d pop_input { /* leave an input level, re-enter the old */ 17585 decr(mp->input_ptr); mp->cur_input=mp->input_stack[mp->input_ptr]; 17586 } 17587 17588@ Here is a procedure that starts a new level of token-list input, given 17589a token list |p| and its type |t|. If |t=macro|, the calling routine should 17590set |name|, reset~|loc|, and increase the macro's reference count. 17591 17592@d back_list(A) mp_begin_token_list(mp, (A), (quarterword)backed_up) /* backs up a simple token list */ 17593 17594@c 17595static void mp_begin_token_list (MP mp, mp_node p, quarterword t) { 17596 push_input; 17597 nstart = p; 17598 token_type = t; 17599 param_start = mp->param_ptr; 17600 nloc = p; 17601} 17602 17603 17604@ When a token list has been fully scanned, the following computations 17605should be done as we leave that level of input. 17606@^inner loop@> 17607 17608@c 17609static void mp_end_token_list (MP mp) { /* leave a token-list input level */ 17610 mp_node p; /* temporary register */ 17611 if (token_type >= backed_up) { /* token list to be deleted */ 17612 if (token_type <= inserted) { 17613 mp_flush_token_list (mp, nstart); 17614 goto DONE; 17615 } else { 17616 mp_delete_mac_ref (mp, nstart); /* update reference count */ 17617 } 17618 } 17619 while (mp->param_ptr > param_start) { /* parameters must be flushed */ 17620 decr (mp->param_ptr); 17621 p = mp->param_stack[mp->param_ptr]; 17622 if (p != NULL) { 17623 if (mp_link (p) == MP_VOID) { /* it's an \&{expr} parameter */ 17624 mp_recycle_value (mp, p); 17625 mp_free_value_node (mp, p); 17626 } else { 17627 mp_flush_token_list (mp, p); /* it's a \&{suffix} or \&{text} parameter */ 17628 } 17629 } 17630 } 17631DONE: 17632 pop_input; 17633 check_interrupt; 17634} 17635 17636 17637@ The contents of |cur_cmd,cur_mod,cur_sym| are placed into an equivalent 17638token by the |cur_tok| routine. 17639@^inner loop@> 17640 17641@c 17642@<Declare the procedure called |make_exp_copy|@>; 17643static mp_node mp_cur_tok (MP mp) { 17644 mp_node p; /* a new token node */ 17645 if (cur_sym() == NULL && (cur_sym_mod() == 0 || cur_sym_mod() == mp_normal_sym)) { 17646 if (cur_cmd() == mp_capsule_token) { 17647 mp_number save_exp_num; /* possible |cur_exp| numerical to be restored */ 17648 mp_value save_exp = mp->cur_exp; /* |cur_exp| to be restored */ 17649 new_number (save_exp_num); 17650 number_clone (save_exp_num, cur_exp_value_number()); 17651 mp_make_exp_copy (mp, cur_mod_node()); 17652 p = mp_stash_cur_exp (mp); 17653 mp_link (p) = NULL; 17654 mp->cur_exp = save_exp; 17655 number_clone (mp->cur_exp.data.n, save_exp_num); 17656 free_number (save_exp_num); 17657 } else { 17658 p = mp_get_token_node (mp); 17659 mp_name_type (p) = mp_token; 17660 if (cur_cmd() == mp_numeric_token) { 17661 set_value_number (p, cur_mod_number()); 17662 mp_type (p) = mp_known; 17663 } else { 17664 set_value_str (p, cur_mod_str()); 17665 mp_type (p) = mp_string_type; 17666 } 17667 } 17668 } else { 17669 p = mp_get_symbolic_node (mp); 17670 set_mp_sym_sym (p, cur_sym()); 17671 mp_name_type (p) = cur_sym_mod(); 17672 } 17673 return p; 17674} 17675 17676 17677@ Sometimes \MP\ has read too far and wants to ``unscan'' what it has 17678seen. The |back_input| procedure takes care of this by putting the token 17679just scanned back into the input stream, ready to be read again. 17680If |cur_sym<>0|, the values of |cur_cmd| and |cur_mod| are irrelevant. 17681 17682@<Declarations@>= 17683static void mp_back_input (MP mp); 17684 17685@ @c 17686void mp_back_input (MP mp) { /* undoes one token of input */ 17687 mp_node p; /* a token list of length one */ 17688 p = mp_cur_tok (mp); 17689 while (token_state && (nloc == NULL)) 17690 mp_end_token_list (mp); /* conserve stack space */ 17691 back_list (p); 17692} 17693 17694 17695@ The |back_error| routine is used when we want to restore or replace an 17696offending token just before issuing an error message. We disable interrupts 17697during the call of |back_input| so that the help message won't be lost. 17698 17699@<Declarations@>= 17700static void mp_back_error (MP mp, const char *msg, const char **hlp, boolean deletions_allowed) ; 17701 17702@ @c 17703static void mp_back_error (MP mp, const char *msg, const char **hlp, boolean deletions_allowed) { 17704 /* back up one token and call |error| */ 17705 mp->OK_to_interrupt = false; 17706 mp_back_input (mp); 17707 mp->OK_to_interrupt = true; 17708 mp_error (mp, msg, hlp, deletions_allowed); 17709} 17710static void mp_ins_error (MP mp, const char *msg, const char **hlp, boolean deletions_allowed) { 17711 /* back up one inserted token and call |error| */ 17712 mp->OK_to_interrupt = false; 17713 mp_back_input (mp); 17714 token_type = (quarterword) inserted; 17715 mp->OK_to_interrupt = true; 17716 mp_error (mp, msg, hlp, deletions_allowed); 17717} 17718 17719 17720@ The |begin_file_reading| procedure starts a new level of input for lines 17721of characters to be read from a file, or as an insertion from the 17722terminal. It does not take care of opening the file, nor does it set |loc| 17723or |limit| or |line|. 17724@^system dependencies@> 17725 17726@c 17727void mp_begin_file_reading (MP mp) { 17728 if (mp->in_open == (mp->max_in_open-1)) 17729 mp_reallocate_input_stack (mp, (mp->max_in_open + mp->max_in_open / 4)); 17730 if (mp->first == mp->buf_size) 17731 mp_reallocate_buffer (mp, (mp->buf_size + mp->buf_size / 4)); 17732 mp->in_open++; 17733 push_input; 17734 iindex = (quarterword) mp->in_open; 17735 if (mp->in_open_max < mp->in_open) 17736 mp->in_open_max = mp->in_open; 17737 mp->mpx_name[iindex] = absent; 17738 start = (halfword) mp->first; 17739 name = is_term; /* |terminal_input| is now |true| */ 17740} 17741 17742 17743@ Conversely, the variables must be downdated when such a level of input 17744is finished. Any associated \.{MPX} file must also be closed and popped 17745off the file stack. While finishing preloading, it is possible that the file 17746does not actually end with 'dump', so we capture that case here as well. 17747 17748@c 17749static void mp_end_file_reading (MP mp) { 17750 if (mp->reading_preload && mp->input_ptr == 0) { 17751 set_cur_sym(mp->frozen_dump); 17752 mp_back_input (mp); 17753 return; 17754 } 17755 if (mp->in_open > iindex) { 17756 if ((mp->mpx_name[mp->in_open] == absent) || (name <= max_spec_src)) { 17757 mp_confusion (mp, "endinput"); 17758@:this can't happen endinput}{\quad endinput@>; 17759 } else { 17760 (mp->close_file) (mp, mp->input_file[mp->in_open]); /* close an \.{MPX} file */ 17761 delete_str_ref (mp->mpx_name[mp->in_open]); 17762 decr (mp->in_open); 17763 } 17764 } 17765 mp->first = (size_t) start; 17766 if (iindex != mp->in_open) 17767 mp_confusion (mp, "endinput"); 17768 if (name > max_spec_src) { 17769 (mp->close_file) (mp, cur_file); 17770 xfree (in_ext); 17771 xfree (in_name); 17772 xfree (in_area); 17773 } 17774 pop_input; 17775 decr (mp->in_open); 17776} 17777 17778 17779@ Here is a function that tries to resume input from an \.{MPX} file already 17780associated with the current input file. It returns |false| if this doesn't 17781work. 17782 17783@c 17784static boolean mp_begin_mpx_reading (MP mp) { 17785 if (mp->in_open != iindex + 1) { 17786 return false; 17787 } else { 17788 if (mp->mpx_name[mp->in_open] <= absent) 17789 mp_confusion (mp, "mpx"); 17790 if (mp->first == mp->buf_size) 17791 mp_reallocate_buffer (mp, (mp->buf_size + (mp->buf_size / 4))); 17792 push_input; 17793 iindex = (quarterword) mp->in_open; 17794 start = (halfword) mp->first; 17795 name = mp->mpx_name[mp->in_open]; 17796 add_str_ref (name); 17797 /* Put an empty line in the input buffer */ 17798 /* We want to make it look as though we have just read a blank line 17799 without really doing so. */ 17800 mp->last = mp->first; 17801 limit = (halfword) mp->last; 17802 /* simulate |input_ln| and |firm_up_the_line| */ 17803 mp->buffer[limit] = xord ('%'); 17804 mp->first = (size_t) (limit + 1); 17805 loc = start; 17806 return true; 17807 } 17808} 17809 17810 17811@ This procedure temporarily stops reading an \.{MPX} file. 17812 17813@c 17814static void mp_end_mpx_reading (MP mp) { 17815 if (mp->in_open != iindex) 17816 mp_confusion (mp, "mpx"); 17817@:this can't happen mpx}{\quad mpx@>; 17818 if (loc < limit) { 17819 /* Complain that we are not at the end of a line in the \.{MPX} file */ 17820 /* Here we enforce a restriction that simplifies the input stacks considerably. 17821 This should not inconvenience the user because \.{MPX} files are generated 17822 by an auxiliary program called \.{DVItoMP}. */ 17823 const char *hlp[] = { 17824 "This file contains picture expressions for btex...etex", 17825 "blocks. Such files are normally generated automatically", 17826 "but this one seems to be messed up. I'm going to ignore", 17827 "the rest of this line.", 17828 NULL }; 17829 mp_error (mp, "`mpxbreak' must be at the end of a line", hlp, true); 17830 } 17831 mp->first = (size_t) start; 17832 pop_input; 17833} 17834 17835@ In order to keep the stack from overflowing during a long sequence of 17836inserted `\.{show}' commands, the following routine removes completed 17837error-inserted lines from memory. 17838 17839@c 17840void mp_clear_for_error_prompt (MP mp) { 17841 while (file_state && terminal_input && (mp->input_ptr > 0) && (loc == limit)) 17842 mp_end_file_reading (mp); 17843 mp_print_ln (mp); 17844 clear_terminal(); 17845} 17846 17847 17848@ To get \MP's whole input mechanism going, we perform the following 17849actions. 17850 17851@<Initialize the input routines@>= 17852{ 17853 mp->input_ptr = 0; 17854 mp->max_in_stack = file_bottom; 17855 mp->in_open = file_bottom; 17856 mp->open_parens = 0; 17857 mp->max_buf_stack = 0; 17858 mp->param_ptr = 0; 17859 mp->max_param_stack = 0; 17860 mp->first = 0; 17861 start = 0; 17862 iindex = file_bottom; 17863 line = 0; 17864 name = is_term; 17865 mp->mpx_name[file_bottom] = absent; 17866 mp->force_eof = false; 17867 if (!mp_init_terminal (mp)) 17868 mp_jump_out (mp); 17869 limit = (halfword) mp->last; 17870 mp->first = mp->last + 1; 17871 /* |init_terminal| has set |loc| and |last| */ 17872} 17873 17874 17875@* Getting the next token. 17876The heart of \MP's input mechanism is the |get_next| procedure, which 17877we shall develop in the next few sections of the program. Perhaps we 17878shouldn't actually call it the ``heart,'' however; it really acts as \MP's 17879eyes and mouth, reading the source files and gobbling them up. And it also 17880helps \MP\ to regurgitate stored token lists that are to be processed again. 17881 17882The main duty of |get_next| is to input one token and to set |cur_cmd| 17883and |cur_mod| to that token's command code and modifier. Furthermore, if 17884the input token is a symbolic token, that token's |hash| address 17885is stored in |cur_sym|; otherwise |cur_sym| is set to zero. 17886 17887Underlying this simple description is a certain amount of complexity 17888because of all the cases that need to be handled. 17889However, the inner loop of |get_next| is reasonably short and fast. 17890 17891@ Before getting into |get_next|, we need to consider a mechanism by which 17892\MP\ helps keep errors from propagating too far. Whenever the program goes 17893into a mode where it keeps calling |get_next| repeatedly until a certain 17894condition is met, it sets |scanner_status| to some value other than |normal|. 17895Then if an input file ends, or if an `\&{outer}' symbol appears, 17896an appropriate error recovery will be possible. 17897 17898The global variable |warning_info| helps in this error recovery by providing 17899additional information. For example, |warning_info| might indicate the 17900name of a macro whose replacement text is being scanned. 17901 17902@d normal 0 /* |scanner_status| at ``quiet times'' */ 17903@d skipping 1 /* |scanner_status| when false conditional text is being skipped */ 17904@d flushing 2 /* |scanner_status| when junk after a statement is being ignored */ 17905@d absorbing 3 /* |scanner_status| when a \&{text} parameter is being scanned */ 17906@d var_defining 4 /* |scanner_status| when a \&{vardef} is being scanned */ 17907@d op_defining 5 /* |scanner_status| when a macro \&{def} is being scanned */ 17908@d loop_defining 6 /* |scanner_status| when a \&{for} loop is being scanned */ 17909 17910@<Glob...@>= 17911#define tex_flushing 7 /* |scanner_status| when skipping \TeX\ material */ 17912integer scanner_status; /* are we scanning at high speed? */ 17913mp_sym warning_info; /* if so, what else do we need to know, 17914 in case an error occurs? */ 17915integer warning_line; 17916mp_node warning_info_node; 17917 17918@ @<Initialize the input routines@>= 17919mp->scanner_status = normal; 17920 17921@ The following subroutine 17922is called when an `\&{outer}' symbolic token has been scanned or 17923when the end of a file has been reached. These two cases are distinguished 17924by |cur_sym|, which is zero at the end of a file. 17925 17926@c 17927static boolean mp_check_outer_validity (MP mp) { 17928 mp_node p; /* points to inserted token list */ 17929 if (mp->scanner_status == normal) { 17930 return true; 17931 } else if (mp->scanner_status == tex_flushing) { 17932 @<Check if the file has ended while flushing \TeX\ material and set the 17933 result value for |check_outer_validity|@>; 17934 } else { 17935 @<Back up an outer symbolic token so that it can be reread@>; 17936 if (mp->scanner_status > skipping) { 17937 @<Tell the user what has run away and try to recover@>; 17938 } else { 17939 char msg[256]; 17940 const char *hlp[] = { 17941 "A forbidden `outer' token occurred in skipped text.", 17942 "This kind of error happens when you say `if...' and forget", 17943 "the matching `fi'. I've inserted a `fi'; this might work.", 17944 NULL }; 17945 mp_snprintf(msg, 256, "Incomplete if; all text was ignored after line %d", (int)mp->warning_line); 17946@.Incomplete if...@>; 17947 if (cur_sym() == NULL) { 17948 hlp[0] = "The file ended while I was skipping conditional text."; 17949 } 17950 set_cur_sym (mp->frozen_fi); 17951 mp_ins_error (mp, msg, hlp, false); 17952 } 17953 return false; 17954 } 17955} 17956 17957 17958@ @<Check if the file has ended while flushing \TeX\ material and set...@>= 17959if (cur_sym() != NULL) { 17960 return true; 17961} else { 17962 char msg[256]; 17963 const char *hlp[] = { 17964 "The file ended while I was looking for the `etex' to", 17965 "finish this TeX material. I've inserted `etex' now.", 17966 NULL }; 17967 mp_snprintf(msg, 256, "TeX mode didn't end; all text was ignored after line %d", (int)mp->warning_line); 17968 set_cur_sym(mp->frozen_etex); 17969 mp_ins_error (mp, msg, hlp, false); 17970 return false; 17971} 17972 17973 17974@ @<Back up an outer symbolic token so that it can be reread@>= 17975if (cur_sym() != NULL) { 17976 p = mp_get_symbolic_node (mp); 17977 set_mp_sym_sym (p, cur_sym()); 17978 mp_name_type (p) = cur_sym_mod(); 17979 back_list (p); /* prepare to read the symbolic token again */ 17980} 17981 17982@ @<Tell the user what has run away...@>= 17983{ 17984 char msg[256]; 17985 const char *msg_start = NULL; 17986 const char *hlp[] = { 17987 "I suspect you have forgotten an `enddef',", 17988 "causing me to read past where you wanted me to stop.", 17989 "I'll try to recover; but if the error is serious,", 17990 "you'd better type `E' or `X' now and fix your file.", 17991 NULL }; 17992 mp_runaway (mp); /* print the definition-so-far */ 17993 if (cur_sym() == NULL) { 17994 msg_start = "File ended while scanning"; 17995@.File ended while scanning...@> 17996 } else { 17997 msg_start = "Forbidden token found while scanning"; 17998@.Forbidden token found...@> 17999 } 18000 switch (mp->scanner_status) { 18001 @<Complete the error message, 18002 and set |cur_sym| to a token that might help recover from the error@> 18003 } /* there are no other cases */ 18004 mp_ins_error (mp, msg, hlp, true); 18005} 18006 18007 18008@ As we consider various kinds of errors, it is also appropriate to 18009change the first line of the help message just given; |help_line[3]| 18010points to the string that might be changed. 18011 18012@<Complete the error message,...@>= 18013case flushing: 18014 mp_snprintf (msg, 256, "%s to the end of the statement", msg_start); 18015 hlp[0] = "A previous error seems to have propagated,"; 18016 set_cur_sym(mp->frozen_semicolon); 18017 break; 18018case absorbing: 18019 mp_snprintf (msg, 256, "%s a text argument", msg_start); 18020 hlp[0] = "It seems that a right delimiter was left out,"; 18021 if (mp->warning_info == NULL) { 18022 set_cur_sym(mp->frozen_end_group); 18023 } else { 18024 set_cur_sym(mp->frozen_right_delimiter); 18025 /* the next line makes sure that the inserted delimiter will 18026 match the delimiter that already was read. */ 18027 set_equiv_sym (cur_sym(), mp->warning_info); 18028 } 18029 break; 18030case var_defining: 18031 { 18032 mp_string s; 18033 int old_setting = mp->selector; 18034 mp->selector = new_string; 18035 mp_print_variable_name (mp, mp->warning_info_node); 18036 s = mp_make_string (mp); 18037 mp->selector = old_setting; 18038 mp_snprintf (msg, 256, "%s the definition of %s", msg_start, s->str); 18039 delete_str_ref(s); 18040 } 18041 set_cur_sym(mp->frozen_end_def); 18042 break; 18043case op_defining: 18044 { 18045 char *s = mp_str(mp, text(mp->warning_info)); 18046 mp_snprintf (msg, 256, "%s the definition of %s", msg_start, s); 18047 } 18048 set_cur_sym(mp->frozen_end_def); 18049 break; 18050case loop_defining: 18051 { 18052 char *s = mp_str(mp, text(mp->warning_info)); 18053 mp_snprintf (msg, 256, "%s the text of a %s loop", msg_start, s); 18054 } 18055 hlp[0] = "I suspect you have forgotten an `endfor',"; 18056 set_cur_sym(mp->frozen_end_for); 18057break; 18058 18059@ The |runaway| procedure displays the first part of the text that occurred 18060when \MP\ began its special |scanner_status|, if that text has been saved. 18061 18062@<Declarations@>= 18063static void mp_runaway (MP mp); 18064 18065@ @c 18066void mp_runaway (MP mp) { 18067 if (mp->scanner_status > flushing) { 18068 mp_print_nl (mp, "Runaway "); 18069 switch (mp->scanner_status) { 18070 case absorbing: 18071 mp_print (mp, "text?"); 18072 break; 18073 case var_defining: 18074 case op_defining: 18075 mp_print (mp, "definition?"); 18076 break; 18077 case loop_defining: 18078 mp_print (mp, "loop?"); 18079 break; 18080 } /* there are no other cases */ 18081 mp_print_ln (mp); 18082 mp_show_token_list (mp, mp_link (mp->hold_head), NULL, mp->error_line - 10, 18083 0); 18084 } 18085} 18086 18087 18088@ We need to mention a procedure that may be called by |get_next|. 18089 18090@<Declarations@>= 18091static void mp_firm_up_the_line (MP mp); 18092 18093@ And now we're ready to take the plunge into |get_next| itself. 18094Note that the behavior depends on the |scanner_status| because percent signs 18095and double quotes need to be passed over when skipping TeX material. 18096 18097@c 18098void mp_get_next (MP mp) { 18099 /* sets |cur_cmd|, |cur_mod|, |cur_sym| to next token */ 18100 mp_sym cur_sym_; /* speed up access */ 18101RESTART: 18102 set_cur_sym(NULL); 18103 set_cur_sym_mod(0); 18104 if (file_state) { 18105 int k; /* an index into |buffer| */ 18106 ASCII_code c; /* the current character in the buffer */ 18107 int cclass; /* its class number */ 18108 /* Input from external file; |goto restart| if no input found, 18109 or |return| if a non-symbolic token is found */ 18110 /* A percent sign appears in |buffer[limit]|; this makes it unnecessary 18111 to have a special test for end-of-line. */ 18112 SWITCH: 18113 c = mp->buffer[loc]; 18114 incr (loc); 18115 cclass = mp->char_class[c]; 18116 switch (cclass) { 18117 case digit_class: 18118 scan_numeric_token((c - '0')); 18119 return; 18120 break; 18121 case period_class: 18122 cclass = mp->char_class[mp->buffer[loc]]; 18123 if (cclass > period_class) { 18124 goto SWITCH; 18125 } else if (cclass < period_class) { /* |class=digit_class| */ 18126 scan_fractional_token(0); 18127 return; 18128 } 18129 break; 18130 case space_class: 18131 goto SWITCH; 18132 break; 18133 case percent_class: 18134 if (mp->scanner_status == tex_flushing) { 18135 if (loc < limit) 18136 goto SWITCH; 18137 } 18138 /* Move to next line of file, or |goto restart| if there is no next line */ 18139 switch (move_to_next_line(mp)) { 18140 case 1: goto RESTART; break; 18141 case 2: goto COMMON_ENDING; break; 18142 default: break; 18143 } 18144 check_interrupt; 18145 goto SWITCH; 18146 break; 18147 case string_class: 18148 if (mp->scanner_status == tex_flushing) { 18149 goto SWITCH; 18150 } else { 18151 if (mp->buffer[loc] == '"') { 18152 set_cur_mod_str(mp_rts(mp,"")); 18153 } else { 18154 k = loc; 18155 mp->buffer[limit + 1] = xord ('"'); 18156 do { 18157 incr (loc); 18158 } while (mp->buffer[loc] != '"'); 18159 if (loc > limit) { 18160 /* Decry the missing string delimiter and |goto restart| */ 18161 /* We go to |restart| after this error message, not to |SWITCH|, 18162 because the |clear_for_error_prompt| routine might have reinstated 18163 |token_state| after |error| has finished. */ 18164 const char *hlp[] = { 18165 "Strings should finish on the same line as they began.", 18166 "I've deleted the partial string; you might want to", 18167 "insert another by typing, e.g., `I\"new string\"'.", 18168 NULL }; 18169 loc = limit; /* the next character to be read on this line will be |"%"| */ 18170 mp_error (mp, "Incomplete string token has been flushed", hlp, false); 18171 goto RESTART; 18172 } 18173 str_room ((size_t) (loc - k)); 18174 do { 18175 append_char (mp->buffer[k]); 18176 incr (k); 18177 } while (k != loc); 18178 set_cur_mod_str(mp_make_string (mp)); 18179 } 18180 incr (loc); 18181 set_cur_cmd((mp_variable_type)mp_string_token); 18182 return; 18183 } 18184 break; 18185 case isolated_classes: 18186 k = loc - 1; 18187 goto FOUND; 18188 break; 18189 case invalid_class: 18190 if (mp->scanner_status == tex_flushing) { 18191 goto SWITCH; 18192 } else { 18193 /* Decry the invalid character and |goto restart| */ 18194 /* We go to |restart| instead of to |SWITCH|, because we might enter 18195 |token_state| after the error has been dealt with 18196 (cf.\ |clear_for_error_prompt|). */ 18197 const char *hlp[] = { 18198 "A funny symbol that I can\'t read has just been input.", 18199 "Continue, and I'll forget that it ever happened.", 18200 NULL }; 18201 mp_error(mp, "Text line contains an invalid character", hlp, false); 18202 goto RESTART; 18203 } 18204 break; 18205 default: 18206 break; /* letters, etc. */ 18207 } 18208 k = loc - 1; 18209 while (mp->char_class[mp->buffer[loc]] == cclass) 18210 incr (loc); 18211 FOUND: 18212 set_cur_sym(mp_id_lookup (mp, (char *) (mp->buffer + k), (size_t) (loc - k), true)); 18213 18214 } else { 18215 /* Input from token list; |goto restart| if end of list or 18216 if a parameter needs to be expanded, 18217 or |return| if a non-symbolic token is found */ 18218 if (nloc != NULL && mp_type (nloc) == mp_symbol_node) { /* symbolic token */ 18219 int cur_sym_mod_ = mp_name_type (nloc); 18220 halfword cur_info = mp_sym_info (nloc); 18221 set_cur_sym(mp_sym_sym (nloc)); 18222 set_cur_sym_mod(cur_sym_mod_); 18223 nloc = mp_link (nloc); /* move to next */ 18224 if (cur_sym_mod_ == mp_expr_sym) { 18225 set_cur_cmd((mp_variable_type)mp_capsule_token); 18226 set_cur_mod_node(mp->param_stack[param_start + cur_info]); 18227 set_cur_sym_mod(0); 18228 set_cur_sym(NULL); 18229 return; 18230 } else if (cur_sym_mod_ == mp_suffix_sym || cur_sym_mod_ == mp_text_sym) { 18231 mp_begin_token_list (mp, 18232 mp->param_stack[param_start + cur_info], 18233 (quarterword) parameter); 18234 goto RESTART; 18235 } 18236 } else if (nloc != NULL) { 18237 /* Get a stored numeric or string or capsule token and |return| */ 18238 if (mp_name_type (nloc) == mp_token) { 18239 if (mp_type (nloc) == mp_known) { 18240 set_cur_mod_number(value_number (nloc)); 18241 set_cur_cmd((mp_variable_type)mp_numeric_token); 18242 } else { 18243 set_cur_mod_str(value_str (nloc)); 18244 set_cur_cmd((mp_variable_type)mp_string_token); 18245 add_str_ref (cur_mod_str()); 18246 } 18247 } else { 18248 set_cur_mod_node(nloc); 18249 set_cur_cmd((mp_variable_type)mp_capsule_token); 18250 } 18251 nloc = mp_link (nloc); 18252 return; 18253 } else { /* we are done with this token list */ 18254 mp_end_token_list (mp); 18255 goto RESTART; /* resume previous level */ 18256 } 18257 } 18258COMMON_ENDING: 18259 /* When a symbolic token is declared to be `\&{outer}', its command code 18260 is increased by |outer_tag|. */ 18261 cur_sym_ = cur_sym(); 18262 set_cur_cmd(eq_type (cur_sym_)); 18263 set_cur_mod(equiv (cur_sym_)); 18264 set_cur_mod_node(equiv_node (cur_sym_)); 18265 if (cur_cmd() >= mp_outer_tag) { 18266 if (mp_check_outer_validity (mp)) 18267 set_cur_cmd(cur_cmd() - mp_outer_tag); 18268 else 18269 goto RESTART; 18270 } 18271} 18272 18273@ The global variable |force_eof| is normally |false|; it is set |true| 18274by an \&{endinput} command. 18275 18276@<Glob...@>= 18277boolean force_eof; /* should the next \&{input} be aborted early? */ 18278 18279@ @<Declarations@>= 18280static int move_to_next_line (MP mp); 18281 18282@ @c 18283static int move_to_next_line (MP mp) { 18284 if (name > max_spec_src) { 18285 /* Read next line of file into |buffer|, or return 1 18286 (|goto restart|) if the file has ended */ 18287 /* We must decrement |loc| in order to leave the buffer in a valid state 18288 when an error condition causes us to |goto restart| without calling 18289 |end_file_reading|. */ 18290 { 18291 incr (line); 18292 mp->first = (size_t) start; 18293 if (!mp->force_eof) { 18294 if (mp_input_ln (mp, cur_file)) /* not end of file */ 18295 mp_firm_up_the_line (mp); /* this sets |limit| */ 18296 else 18297 mp->force_eof = true; 18298 }; 18299 if (mp->force_eof) { 18300 mp->force_eof = false; 18301 decr (loc); 18302 if (mpx_reading) { 18303 /* Complain that the \.{MPX} file ended unexpectly; then set 18304 |cur_sym:=mp->frozen_mpx_break| and |goto comon_ending| */ 18305 /* We should never actually come to the end of an \.{MPX} file because such 18306 files should have an \&{mpxbreak} after the translation of the last 18307 \&{btex}$\,\ldots\,$\&{etex} block. */ 18308 const char *hlp[] = {"The file had too few picture expressions for btex...etex", 18309 "blocks. Such files are normally generated automatically", 18310 "but this one got messed up. You might want to insert a", 18311 "picture expression now.", 18312 NULL }; 18313 mp->mpx_name[iindex] = mpx_finished; 18314 mp_error (mp, "mpx file ended unexpectedly", hlp, false); 18315 set_cur_sym(mp->frozen_mpx_break); 18316 return 2; 18317 } else { 18318 mp_print_char (mp, xord (')')); 18319 decr (mp->open_parens); 18320 update_terminal(); /* show user that file has been read */ 18321 mp_end_file_reading (mp); /* resume previous level */ 18322 if (mp_check_outer_validity (mp)) 18323 return 1; 18324 else 18325 return 1; 18326 } 18327 } 18328 mp->buffer[limit] = xord ('%'); 18329 mp->first = (size_t) (limit + 1); 18330 loc = start; /* ready to read */ 18331 } 18332 18333 18334 } else { 18335 if (mp->input_ptr > 0) { 18336 /* text was inserted during error recovery or by \&{scantokens} */ 18337 mp_end_file_reading (mp); 18338 /* goto RESTART */ 18339 return 1; /* resume previous level */ 18340 } 18341 if (mp->job_name == NULL 18342 && (mp->selector < log_only || mp->selector >= write_file)) 18343 mp_open_log_file (mp); 18344 if (mp->interaction > mp_nonstop_mode) { 18345 if (limit == start) /* previous line was empty */ 18346 mp_print_nl (mp, "(Please type a command or say `end')"); 18347 mp_print_ln (mp); 18348 mp->first = (size_t) start; 18349 prompt_input ("*"); /* input on-line into |buffer| */ 18350 limit = (halfword) mp->last; 18351 mp->buffer[limit] = xord ('%'); 18352 mp->first = (size_t) (limit + 1); 18353 loc = start; 18354 } else { 18355 mp_fatal_error (mp, "*** (job aborted, no legal end found)"); 18356 /* nonstop mode, which is intended for overnight batch processing, 18357 never waits for on-line input */ 18358 } 18359 } 18360 return 0; 18361} 18362 18363 18364@ If the user has set the |mp_pausing| parameter to some positive value, 18365and if nonstop mode has not been selected, each line of input is displayed 18366on the terminal and the transcript file, followed by `\.{=>}'. 18367\MP\ waits for a response. If the response is NULL (i.e., if nothing is 18368typed except perhaps a few blank spaces), the original 18369line is accepted as it stands; otherwise the line typed is 18370used instead of the line in the file. 18371 18372@c 18373void mp_firm_up_the_line (MP mp) { 18374 size_t k; /* an index into |buffer| */ 18375 limit = (halfword) mp->last; 18376 if ((!mp->noninteractive) 18377 && (number_positive (internal_value (mp_pausing))) 18378 && (mp->interaction > mp_nonstop_mode)) { 18379 wake_up_terminal(); 18380 mp_print_ln (mp); 18381 if (start < limit) { 18382 for (k = (size_t) start; k < (size_t) limit; k++) { 18383 mp_print_char (mp, mp->buffer[k]); 18384 } 18385 } 18386 mp->first = (size_t) limit; 18387 prompt_input ("=>"); /* wait for user response */ 18388@.=>@>; 18389 if (mp->last > mp->first) { 18390 for (k = mp->first; k < mp->last; k++) { /* move line down in buffer */ 18391 mp->buffer[k + (size_t) start - mp->first] = mp->buffer[k]; 18392 } 18393 limit = (halfword) ((size_t) start + mp->last - mp->first); 18394 } 18395 } 18396} 18397 18398 18399@* Dealing with \TeX\ material. 18400The \&{btex}$\,\ldots\,$\&{etex} and \&{verbatimtex}$\,\ldots\,$\&{etex} 18401features need to be implemented at a low level in the scanning process 18402so that \MP\ can stay in synch with the a preprocessor that treats 18403blocks of \TeX\ material as they occur in the input file without trying 18404to expand \MP\ macros. Thus we need a special version of |get_next| 18405that does not expand macros and such but does handle \&{btex}, 18406\&{verbatimtex}, etc. 18407 18408The special version of |get_next| is called |get_t_next|. It works by flushing 18409\&{btex}$\,\ldots\,$\&{etex} and \&{verbatimtex}\allowbreak 18410$\,\ldots\,$\&{etex} blocks, switching to the \.{MPX} file when it sees 18411\&{btex}, and switching back when it sees \&{mpxbreak}. 18412 18413@d btex_code 0 18414@d verbatim_code 1 18415 18416@ @<Put each...@>= 18417mp_primitive (mp, "btex", mp_start_tex, btex_code); 18418@:btex_}{\&{btex} primitive@>; 18419mp_primitive (mp, "verbatimtex", mp_start_tex, verbatim_code); 18420@:verbatimtex_}{\&{verbatimtex} primitive@>; 18421mp_primitive (mp, "etex", mp_etex_marker, 0); 18422mp->frozen_etex = mp_frozen_primitive (mp, "etex", mp_etex_marker, 0); 18423@:etex_}{\&{etex} primitive@>; 18424mp_primitive (mp, "mpxbreak", mp_mpx_break, 0); 18425mp->frozen_mpx_break = mp_frozen_primitive (mp, "mpxbreak", mp_mpx_break, 0); 18426@:mpx_break_}{\&{mpxbreak} primitive@> 18427 18428 18429@ @<Cases of |print_cmd...@>= 18430case mp_start_tex: 18431if (m == btex_code) 18432 mp_print (mp, "btex"); 18433else 18434 mp_print (mp, "verbatimtex"); 18435break; 18436case mp_etex_marker: 18437mp_print (mp, "etex"); 18438break; 18439case mp_mpx_break: 18440mp_print (mp, "mpxbreak"); 18441break; 18442 18443@ Actually, |get_t_next| is a macro that avoids procedure overhead except 18444in the unusual case where \&{btex}, \&{verbatimtex}, \&{etex}, or \&{mpxbreak} 18445is encountered. 18446 18447@d get_t_next(a) do { 18448 mp_get_next (mp); 18449 if (cur_cmd() <= mp_max_pre_command) 18450 mp_t_next (mp); 18451} while (0) 18452 18453@c 18454@ @<Declarations@>= 18455static void mp_t_next (MP mp); 18456static void mp_start_mpx_input (MP mp); 18457 18458@ @c 18459static void mp_t_next (MP mp) { 18460 int old_status; /* saves the |scanner_status| */ 18461 integer old_info; /* saves the |warning_info| */ 18462 18463if ((mp->extensions == 1) && (cur_cmd() == mp_start_tex)) { 18464 @<Pass btex ... etex to script@>; 18465} else { 18466 18467 while (cur_cmd() <= mp_max_pre_command) { 18468 if (cur_cmd() == mp_mpx_break) { 18469 if (!file_state || (mp->mpx_name[iindex] == absent)) { 18470 @<Complain about a misplaced \&{mpxbreak}@>; 18471 } else { 18472 mp_end_mpx_reading (mp); 18473 goto TEX_FLUSH; 18474 } 18475 } else if (cur_cmd() == mp_start_tex) { 18476 if (token_state || (name <= max_spec_src)) { 18477 @<Complain that we are not reading a file@>; 18478 } else if (mpx_reading) { 18479 @<Complain that \.{MPX} files cannot contain \TeX\ material@>; 18480 } else if ((cur_mod() != verbatim_code) && 18481 (mp->mpx_name[iindex] != mpx_finished)) { 18482 if (!mp_begin_mpx_reading (mp)) 18483 mp_start_mpx_input (mp); 18484 } else { 18485 goto TEX_FLUSH; 18486 } 18487 } else { 18488 @<Complain about a misplaced \&{etex}@>; 18489 } 18490 goto COMMON_ENDING; 18491 TEX_FLUSH: 18492 @<Flush the \TeX\ material@>; 18493 COMMON_ENDING: 18494 mp_get_next (mp); 18495 } 18496} 18497} 18498 18499 18500@ We could be in the middle of an operation such as skipping false conditional 18501text when \TeX\ material is encountered, so we must be careful to save the 18502|scanner_status|. 18503 18504@<Flush the \TeX\ material@>= 18505old_status = mp->scanner_status; 18506old_info = mp->warning_line; 18507mp->scanner_status = tex_flushing; 18508mp->warning_line = line; 18509do { 18510 mp_get_next (mp); 18511} while (cur_cmd() != mp_etex_marker); 18512mp->scanner_status = old_status; 18513mp->warning_line = old_info 18514 18515@ @<Complain that \.{MPX} files cannot contain \TeX\ material@>= 18516{ 18517 const char *hlp[] = { 18518 "This file contains picture expressions for btex...etex", 18519 "blocks. Such files are normally generated automatically", 18520 "but this one seems to be messed up. I'll just keep going", 18521 "and hope for the best.", 18522 NULL }; 18523 mp_error (mp, "An mpx file cannot contain btex or verbatimtex blocks", hlp, true); 18524} 18525 18526 18527@ @<Complain that we are not reading a file@>= 18528{ 18529 const char *hlp[] = { 18530 "I'll have to ignore this preprocessor command because it", 18531 "only works when there is a file to preprocess. You might", 18532 "want to delete everything up to the next `etex`.", 18533 NULL }; 18534 mp_error (mp, "You can only use `btex' or `verbatimtex' in a file", hlp, true); 18535} 18536 18537 18538@ @<Complain about a misplaced \&{mpxbreak}@>= 18539{ 18540 const char *hlp[] = { 18541 "I'll ignore this preprocessor command because it", 18542 "doesn't belong here", 18543 NULL }; 18544 mp_error (mp, "Misplaced mpxbreak", hlp, true); 18545} 18546 18547 18548@ @<Complain about a misplaced \&{etex}@>= 18549{ 18550 const char *hlp[] = { 18551 "There is no btex or verbatimtex for this to match", 18552 NULL }; 18553 mp_error (mp, "Extra etex will be ignored", hlp, true); 18554} 18555 18556 18557@* Scanning macro definitions. 18558\MP\ has a variety of ways to tuck tokens away into token lists for later 18559use: Macros can be defined with \&{def}, \&{vardef}, \&{primarydef}, etc.; 18560repeatable code can be defined with \&{for}, \&{forever}, \&{forsuffixes}. 18561All such operations are handled by the routines in this part of the program. 18562 18563The modifier part of each command code is zero for the ``ending delimiters'' 18564like \&{enddef} and \&{endfor}. 18565 18566@d start_def 1 /* command modifier for \&{def} */ 18567@d var_def 2 /* command modifier for \&{vardef} */ 18568@d end_def 0 /* command modifier for \&{enddef} */ 18569@d start_forever 1 /* command modifier for \&{forever} */ 18570@d start_for 2 /* command modifier for \&{forever} */ 18571@d start_forsuffixes 3 /* command modifier for \&{forever} */ 18572@d end_for 0 /* command modifier for \&{endfor} */ 18573 18574@<Put each...@>= 18575mp_primitive (mp, "def", mp_macro_def, start_def); 18576@:def_}{\&{def} primitive@>; 18577mp_primitive (mp, "vardef", mp_macro_def, var_def); 18578@:var_def_}{\&{vardef} primitive@>; 18579mp_primitive (mp, "primarydef", mp_macro_def, mp_secondary_primary_macro); 18580@:primary_def_}{\&{primarydef} primitive@>; 18581mp_primitive (mp, "secondarydef", mp_macro_def, mp_tertiary_secondary_macro); 18582@:secondary_def_}{\&{secondarydef} primitive@>; 18583mp_primitive (mp, "tertiarydef", mp_macro_def, mp_expression_tertiary_macro); 18584@:tertiary_def_}{\&{tertiarydef} primitive@>; 18585mp_primitive (mp, "enddef", mp_macro_def, end_def); 18586mp->frozen_end_def = mp_frozen_primitive (mp, "enddef", mp_macro_def, end_def); 18587@:end_def_}{\&{enddef} primitive@>; 18588mp_primitive (mp, "for", mp_iteration, start_for); 18589@:for_}{\&{for} primitive@>; 18590mp_primitive (mp, "forsuffixes", mp_iteration, start_forsuffixes); 18591@:for_suffixes_}{\&{forsuffixes} primitive@>; 18592mp_primitive (mp, "forever", mp_iteration, start_forever); 18593@:forever_}{\&{forever} primitive@>; 18594mp_primitive (mp, "endfor", mp_iteration, end_for); 18595mp->frozen_end_for = mp_frozen_primitive (mp, "endfor", mp_iteration, end_for); 18596@:end_for_}{\&{endfor} primitive@> 18597 18598 18599@ @<Cases of |print_cmd...@>= 18600case mp_macro_def: 18601if (m <= var_def) { 18602 if (m == start_def) 18603 mp_print (mp, "def"); 18604 else if (m < start_def) 18605 mp_print (mp, "enddef"); 18606 else 18607 mp_print (mp, "vardef"); 18608} else if (m == mp_secondary_primary_macro) { 18609 mp_print (mp, "primarydef"); 18610} else if (m == mp_tertiary_secondary_macro) { 18611 mp_print (mp, "secondarydef"); 18612} else { 18613 mp_print (mp, "tertiarydef"); 18614} 18615break; 18616case mp_iteration: 18617if (m == start_forever) 18618 mp_print (mp, "forever"); 18619else if (m == end_for) 18620 mp_print (mp, "endfor"); 18621else if (m == start_for) 18622 mp_print (mp, "for"); 18623else 18624 mp_print (mp, "forsuffixes"); 18625break; 18626 18627@ Different macro-absorbing operations have different syntaxes, but they 18628also have a lot in common. There is a list of special symbols that are to 18629be replaced by parameter tokens; there is a special command code that 18630ends the definition; the quotation conventions are identical. Therefore 18631it makes sense to have most of the work done by a single subroutine. That 18632subroutine is called |scan_toks|. 18633 18634The first parameter to |scan_toks| is the command code that will 18635terminate scanning (either |macro_def| or |iteration|). 18636 18637The second parameter, |subst_list|, points to a (possibly empty) list 18638of non-symbolic nodes whose |info| and |value| fields specify symbol tokens 18639before and after replacement. The list will be returned to free storage 18640by |scan_toks|. 18641 18642The third parameter is simply appended to the token list that is built. 18643And the final parameter tells how many of the special operations 18644\.{\#\AT!}, \.{\AT!}, and \.{\AT!\#} are to be replaced by suffix parameters. 18645When such parameters are present, they are called \.{(SUFFIX0)}, 18646\.{(SUFFIX1)}, and \.{(SUFFIX2)}. 18647 18648@<Types...@>= 18649typedef struct mp_subst_list_item { 18650 mp_name_type_type info_mod; 18651 quarterword value_mod; 18652 mp_sym info; 18653 halfword value_data; 18654 struct mp_subst_list_item *link; 18655} mp_subst_list_item; 18656 18657@ 18658@c 18659static mp_node mp_scan_toks (MP mp, mp_command_code terminator, 18660 mp_subst_list_item * subst_list, mp_node tail_end, 18661 quarterword suffix_count) { 18662 mp_node p; /* tail of the token list being built */ 18663 mp_subst_list_item *q = NULL; /* temporary for link management */ 18664 integer balance; /* left delimiters minus right delimiters */ 18665 halfword cur_data; 18666 quarterword cur_data_mod = 0; 18667 p = mp->hold_head; 18668 balance = 1; 18669 mp_link (mp->hold_head) = NULL; 18670 while (1) { 18671 get_t_next (mp); 18672 cur_data = -1; 18673 if (cur_sym() != NULL) { 18674 @<Substitute for |cur_sym|, if it's on the |subst_list|@>; 18675 if (cur_cmd() == terminator) { 18676 @<Adjust the balance; |break| if it's zero@>; 18677 } else if (cur_cmd() == mp_macro_special) { 18678 /* Handle quoted symbols, \.{\#\AT!}, \.{\AT!}, or \.{\AT!\#} */ 18679 if (cur_mod() == quote) { 18680 get_t_next (mp); 18681 } else if (cur_mod() <= suffix_count) { 18682 cur_data = cur_mod() - 1; 18683 cur_data_mod = mp_suffix_sym; 18684 } 18685 } 18686 } 18687 if (cur_data != -1) { 18688 mp_node pp = mp_get_symbolic_node (mp); 18689 set_mp_sym_info (pp, cur_data); 18690 mp_name_type (pp) = cur_data_mod; 18691 mp_link (p) = pp; 18692 } else { 18693 mp_link (p) = mp_cur_tok (mp); 18694 } 18695 p = mp_link (p); 18696 } 18697 mp_link (p) = tail_end; 18698 while (subst_list) { 18699 q = subst_list->link; 18700 xfree (subst_list); 18701 subst_list = q; 18702 } 18703 return mp_link (mp->hold_head); 18704} 18705 18706@ 18707@c 18708void mp_print_sym (mp_sym sym) { 18709 printf("{type = %d, v = {type = %d, data = {indep = {scale = %d, serial = %d}, n = %d, str = %p, sym = %p, node = %p, p = %p}}, text = %p}\n", sym->type, sym->v.type, (int)sym->v.data.indep.scale, (int)sym->v.data.indep.serial, 18710 sym->v.data.n.type, sym->v.data.str, sym->v.data.sym, sym->v.data.node, sym->v.data.p, sym->text); 18711 if (is_number(sym->v.data.n)) { 18712 mp_number n = sym->v.data.n; 18713 printf("{data = {dval = %f, val = %d}, type = %d}\n", n.data.dval, n.data.val, n.type); 18714 } 18715 if (sym->text != NULL) { 18716 mp_string t = sym->text; 18717 printf ("{str = %p \"%s\", len = %d, refs = %d}\n", t->str, t->str, (int)t->len, t->refs); 18718 } 18719} 18720 18721@ 18722@<Declarations@>= 18723void mp_print_sym (mp_sym sym) ; 18724 18725@ @<Substitute for |cur_sym|...@>= 18726{ 18727 q = subst_list; 18728 while (q != NULL) { 18729 if (q->info == cur_sym() && q->info_mod == cur_sym_mod()) { 18730 cur_data = q->value_data; 18731 cur_data_mod = q->value_mod; 18732 set_cur_cmd((mp_variable_type)mp_relax); 18733 break; 18734 } 18735 q = q->link; 18736 } 18737} 18738 18739 18740@ @<Adjust the balance; |break| if it's zero@>= 18741if (cur_mod() > 0) { 18742 incr (balance); 18743} else { 18744 decr (balance); 18745 if (balance == 0) 18746 break; 18747} 18748 18749 18750@ Four commands are intended to be used only within macro texts: \&{quote}, 18751\.{\#\AT!}, \.{\AT!}, and \.{\AT!\#}. They are variants of a single command 18752code called |macro_special|. 18753 18754@d quote 0 /* |macro_special| modifier for \&{quote} */ 18755@d macro_prefix 1 /* |macro_special| modifier for \.{\#\AT!} */ 18756@d macro_at 2 /* |macro_special| modifier for \.{\AT!} */ 18757@d macro_suffix 3 /* |macro_special| modifier for \.{\AT!\#} */ 18758 18759@<Put each...@>= 18760mp_primitive (mp, "quote", mp_macro_special, quote); 18761@:quote_}{\&{quote} primitive@>; 18762mp_primitive (mp, "#@@", mp_macro_special, macro_prefix); 18763@:]]]\#\AT!_}{\.{\#\AT!} primitive@>; 18764mp_primitive (mp, "@@", mp_macro_special, macro_at); 18765@:]]]\AT!_}{\.{\AT!} primitive@>; 18766mp_primitive (mp, "@@#", mp_macro_special, macro_suffix); 18767@:]]]\AT!\#_}{\.{\AT!\#} primitive@> 18768 18769 18770@ @<Cases of |print_cmd...@>= 18771case mp_macro_special: 18772switch (m) { 18773case macro_prefix: 18774 mp_print (mp, "#@@"); 18775 break; 18776case macro_at: 18777 mp_print_char (mp, xord ('@@')); 18778 break; 18779case macro_suffix: 18780 mp_print (mp, "@@#"); 18781 break; 18782default: 18783 mp_print (mp, "quote"); 18784 break; 18785} 18786break; 18787 18788@ Here is a routine that's used whenever a token will be redefined. If 18789the user's token is unredefinable, the `|mp->frozen_inaccessible|' token is 18790substituted; the latter is redefinable but essentially impossible to use, 18791hence \MP's tables won't get fouled up. 18792 18793@c 18794static void mp_get_symbol (MP mp) { /* sets |cur_sym| to a safe symbol */ 18795RESTART: 18796 get_t_next (mp); 18797 if ((cur_sym() == NULL) || mp_is_frozen(mp, cur_sym())) { 18798 const char *hlp[] = { 18799 "Sorry: You can\'t redefine a number, string, or expr.", 18800 "I've inserted an inaccessible symbol so that your", 18801 "definition will be completed without mixing me up too badly.", 18802 NULL }; 18803 if (cur_sym() != NULL) 18804 hlp[0] = "Sorry: You can\'t redefine my error-recovery tokens."; 18805 else if (cur_cmd() == mp_string_token) 18806 delete_str_ref (cur_mod_str()); 18807 set_cur_sym(mp->frozen_inaccessible); 18808 mp_ins_error (mp, "Missing symbolic token inserted", hlp, true); 18809@.Missing symbolic token...@>; 18810 goto RESTART; 18811 } 18812} 18813 18814 18815@ Before we actually redefine a symbolic token, we need to clear away its 18816former value, if it was a variable. The following stronger version of 18817|get_symbol| does that. 18818 18819@c 18820static void mp_get_clear_symbol (MP mp) { 18821 mp_get_symbol (mp); 18822 mp_clear_symbol (mp, cur_sym(), false); 18823} 18824 18825 18826@ Here's another little subroutine; it checks that an equals sign 18827or assignment sign comes along at the proper place in a macro definition. 18828 18829@c 18830static void mp_check_equals (MP mp) { 18831 if (cur_cmd() != mp_equals) 18832 if (cur_cmd() != mp_assignment) { 18833 const char *hlp[] = { 18834 "The next thing in this `def' should have been `=',", 18835 "because I've already looked at the definition heading.", 18836 "But don't worry; I'll pretend that an equals sign", 18837 "was present. Everything from here to `enddef'", 18838 "will be the replacement text of this macro.", 18839 NULL }; 18840 mp_back_error (mp, "Missing `=' has been inserted", hlp, true); 18841@.Missing `='@>; 18842 } 18843} 18844 18845 18846@ A \&{primarydef}, \&{secondarydef}, or \&{tertiarydef} is rather easily 18847handled now that we have |scan_toks|. In this case there are 18848two parameters, which will be \.{EXPR0} and \.{EXPR1}. 18849 18850@c 18851static void mp_make_op_def (MP mp) { 18852 mp_command_code m; /* the type of definition */ 18853 mp_node q, r; /* for list manipulation */ 18854 mp_subst_list_item *qm = NULL, *qn = NULL; 18855 m = cur_mod(); 18856 mp_get_symbol (mp); 18857 qm = xmalloc (1, sizeof (mp_subst_list_item)); 18858 qm->link = NULL; 18859 qm->info = cur_sym(); 18860 qm->info_mod = cur_sym_mod(); 18861 qm->value_data = 0; 18862 qm->value_mod = mp_expr_sym; 18863 mp_get_clear_symbol (mp); 18864 mp->warning_info = cur_sym(); 18865 mp_get_symbol (mp); 18866 qn = xmalloc (1, sizeof (mp_subst_list_item)); 18867 qn->link = qm; 18868 qn->info = cur_sym(); 18869 qn->info_mod = cur_sym_mod(); 18870 qn->value_data = 1; 18871 qn->value_mod = mp_expr_sym; 18872 get_t_next (mp); 18873 mp_check_equals (mp); 18874 mp->scanner_status = op_defining; 18875 q = mp_get_symbolic_node (mp); 18876 set_ref_count (q, 0); 18877 r = mp_get_symbolic_node (mp); 18878 mp_link (q) = r; 18879 set_mp_sym_info (r, mp_general_macro); 18880 mp_name_type (r) = mp_macro_sym; 18881 mp_link (r) = mp_scan_toks (mp, mp_macro_def, qn, NULL, 0); 18882 mp->scanner_status = normal; 18883 set_eq_type (mp->warning_info, m); 18884 set_equiv_node (mp->warning_info, q); 18885 mp_get_x_next (mp); 18886} 18887 18888 18889@ Parameters to macros are introduced by the keywords \&{expr}, 18890\&{suffix}, \&{text}, \&{primary}, \&{secondary}, and \&{tertiary}. 18891 18892@<Put each...@>= 18893mp_primitive (mp, "expr", mp_param_type, mp_expr_param); 18894@:expr_}{\&{expr} primitive@>; 18895mp_primitive (mp, "suffix", mp_param_type, mp_suffix_param); 18896@:suffix_}{\&{suffix} primitive@>; 18897mp_primitive (mp, "text", mp_param_type, mp_text_param); 18898@:text_}{\&{text} primitive@>; 18899mp_primitive (mp, "primary", mp_param_type, mp_primary_macro); 18900@:primary_}{\&{primary} primitive@>; 18901mp_primitive (mp, "secondary", mp_param_type, mp_secondary_macro); 18902@:secondary_}{\&{secondary} primitive@>; 18903mp_primitive (mp, "tertiary", mp_param_type, mp_tertiary_macro); 18904@:tertiary_}{\&{tertiary} primitive@> 18905 18906 18907@ @<Cases of |print_cmd...@>= 18908case mp_param_type: 18909if (m == mp_expr_param) 18910 mp_print (mp, "expr"); 18911else if (m == mp_suffix_param) 18912 mp_print (mp, "suffix"); 18913else if (m == mp_text_param) 18914 mp_print (mp, "text"); 18915else if (m == mp_primary_macro) 18916 mp_print (mp, "primary"); 18917else if (m == mp_secondary_macro) 18918 mp_print (mp, "secondary"); 18919else 18920 mp_print (mp, "tertiary"); 18921break; 18922 18923@ Let's turn next to the more complex processing associated with \&{def} 18924and \&{vardef}. When the following procedure is called, |cur_mod| 18925should be either |start_def| or |var_def|. 18926 18927Note that although the macro scanner allows |def = := enddef| and 18928|def := = enddef|; |def = = enddef| and |def := := enddef| will generate 18929an error because by the time the second of the two identical tokens is 18930seen, its meaning has already become undefined. 18931 18932@c 18933static void mp_scan_def (MP mp) { 18934 int m; /* the type of definition */ 18935 int n; /* the number of special suffix parameters */ 18936 int k; /* the total number of parameters */ 18937 int c; /* the kind of macro we're defining */ 18938 mp_subst_list_item *r = NULL, *rp = NULL; /* parameter-substitution list */ 18939 mp_node q; /* tail of the macro token list */ 18940 mp_node p; /* temporary storage */ 18941 quarterword sym_type; /* |expr_sym|, |suffix_sym|, or |text_sym| */ 18942 mp_sym l_delim, r_delim; /* matching delimiters */ 18943 m = cur_mod(); 18944 c = mp_general_macro; 18945 mp_link (mp->hold_head) = NULL; 18946 q = mp_get_symbolic_node (mp); 18947 set_ref_count (q, 0); 18948 r = NULL; 18949 /* Scan the token or variable to be defined; 18950 set |n|, |scanner_status|, and |warning_info| */ 18951 if (m == start_def) { 18952 mp_get_clear_symbol (mp); 18953 mp->warning_info = cur_sym(); 18954 get_t_next (mp); 18955 mp->scanner_status = op_defining; 18956 n = 0; 18957 set_eq_type (mp->warning_info, mp_defined_macro); 18958 set_equiv_node (mp->warning_info, q); 18959 } else { /* |var_def| */ 18960 p = mp_scan_declared_variable (mp); 18961 mp_flush_variable (mp, equiv_node (mp_sym_sym (p)), mp_link (p), true); 18962 mp->warning_info_node = mp_find_variable (mp, p); 18963 mp_flush_node_list (mp, p); 18964 if (mp->warning_info_node == NULL) { 18965 /* Change to `\.{a bad variable}' */ 18966 const char *hlp[] = { 18967 "After `vardef a' you can\'t say `vardef a.b'.", 18968 "So I'll have to discard this definition.", 18969 NULL }; 18970 mp_error (mp, "This variable already starts with a macro", hlp, true); 18971 mp->warning_info_node = mp->bad_vardef; 18972 } 18973 mp->scanner_status = var_defining; 18974 n = 2; 18975 if (cur_cmd() == mp_macro_special && cur_mod() == macro_suffix) { /* \.{\AT!\#} */ 18976 n = 3; 18977 get_t_next (mp); 18978 } 18979 mp_type (mp->warning_info_node) = (quarterword) (mp_unsuffixed_macro - 2 + n); 18980 /* |mp_suffixed_macro=mp_unsuffixed_macro+1| */ 18981 set_value_node (mp->warning_info_node, q); 18982 } 18983 18984 k = n; 18985 if (cur_cmd() == mp_left_delimiter) { 18986 /* Absorb delimited parameters, putting them into lists |q| and |r| */ 18987 do { 18988 l_delim = cur_sym(); 18989 r_delim = equiv_sym (cur_sym()); 18990 get_t_next (mp); 18991 if ((cur_cmd() == mp_param_type) && (cur_mod() == mp_expr_param)) { 18992 sym_type = mp_expr_sym; 18993 } else if ((cur_cmd() == mp_param_type) && (cur_mod() == mp_suffix_param)) { 18994 sym_type = mp_suffix_sym; 18995 } else if ((cur_cmd() == mp_param_type) && (cur_mod() == mp_text_param)) { 18996 sym_type = mp_text_sym; 18997 } else { 18998 const char *hlp[] = { "You should've had `expr' or `suffix' or `text' here.", NULL }; 18999 mp_back_error (mp, "Missing parameter type; `expr' will be assumed", hlp, true); 19000 sym_type = mp_expr_sym; 19001 } 19002 /* Absorb parameter tokens for type |sym_type| */ 19003 do { 19004 mp_link (q) = mp_get_symbolic_node (mp); 19005 q = mp_link (q); 19006 mp_name_type (q) = sym_type; 19007 set_mp_sym_info (q, k); 19008 mp_get_symbol (mp); 19009 rp = xmalloc (1, sizeof (mp_subst_list_item)); 19010 rp->link = NULL; 19011 rp->value_data = k; 19012 rp->value_mod = sym_type; 19013 rp->info = cur_sym(); 19014 rp->info_mod = cur_sym_mod(); 19015 mp_check_param_size (mp, k); 19016 incr (k); 19017 rp->link = r; 19018 r = rp; 19019 get_t_next (mp); 19020 } while (cur_cmd() == mp_comma); 19021 19022 mp_check_delimiter (mp, l_delim, r_delim); 19023 get_t_next (mp); 19024 } while (cur_cmd() == mp_left_delimiter); 19025 19026 } 19027 if (cur_cmd() == mp_param_type) { 19028 /* Absorb undelimited parameters, putting them into list |r| */ 19029 rp = xmalloc (1, sizeof (mp_subst_list_item)); 19030 rp->link = NULL; 19031 rp->value_data = k; 19032 if (cur_mod() == mp_expr_param) { 19033 rp->value_mod = mp_expr_sym; 19034 c = mp_expr_macro; 19035 } else if (cur_mod() == mp_suffix_param) { 19036 rp->value_mod = mp_suffix_sym; 19037 c = mp_suffix_macro; 19038 } else if (cur_mod() == mp_text_param) { 19039 rp->value_mod = mp_text_sym; 19040 c = mp_text_macro; 19041 } else { 19042 c = cur_mod(); 19043 rp->value_mod = mp_expr_sym; 19044 } 19045 mp_check_param_size (mp, k); 19046 incr (k); 19047 mp_get_symbol (mp); 19048 rp->info = cur_sym(); 19049 rp->info_mod = cur_sym_mod(); 19050 rp->link = r; 19051 r = rp; 19052 get_t_next (mp); 19053 if (c == mp_expr_macro) { 19054 if (cur_cmd() == mp_of_token) { 19055 c = mp_of_macro; 19056 rp = xmalloc (1, sizeof (mp_subst_list_item)); 19057 rp->link = NULL; 19058 mp_check_param_size (mp, k); 19059 rp->value_data = k; 19060 rp->value_mod = mp_expr_sym; 19061 mp_get_symbol (mp); 19062 rp->info = cur_sym(); 19063 rp->info_mod = cur_sym_mod(); 19064 rp->link = r; 19065 r = rp; 19066 get_t_next (mp); 19067 } 19068 } 19069 } 19070 mp_check_equals (mp); 19071 p = mp_get_symbolic_node (mp); 19072 set_mp_sym_info (p, c); 19073 mp_name_type (p) = mp_macro_sym; 19074 mp_link (q) = p; 19075 /* Attach the replacement text to the tail of node |p| */ 19076 /* We don't put `|mp->frozen_end_group|' into the replacement text of 19077 a \&{vardef}, because the user may want to redefine `\.{endgroup}'. */ 19078 if (m == start_def) { 19079 mp_link (p) = mp_scan_toks (mp, mp_macro_def, r, NULL, (quarterword) n); 19080 } else { 19081 mp_node qq = mp_get_symbolic_node (mp); 19082 set_mp_sym_sym (qq, mp->bg_loc); 19083 mp_link (p) = qq; 19084 p = mp_get_symbolic_node (mp); 19085 set_mp_sym_sym (p, mp->eg_loc); 19086 mp_link (qq) = mp_scan_toks (mp, mp_macro_def, r, p, (quarterword) n); 19087 } 19088 if (mp->warning_info_node == mp->bad_vardef) 19089 mp_flush_token_list (mp, value_node (mp->bad_vardef)); 19090 mp->scanner_status = normal; 19091 mp_get_x_next (mp); 19092} 19093 19094@ @<Glob...@>= 19095mp_sym bg_loc; 19096mp_sym eg_loc; /* hash addresses of `\.{begingroup}' and `\.{endgroup}' */ 19097 19098@ @<Initialize table entries@>= 19099mp->bad_vardef = mp_get_value_node (mp); 19100mp_name_type (mp->bad_vardef) = mp_root; 19101set_value_sym (mp->bad_vardef, mp->frozen_bad_vardef); 19102 19103@ @<Free table entries@>= 19104mp_free_value_node (mp, mp->bad_vardef); 19105 19106 19107@* Expanding the next token. 19108Only a few command codes |<min_command| can possibly be returned by 19109|get_t_next|; in increasing order, they are 19110|if_test|, |fi_or_else|, |input|, |iteration|, |repeat_loop|, 19111|exit_test|, |relax|, |scan_tokens|, |run_script|, |expand_after|, and |defined_macro|. 19112 19113\MP\ usually gets the next token of input by saying |get_x_next|. This is 19114like |get_t_next| except that it keeps getting more tokens until 19115finding |cur_cmd>=min_command|. In other words, |get_x_next| expands 19116macros and removes conditionals or iterations or input instructions that 19117might be present. 19118 19119It follows that |get_x_next| might invoke itself recursively. In fact, 19120there is massive recursion, since macro expansion can involve the 19121scanning of arbitrarily complex expressions, which in turn involve 19122macro expansion and conditionals, etc. 19123@^recursion@> 19124 19125Therefore it's necessary to declare a whole bunch of |forward| 19126procedures at this point, and to insert some other procedures 19127that will be invoked by |get_x_next|. 19128 19129@<Declarations@>= 19130static void mp_scan_primary (MP mp); 19131static void mp_scan_secondary (MP mp); 19132static void mp_scan_tertiary (MP mp); 19133static void mp_scan_expression (MP mp); 19134static void mp_scan_suffix (MP mp); 19135static void mp_pass_text (MP mp); 19136static void mp_conditional (MP mp); 19137static void mp_start_input (MP mp); 19138static void mp_begin_iteration (MP mp); 19139static void mp_resume_iteration (MP mp); 19140static void mp_stop_iteration (MP mp); 19141 19142@ A recursion depth counter is used to discover infinite recursions. 19143(Near) infinite recursion is a problem because it translates into 19144C function calls that eat up the available call stack. A better solution 19145would be to depend on signal trapping, but that is problematic when 19146Metapost is used as a library. 19147 19148@<Global...@>= 19149int expand_depth_count; /* current expansion depth */ 19150int expand_depth; /* current expansion depth */ 19151 19152@ The limit is set at |10000|, which should be enough to allow 19153normal usages of metapost while preventing the most obvious 19154crashes on most all operating systems, but the value can be 19155raised if the runtime system allows a larger C stack. 19156@^system dependencies@> 19157 19158@<Set initial...@>= 19159mp->expand_depth = 10000; 19160 19161@ Even better would be if the system allows discovery of the amount of 19162space available on the call stack. 19163@^system dependencies@> 19164 19165In any case, when the limit is crossed, that is a fatal error. 19166 19167@d check_expansion_depth() if (++mp->expand_depth_count >= mp->expand_depth) 19168 mp_expansion_depth_error(mp) 19169 19170@c 19171static void mp_expansion_depth_error (MP mp) { 19172 const char *hlp[] = { 19173 "Recursive macro expansion cannot be unlimited because of runtime", 19174 "stack constraints. The limit is 10000 recursion levels in total.", 19175 NULL }; 19176 if ( mp->interaction==mp_error_stop_mode ) 19177 mp->interaction=mp_scroll_mode; /* no more interaction */ 19178 if ( mp->log_opened ) 19179 mp_error(mp, "Maximum expansion depth reached", hlp, true); 19180 mp->history=mp_fatal_error_stop; 19181 mp_jump_out(mp); 19182} 19183 19184 19185@ An auxiliary subroutine called |expand| is used by |get_x_next| 19186when it has to do exotic expansion commands. 19187 19188@c 19189static void mp_expand (MP mp) { 19190 size_t k; /* something that we hope is |<=buf_size| */ 19191 size_t j; /* index into |str_pool| */ 19192 check_expansion_depth(); 19193 if (number_greater (internal_value (mp_tracing_commands), unity_t)) 19194 if (cur_cmd() != mp_defined_macro) 19195 show_cur_cmd_mod; 19196 switch (cur_cmd()) { 19197 case mp_if_test: 19198 mp_conditional (mp); /* this procedure is discussed in Part 36 below */ 19199 break; 19200 case mp_fi_or_else: 19201 @<Terminate the current conditional and skip to \&{fi}@>; 19202 break; 19203 case mp_input: 19204 @<Initiate or terminate input from a file@>; 19205 break; 19206 case mp_iteration: 19207 if (cur_mod() == end_for) { 19208 @<Scold the user for having an extra \&{endfor}@>; 19209 } else { 19210 mp_begin_iteration (mp); /* this procedure is discussed in Part 37 below */ 19211 } 19212 break; 19213 case mp_repeat_loop: 19214 @<Repeat a loop@>; 19215 break; 19216 case mp_exit_test: 19217 @<Exit a loop if the proper time has come@>; 19218 break; 19219 case mp_relax: 19220 break; 19221 case mp_expand_after: 19222 @<Expand the token after the next token@>; 19223 break; 19224 case mp_scan_tokens: 19225 @<Put a string into the input buffer@>; 19226 break; 19227 case mp_runscript: 19228 @<Put a script result string into the input buffer@>; 19229 break; 19230 case mp_maketext: 19231 @<Put a maketext result string into the input buffer@>; 19232 break; 19233 case mp_defined_macro: 19234 mp_macro_call (mp, cur_mod_node(), NULL, cur_sym()); 19235 break; 19236 default: 19237 break; /* make the compiler happy */ 19238 }; /* there are no other cases */ 19239 mp->expand_depth_count--; 19240} 19241 19242 19243@ @<Scold the user...@>= 19244{ 19245 const char *hlp[] = { 19246 "I'm not currently working on a for loop,", 19247 "so I had better not try to end anything.", 19248 NULL }; 19249 mp_error (mp, "Extra `endfor'", hlp, true); 19250@.Extra `endfor'@>; 19251} 19252 19253 19254@ The processing of \&{input} involves the |start_input| subroutine, 19255which will be declared later; the processing of \&{endinput} is trivial. 19256 19257@<Put each...@>= 19258mp_primitive (mp, "input", mp_input, 0); 19259@:input_}{\&{input} primitive@>; 19260mp_primitive (mp, "endinput", mp_input, 1); 19261@:end_input_}{\&{endinput} primitive@> 19262 19263 19264@ @<Cases of |print_cmd_mod|...@>= 19265case mp_input: 19266if (m == 0) 19267 mp_print (mp, "input"); 19268else 19269 mp_print (mp, "endinput"); 19270break; 19271 19272@ @<Initiate or terminate input...@>= 19273if (cur_mod() > 0) 19274 mp->force_eof = true; 19275else 19276 mp_start_input (mp) 19277 19278 19279@ We'll discuss the complicated parts of loop operations later. For now 19280it suffices to know that there's a global variable called |loop_ptr| 19281that will be |NULL| if no loop is in progress. 19282 19283@<Repeat a loop@>= 19284{ 19285 while (token_state && (nloc == NULL)) 19286 mp_end_token_list (mp); /* conserve stack space */ 19287 if (mp->loop_ptr == NULL) { 19288 const char *hlp[] = { 19289 "I'm confused; after exiting from a loop, I still seem", 19290 "to want to repeat it. I'll try to forget the problem.", 19291 NULL }; 19292 mp_error (mp, "Lost loop", hlp, true); 19293@.Lost loop@>; 19294 } else { 19295 mp_resume_iteration (mp); /* this procedure is in Part 37 below */ 19296 } 19297} 19298 19299 19300@ @<Exit a loop if the proper time has come@>= 19301{ 19302 mp_get_boolean (mp); 19303 if (number_greater (internal_value (mp_tracing_commands), unity_t)) 19304 mp_show_cmd_mod (mp, mp_nullary, cur_exp_value_boolean ()); 19305 if (cur_exp_value_boolean () == mp_true_code) { 19306 if (mp->loop_ptr == NULL) { 19307 const char *hlp[] = { 19308 "Why say `exitif' when there's nothing to exit from?", 19309 NULL }; 19310 if (cur_cmd() == mp_semicolon) 19311 mp_error (mp, "No loop is in progress", hlp, true); 19312 else 19313 mp_back_error (mp, "No loop is in progress", hlp, true); 19314@.No loop is in progress@>; 19315 } else { 19316 @<Exit prematurely from an iteration@>; 19317 } 19318 } else if (cur_cmd() != mp_semicolon) { 19319 const char *hlp[] = { 19320 "After `exitif <boolean exp>' I expect to see a semicolon.", 19321 "I shall pretend that one was there.", 19322 NULL }; 19323 mp_back_error (mp, "Missing `;' has been inserted", hlp, true); 19324@.Missing `;'@>; 19325 } 19326} 19327 19328 19329@ Here we use the fact that |forever_text| is the only |token_type| that 19330is less than |loop_text|. 19331 19332@<Exit prematurely...@>= 19333{ 19334 mp_node p = NULL; 19335 do { 19336 if (file_state) { 19337 mp_end_file_reading (mp); 19338 } else { 19339 if (token_type <= loop_text) 19340 p = nstart; 19341 mp_end_token_list (mp); 19342 } 19343 } while (p == NULL); 19344 if (p != mp->loop_ptr->info) 19345 mp_fatal_error (mp, "*** (loop confusion)"); 19346@.loop confusion@>; 19347 mp_stop_iteration (mp); /* this procedure is in Part 34 below */ 19348} 19349 19350 19351@ @<Expand the token after the next token@>= 19352{ 19353 mp_node p; 19354 get_t_next (mp); 19355 p = mp_cur_tok (mp); 19356 get_t_next (mp); 19357 if (cur_cmd() < mp_min_command) 19358 mp_expand (mp); 19359 else 19360 mp_back_input (mp); 19361 back_list (p); 19362} 19363 19364 19365@ @<Put a string into the input buffer@>= 19366{ 19367 mp_get_x_next (mp); 19368 mp_scan_primary (mp); 19369 if (mp->cur_exp.type != mp_string_type) { 19370 mp_value new_expr; 19371 const char *hlp[] = { 19372 "I'm going to flush this expression, since", 19373 "scantokens should be followed by a known string.", 19374 NULL }; 19375 memset(&new_expr,0,sizeof(mp_value)); 19376 new_number(new_expr.data.n); 19377 mp_disp_err (mp, NULL); 19378 mp_back_error (mp, "Not a string", hlp, true); 19379@.Not a string@>; 19380 mp_get_x_next (mp); 19381 mp_flush_cur_exp (mp, new_expr); 19382 } else { 19383 mp_back_input (mp); 19384 if (cur_exp_str ()->len > 0) 19385 @<Pretend we're reading a new one-line file@>; 19386 } 19387} 19388 19389@ @<Run a script@>= 19390if (s != NULL) { 19391 int k ; 19392 size_t size = strlen(s); 19393 memset(&new_expr,0,sizeof(mp_value)); 19394 new_number(new_expr.data.n); 19395 mp_begin_file_reading (mp); 19396 name = is_scantok; 19397 mp->last = mp->first; 19398 k = mp->first + size; 19399 if (k >= mp->max_buf_stack) { 19400 while (k >= mp->buf_size) { 19401 mp_reallocate_buffer (mp, (mp->buf_size + (mp->buf_size / 4))); 19402 } 19403 mp->max_buf_stack = k + 1; 19404 } 19405 limit = (halfword) k; 19406 (void) memcpy ((mp->buffer + mp->first), s, size); 19407 free(s); 19408 mp->buffer[limit] = xord ('%'); 19409 mp->first = (size_t) (limit + 1); 19410 loc = start; 19411 mp_flush_cur_exp (mp, new_expr); 19412} 19413 19414@ @<Put a script result string into the input buffer@>= 19415{ 19416 19417 if (mp->extensions == 0) { 19418 return ; 19419 } 19420 mp_get_x_next (mp); 19421 mp_scan_primary (mp); 19422 if (mp->cur_exp.type != mp_string_type) { 19423 mp_value new_expr; 19424 const char *hlp[] = { 19425 "I'm going to flush this expression, since", 19426 "runscript should be followed by a known string.", 19427 NULL }; 19428 memset(&new_expr,0,sizeof(mp_value)); 19429 new_number(new_expr.data.n); 19430 mp_disp_err (mp, NULL); 19431 mp_back_error (mp, "Not a string", hlp, true); 19432 @.Not a string@>; 19433 mp_get_x_next (mp); 19434 mp_flush_cur_exp (mp, new_expr); 19435 } else { 19436 mp_back_input (mp); 19437 if (cur_exp_str ()->len > 0) { 19438 mp_value new_expr; 19439 char *s = mp->run_script(mp,(const char*) cur_exp_str()->str) ; 19440 @<Run a script@> 19441 } 19442 } 19443} 19444 19445@ @<Pass btex ... etex to script@>= 19446{ 19447 int first ; 19448 while ((loc < limit - 4) && (mp->buffer[loc] == ' ')) { 19449 incr(loc); 19450 } 19451 first = loc ; 19452 if (mp->buffer[loc-1] == ' ') { 19453 decr(loc); 19454 } 19455 while (loc < limit - 5) { 19456 if (mp->buffer[loc] == ' ') { 19457 incr(loc); 19458 if (mp->buffer[loc] == 'e') { 19459 incr(loc); 19460 if (mp->buffer[loc] == 't') { 19461 incr(loc) ; 19462 if (mp->buffer[loc] == 'e') { 19463 incr(loc) ; 19464 if (mp->buffer[loc] == 'x') { 19465 /* start action */ 19466 char *s, *txt ; 19467 int size ; 19468 mp_value new_expr; 19469 size = loc - first + 1 - 4 ; 19470 if (size < 0) { 19471 size = 0 ; 19472 } else { 19473 while ((size > 1) && (mp->buffer[first+size-1] == ' ')) { 19474 decr(size); 19475 } 19476 } 19477 txt = malloc(size+1); 19478 if (size > 0) { 19479 (void) memcpy (txt, mp->buffer + first, size); 19480 } 19481 txt[size] = '\0'; 19482 incr(loc); 19483 s = mp->make_text(mp,txt,(cur_mod() == verbatim_code)) ; /* we could pass the size */ 19484 @<Run a script@> 19485 /* done */ 19486 free(txt); 19487 break ; 19488 } else { 19489 // decr(loc) ; 19490 } 19491 } 19492 } 19493 } 19494 } else { 19495 incr(loc); 19496 } 19497 } 19498} 19499 19500@ @<Put a maketext result string into the input buffer@>= 19501{ 19502 if (mp->extensions == 0) { 19503 return ; 19504 } 19505 mp_get_x_next (mp); 19506 mp_scan_primary (mp); 19507 if (mp->cur_exp.type != mp_string_type) { 19508 mp_value new_expr; 19509 const char *hlp[] = { 19510 "I'm going to flush this expression, since", 19511 "makete should be followed by a known string.", 19512 NULL }; 19513 memset(&new_expr,0,sizeof(mp_value)); 19514 new_number(new_expr.data.n); 19515 mp_disp_err (mp, NULL); 19516 mp_back_error (mp, "Not a string", hlp, true); 19517 @.Not a string@>; 19518 mp_get_x_next (mp); 19519 mp_flush_cur_exp (mp, new_expr); 19520 } else { 19521 mp_back_input (mp); 19522 if (cur_exp_str ()->len > 0) { 19523 mp_value new_expr; 19524 char *s = mp->make_text(mp,(const char*) cur_exp_str()->str,0) ; 19525 @<Run a script@> 19526 } 19527 } 19528} 19529 19530@ @<Pretend we're reading a new one-line file@>= 19531{ 19532 mp_value new_expr; 19533 memset(&new_expr,0,sizeof(mp_value)); 19534 new_number(new_expr.data.n); 19535 mp_begin_file_reading (mp); 19536 name = is_scantok; 19537 k = mp->first + (size_t) cur_exp_str ()->len; 19538 if (k >= mp->max_buf_stack) { 19539 while (k >= mp->buf_size) { 19540 mp_reallocate_buffer (mp, (mp->buf_size + (mp->buf_size / 4))); 19541 } 19542 mp->max_buf_stack = k + 1; 19543 } 19544 j = 0; 19545 limit = (halfword) k; 19546 while (mp->first < (size_t) limit) { 19547 mp->buffer[mp->first] = *(cur_exp_str ()->str + j); 19548 j++; 19549 incr (mp->first); 19550 } 19551 mp->buffer[limit] = xord ('%'); 19552 mp->first = (size_t) (limit + 1); 19553 loc = start; 19554 mp_flush_cur_exp (mp, new_expr); 19555} 19556 19557 19558@ Here finally is |get_x_next|. 19559 19560The expression scanning routines to be considered later 19561communicate via the global quantities |cur_type| and |cur_exp|; 19562we must be very careful to save and restore these quantities while 19563macros are being expanded. 19564@^inner loop@> 19565 19566@<Declarations@>= 19567static void mp_get_x_next (MP mp); 19568 19569@ @c 19570void mp_get_x_next (MP mp) { 19571 mp_node save_exp; /* a capsule to save |cur_type| and |cur_exp| */ 19572 get_t_next (mp); 19573 if (cur_cmd() < mp_min_command) { 19574 save_exp = mp_stash_cur_exp (mp); 19575 do { 19576 if (cur_cmd() == mp_defined_macro) 19577 mp_macro_call (mp, cur_mod_node(), NULL, cur_sym()); 19578 else 19579 mp_expand (mp); 19580 get_t_next (mp); 19581 } while (cur_cmd() < mp_min_command); 19582 mp_unstash_cur_exp (mp, save_exp); /* that restores |cur_type| and |cur_exp| */ 19583 } 19584} 19585 19586 19587@ Now let's consider the |macro_call| procedure, which is used to start up 19588all user-defined macros. Since the arguments to a macro might be expressions, 19589|macro_call| is recursive. 19590@^recursion@> 19591 19592The first parameter to |macro_call| points to the reference count of the 19593token list that defines the macro. The second parameter contains any 19594arguments that have already been parsed (see below). The third parameter 19595points to the symbolic token that names the macro. If the third parameter 19596is |NULL|, the macro was defined by \&{vardef}, so its name can be 19597reconstructed from the prefix and ``at'' arguments found within the 19598second parameter. 19599 19600What is this second parameter? It's simply a linked list of symbolic items, 19601whose |info| fields point to the arguments. In other words, if |arg_list=NULL|, 19602no arguments have been scanned yet; otherwise |mp_info(arg_list)| points to 19603the first scanned argument, and |mp_link(arg_list)| points to the list of 19604further arguments (if any). 19605 19606Arguments of type \&{expr} are so-called capsules, which we will 19607discuss later when we concentrate on expressions; they can be 19608recognized easily because their |link| field is |void|. Arguments of type 19609\&{suffix} and \&{text} are token lists without reference counts. 19610 19611@ After argument scanning is complete, the arguments are moved to the 19612|param_stack|. (They can't be put on that stack any sooner, because 19613the stack is growing and shrinking in unpredictable ways as more arguments 19614are being acquired.) Then the macro body is fed to the scanner; i.e., 19615the replacement text of the macro is placed at the top of the \MP's 19616input stack, so that |get_t_next| will proceed to read it next. 19617 19618@<Declarations@>= 19619static void mp_macro_call (MP mp, mp_node def_ref, mp_node arg_list, 19620 mp_sym macro_name); 19621 19622@ @c 19623void mp_macro_call (MP mp, mp_node def_ref, mp_node arg_list, mp_sym macro_name) { 19624 /* invokes a user-defined control sequence */ 19625 mp_node r; /* current node in the macro's token list */ 19626 mp_node p, q; /* for list manipulation */ 19627 integer n; /* the number of arguments */ 19628 mp_node tail = 0; /* tail of the argument list */ 19629 mp_sym l_delim = NULL, r_delim = NULL; /* a delimiter pair */ 19630 r = mp_link (def_ref); 19631 add_mac_ref (def_ref); 19632 if (arg_list == NULL) { 19633 n = 0; 19634 } else { 19635 @<Determine the number |n| of arguments already supplied, 19636 and set |tail| to the tail of |arg_list|@>; 19637 } 19638 if (number_positive (internal_value (mp_tracing_macros))) { 19639 @<Show the text of the macro being expanded, and the existing arguments@>; 19640 } 19641 @<Scan the remaining arguments, if any; set |r| to the first token 19642 of the replacement text@>; 19643 @<Feed the arguments and replacement text to the scanner@>; 19644} 19645 19646 19647@ @<Show the text of the macro...@>= 19648mp_begin_diagnostic (mp); 19649mp_print_ln (mp); 19650mp_print_macro_name (mp, arg_list, macro_name); 19651if (n == 3) 19652 mp_print (mp, "@@#"); /* indicate a suffixed macro */ 19653mp_show_macro (mp, def_ref, NULL, 100000); 19654if (arg_list != NULL) { 19655 n = 0; 19656 p = arg_list; 19657 do { 19658 q = (mp_node)mp_sym_sym (p); 19659 mp_print_arg (mp, q, n, 0, 0); 19660 incr (n); 19661 p = mp_link (p); 19662 } while (p != NULL); 19663} 19664mp_end_diagnostic (mp, false) 19665 19666 19667@ @<Declarations@>= 19668static void mp_print_macro_name (MP mp, mp_node a, mp_sym n); 19669 19670@ @c 19671void mp_print_macro_name (MP mp, mp_node a, mp_sym n) { 19672 mp_node p, q; /* they traverse the first part of |a| */ 19673 if (n != NULL) { 19674 mp_print_text (n); 19675 } else { 19676 p = (mp_node)mp_sym_sym (a); 19677 if (p == NULL) { 19678 mp_print_text (mp_sym_sym ((mp_node)mp_sym_sym (mp_link (a)))); 19679 } else { 19680 q = p; 19681 while (mp_link (q) != NULL) 19682 q = mp_link (q); 19683 mp_link (q) = (mp_node)mp_sym_sym (mp_link (a)); 19684 mp_show_token_list (mp, p, NULL, 1000, 0); 19685 mp_link (q) = NULL; 19686 } 19687 } 19688} 19689 19690 19691@ @<Declarations@>= 19692static void mp_print_arg (MP mp, mp_node q, integer n, halfword b, 19693 quarterword bb); 19694 19695@ @c 19696void mp_print_arg (MP mp, mp_node q, integer n, halfword b, quarterword bb) { 19697 if (q && mp_link (q) == MP_VOID) { 19698 mp_print_nl (mp, "(EXPR"); 19699 } else { 19700 if ((bb < mp_text_sym) && (b != mp_text_macro)) 19701 mp_print_nl (mp, "(SUFFIX"); 19702 else 19703 mp_print_nl (mp, "(TEXT"); 19704 } 19705 mp_print_int (mp, n); 19706 mp_print (mp, ")<-"); 19707 if (q && mp_link (q) == MP_VOID) 19708 mp_print_exp (mp, q, 1); 19709 else 19710 mp_show_token_list (mp, q, NULL, 1000, 0); 19711} 19712 19713 19714@ @<Determine the number |n| of arguments already supplied...@>= 19715{ 19716 n = 1; 19717 tail = arg_list; 19718 while (mp_link (tail) != NULL) { 19719 incr (n); 19720 tail = mp_link (tail); 19721 } 19722} 19723 19724 19725@ @<Scan the remaining arguments, if any; set |r|...@>= 19726set_cur_cmd(mp_comma + 1); /* anything |<>comma| will do */ 19727while (mp_name_type (r) == mp_expr_sym || 19728 mp_name_type (r) == mp_suffix_sym || mp_name_type (r) == mp_text_sym) { 19729 @<Scan the delimited argument represented by |mp_sym_info(r)|@>; 19730 r = mp_link (r); 19731} 19732if (cur_cmd() == mp_comma) { 19733 char msg[256]; 19734 const char *hlp[] = { 19735 "I'm going to assume that the comma I just read was a", 19736 "right delimiter, and then I'll begin expanding the macro.", 19737 "You might want to delete some tokens before continuing.", 19738 NULL }; 19739 mp_string rname; 19740 int old_setting = mp->selector; 19741 mp->selector = new_string; 19742 mp_print_macro_name (mp, arg_list, macro_name); 19743 rname = mp_make_string(mp); 19744 mp->selector = old_setting; 19745 mp_snprintf (msg, 256, "Too many arguments to %s; Missing `%s' has been inserted", 19746 mp_str(mp, rname), mp_str(mp, text(r_delim))); 19747 delete_str_ref(rname); 19748@.Too many arguments...@>; 19749@.Missing `)'...@>; 19750 mp_error (mp, msg, hlp, true); 19751} 19752if (mp_sym_info (r) != mp_general_macro) { 19753 @<Scan undelimited argument(s)@>; 19754} 19755r = mp_link (r) 19756 19757 19758@ At this point, the reader will find it advisable to review the explanation 19759of token list format that was presented earlier, paying special attention to 19760the conventions that apply only at the beginning of a macro's token list. 19761 19762On the other hand, the reader will have to take the expression-parsing 19763aspects of the following program on faith; we will explain |cur_type| 19764and |cur_exp| later. (Several things in this program depend on each other, 19765and it's necessary to jump into the circle somewhere.) 19766 19767@<Scan the delimited argument represented by |mp_sym_info(r)|@>= 19768if (cur_cmd() != mp_comma) { 19769 mp_get_x_next (mp); 19770 if (cur_cmd() != mp_left_delimiter) { 19771 char msg[256]; 19772 const char *hlp[] = { 19773 "That macro has more parameters than you thought.", 19774 "I'll continue by pretending that each missing argument", 19775 "is either zero or null.", 19776 NULL }; 19777 mp_string sname; 19778 int old_setting = mp->selector; 19779 mp->selector = new_string; 19780 mp_print_macro_name (mp, arg_list, macro_name); 19781 sname = mp_make_string(mp); 19782 mp->selector = old_setting; 19783 mp_snprintf (msg, 256, "Missing argument to %s", mp_str(mp, sname)); 19784@.Missing argument...@>; 19785 delete_str_ref(sname); 19786 if (mp_name_type (r) == mp_suffix_sym || mp_name_type (r) == mp_text_sym) { 19787 set_cur_exp_value_number (zero_t); /* todo: this was |null| */ 19788 mp->cur_exp.type = mp_token_list; 19789 } else { 19790 set_cur_exp_value_number (zero_t); 19791 mp->cur_exp.type = mp_known; 19792 } 19793 mp_back_error (mp, msg, hlp, true); 19794 set_cur_cmd((mp_variable_type)mp_right_delimiter); 19795 goto FOUND; 19796 } 19797 l_delim = cur_sym(); 19798 r_delim = equiv_sym (cur_sym()); 19799} 19800@<Scan the argument represented by |mp_sym_info(r)|@>; 19801if (cur_cmd() != mp_comma) 19802 @<Check that the proper right delimiter was present@>; 19803FOUND: 19804@<Append the current expression to |arg_list|@> 19805 19806 19807@ @<Check that the proper right delim...@>= 19808if ((cur_cmd() != mp_right_delimiter) || (equiv_sym (cur_sym()) != l_delim)) { 19809 if (mp_name_type (mp_link (r)) == mp_expr_sym || 19810 mp_name_type (mp_link (r)) == mp_suffix_sym || 19811 mp_name_type (mp_link (r)) == mp_text_sym) { 19812 const char *hlp[] = { 19813 "I've finished reading a macro argument and am about to", 19814 "read another; the arguments weren't delimited correctly.", 19815 "You might want to delete some tokens before continuing.", 19816 NULL }; 19817 mp_back_error (mp, "Missing `,' has been inserted", hlp, true); 19818@.Missing `,'@>; 19819 set_cur_cmd((mp_variable_type)mp_comma); 19820 } else { 19821 char msg[256]; 19822 const char *hlp[] = { 19823 "I've gotten to the end of the macro parameter list.", 19824 "You might want to delete some tokens before continuing.", 19825 NULL }; 19826 mp_snprintf(msg, 256, "Missing `%s' has been inserted", mp_str(mp, text(r_delim))); 19827@.Missing `)'@>; 19828 mp_back_error (mp, msg, hlp, true); 19829 } 19830} 19831 19832@ A \&{suffix} or \&{text} parameter will have been scanned as 19833a token list pointed to by |cur_exp|, in which case we will have 19834|cur_type=token_list|. 19835 19836@<Append the current expression to |arg_list|@>= 19837{ 19838 p = mp_get_symbolic_node (mp); 19839 if (mp->cur_exp.type == mp_token_list) 19840 set_mp_sym_sym (p, mp->cur_exp.data.node); 19841 else 19842 set_mp_sym_sym (p, mp_stash_cur_exp (mp)); 19843 if (number_positive (internal_value (mp_tracing_macros))) { 19844 mp_begin_diagnostic (mp); 19845 mp_print_arg (mp, (mp_node)mp_sym_sym (p), n, mp_sym_info (r), mp_name_type (r)); 19846 mp_end_diagnostic (mp, false); 19847 } 19848 if (arg_list == NULL) { 19849 arg_list = p; 19850 } else { 19851 mp_link (tail) = p; 19852 } 19853 tail = p; 19854 incr (n); 19855} 19856 19857 19858@ @<Scan the argument represented by |mp_sym_info(r)|@>= 19859if (mp_name_type (r) == mp_text_sym) { 19860 mp_scan_text_arg (mp, l_delim, r_delim); 19861} else { 19862 mp_get_x_next (mp); 19863 if (mp_name_type (r) == mp_suffix_sym) 19864 mp_scan_suffix (mp); 19865 else 19866 mp_scan_expression (mp); 19867} 19868 19869 19870@ The parameters to |scan_text_arg| are either a pair of delimiters 19871or zero; the latter case is for undelimited text arguments, which 19872end with the first semicolon or \&{endgroup} or \&{end} that is not 19873contained in a group. 19874 19875@<Declarations@>= 19876static void mp_scan_text_arg (MP mp, mp_sym l_delim, mp_sym r_delim); 19877 19878@ @c 19879void mp_scan_text_arg (MP mp, mp_sym l_delim, mp_sym r_delim) { 19880 integer balance; /* excess of |l_delim| over |r_delim| */ 19881 mp_node p; /* list tail */ 19882 mp->warning_info = l_delim; 19883 mp->scanner_status = absorbing; 19884 p = mp->hold_head; 19885 balance = 1; 19886 mp_link (mp->hold_head) = NULL; 19887 while (1) { 19888 get_t_next (mp); 19889 if (l_delim == NULL) { 19890 @<Adjust the balance for an undelimited argument; |break| if done@>; 19891 } else { 19892 @<Adjust the balance for a delimited argument; |break| if done@>; 19893 } 19894 mp_link (p) = mp_cur_tok (mp); 19895 p = mp_link (p); 19896 } 19897 set_cur_exp_node (mp_link (mp->hold_head)); 19898 mp->cur_exp.type = mp_token_list; 19899 mp->scanner_status = normal; 19900} 19901 19902 19903@ @<Adjust the balance for a delimited argument...@>= 19904if (cur_cmd() == mp_right_delimiter) { 19905 if (equiv_sym (cur_sym()) == l_delim) { 19906 decr (balance); 19907 if (balance == 0) 19908 break; 19909 } 19910} else if (cur_cmd() == mp_left_delimiter) { 19911 if (equiv_sym (cur_sym()) == r_delim) 19912 incr (balance); 19913} 19914 19915@ @<Adjust the balance for an undelimited...@>= 19916if (mp_end_of_statement) { /* |cur_cmd=semicolon|, |end_group|, or |stop| */ 19917 if (balance == 1) { 19918 break; 19919 } else { 19920 if (cur_cmd() == mp_end_group) 19921 decr (balance); 19922 } 19923} else if (cur_cmd() == mp_begin_group) { 19924 incr (balance); 19925} 19926 19927@ @<Scan undelimited argument(s)@>= 19928{ 19929 if (mp_sym_info (r) < mp_text_macro) { 19930 mp_get_x_next (mp); 19931 if (mp_sym_info (r) != mp_suffix_macro) { 19932 if ((cur_cmd() == mp_equals) || (cur_cmd() == mp_assignment)) 19933 mp_get_x_next (mp); 19934 } 19935 } 19936 switch (mp_sym_info (r)) { 19937 case mp_primary_macro: 19938 mp_scan_primary (mp); 19939 break; 19940 case mp_secondary_macro: 19941 mp_scan_secondary (mp); 19942 break; 19943 case mp_tertiary_macro: 19944 mp_scan_tertiary (mp); 19945 break; 19946 case mp_expr_macro: 19947 mp_scan_expression (mp); 19948 break; 19949 case mp_of_macro: 19950 @<Scan an expression followed by `\&{of} $\langle$primary$\rangle$'@>; 19951 break; 19952 case mp_suffix_macro: 19953 @<Scan a suffix with optional delimiters@>; 19954 break; 19955 case mp_text_macro: 19956 mp_scan_text_arg (mp, NULL, NULL); 19957 break; 19958 } /* there are no other cases */ 19959 mp_back_input (mp); 19960 @<Append the current expression to |arg_list|@>; 19961} 19962 19963 19964@ @<Scan an expression followed by `\&{of} $\langle$primary$\rangle$'@>= 19965{ 19966 mp_scan_expression (mp); 19967 p = mp_get_symbolic_node (mp); 19968 set_mp_sym_sym (p, mp_stash_cur_exp (mp)); 19969 if (number_positive (internal_value (mp_tracing_macros))) { 19970 mp_begin_diagnostic (mp); 19971 mp_print_arg (mp, (mp_node)mp_sym_sym (p), n, 0, 0); 19972 mp_end_diagnostic (mp, false); 19973 } 19974 if (arg_list == NULL) 19975 arg_list = p; 19976 else 19977 mp_link (tail) = p; 19978 tail = p; 19979 incr (n); 19980 if (cur_cmd() != mp_of_token) { 19981 char msg[256]; 19982 mp_string sname; 19983 const char *hlp[] = { 19984 "I've got the first argument; will look now for the other.", 19985 NULL }; 19986 int old_setting = mp->selector; 19987 mp->selector = new_string; 19988 mp_print_macro_name (mp, arg_list, macro_name); 19989 sname = mp_make_string(mp); 19990 mp->selector = old_setting; 19991 mp_snprintf (msg, 256, "Missing `of' has been inserted for %s", mp_str(mp, sname)); 19992 delete_str_ref(sname); 19993@.Missing `of'@>; 19994 mp_back_error (mp, msg, hlp, true); 19995 } 19996 mp_get_x_next (mp); 19997 mp_scan_primary (mp); 19998} 19999 20000 20001@ @<Scan a suffix with optional delimiters@>= 20002{ 20003 if (cur_cmd() != mp_left_delimiter) { 20004 l_delim = NULL; 20005 } else { 20006 l_delim = cur_sym(); 20007 r_delim = equiv_sym (cur_sym()); 20008 mp_get_x_next (mp); 20009 } 20010 mp_scan_suffix (mp); 20011 if (l_delim != NULL) { 20012 if ((cur_cmd() != mp_right_delimiter) || (equiv_sym (cur_sym()) != l_delim)) { 20013 char msg[256]; 20014 const char *hlp[] = { 20015 "I've gotten to the end of the macro parameter list.", 20016 "You might want to delete some tokens before continuing.", 20017 NULL }; 20018 mp_snprintf(msg, 256, "Missing `%s' has been inserted", mp_str (mp, text (r_delim))); 20019@.Missing `)'@>; 20020 mp_back_error (mp, msg, hlp, true); 20021 } 20022 mp_get_x_next (mp); 20023 } 20024} 20025 20026 20027@ Before we put a new token list on the input stack, it is wise to clean off 20028all token lists that have recently been depleted. Then a user macro that ends 20029with a call to itself will not require unbounded stack space. 20030 20031@<Feed the arguments and replacement text to the scanner@>= 20032while (token_state && (nloc == NULL)) 20033 mp_end_token_list (mp); /* conserve stack space */ 20034if (mp->param_ptr + n > mp->max_param_stack) { 20035 mp->max_param_stack = mp->param_ptr + n; 20036 mp_check_param_size (mp, mp->max_param_stack); 20037@:MetaPost capacity exceeded parameter stack size}{\quad parameter stack size@> 20038} 20039mp_begin_token_list (mp, def_ref, (quarterword) macro); 20040if (macro_name) 20041 name = text (macro_name); 20042else 20043 name = NULL; 20044nloc = r; 20045if (n > 0) { 20046 p = arg_list; 20047 do { 20048 mp->param_stack[mp->param_ptr] = (mp_node)mp_sym_sym (p); 20049 incr (mp->param_ptr); 20050 p = mp_link (p); 20051 } while (p != NULL); 20052 mp_flush_node_list (mp, arg_list); 20053} 20054 20055@ It's sometimes necessary to put a single argument onto |param_stack|. 20056The |stack_argument| subroutine does this. 20057 20058@c 20059static void mp_stack_argument (MP mp, mp_node p) { 20060 if (mp->param_ptr == mp->max_param_stack) { 20061 incr (mp->max_param_stack); 20062 mp_check_param_size (mp, mp->max_param_stack); 20063 } 20064 mp->param_stack[mp->param_ptr] = p; 20065 incr (mp->param_ptr); 20066} 20067 20068 20069@* Conditional processing. 20070Let's consider now the way \&{if} commands are handled. 20071 20072Conditions can be inside conditions, and this nesting has a stack 20073that is independent of other stacks. 20074Four global variables represent the top of the condition stack: 20075|cond_ptr| points to pushed-down entries, if~any; |cur_if| tells whether 20076we are processing \&{if} or \&{elseif}; |if_limit| specifies 20077the largest code of a |fi_or_else| command that is syntactically legal; 20078and |if_line| is the line number at which the current conditional began. 20079 20080If no conditions are currently in progress, the condition stack has the 20081special state |cond_ptr=NULL|, |if_limit=normal|, |cur_if=0|, |if_line=0|. 20082Otherwise |cond_ptr| points to a non-symbolic node; the |type|, |name_type|, and 20083|link| fields of the first word contain |if_limit|, |cur_if|, and 20084|cond_ptr| at the next level, and the second word contains the 20085corresponding |if_line|. 20086 20087@d if_line_field(A) ((mp_if_node)(A))->if_line_field_ 20088@d if_code 1 /* code for \&{if} being evaluated */ 20089@d fi_code 2 /* code for \&{fi} */ 20090@d else_code 3 /* code for \&{else} */ 20091@d else_if_code 4 /* code for \&{elseif} */ 20092 20093@<MPlib internal header stuff@>= 20094typedef struct mp_if_node_data { 20095 NODE_BODY; 20096 int if_line_field_; 20097} mp_if_node_data; 20098typedef struct mp_if_node_data *mp_if_node; 20099 20100@ 20101@d if_node_size sizeof(struct mp_if_node_data) /* number of words in stack entry for conditionals */ 20102 20103@c 20104static mp_node mp_get_if_node (MP mp) { 20105 mp_if_node p = (mp_if_node) malloc_node (if_node_size); 20106 mp_type (p) = mp_if_node_type; 20107 return (mp_node) p; 20108} 20109 20110 20111@ @<Glob...@>= 20112mp_node cond_ptr; /* top of the condition stack */ 20113integer if_limit; /* upper bound on |fi_or_else| codes */ 20114quarterword cur_if; /* type of conditional being worked on */ 20115integer if_line; /* line where that conditional began */ 20116 20117@ @<Set init...@>= 20118mp->cond_ptr = NULL; 20119mp->if_limit = normal; 20120mp->cur_if = 0; 20121mp->if_line = 0; 20122 20123@ @<Put each...@>= 20124mp_primitive (mp, "if", mp_if_test, if_code); 20125@:if_}{\&{if} primitive@>; 20126mp_primitive (mp, "fi", mp_fi_or_else, fi_code); 20127mp->frozen_fi = mp_frozen_primitive (mp, "fi", mp_fi_or_else, fi_code); 20128@:fi_}{\&{fi} primitive@>; 20129mp_primitive (mp, "else", mp_fi_or_else, else_code); 20130@:else_}{\&{else} primitive@>; 20131mp_primitive (mp, "elseif", mp_fi_or_else, else_if_code); 20132@:else_if_}{\&{elseif} primitive@> 20133 20134 20135@ @<Cases of |print_cmd_mod|...@>= 20136case mp_if_test: 20137case mp_fi_or_else: 20138switch (m) { 20139case if_code: 20140 mp_print (mp, "if"); 20141 break; 20142case fi_code: 20143 mp_print (mp, "fi"); 20144 break; 20145case else_code: 20146 mp_print (mp, "else"); 20147 break; 20148default: 20149 mp_print (mp, "elseif"); 20150 break; 20151} 20152break; 20153 20154@ Here is a procedure that ignores text until coming to an \&{elseif}, 20155\&{else}, or \&{fi} at level zero of $\&{if}\ldots\&{fi}$ 20156nesting. After it has acted, |cur_mod| will indicate the token that 20157was found. 20158 20159\MP's smallest two command codes are |if_test| and |fi_or_else|; this 20160makes the skipping process a bit simpler. 20161 20162@c 20163void mp_pass_text (MP mp) { 20164 integer l = 0; 20165 mp->scanner_status = skipping; 20166 mp->warning_line = mp_true_line (mp); 20167 while (1) { 20168 get_t_next (mp); 20169 if (cur_cmd() <= mp_fi_or_else) { 20170 if (cur_cmd() < mp_fi_or_else) { 20171 incr (l); 20172 } else { 20173 if (l == 0) 20174 break; 20175 if (cur_mod() == fi_code) 20176 decr (l); 20177 } 20178 } else { 20179 @<Decrease the string reference count, 20180 if the current token is a string@>; 20181 } 20182 } 20183 mp->scanner_status = normal; 20184} 20185 20186 20187@ @<Decrease the string reference count...@>= 20188if (cur_cmd() == mp_string_token) { 20189 delete_str_ref (cur_mod_str()); 20190} 20191 20192@ When we begin to process a new \&{if}, we set |if_limit:=if_code|; then 20193if \&{elseif} or \&{else} or \&{fi} occurs before the current \&{if} 20194condition has been evaluated, a colon will be inserted. 20195A construction like `\.{if fi}' would otherwise get \MP\ confused. 20196 20197@<Push the condition stack@>= 20198{ 20199 p = mp_get_if_node (mp); 20200 mp_link (p) = mp->cond_ptr; 20201 mp_type (p) = (quarterword) mp->if_limit; 20202 mp_name_type (p) = mp->cur_if; 20203 if_line_field (p) = mp->if_line; 20204 mp->cond_ptr = p; 20205 mp->if_limit = if_code; 20206 mp->if_line = mp_true_line (mp); 20207 mp->cur_if = if_code; 20208} 20209 20210 20211@ @<Pop the condition stack@>= 20212{ 20213 mp_node p = mp->cond_ptr; 20214 mp->if_line = if_line_field (p); 20215 mp->cur_if = mp_name_type (p); 20216 mp->if_limit = mp_type (p); 20217 mp->cond_ptr = mp_link (p); 20218 mp_free_node (mp, p, if_node_size); 20219} 20220 20221 20222@ Here's a procedure that changes the |if_limit| code corresponding to 20223a given value of |cond_ptr|. 20224 20225@c 20226static void mp_change_if_limit (MP mp, quarterword l, mp_node p) { 20227 mp_node q; 20228 if (p == mp->cond_ptr) { 20229 mp->if_limit = l; /* that's the easy case */ 20230 } else { 20231 q = mp->cond_ptr; 20232 while (1) { 20233 if (q == NULL) 20234 mp_confusion (mp, "if"); 20235@:this can't happen if}{\quad if@>; 20236 /* clang: dereference of null pointer */ assert(q); 20237 if (mp_link (q) == p) { 20238 mp_type (q) = l; 20239 return; 20240 } 20241 q = mp_link (q); 20242 } 20243 } 20244} 20245 20246 20247@ The user is supposed to put colons into the proper parts of conditional 20248statements. Therefore, \MP\ has to check for their presence. 20249 20250@c 20251static void mp_check_colon (MP mp) { 20252 if (cur_cmd() != mp_colon) { 20253 const char *hlp[] = { 20254 "There should've been a colon after the condition.", 20255 "I shall pretend that one was there.", 20256 NULL }; 20257 mp_back_error (mp, "Missing `:' has been inserted", hlp, true); 20258@.Missing `:'@>; 20259 } 20260} 20261 20262 20263@ A condition is started when the |get_x_next| procedure encounters 20264an |if_test| command; in that case |get_x_next| calls |conditional|, 20265which is a recursive procedure. 20266@^recursion@> 20267 20268@c 20269void mp_conditional (MP mp) { 20270 mp_node save_cond_ptr; /* |cond_ptr| corresponding to this conditional */ 20271 int new_if_limit; /* future value of |if_limit| */ 20272 mp_node p; /* temporary register */ 20273 @<Push the condition stack@>; 20274 save_cond_ptr = mp->cond_ptr; 20275RESWITCH: 20276 mp_get_boolean (mp); 20277 new_if_limit = else_if_code; 20278 if (number_greater (internal_value (mp_tracing_commands), unity_t)) { 20279 @<Display the boolean value of |cur_exp|@>; 20280 } 20281FOUND: 20282 mp_check_colon (mp); 20283 if (cur_exp_value_boolean () == mp_true_code) { 20284 mp_change_if_limit (mp, (quarterword) new_if_limit, save_cond_ptr); 20285 return; /* wait for \&{elseif}, \&{else}, or \&{fi} */ 20286 }; 20287 @<Skip to \&{elseif} or \&{else} or \&{fi}, then |goto done|@>; 20288DONE: 20289 mp->cur_if = (quarterword) cur_mod(); 20290 mp->if_line = mp_true_line (mp); 20291 if (cur_mod() == fi_code) { 20292 @<Pop the condition stack@> 20293 } else if (cur_mod() == else_if_code) { 20294 goto RESWITCH; 20295 } else { 20296 set_cur_exp_value_boolean (mp_true_code); 20297 new_if_limit = fi_code; 20298 mp_get_x_next (mp); 20299 goto FOUND; 20300 } 20301} 20302 20303 20304@ In a construction like `\&{if} \&{if} \&{true}: $0=1$: \\{foo} 20305\&{else}: \\{bar} \&{fi}', the first \&{else} 20306that we come to after learning that the \&{if} is false is not the 20307\&{else} we're looking for. Hence the following curious logic is needed. 20308 20309@<Skip to \&{elseif}...@>= 20310while (1) { 20311 mp_pass_text (mp); 20312 if (mp->cond_ptr == save_cond_ptr) 20313 goto DONE; 20314 else if (cur_mod() == fi_code) 20315 @<Pop the condition stack@>; 20316} 20317 20318 20319@ @<Display the boolean value...@>= 20320{ 20321 mp_begin_diagnostic (mp); 20322 if (cur_exp_value_boolean () == mp_true_code) 20323 mp_print (mp, "{true}"); 20324 else 20325 mp_print (mp, "{false}"); 20326 mp_end_diagnostic (mp, false); 20327} 20328 20329 20330@ The processing of conditionals is complete except for the following 20331code, which is actually part of |get_x_next|. It comes into play when 20332\&{elseif}, \&{else}, or \&{fi} is scanned. 20333 20334@<Terminate the current conditional and skip to \&{fi}@>= 20335if (cur_mod() > mp->if_limit) { 20336 if (mp->if_limit == if_code) { /* condition not yet evaluated */ 20337 const char *hlp[] = { "Something was missing here", NULL }; 20338 mp_back_input (mp); 20339 set_cur_sym(mp->frozen_colon); 20340 mp_ins_error (mp, "Missing `:' has been inserted", hlp, true); 20341@.Missing `:'@>; 20342 } else { 20343 const char *hlp[] = {"I'm ignoring this; it doesn't match any if.", NULL}; 20344 if (cur_mod() == fi_code) { 20345 mp_error(mp, "Extra fi", hlp, true); 20346@.Extra fi@>; 20347 } else if (cur_mod() == else_code) { 20348 mp_error(mp, "Extra else", hlp, true); 20349@.Extra else@> 20350 } else { 20351 mp_error(mp, "Extra elseif", hlp, true); 20352@.Extra elseif@> 20353 } 20354 } 20355} else { 20356 while (cur_mod() != fi_code) 20357 mp_pass_text (mp); /* skip to \&{fi} */ 20358 @<Pop the condition stack@>; 20359} 20360 20361 20362@* Iterations. 20363To bring our treatment of |get_x_next| to a close, we need to consider what 20364\MP\ does when it sees \&{for}, \&{forsuffixes}, and \&{forever}. 20365 20366There's a global variable |loop_ptr| that keeps track of the \&{for} loops 20367that are currently active. If |loop_ptr=NULL|, no loops are in progress; 20368otherwise |loop_ptr.info| points to the iterative text of the current 20369(innermost) loop, and |loop_ptr.link| points to the data for any other 20370loops that enclose the current one. 20371 20372A loop-control node also has two other fields, called |type| and 20373|list|, whose contents depend on the type of loop: 20374 20375\yskip\indent|loop_ptr.type=NULL| means that the link of |loop_ptr.list| 20376points to a list of symbolic nodes whose |info| fields point to the 20377remaining argument values of a suffix list and expression list. 20378In this case, an extra field |loop_ptr.start_list| is needed to 20379make sure that |resume_operation| skips ahead. 20380 20381\yskip\indent|loop_ptr.type=MP_VOID| means that the current loop is 20382`\&{forever}'. 20383 20384\yskip\indent|loop_ptr.type=PROGRESSION_FLAG| means that 20385|loop_ptr.value|, |loop_ptr.step_size|, and |loop_ptr.final_value| 20386contain the data for an arithmetic progression. 20387 20388\yskip\indent|loop_ptr.type=p>PROGRESSION_FLAG| means that |p| points to an edge 20389header and |loop_ptr.list| points into the graphical object list for 20390that edge header. 20391 20392@d PROGRESSION_FLAG (mp_node)(2) /* |NULL+2| */ 20393 /* |loop_type| value when |loop_list| points to a progression node */ 20394 20395@<Types...@>= 20396typedef struct mp_loop_data { 20397 mp_sym var ; /* the var of the loop */ 20398 mp_node info; /* iterative text of this loop */ 20399 mp_node type; /* the special type of this loop, or a pointer into 20400 mem */ 20401 mp_node list; /* the remaining list elements */ 20402 mp_node list_start; /* head fo the list of elements */ 20403 mp_number old_value; /* previous value of current arithmetic value */ 20404 mp_number value; /* current arithmetic value */ 20405 mp_number step_size; /* arithmetic step size */ 20406 mp_number final_value; /* end arithmetic value */ 20407 struct mp_loop_data *link; /* the enclosing loop, if any */ 20408} mp_loop_data; 20409 20410@ @<Glob...@>= 20411mp_loop_data *loop_ptr; /* top of the loop-control-node stack */ 20412 20413@ @<Set init...@>= 20414mp->loop_ptr = NULL; 20415 20416@ If the expressions that define an arithmetic progression in a 20417\&{for} loop don't have known numeric values, the |bad_for| subroutine 20418screams at the user. 20419 20420@c 20421static void mp_bad_for (MP mp, const char *s) { 20422 char msg[256]; 20423 mp_value new_expr; 20424 const char *hlp[] = {"When you say `for x=a step b until c',", 20425 "the initial value `a' and the step size `b'", 20426 "and the final value `c' must have known numeric values.", 20427 "I'm zeroing this one. Proceed, with fingers crossed.", 20428 NULL }; 20429 memset(&new_expr,0,sizeof(mp_value)); 20430 new_number(new_expr.data.n); 20431 mp_disp_err (mp, NULL); 20432 /* show the bad expression above the message */ 20433 mp_snprintf(msg, 256, "Improper %s has been replaced by 0", s); 20434@.Improper...replaced by 0@>; 20435 mp_back_error (mp, msg, hlp, true); 20436 mp_get_x_next (mp); 20437 mp_flush_cur_exp (mp, new_expr); 20438} 20439 20440 20441@ Here's what \MP\ does when \&{for}, \&{forsuffixes}, or \&{forever} 20442has just been scanned. (This code requires slight familiarity with 20443expression-parsing routines that we have not yet discussed; but it 20444seems to belong in the present part of the program, even though the 20445original author didn't write it until later. The reader may wish to 20446come back to it.) 20447 20448@c 20449void mp_begin_iteration (MP mp) { 20450 halfword m; /* |start_for| (\&{for}) or |start_forsuffixes| 20451 (\&{forsuffixes}) */ 20452 mp_sym n; /* hash address of the current symbol */ 20453 mp_loop_data *s; /* the new loop-control node */ 20454 mp_subst_list_item *p = NULL; /* substitution list for |scan_toks| 20455 */ 20456 mp_node q; /* link manipulation register */ 20457 m = cur_mod(); 20458 n = cur_sym(); 20459 s = xmalloc (1, sizeof (mp_loop_data)); 20460 s->type = s->list = s->info = s->list_start = NULL; 20461 s->link = NULL; s->var = NULL; 20462 new_number (s->value);new_number (s->old_value); 20463 new_number (s->step_size); 20464 new_number (s->final_value); 20465 if (m == start_forever) { 20466 s->type = MP_VOID; 20467 p = NULL; 20468 mp_get_x_next (mp); 20469 } else { 20470 mp_get_symbol (mp); 20471 p = xmalloc (1, sizeof (mp_subst_list_item)); 20472 p->link = NULL; 20473 p->info = cur_sym(); 20474 s->var = cur_sym(); 20475 p->info_mod = cur_sym_mod(); 20476 p->value_data = 0; 20477 if (m == start_for) { 20478 p->value_mod = mp_expr_sym; 20479 } else { /* |start_forsuffixes| */ 20480 p->value_mod = mp_suffix_sym; 20481 } 20482 mp_get_x_next (mp); 20483 if (cur_cmd() == mp_within_token) { 20484 @<Set up a picture iteration@>; 20485 } else { 20486 @<Check for the assignment in a loop header@>; 20487 @<Scan the values to be used in the loop@>; 20488 } 20489 } 20490 @<Check for the presence of a colon@>; 20491 @<Scan the loop text and put it on the loop control stack@>; 20492 mp_resume_iteration (mp); 20493} 20494 20495 20496@ @<Check for the assignment in a loop header@>= 20497if ((cur_cmd() != mp_equals) && (cur_cmd() != mp_assignment)) { 20498 const char *hlp[] = { 20499 "The next thing in this loop should have been `=' or `:='.", 20500 "But don't worry; I'll pretend that an equals sign", 20501 "was present, and I'll look for the values next.", 20502 NULL }; 20503 mp_back_error (mp, "Missing `=' has been inserted", hlp, true); 20504@.Missing `='@>; 20505} 20506 20507@ @<Check for the presence of a colon@>= 20508if (cur_cmd() != mp_colon) { 20509 const char *hlp[] = { 20510 "The next thing in this loop should have been a `:'.", 20511 "So I'll pretend that a colon was present;", 20512 "everything from here to `endfor' will be iterated.", 20513 NULL }; 20514 mp_back_error (mp, "Missing `:' has been inserted", hlp, true); 20515@.Missing `:'@>; 20516} 20517 20518@ We append a special |mp->frozen_repeat_loop| token in place of the 20519`\&{endfor}' at the end of the loop. This will come through \MP's 20520scanner at the proper time to cause the loop to be repeated. 20521 20522(If the user tries some shenanigan like `\&{for} $\ldots$ \&{let} 20523\&{endfor}', he will be foiled by the |get_symbol| routine, which 20524keeps frozen tokens unchanged. Furthermore the 20525|mp->frozen_repeat_loop| is an \&{outer} token, so it won't be lost 20526accidentally.) 20527 20528@ @<Scan the loop text...@>= 20529q = mp_get_symbolic_node (mp); 20530set_mp_sym_sym (q, mp->frozen_repeat_loop); 20531mp->scanner_status = loop_defining; 20532mp->warning_info = n; 20533s->info = mp_scan_toks (mp, mp_iteration, p, q, 0); 20534mp->scanner_status = normal; 20535s->link = mp->loop_ptr; 20536mp->loop_ptr = s 20537 20538@ @<Initialize table...@>= 20539mp->frozen_repeat_loop = 20540mp_frozen_primitive (mp, " ENDFOR", mp_repeat_loop + mp_outer_tag, 0); 20541 20542@ The loop text is inserted into \MP's scanning apparatus by the 20543|resume_iteration| routine. 20544 20545@c 20546void mp_resume_iteration (MP mp) { 20547 mp_node p, q; /* link registers */ 20548 p = mp->loop_ptr->type; 20549 if (p == PROGRESSION_FLAG) { 20550 set_cur_exp_value_number (mp->loop_ptr->value); 20551 if (@<The arithmetic progression has ended@>) { 20552 mp_stop_iteration (mp); 20553 return; 20554 } 20555 mp->cur_exp.type = mp_known; 20556 q = mp_stash_cur_exp (mp); /* make |q| an \&{expr} argument */ 20557 number_clone (mp->loop_ptr->old_value, cur_exp_value_number ()); 20558 set_number_from_addition (mp->loop_ptr->value, cur_exp_value_number (), mp->loop_ptr->step_size); 20559 /* set |value(p)| for the next iteration */ 20560 /* detect numeric overflow */ 20561 if (number_positive(mp->loop_ptr->step_size) && 20562 number_less(mp->loop_ptr->value, cur_exp_value_number ())) { 20563 if (number_positive(mp->loop_ptr->final_value)) { 20564 number_clone (mp->loop_ptr->value, mp->loop_ptr->final_value); 20565 number_add_scaled (mp->loop_ptr->final_value, -1); 20566 } else { 20567 number_clone (mp->loop_ptr->value, mp->loop_ptr->final_value); 20568 number_add_scaled (mp->loop_ptr->value, 1); 20569 } 20570 } else if (number_negative(mp->loop_ptr->step_size) && 20571 number_greater (mp->loop_ptr->value, cur_exp_value_number ())) { 20572 if (number_negative (mp->loop_ptr->final_value)) { 20573 number_clone (mp->loop_ptr->value, mp->loop_ptr->final_value); 20574 number_add_scaled (mp->loop_ptr->final_value, 1); 20575 } else { 20576 number_clone (mp->loop_ptr->value, mp->loop_ptr->final_value); 20577 number_add_scaled (mp->loop_ptr->value, -1); 20578 } 20579 } 20580 } else if (p == NULL) { 20581 p = mp->loop_ptr->list; 20582 if (p != NULL && p == mp->loop_ptr->list_start) { 20583 q = p; 20584 p = mp_link (p); 20585 mp_free_symbolic_node (mp, q); 20586 mp->loop_ptr->list = p; 20587 } 20588 if (p == NULL) { 20589 mp_stop_iteration (mp); 20590 return; 20591 } 20592 mp->loop_ptr->list = mp_link (p); 20593 q = (mp_node)mp_sym_sym (p); 20594 if (q) 20595 number_clone (mp->loop_ptr->old_value, q->data.n); 20596 mp_free_symbolic_node (mp, p); 20597 } else if (p == MP_VOID) { 20598 mp_begin_token_list (mp, mp->loop_ptr->info, (quarterword) forever_text); 20599 return; 20600 } else { 20601 @<Make |q| a capsule containing the next picture component from 20602 |loop_list(loop_ptr)| or |goto not_found|@>; 20603 } 20604 mp_begin_token_list (mp, mp->loop_ptr->info, (quarterword) loop_text); 20605 mp_stack_argument (mp, q); 20606 if (number_greater (internal_value (mp_tracing_commands), unity_t)) { 20607 @<Trace the start of a loop@>; 20608 } 20609 return; 20610NOT_FOUND: 20611 mp_stop_iteration (mp); 20612} 20613 20614 20615@ @<The arithmetic progression has ended@>= 20616(number_positive(mp->loop_ptr->step_size) && number_greater(cur_exp_value_number (), mp->loop_ptr->final_value)) 20617|| 20618(number_negative(mp->loop_ptr->step_size) && number_less(cur_exp_value_number (), mp->loop_ptr->final_value)) 20619 20620 20621@ @<Trace the start of a loop@>= 20622{ 20623 mp_begin_diagnostic (mp); 20624 mp_print_nl (mp, "{loop value="); 20625@.loop value=n@>; 20626 if ((q != NULL) && (mp_link (q) == MP_VOID)) 20627 mp_print_exp (mp, q, 1); 20628 else 20629 mp_show_token_list (mp, q, NULL, 50, 0); 20630 mp_print_char (mp, xord ('}')); 20631 mp_end_diagnostic (mp, false); 20632} 20633 20634 20635@ @<Make |q| a capsule containing the next picture component 20636from...@>= 20637{ 20638 q = mp->loop_ptr->list; 20639 if (q == NULL) 20640 goto NOT_FOUND; 20641 if ( ! is_start_or_stop(q) ) 20642 q=mp_link(q); 20643 else if ( ! is_stop(q) ) 20644 q=mp_skip_1component(mp, q); 20645 else 20646 goto NOT_FOUND; 20647 20648 set_cur_exp_node ((mp_node)mp_copy_objects (mp, mp->loop_ptr->list, q)); 20649 mp_init_bbox (mp, (mp_edge_header_node)cur_exp_node ()); 20650 mp->cur_exp.type = mp_picture_type; 20651 mp->loop_ptr->list = q; 20652 q = mp_stash_cur_exp (mp); 20653} 20654 20655 20656@ A level of loop control disappears when |resume_iteration| has 20657decided not to resume, or when an \&{exitif} construction has removed 20658the loop text from the input stack. 20659 20660@c 20661void mp_stop_iteration (MP mp) { 20662 mp_node p, q; /* the usual */ 20663 mp_loop_data *tmp; /* for free() */ 20664 p = mp->loop_ptr->type; 20665 if (p == PROGRESSION_FLAG) { 20666 mp_free_symbolic_node (mp, mp->loop_ptr->list); 20667 } else if (p == NULL) { 20668 q = mp->loop_ptr->list; 20669 while (q != NULL) { 20670 p = (mp_node)mp_sym_sym (q); 20671 if (p != NULL) { 20672 if (mp_link (p) == MP_VOID) { /* it's an \&{expr} parameter */ 20673 mp_recycle_value (mp, p); 20674 mp_free_value_node (mp, p); 20675 } else { 20676 mp_flush_token_list (mp, p); /* it's a \&{suffix} or \&{text} 20677 parameter */ 20678 } 20679 } 20680 p = q; 20681 q = mp_link (q); 20682 mp_free_symbolic_node (mp, p); 20683 } 20684 } else if (p > PROGRESSION_FLAG) { 20685 delete_edge_ref (p); 20686 } 20687 tmp = mp->loop_ptr; 20688 mp->loop_ptr = tmp->link; 20689 mp_flush_token_list (mp, tmp->info); 20690 free_number (tmp->value); 20691 free_number (tmp->step_size); 20692 free_number (tmp->final_value); 20693 xfree (tmp); 20694} 20695 20696 20697@ Now that we know all about loop control, we can finish up the 20698missing portion of |begin_iteration| and we'll be done. 20699 20700The following code is performed after the `\.=' has been scanned in a 20701\&{for} construction (if |m=start_for|) or a \&{forsuffixes} 20702construction (if |m=start_forsuffixes|). 20703 20704@<Scan the values to be used in the loop@>= 20705s->type = NULL; 20706s->list = mp_get_symbolic_node (mp); 20707s->list_start = s->list; 20708q = s->list; 20709do { 20710 mp_get_x_next (mp); 20711 if (m != start_for) { 20712 mp_scan_suffix (mp); 20713 } else { 20714 if (cur_cmd() >= mp_colon) 20715 if (cur_cmd() <= mp_comma) 20716 goto CONTINUE; 20717 mp_scan_expression (mp); 20718 if (cur_cmd() == mp_step_token) 20719 if (q == s->list) { 20720 @<Prepare for step-until construction and |break|@>; 20721 } 20722 set_cur_exp_node (mp_stash_cur_exp (mp)); 20723 } 20724 mp_link (q) = mp_get_symbolic_node (mp); 20725 q = mp_link (q); 20726 set_mp_sym_sym (q, mp->cur_exp.data.node); 20727 if (m == start_for) 20728 mp_name_type (q) = mp_expr_sym; 20729 else if (m == start_forsuffixes) 20730 mp_name_type (q) = mp_suffix_sym; 20731 mp->cur_exp.type = mp_vacuous; 20732CONTINUE: 20733 ; 20734} while (cur_cmd() == mp_comma) 20735 20736@ @<Prepare for step-until construction and |break|@>= 20737{ 20738 if (mp->cur_exp.type != mp_known) 20739 mp_bad_for (mp, "initial value"); 20740 number_clone (s->value, cur_exp_value_number ()); 20741 number_clone (s->old_value, cur_exp_value_number ()); 20742 mp_get_x_next (mp); 20743 mp_scan_expression (mp); 20744 if (mp->cur_exp.type != mp_known) 20745 mp_bad_for (mp, "step size"); 20746 number_clone (s->step_size, cur_exp_value_number ()); 20747 if (cur_cmd() != mp_until_token) { 20748 const char *hlp[] = { 20749 "I assume you meant to say `until' after `step'.", 20750 "So I'll look for the final value and colon next.", 20751 NULL }; 20752 mp_back_error (mp, "Missing `until' has been inserted", hlp, true); 20753@.Missing `until'@>; 20754 } 20755 mp_get_x_next (mp); 20756 mp_scan_expression (mp); 20757 if (mp->cur_exp.type != mp_known) 20758 mp_bad_for (mp, "final value"); 20759 number_clone (s->final_value, cur_exp_value_number ()); 20760 s->type = PROGRESSION_FLAG; 20761 break; 20762} 20763 20764 20765@ The last case is when we have just seen ``\&{within}'', and we need to 20766parse a picture expression and prepare to iterate over it. 20767 20768@<Set up a picture iteration@>= 20769{ 20770 mp_get_x_next (mp); 20771 mp_scan_expression (mp); 20772 @<Make sure the current expression is a known picture@>; 20773 s->type = mp->cur_exp.data.node; 20774 mp->cur_exp.type = mp_vacuous; 20775 q = mp_link (edge_list (mp->cur_exp.data.node)); 20776 if (q != NULL) 20777 if (is_start_or_stop (q)) 20778 if (mp_skip_1component (mp, q) == NULL) 20779 q = mp_link (q); 20780 s->list = q; 20781} 20782 20783 20784@ @<Make sure the current expression is a known picture@>= 20785if (mp->cur_exp.type != mp_picture_type) { 20786 mp_value new_expr; 20787 const char *hlp[] = { "When you say `for x in p', p must be a known picture.", NULL }; 20788 memset(&new_expr,0,sizeof(mp_value)); 20789 new_number(new_expr.data.n); 20790 new_expr.data.node = (mp_node)mp_get_edge_header_node (mp); 20791 mp_disp_err (mp, NULL); 20792 mp_back_error (mp,"Improper iteration spec has been replaced by nullpicture", hlp, true); 20793 mp_get_x_next (mp); 20794 mp_flush_cur_exp (mp, new_expr); 20795 mp_init_edges (mp, (mp_edge_header_node)mp->cur_exp.data.node); 20796 mp->cur_exp.type = mp_picture_type; 20797} 20798 20799@* File names. 20800It's time now to fret about file names. Besides the fact that different 20801operating systems treat files in different ways, we must cope with the 20802fact that completely different naming conventions are used by different 20803groups of people. The following programs show what is required for one 20804particular operating system; similar routines for other systems are not 20805difficult to devise. 20806@^system dependencies@> 20807 20808\MP\ assumes that a file name has three parts: the name proper; its 20809``extension''; and a ``file area'' where it is found in an external file 20810system. The extension of an input file is assumed to be 20811`\.{.mp}' unless otherwise specified; it is `\.{.log}' on the 20812transcript file that records each run of \MP; it is `\.{.tfm}' on the font 20813metric files that describe characters in any fonts created by \MP; it is 20814`\.{.ps}' or `.{\it nnn}' for some number {\it nnn} on the \ps\ output files. 20815The file area can be arbitrary on input files, but files are usually 20816output to the user's current area. If an input file cannot be 20817found on the specified area, \MP\ will look for it on a special system 20818area; this special area is intended for commonly used input files. 20819 20820Simple uses of \MP\ refer only to file names that have no explicit 20821extension or area. For example, a person usually says `\.{input} \.{cmr10}' 20822instead of `\.{input} \.{cmr10.new}'. Simple file 20823names are best, because they make the \MP\ source files portable; 20824whenever a file name consists entirely of letters and digits, it should be 20825treated in the same way by all implementations of \MP. However, users 20826need the ability to refer to other files in their environment, especially 20827when responding to error messages concerning unopenable files; therefore 20828we want to let them use the syntax that appears in their favorite 20829operating system. 20830 20831@ \MP\ uses the same conventions that have proved to be satisfactory for 20832\TeX\ and \MF. In order to isolate the system-dependent aspects of file names, 20833@^system dependencies@> 20834the system-independent parts of \MP\ are expressed in terms 20835of three system-dependent 20836procedures called |begin_name|, |more_name|, and |end_name|. In 20837essence, if the user-specified characters of the file name are $c_1\ldots c_n$, 20838the system-independent driver program does the operations 20839$$|begin_name|;\,|more_name|(c_1);\,\ldots\,;\,|more_name|(c_n); 20840\,|end_name|.$$ 20841These three procedures communicate with each other via global variables. 20842Afterwards the file name will appear in the string pool as three strings 20843called |cur_name|\penalty10000\hskip-.05em, 20844|cur_area|, and |cur_ext|; the latter two are NULL (i.e., 20845|""|), unless they were explicitly specified by the user. 20846 20847Actually the situation is slightly more complicated, because \MP\ needs 20848to know when the file name ends. The |more_name| routine is a function 20849(with side effects) that returns |true| on the calls |more_name|$(c_1)$, 20850\dots, |more_name|$(c_{n-1})$. The final call |more_name|$(c_n)$ 20851returns |false|; or, it returns |true| and $c_n$ is the last character 20852on the current input line. In other words, 20853|more_name| is supposed to return |true| unless it is sure that the 20854file name has been completely scanned; and |end_name| is supposed to be able 20855to finish the assembly of |cur_name|, |cur_area|, and |cur_ext| regardless of 20856whether $|more_name|(c_n)$ returned |true| or |false|. 20857 20858@<Glob...@>= 20859char *cur_name; /* name of file just scanned */ 20860char *cur_area; /* file area just scanned, or \.{""} */ 20861char *cur_ext; /* file extension just scanned, or \.{""} */ 20862 20863@ It is easier to maintain reference counts if we assign initial values. 20864 20865@<Set init...@>= 20866mp->cur_name = xstrdup (""); 20867mp->cur_area = xstrdup (""); 20868mp->cur_ext = xstrdup (""); 20869 20870@ @<Dealloc variables@>= 20871xfree (mp->cur_area); 20872xfree (mp->cur_name); 20873xfree (mp->cur_ext); 20874 20875@ The file names we shall deal with for illustrative purposes have the 20876following structure: If the name contains `\.>' or `\.:', the file area 20877consists of all characters up to and including the final such character; 20878otherwise the file area is null. If the remaining file name contains 20879`\..', the file extension consists of all such characters from the first 20880remaining `\..' to the end, otherwise the file extension is null. 20881@^system dependencies@> 20882 20883We can scan such file names easily by using two global variables that keep track 20884of the occurrences of area and extension delimiters. 20885 20886@<Glob...@>= 20887integer area_delimiter; 20888 /* most recent `\.>' or `\.:' relative to |str_start[str_ptr]| */ 20889integer ext_delimiter; /* the relevant `\..', if any */ 20890boolean quoted_filename; /* whether the filename is wrapped in " markers */ 20891 20892@ Here now is the first of the system-dependent routines for file name scanning. 20893@^system dependencies@> 20894 20895@<Declarations@>= 20896static void mp_begin_name (MP mp); 20897static boolean mp_more_name (MP mp, ASCII_code c); 20898static void mp_end_name (MP mp); 20899 20900@ @c 20901void mp_begin_name (MP mp) { 20902 xfree (mp->cur_name); 20903 xfree (mp->cur_area); 20904 xfree (mp->cur_ext); 20905 mp->area_delimiter = -1; 20906 mp->ext_delimiter = -1; 20907 mp->quoted_filename = false; 20908} 20909 20910 20911@ And here's the second. 20912@^system dependencies@> 20913 20914@c 20915#ifndef IS_DIR_SEP 20916#define IS_DIR_SEP(c) (c=='/' || c=='\\') 20917#endif 20918boolean mp_more_name (MP mp, ASCII_code c) { 20919 if (c == '"') { 20920 mp->quoted_filename = !mp->quoted_filename; 20921 } else if ((c == ' ' || c == '\t') && (mp->quoted_filename == false)) { 20922 return false; 20923 } else { 20924 if (IS_DIR_SEP (c)) { 20925 mp->area_delimiter = (integer) mp->cur_length; 20926 mp->ext_delimiter = -1; 20927 } else if (c == '.') { 20928 mp->ext_delimiter = (integer) mp->cur_length; 20929 } 20930 append_char (c); /* contribute |c| to the current string */ 20931 } 20932 return true; 20933} 20934 20935 20936@ The third. 20937@^system dependencies@> 20938 20939@d copy_pool_segment(A,B,C) { 20940 A = xmalloc(C+1,sizeof(char)); 20941 (void)memcpy(A,(char *)(mp->cur_string+B),C); 20942 A[C] = 0;} 20943 20944@c 20945void mp_end_name (MP mp) { 20946 size_t s = 0; /* length of area, name, and extension */ 20947 size_t len; 20948 /* "my/w.mp" */ 20949 if (mp->area_delimiter < 0) { 20950 mp->cur_area = xstrdup (""); 20951 } else { 20952 len = (size_t) mp->area_delimiter - s + 1; 20953 copy_pool_segment (mp->cur_area, s, len); 20954 s += len; 20955 } 20956 if (mp->ext_delimiter < 0) { 20957 mp->cur_ext = xstrdup (""); 20958 len = (unsigned) (mp->cur_length - s); 20959 } else { 20960 copy_pool_segment (mp->cur_ext, mp->ext_delimiter, 20961 (mp->cur_length - (size_t) mp->ext_delimiter)); 20962 len = (size_t) mp->ext_delimiter - s; 20963 } 20964 copy_pool_segment (mp->cur_name, s, len); 20965 mp_reset_cur_string (mp); 20966} 20967 20968 20969@ Conversely, here is a routine that takes three strings and prints a file 20970name that might have produced them. (The routine is system dependent, because 20971some operating systems put the file area last instead of first.) 20972@^system dependencies@> 20973 20974@<Basic printing...@>= 20975static void mp_print_file_name (MP mp, char *n, char *a, char *e) { 20976 boolean must_quote = false; 20977 if (((a != NULL) && (strchr (a, ' ') != NULL)) || 20978 ((n != NULL) && (strchr (n, ' ') != NULL)) || 20979 ((e != NULL) && (strchr (e, ' ') != NULL))) 20980 must_quote = true; 20981 if (must_quote) 20982 mp_print_char (mp, (ASCII_code) '"'); 20983 mp_print (mp, a); 20984 mp_print (mp, n); 20985 mp_print (mp, e); 20986 if (must_quote) 20987 mp_print_char (mp, (ASCII_code) '"'); 20988} 20989 20990 20991@ Another system-dependent routine is needed to convert three internal 20992\MP\ strings 20993to the |name_of_file| value that is used to open files. The present code 20994allows both lowercase and uppercase letters in the file name. 20995@^system dependencies@> 20996 20997@d append_to_name(A) { mp->name_of_file[k++]=(char)xchr(xord((ASCII_code)(A))); } 20998 20999@ @c 21000void mp_pack_file_name (MP mp, const char *n, const char *a, const char *e) { 21001 integer k; /* number of positions filled in |name_of_file| */ 21002 const char *j; /* a character index */ 21003 size_t slen; 21004 k = 0; 21005 assert (n != NULL); 21006 xfree (mp->name_of_file); 21007 slen = strlen (n) + 1; 21008 if (a != NULL) 21009 slen += strlen (a); 21010 if (e != NULL) 21011 slen += strlen (e); 21012 mp->name_of_file = xmalloc (slen, 1); 21013 if (a != NULL) { 21014 for (j = a; *j != '\0'; j++) { 21015 append_to_name (*j); 21016 } 21017 } 21018 for (j = n; *j != '\0'; j++) { 21019 append_to_name (*j); 21020 } 21021 if (e != NULL) { 21022 for (j = e; *j != '\0'; j++) { 21023 append_to_name (*j); 21024 } 21025 } 21026 mp->name_of_file[k] = 0; 21027} 21028 21029 21030@ @<Internal library declarations@>= 21031void mp_pack_file_name (MP mp, const char *n, const char *a, const char *e); 21032 21033@ @<Option variables@>= 21034char *mem_name; /* for commandline */ 21035 21036@ Stripping a |.mem| extension here is for backward compatibility. 21037 21038@<Find and load preload file, if required@>= 21039if (!opt->ini_version) { 21040 mp->mem_name = xstrdup (opt->mem_name); 21041 if (mp->mem_name) { 21042 size_t l = strlen (mp->mem_name); 21043 if (l > 4) { 21044 char *test = strstr (mp->mem_name, ".mem"); 21045 if (test == mp->mem_name + l - 4) { 21046 *test = 0; 21047 } 21048 } 21049 } 21050 if (mp->mem_name != NULL) { 21051 if (!mp_open_mem_file (mp)) { 21052 mp->history = mp_fatal_error_stop; 21053 mp_jump_out (mp); 21054 } 21055 } 21056} 21057 21058 21059 21060@ @<Dealloc variables@>= 21061xfree (mp->mem_name); 21062 21063@ This part of the program becomes active when a ``virgin'' \MP\ is 21064trying to get going, just after the preliminary initialization. 21065The buffer contains the first line of input in |buffer[loc..(last-1)]|, 21066where |loc<last| and |buffer[loc]<>""|. 21067 21068@<Declarations@>= 21069static boolean mp_open_mem_name (MP mp); 21070static boolean mp_open_mem_file (MP mp); 21071 21072@ @c 21073boolean mp_open_mem_name (MP mp) { 21074 if (mp->mem_name != NULL) { 21075 size_t l = strlen (mp->mem_name); 21076 char *s = xstrdup (mp->mem_name); 21077 if (l > 4) { 21078 char *test = strstr (s, ".mp"); 21079 if (test == NULL || test != s + l - 4) { 21080 s = xrealloc (s, l + 5, 1); 21081 strcat (s, ".mp"); 21082 } 21083 } else { 21084 s = xrealloc (s, l + 5, 1); 21085 strcat (s, ".mp"); 21086 } 21087 s = (mp->find_file) (mp, s, "r", mp_filetype_program); 21088 xfree(mp->name_of_file); 21089 if (s == NULL) 21090 return false; 21091 mp->name_of_file = xstrdup(s); 21092 mp->mem_file = (mp->open_file) (mp, s, "r", mp_filetype_program); 21093 free (s); 21094 if (mp->mem_file) 21095 return true; 21096 } 21097 return false; 21098} 21099boolean mp_open_mem_file (MP mp) { 21100 if (mp->mem_file != NULL) 21101 return true; 21102 if (mp_open_mem_name (mp)) 21103 return true; 21104 if (mp_xstrcmp (mp->mem_name, "plain")) { 21105 wake_up_terminal(); 21106 wterm ("Sorry, I can\'t find the '"); 21107 wterm (mp->mem_name); 21108 wterm ("' preload file; will try 'plain'."); 21109 wterm_cr; 21110@.Sorry, I can't find...@>; 21111 update_terminal(); 21112 /* now pull out all the stops: try for the system \.{plain} file */ 21113 xfree (mp->mem_name); 21114 mp->mem_name = xstrdup ("plain"); 21115 if (mp_open_mem_name (mp)) 21116 return true; 21117 } 21118 wake_up_terminal(); 21119 wterm_ln ("I can't find the 'plain' preload file!\n"); 21120@.I can't find PLAIN...@> 21121@.plain@>; 21122 return false; 21123} 21124 21125 21126@ Operating systems often make it possible to determine the exact name (and 21127possible version number) of a file that has been opened. The following routine, 21128which simply makes a \MP\ string from the value of |name_of_file|, should 21129ideally be changed to deduce the full name of file~|f|, which is the file 21130most recently opened, if it is possible to do this. 21131@^system dependencies@> 21132 21133@ @c 21134static mp_string mp_make_name_string (MP mp) { 21135 int k; /* index into |name_of_file| */ 21136 int name_length = (int) strlen (mp->name_of_file); 21137 str_room (name_length); 21138 for (k = 0; k < name_length; k++) { 21139 append_char (xord ((ASCII_code) mp->name_of_file[k])); 21140 } 21141 return mp_make_string (mp); 21142} 21143 21144 21145@ Now let's consider the ``driver'' 21146routines by which \MP\ deals with file names 21147in a system-independent manner. First comes a procedure that looks for a 21148file name in the input by taking the information from the input buffer. 21149(We can't use |get_next|, because the conversion to tokens would 21150destroy necessary information.) 21151 21152This procedure doesn't allow semicolons or percent signs to be part of 21153file names, because of other conventions of \MP. 21154{\sl The {\logos METAFONT\/}book} doesn't 21155use semicolons or percents immediately after file names, but some users 21156no doubt will find it natural to do so; therefore system-dependent 21157changes to allow such characters in file names should probably 21158be made with reluctance, and only when an entire file name that 21159includes special characters is ``quoted'' somehow. 21160@^system dependencies@> 21161 21162@c 21163static void mp_scan_file_name (MP mp) { 21164 mp_begin_name (mp); 21165 while (mp->buffer[loc] == ' ') 21166 incr (loc); 21167 while (1) { 21168 if ((mp->buffer[loc] == ';') || (mp->buffer[loc] == '%')) 21169 break; 21170 if (!mp_more_name (mp, mp->buffer[loc])) 21171 break; 21172 incr (loc); 21173 } 21174 mp_end_name (mp); 21175} 21176 21177 21178@ Here is another version that takes its input from a string. 21179 21180@<Declare subroutines for parsing file names@>= 21181void mp_str_scan_file (MP mp, mp_string s); 21182 21183@ @c 21184void mp_str_scan_file (MP mp, mp_string s) { 21185 size_t p, q; /* current position and stopping point */ 21186 mp_begin_name (mp); 21187 p = 0; 21188 q = s->len; 21189 while (p < q) { 21190 if (!mp_more_name (mp, *(s->str + p))) 21191 break; 21192 incr (p); 21193 } 21194 mp_end_name (mp); 21195} 21196 21197 21198@ And one that reads from a |char*|. 21199 21200@<Declare subroutines for parsing file names@>= 21201extern void mp_ptr_scan_file (MP mp, char *s); 21202 21203@ @c 21204void mp_ptr_scan_file (MP mp, char *s) { 21205 char *p, *q; /* current position and stopping point */ 21206 mp_begin_name (mp); 21207 p = s; 21208 q = p + strlen (s); 21209 while (p < q) { 21210 if (!mp_more_name (mp, (ASCII_code) (*p))) 21211 break; 21212 p++; 21213 } 21214 mp_end_name (mp); 21215} 21216 21217 21218@ The option variable |job_name| contains the file name that was first 21219\&{input} by the user. This name is used to initialize the |job_name| global 21220as well as the |mp_job_name| internal, and is extended by `\.{.log}' and 21221`\.{ps}' and `\.{.mem}' and `\.{.tfm}' in order to make the names of \MP's 21222output files. 21223 21224@<Glob...@>= 21225boolean log_opened; /* has the transcript file been opened? */ 21226char *log_name; /* full name of the log file */ 21227 21228@ @<Option variables@>= 21229char *job_name; /* principal file name */ 21230 21231@ Initially |job_name=NULL|; it becomes nonzero as soon as the true name is known. 21232We have |job_name=NULL| if and only if the `\.{log}' file has not been opened, 21233except of course for a short time just after |job_name| has become nonzero. 21234 21235@<Allocate or ...@>= 21236mp->job_name = mp_xstrdup (mp, opt->job_name); 21237/*| 21238if (mp->job_name != NULL) { 21239 char *s = mp->job_name + strlen (mp->job_name); 21240 while (s > mp->job_name) { 21241 if (*s == '.') { 21242 *s = '\0'; 21243 } 21244 s--; 21245 } 21246} 21247|*/ 21248if (opt->noninteractive) { 21249 if (mp->job_name == NULL) 21250 mp->job_name = mp_xstrdup (mp, mp->mem_name); 21251} 21252mp->log_opened = false; 21253 21254@ Cannot do this earlier because at the |<Allocate or ...>|, the string 21255pool is not yet initialized. 21256 21257@<Fix up |mp->internal[mp_job_name]|@>= 21258if (mp->job_name != NULL) { 21259 if (internal_string (mp_job_name) != 0) 21260 delete_str_ref (internal_string (mp_job_name)); 21261 set_internal_string (mp_job_name, mp_rts (mp, mp->job_name)); 21262} 21263 21264@ @<Dealloc variables@>= 21265xfree (mp->job_name); 21266 21267@ Here is a routine that manufactures the output file names, assuming that 21268|job_name<>0|. It ignores and changes the current settings of |cur_area| 21269and |cur_ext|. 21270 21271@d pack_cur_name mp_pack_file_name(mp, mp->cur_name,mp->cur_area,mp->cur_ext) 21272 21273@<Internal library ...@>= 21274void mp_pack_job_name (MP mp, const char *s); 21275 21276@ @c 21277void mp_pack_job_name (MP mp, const char *s) { /* |s = ".log"|, |".mem"|, |".ps"|, or .\\{nnn} */ 21278 xfree (mp->cur_name); 21279 mp->cur_name = xstrdup (mp->job_name); 21280 xfree (mp->cur_area); 21281 mp->cur_area = xstrdup (""); 21282 xfree (mp->cur_ext); 21283 mp->cur_ext = xstrdup (s); 21284 pack_cur_name; 21285} 21286 21287 21288@ If some trouble arises when \MP\ tries to open a file, the following 21289routine calls upon the user to supply another file name. Parameter~|s| 21290is used in the error message to identify the type of file; parameter~|e| 21291is the default extension if none is given. Upon exit from the routine, 21292variables |cur_name|, |cur_area|, |cur_ext|, and |name_of_file| are 21293ready for another attempt at file opening. 21294 21295@<Internal library ...@>= 21296void mp_prompt_file_name (MP mp, const char *s, const char *e); 21297 21298@ @c 21299void mp_prompt_file_name (MP mp, const char *s, const char *e) { 21300 size_t k; /* index into |buffer| */ 21301 char *saved_cur_name; 21302 if (mp->interaction == mp_scroll_mode) 21303 wake_up_terminal(); 21304 if (strcmp (s, "input file name") == 0) { 21305 mp_print_err (mp, "I can\'t open file `"); 21306@.I can't find file x@> 21307 } else { 21308 mp_print_err (mp, "I can\'t write on file `"); 21309@.I can't write on file x@> 21310 } 21311 if (strcmp (s, "file name for output") == 0) { 21312 mp_print (mp, mp->name_of_file); 21313 } else { 21314 mp_print_file_name (mp, mp->cur_name, mp->cur_area, mp->cur_ext); 21315 } 21316 mp_print (mp, "'."); 21317 if (strcmp (e, "") == 0) 21318 mp_show_context (mp); 21319 mp_print_nl (mp, "Please type another "); 21320 mp_print (mp, s); 21321@.Please type...@>; 21322 if (mp->noninteractive || mp->interaction < mp_scroll_mode) 21323 mp_fatal_error (mp, "*** (job aborted, file error in nonstop mode)"); 21324@.job aborted, file error...@>; 21325 saved_cur_name = xstrdup (mp->cur_name); 21326 clear_terminal(); 21327 prompt_input (": "); 21328 @<Scan file name in the buffer@>; 21329 if (strcmp (mp->cur_ext, "") == 0) 21330 mp->cur_ext = xstrdup (e); 21331 if (strlen (mp->cur_name) == 0) { 21332 mp->cur_name = saved_cur_name; 21333 } else { 21334 xfree (saved_cur_name); 21335 } 21336 pack_cur_name; 21337} 21338 21339 21340@ @<Scan file name in the buffer@>= 21341{ 21342 mp_begin_name (mp); 21343 k = mp->first; 21344 while ((mp->buffer[k] == ' ') && (k < mp->last)) 21345 incr (k); 21346 while (1) { 21347 if (k == mp->last) 21348 break; 21349 if (!mp_more_name (mp, mp->buffer[k])) 21350 break; 21351 incr (k); 21352 } 21353 mp_end_name (mp); 21354} 21355 21356 21357@ The |open_log_file| routine is used to open the transcript file and to help 21358it catch up to what has previously been printed on the terminal. 21359 21360@c 21361void mp_open_log_file (MP mp) { 21362 unsigned old_setting; /* previous |selector| setting */ 21363 int k; /* index into |months| and |buffer| */ 21364 int l; /* end of first input line */ 21365 integer m; /* the current month */ 21366 const char *months = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"; 21367 /* abbreviations of month names */ 21368 if (mp->log_opened) 21369 return; 21370 old_setting = mp->selector; 21371 if (mp->job_name == NULL) { 21372 mp->job_name = xstrdup ("mpout"); 21373 @<Fix up |mp->internal[mp_job_name]|@>; 21374 } 21375 mp_pack_job_name (mp, ".log"); 21376 while (!mp_open_out (mp, &mp->log_file, mp_filetype_log)) { 21377 @<Try to get a different log file name@>; 21378 } 21379 mp->log_name = xstrdup (mp->name_of_file); 21380 mp->selector = log_only; 21381 mp->log_opened = true; 21382 @<Print the banner line, including the date and time@>; 21383 mp->input_stack[mp->input_ptr] = mp->cur_input; 21384 /* make sure bottom level is in memory */ 21385 if (!mp->noninteractive) { 21386 mp_print_nl (mp, "**"); 21387@.**@>; 21388 l = mp->input_stack[0].limit_field - 1; /* last position of first line */ 21389 for (k = 0; k <= l; k++) 21390 mp_print_char (mp, mp->buffer[k]); 21391 mp_print_ln (mp); /* now the transcript file contains the first line of input */ 21392 } 21393 mp->selector = old_setting + 2; /* |log_only| or |term_and_log| */ 21394} 21395 21396 21397@ @<Dealloc variables@>= 21398xfree (mp->log_name); 21399 21400@ Sometimes |open_log_file| is called at awkward moments when \MP\ is 21401unable to print error messages or even to |show_context|. 21402The |prompt_file_name| routine can result in a |fatal_error|, but the |error| 21403routine will not be invoked because |log_opened| will be false. 21404 21405The normal idea of |mp_batch_mode| is that nothing at all should be written 21406on the terminal. However, in the unusual case that 21407no log file could be opened, we make an exception and allow 21408an explanatory message to be seen. 21409 21410Incidentally, the program always refers to the log file as a `\.{transcript 21411file}', because some systems cannot use the extension `\.{.log}' for 21412this file. 21413 21414@<Try to get a different log file name@>= 21415{ 21416 mp->selector = term_only; 21417 mp_prompt_file_name (mp, "transcript file name", ".log"); 21418} 21419 21420 21421@ @<Print the banner...@>= 21422{ 21423 wlog (mp->banner); 21424 mp_print (mp, " "); 21425 mp_print_int (mp, round_unscaled (internal_value (mp_day))); 21426 mp_print_char (mp, xord (' ')); 21427 m = round_unscaled (internal_value (mp_month)); 21428 for (k = 3 * m - 3; k < 3 * m; k++) { 21429 wlog_chr ((unsigned char) months[k]); 21430 } 21431 mp_print_char (mp, xord (' ')); 21432 mp_print_int (mp, round_unscaled (internal_value (mp_year))); 21433 mp_print_char (mp, xord (' ')); 21434 mp_print_dd (mp, round_unscaled (internal_value (mp_hour))); 21435 mp_print_char (mp, xord (':')); 21436 mp_print_dd (mp, round_unscaled (internal_value (mp_minute))); 21437} 21438 21439 21440@ The |try_extension| function tries to open an input file determined by 21441|cur_name|, |cur_area|, and the argument |ext|. It returns |false| if it 21442can't find the file in |cur_area| or the appropriate system area. 21443 21444@c 21445static boolean mp_try_extension (MP mp, const char *ext) { 21446 mp_pack_file_name (mp, mp->cur_name, mp->cur_area, ext); 21447 in_name = xstrdup (mp->cur_name); 21448 in_area = xstrdup (mp->cur_area); 21449 in_ext = xstrdup (ext); 21450 if (mp_open_in (mp, &cur_file, mp_filetype_program)) { 21451 return true; 21452 } else { 21453 mp_pack_file_name (mp, mp->cur_name, NULL, ext); 21454 return mp_open_in (mp, &cur_file, mp_filetype_program); 21455 } 21456} 21457 21458 21459@ Let's turn now to the procedure that is used to initiate file reading 21460when an `\.{input}' command is being processed. 21461 21462@c 21463void mp_start_input (MP mp) { /* \MP\ will \.{input} something */ 21464 char *fname = NULL; 21465 @<Put the desired file name in |(cur_name,cur_ext,cur_area)|@>; 21466 while (1) { 21467 mp_begin_file_reading (mp); /* set up |cur_file| and new level of input */ 21468 if (strlen (mp->cur_ext) == 0) { 21469 if (mp_try_extension (mp, ".mp")) 21470 break; 21471 else if (mp_try_extension (mp, "")) 21472 break; 21473 else if (mp_try_extension (mp, ".mf")) 21474 break; 21475 } else if (mp_try_extension (mp, mp->cur_ext)) { 21476 break; 21477 } 21478 mp_end_file_reading (mp); /* remove the level that didn't work */ 21479 mp_prompt_file_name (mp, "input file name", ""); 21480 } 21481 name = mp_make_name_string (mp); 21482 fname = xstrdup (mp->name_of_file); 21483 if (mp->job_name == NULL) { 21484 mp->job_name = xstrdup (mp->cur_name); 21485 @<Fix up |mp->internal[mp_job_name]|@>; 21486 } 21487 if (!mp->log_opened) { 21488 mp_open_log_file (mp); 21489 } /* |open_log_file| doesn't |show_context|, so |limit| 21490 and |loc| needn't be set to meaningful values yet */ 21491 if (((int) mp->term_offset + (int) strlen (fname)) > (mp->max_print_line - 2)) 21492 mp_print_ln (mp); 21493 else if ((mp->term_offset > 0) || (mp->file_offset > 0)) 21494 mp_print_char (mp, xord (' ')); 21495 mp_print_char (mp, xord ('(')); 21496 incr (mp->open_parens); 21497 mp_print (mp, fname); 21498 xfree (fname); 21499 update_terminal(); 21500 @<Flush |name| and replace it with |cur_name| if it won't be needed@>; 21501 @<Read the first line of the new file@>; 21502} 21503 21504 21505@ This code should be omitted if |make_name_string| returns something other 21506than just a copy of its argument and the full file name is needed for opening 21507\.{MPX} files or implementing the switch-to-editor option. 21508@^system dependencies@> 21509 21510@<Flush |name| and replace it with |cur_name| if it won't be needed@>= 21511mp_flush_string (mp, name); 21512name = mp_rts (mp, mp->cur_name); 21513xfree (mp->cur_name) 21514 21515 21516@ If the file is empty, it is considered to contain a single blank line, 21517so there is no need to test the return value. 21518 21519@<Read the first line...@>= 21520{ 21521 line = 1; 21522 (void) mp_input_ln (mp, cur_file); 21523 mp_firm_up_the_line (mp); 21524 mp->buffer[limit] = xord ('%'); 21525 mp->first = (size_t) (limit + 1); 21526 loc = start; 21527} 21528 21529 21530@ @<Put the desired file name in |(cur_name,cur_ext,cur_area)|@>= 21531while (token_state && (nloc == NULL)) 21532 mp_end_token_list (mp); 21533if (token_state) { 21534 const char *hlp[] = { 21535 "Sorry...I've converted what follows to tokens,", 21536 "possibly garbaging the name you gave.", 21537 "Please delete the tokens and insert the name again.", 21538 NULL }; 21539 mp_error (mp, "File names can't appear within macros", hlp, true); 21540@.File names can't...@>; 21541} 21542if (file_state) { 21543 mp_scan_file_name (mp); 21544} else { 21545 xfree (mp->cur_name); 21546 mp->cur_name = xstrdup (""); 21547 xfree (mp->cur_ext); 21548 mp->cur_ext = xstrdup (""); 21549 xfree (mp->cur_area); 21550 mp->cur_area = xstrdup (""); 21551} 21552 21553 21554@ The following simple routine starts reading the \.{MPX} file associated 21555with the current input file. 21556 21557@c 21558void mp_start_mpx_input (MP mp) { 21559 char *origname = NULL; /* a copy of nameoffile */ 21560 mp_pack_file_name (mp, in_name, in_area, in_ext); 21561 origname = xstrdup (mp->name_of_file); 21562 mp_pack_file_name (mp, in_name, in_area, ".mpx"); 21563 if (!(mp->run_make_mpx) (mp, origname, mp->name_of_file)) 21564 goto NOT_FOUND; 21565 mp_begin_file_reading (mp); 21566 if (!mp_open_in (mp, &cur_file, mp_filetype_program)) { 21567 mp_end_file_reading (mp); 21568 goto NOT_FOUND; 21569 } 21570 name = mp_make_name_string (mp); 21571 mp->mpx_name[iindex] = name; 21572 add_str_ref (name); 21573 @<Read the first line of the new file@>; 21574 xfree (origname); 21575 return; 21576NOT_FOUND: 21577 @<Explain that the \.{MPX} file can't be read and |succumb|@>; 21578 xfree (origname); 21579} 21580 21581 21582@ This should ideally be changed to do whatever is necessary to create the 21583\.{MPX} file given by |name_of_file| if it does not exist or if it is out 21584of date. This requires invoking \.{MPtoTeX} on the |origname| and passing 21585the results through \TeX\ and \.{DVItoMP}. (It is possible to use a 21586completely different typesetting program if suitable postprocessor is 21587available to perform the function of \.{DVItoMP}.) 21588@^system dependencies@> 21589 21590@ @<Exported types@>= 21591typedef int (*mp_makempx_cmd) (MP mp, char *origname, char *mtxname); 21592 21593@ @<Option variables@>= 21594mp_makempx_cmd run_make_mpx; 21595 21596@ @<Allocate or initialize ...@>= 21597set_callback_option (run_make_mpx); 21598 21599@ @<Declarations@>= 21600static int mp_run_make_mpx (MP mp, char *origname, char *mtxname); 21601 21602@ The default does nothing. 21603@c 21604int mp_run_make_mpx (MP mp, char *origname, char *mtxname) { 21605 (void) mp; 21606 (void) origname; 21607 (void) mtxname; 21608 return false; 21609} 21610 21611 21612@ @<Explain that the \.{MPX} file can't be read and |succumb|@>= 21613{ 21614 const char *hlp[] = { 21615 "The two files given above are one of your source files", 21616 "and an auxiliary file I need to read to find out what your", 21617 "btex..etex blocks mean. If you don't know why I had trouble,", 21618 "try running it manually through MPtoTeX, TeX, and DVItoMP", 21619 NULL }; 21620 if (mp->interaction == mp_error_stop_mode) 21621 wake_up_terminal(); 21622 mp_print_nl (mp, ">> "); 21623 mp_print (mp, origname); 21624 mp_print_nl (mp, ">> "); 21625 mp_print (mp, mp->name_of_file); 21626 xfree (origname); 21627 if ( mp->interaction==mp_error_stop_mode ) 21628 mp->interaction=mp_scroll_mode; /* no more interaction */ 21629 if ( mp->log_opened ) 21630 mp_error(mp, "! Unable to read mpx file", hlp, true); 21631 mp->history=mp_fatal_error_stop; 21632 mp_jump_out(mp); /* irrecoverable error */ 21633} 21634 21635@ The last file-opening commands are for files accessed via the \&{readfrom} 21636@:read_from_}{\&{readfrom} primitive@> 21637operator and the \&{write} command. Such files are stored in separate arrays. 21638@:write_}{\&{write} primitive@> 21639 21640@<Types in the outer block@>= 21641typedef unsigned int readf_index; /* |0..max_read_files| */ 21642typedef unsigned int write_index; /* |0..max_write_files| */ 21643 21644@ @<Glob...@>= 21645readf_index max_read_files; /* maximum number of simultaneously open \&{readfrom} files */ 21646void **rd_file; /* \&{readfrom} files */ 21647char **rd_fname; /* corresponding file name or 0 if file not open */ 21648readf_index read_files; /* number of valid entries in the above arrays */ 21649write_index max_write_files; /* maximum number of simultaneously open \&{write} */ 21650void **wr_file; /* \&{write} files */ 21651char **wr_fname; /* corresponding file name or 0 if file not open */ 21652write_index write_files; /* number of valid entries in the above arrays */ 21653 21654@ @<Allocate or initialize ...@>= 21655mp->max_read_files = 8; 21656mp->rd_file = xmalloc ((mp->max_read_files + 1), sizeof (void *)); 21657mp->rd_fname = xmalloc ((mp->max_read_files + 1), sizeof (char *)); 21658memset (mp->rd_fname, 0, sizeof (char *) * (mp->max_read_files + 1)); 21659mp->max_write_files = 8; 21660mp->wr_file = xmalloc ((mp->max_write_files + 1), sizeof (void *)); 21661mp->wr_fname = xmalloc ((mp->max_write_files + 1), sizeof (char *)); 21662memset (mp->wr_fname, 0, sizeof (char *) * (mp->max_write_files + 1)); 21663 21664 21665@ This routine starts reading the file named by string~|s| without setting 21666|loc|, |limit|, or |name|. It returns |false| if the file is empty or cannot 21667be opened. Otherwise it updates |rd_file[n]| and |rd_fname[n]|. 21668 21669@c 21670static boolean mp_start_read_input (MP mp, char *s, readf_index n) { 21671 mp_ptr_scan_file (mp, s); 21672 pack_cur_name; 21673 mp_begin_file_reading (mp); 21674 if (!mp_open_in (mp, &mp->rd_file[n], (int) (mp_filetype_text + n))) 21675 goto NOT_FOUND; 21676 if (!mp_input_ln (mp, mp->rd_file[n])) { 21677 (mp->close_file) (mp, mp->rd_file[n]); 21678 goto NOT_FOUND; 21679 } 21680 mp->rd_fname[n] = xstrdup (s); 21681 return true; 21682NOT_FOUND: 21683 mp_end_file_reading (mp); 21684 return false; 21685} 21686 21687 21688@ Open |wr_file[n]| using file name~|s| and update |wr_fname[n]|. 21689 21690@<Declarations@>= 21691static void mp_open_write_file (MP mp, char *s, readf_index n); 21692 21693@ @c 21694void mp_open_write_file (MP mp, char *s, readf_index n) { 21695 mp_ptr_scan_file (mp, s); 21696 pack_cur_name; 21697 while (!mp_open_out (mp, &mp->wr_file[n], (int) (mp_filetype_text + n))) 21698 mp_prompt_file_name (mp, "file name for write output", ""); 21699 mp->wr_fname[n] = xstrdup (s); 21700} 21701 21702 21703@* Introduction to the parsing routines. 21704We come now to the central nervous system that sparks many of \MP's activities. 21705By evaluating expressions, from their primary constituents to ever larger 21706subexpressions, \MP\ builds the structures that ultimately define complete 21707pictures or fonts of type. 21708 21709Four mutually recursive subroutines are involved in this process: We call them 21710$$\hbox{|scan_primary|, |scan_secondary|, |scan_tertiary|, 21711and |scan_expression|.}$$ 21712@^recursion@> 21713Each of them is parameterless and begins with the first token to be scanned 21714already represented in |cur_cmd|, |cur_mod|, and |cur_sym|. After execution, 21715the value of the primary or secondary or tertiary or expression that was 21716found will appear in the global variables |cur_type| and |cur_exp|. The 21717token following the expression will be represented in |cur_cmd|, |cur_mod|, 21718and |cur_sym|. 21719 21720Technically speaking, the parsing algorithms are ``LL(1),'' more or less; 21721backup mechanisms have been added in order to provide reasonable error 21722recovery. 21723 21724@d cur_exp_value_boolean() number_to_int (mp->cur_exp.data.n) 21725@d cur_exp_value_number() mp->cur_exp.data.n 21726@d cur_exp_node() mp->cur_exp.data.node 21727@d cur_exp_str() mp->cur_exp.data.str 21728@d cur_exp_knot() mp->cur_exp.data.p 21729 21730@d set_cur_exp_value_scaled(A) do { 21731 if (cur_exp_str()) { 21732 delete_str_ref(cur_exp_str()); 21733 } 21734 set_number_from_scaled (mp->cur_exp.data.n, (A)); 21735 cur_exp_node() = NULL; 21736 cur_exp_str() = NULL; 21737 cur_exp_knot() = NULL; 21738 } while (0) 21739@d set_cur_exp_value_boolean(A) do { 21740 if (cur_exp_str()) { 21741 delete_str_ref(cur_exp_str()); 21742 } 21743 set_number_from_int (mp->cur_exp.data.n, (A)); 21744 cur_exp_node() = NULL; 21745 cur_exp_str() = NULL; 21746 cur_exp_knot() = NULL; 21747 } while (0) 21748@d set_cur_exp_value_number(A) do { 21749 if (cur_exp_str()) { 21750 delete_str_ref(cur_exp_str()); 21751 } 21752 number_clone (mp->cur_exp.data.n, (A)); 21753 cur_exp_node() = NULL; 21754 cur_exp_str() = NULL; 21755 cur_exp_knot() = NULL; 21756 } while (0) 21757@d set_cur_exp_node(A) do { 21758 if (cur_exp_str()) { 21759 delete_str_ref(cur_exp_str()); 21760 } 21761 cur_exp_node() = A; 21762 cur_exp_str() = NULL; 21763 cur_exp_knot() = NULL; 21764 set_number_to_zero (mp->cur_exp.data.n); 21765 } while (0) 21766@d set_cur_exp_str(A) do { 21767 if (cur_exp_str()) { 21768 delete_str_ref(cur_exp_str()); 21769 } 21770 cur_exp_str() = A; 21771 add_str_ref(cur_exp_str()); 21772 cur_exp_node() = NULL; 21773 cur_exp_knot() = NULL; 21774 set_number_to_zero (mp->cur_exp.data.n); 21775 } while (0) 21776@d set_cur_exp_knot(A) do { 21777 if (cur_exp_str()) { 21778 delete_str_ref(cur_exp_str()); 21779 } 21780 cur_exp_knot() = A; 21781 cur_exp_node() = NULL; 21782 cur_exp_str() = NULL; 21783 set_number_to_zero (mp->cur_exp.data.n); 21784 } while (0) 21785 21786 21787@ @<Glob...@>= 21788mp_value cur_exp; /* the value of the expression just found */ 21789 21790@ @<Set init...@>= 21791memset (&mp->cur_exp.data, 0, sizeof (mp_value)); 21792new_number(mp->cur_exp.data.n); 21793 21794@ @<Free table ...@>= 21795free_number(mp->cur_exp.data.n); 21796 21797@ Many different kinds of expressions are possible, so it is wise to have 21798precise descriptions of what |cur_type| and |cur_exp| mean in all cases: 21799 21800\smallskip\hang 21801|cur_type=mp_vacuous| means that this expression didn't turn out to have a 21802value at all, because it arose from a \&{begingroup}$\,\ldots\,$\&{endgroup} 21803construction in which there was no expression before the \&{endgroup}. 21804In this case |cur_exp| has some irrelevant value. 21805 21806\smallskip\hang 21807|cur_type=mp_boolean_type| means that |cur_exp| is either |true_code| 21808or |false_code|. 21809 21810\smallskip\hang 21811|cur_type=mp_unknown_boolean| means that |cur_exp| points to a capsule 21812node that is in 21813a ring of equivalent booleans whose value has not yet been defined. 21814 21815\smallskip\hang 21816|cur_type=mp_string_type| means that |cur_exp| is a string number (i.e., an 21817integer in the range |0<=cur_exp<str_ptr|). That string's reference count 21818includes this particular reference. 21819 21820\smallskip\hang 21821|cur_type=mp_unknown_string| means that |cur_exp| points to a capsule 21822node that is in 21823a ring of equivalent strings whose value has not yet been defined. 21824 21825\smallskip\hang 21826|cur_type=mp_pen_type| means that |cur_exp| points to a node in a pen. Nobody 21827else points to any of the nodes in this pen. The pen may be polygonal or 21828elliptical. 21829 21830\smallskip\hang 21831|cur_type=mp_unknown_pen| means that |cur_exp| points to a capsule 21832node that is in 21833a ring of equivalent pens whose value has not yet been defined. 21834 21835\smallskip\hang 21836|cur_type=mp_path_type| means that |cur_exp| points to a the first node of 21837a path; nobody else points to this particular path. The control points of 21838the path will have been chosen. 21839 21840\smallskip\hang 21841|cur_type=mp_unknown_path| means that |cur_exp| points to a capsule 21842node that is in 21843a ring of equivalent paths whose value has not yet been defined. 21844 21845\smallskip\hang 21846|cur_type=mp_picture_type| means that |cur_exp| points to an edge header node. 21847There may be other pointers to this particular set of edges. The header node 21848contains a reference count that includes this particular reference. 21849 21850\smallskip\hang 21851|cur_type=mp_unknown_picture| means that |cur_exp| points to a capsule 21852node that is in 21853a ring of equivalent pictures whose value has not yet been defined. 21854 21855\smallskip\hang 21856|cur_type=mp_transform_type| means that |cur_exp| points to a |mp_transform_type| 21857capsule node. The |value| part of this capsule 21858points to a transform node that contains six numeric values, 21859each of which is |independent|, |dependent|, |mp_proto_dependent|, or |known|. 21860 21861\smallskip\hang 21862|cur_type=mp_color_type| means that |cur_exp| points to a |color_type| 21863capsule node. The |value| part of this capsule 21864points to a color node that contains three numeric values, 21865each of which is |independent|, |dependent|, |mp_proto_dependent|, or |known|. 21866 21867\smallskip\hang 21868|cur_type=mp_cmykcolor_type| means that |cur_exp| points to a |mp_cmykcolor_type| 21869capsule node. The |value| part of this capsule 21870points to a color node that contains four numeric values, 21871each of which is |independent|, |dependent|, |mp_proto_dependent|, or |known|. 21872 21873\smallskip\hang 21874|cur_type=mp_pair_type| means that |cur_exp| points to a capsule 21875node whose type is |mp_pair_type|. The |value| part of this capsule 21876points to a pair node that contains two numeric values, 21877each of which is |independent|, |dependent|, |mp_proto_dependent|, or |known|. 21878 21879\smallskip\hang 21880|cur_type=mp_known| means that |cur_exp| is a |scaled| value. 21881 21882\smallskip\hang 21883|cur_type=mp_dependent| means that |cur_exp| points to a capsule node whose type 21884is |dependent|. The |dep_list| field in this capsule points to the associated 21885dependency list. 21886 21887\smallskip\hang 21888|cur_type=mp_proto_dependent| means that |cur_exp| points to a |mp_proto_dependent| 21889capsule node. The |dep_list| field in this capsule 21890points to the associated dependency list. 21891 21892\smallskip\hang 21893|cur_type=independent| means that |cur_exp| points to a capsule node 21894whose type is |independent|. This somewhat unusual case can arise, for 21895example, in the expression 21896`$x+\&{begingroup}\penalty0\,\&{string}\,x; 0\,\&{endgroup}$'. 21897 21898\smallskip\hang 21899|cur_type=mp_token_list| means that |cur_exp| points to a linked list of 21900tokens. 21901 21902\smallskip\noindent 21903The possible settings of |cur_type| have been listed here in increasing 21904numerical order. Notice that |cur_type| will never be |mp_numeric_type| or 21905|suffixed_macro| or |mp_unsuffixed_macro|, although variables of those types 21906are allowed. Conversely, \MP\ has no variables of type |mp_vacuous| or 21907|token_list|. 21908 21909@ Capsules are non-symbolic nodes that have a similar meaning 21910to |cur_type| and |cur_exp|. Such nodes have |name_type=capsule|, 21911and their |type| field is one of the possibilities for |cur_type| listed above. 21912Also |link<=void| in capsules that aren't part of a token list. 21913 21914The |value| field of a capsule is, in most cases, the value that 21915corresponds to its |type|, as |cur_exp| corresponds to |cur_type|. 21916However, when |cur_exp| would point to a capsule, 21917no extra layer of indirection is present; the |value| 21918field is what would have been called |value(cur_exp)| if it had not been 21919encapsulated. Furthermore, if the type is |dependent| or 21920|mp_proto_dependent|, the |value| field of a capsule is replaced by 21921|dep_list| and |prev_dep| fields, since dependency lists in capsules are 21922always part of the general |dep_list| structure. 21923 21924The |get_x_next| routine is careful not to change the values of |cur_type| 21925and |cur_exp| when it gets an expanded token. However, |get_x_next| might 21926call a macro, which might parse an expression, which might execute lots of 21927commands in a group; hence it's possible that |cur_type| might change 21928from, say, |mp_unknown_boolean| to |mp_boolean_type|, or from |dependent| to 21929|known| or |independent|, during the time |get_x_next| is called. The 21930programs below are careful to stash sensitive intermediate results in 21931capsules, so that \MP's generality doesn't cause trouble. 21932 21933Here's a procedure that illustrates these conventions. It takes 21934the contents of $(|cur_type|\kern-.3pt,|cur_exp|\kern-.3pt)$ 21935and stashes them away in a 21936capsule. It is not used when |cur_type=mp_token_list|. 21937After the operation, |cur_type=mp_vacuous|; hence there is no need to 21938copy path lists or to update reference counts, etc. 21939 21940The special link |MP_VOID| is put on the capsule returned by 21941|stash_cur_exp|, because this procedure is used to store macro parameters 21942that must be easily distinguishable from token lists. 21943 21944@<Declare the stashing/unstashing routines@>= 21945static mp_node mp_stash_cur_exp (MP mp) { 21946 mp_node p; /* the capsule that will be returned */ 21947 mp_variable_type exp_type = mp->cur_exp.type; 21948 switch (exp_type) { 21949 case unknown_types: 21950 case mp_transform_type: 21951 case mp_color_type: 21952 case mp_pair_type: 21953 case mp_dependent: 21954 case mp_proto_dependent: 21955 case mp_independent: 21956 case mp_cmykcolor_type: 21957 p = cur_exp_node (); 21958 break; 21959 /* |case mp_path_type: case mp_pen_type: case mp_string_type:| */ 21960 default: 21961 p = mp_get_value_node (mp); 21962 mp_name_type (p) = mp_capsule; 21963 mp_type (p) = mp->cur_exp.type; 21964 set_value_number (p, cur_exp_value_number ()); /* this also resets the rest to 0/NULL */ 21965 if (cur_exp_str ()) { 21966 set_value_str (p, cur_exp_str ()); 21967 } else if (cur_exp_knot ()) { 21968 set_value_knot (p, cur_exp_knot ()); 21969 } else if (cur_exp_node ()) { 21970 set_value_node (p, cur_exp_node ()); 21971 } 21972 break; 21973 } 21974 mp->cur_exp.type = mp_vacuous; 21975 mp_link (p) = MP_VOID; 21976 return p; 21977} 21978 21979 21980@ The inverse of |stash_cur_exp| is the following procedure, which 21981deletes an unnecessary capsule and puts its contents into |cur_type| 21982and |cur_exp|. 21983 21984The program steps of \MP\ can be divided into two categories: those in 21985which |cur_type| and |cur_exp| are ``alive'' and those in which they are 21986``dead,'' in the sense that |cur_type| and |cur_exp| contain relevant 21987information or not. It's important not to ignore them when they're alive, 21988and it's important not to pay attention to them when they're dead. 21989 21990There's also an intermediate category: If |cur_type=mp_vacuous|, then 21991|cur_exp| is irrelevant, hence we can proceed without caring if |cur_type| 21992and |cur_exp| are alive or dead. In such cases we say that |cur_type| 21993and |cur_exp| are {\sl dormant}. It is permissible to call |get_x_next| 21994only when they are alive or dormant. 21995 21996The \\{stash} procedure above assumes that |cur_type| and |cur_exp| 21997are alive or dormant. The \\{unstash} procedure assumes that they are 21998dead or dormant; it resuscitates them. 21999 22000@<Declare the stashing/unstashing...@>= 22001static void mp_unstash_cur_exp (MP mp, mp_node p); 22002 22003@ @c 22004void mp_unstash_cur_exp (MP mp, mp_node p) { 22005 mp->cur_exp.type = mp_type (p); 22006 switch (mp->cur_exp.type) { 22007 case unknown_types: 22008 case mp_transform_type: 22009 case mp_color_type: 22010 case mp_pair_type: 22011 case mp_dependent: 22012 case mp_proto_dependent: 22013 case mp_independent: 22014 case mp_cmykcolor_type: 22015 set_cur_exp_node (p); 22016 break; 22017 case mp_token_list: /* this is how symbols are stashed */ 22018 set_cur_exp_node (value_node(p)); 22019 mp_free_value_node (mp, p); 22020 break; 22021 case mp_path_type: 22022 case mp_pen_type: 22023 set_cur_exp_knot (value_knot (p)); 22024 mp_free_value_node (mp, p); 22025 break; 22026 case mp_string_type: 22027 set_cur_exp_str (value_str (p)); 22028 mp_free_value_node (mp, p); 22029 break; 22030 case mp_picture_type: 22031 set_cur_exp_node (value_node (p)); 22032 mp_free_value_node (mp, p); 22033 break; 22034 case mp_boolean_type: 22035 case mp_known: 22036 set_cur_exp_value_number (value_number (p)); 22037 mp_free_value_node (mp, p); 22038 break; 22039 default: 22040 set_cur_exp_value_number (value_number (p)); 22041 if (value_knot(p)) { 22042 set_cur_exp_knot (value_knot (p)); 22043 } else if (value_node(p)) { 22044 set_cur_exp_node (value_node (p)); 22045 } else if (value_str(p)) { 22046 set_cur_exp_str (value_str (p)); 22047 } 22048 mp_free_value_node (mp, p); 22049 break; 22050 } 22051} 22052 22053 22054@ The following procedure prints the values of expressions in an 22055abbreviated format. If its first parameter |p| is NULL, the value of 22056|(cur_type,cur_exp)| is displayed; otherwise |p| should be a capsule 22057containing the desired value. The second parameter controls the amount of 22058output. If it is~0, dependency lists will be abbreviated to 22059`\.{linearform}' unless they consist of a single term. If it is greater 22060than~1, complicated structures (pens, pictures, and paths) will be displayed 22061in full. 22062@.linearform@> 22063 22064@<Declarations@>= 22065@<Declare the procedure called |print_dp|@>; 22066@<Declare the stashing/unstashing routines@>; 22067static void mp_print_exp (MP mp, mp_node p, quarterword verbosity); 22068 22069@ @c 22070void mp_print_exp (MP mp, mp_node p, quarterword verbosity) { 22071 boolean restore_cur_exp; /* should |cur_exp| be restored? */ 22072 mp_variable_type t; /* the type of the expression */ 22073 mp_number vv; /* the value of the expression */ 22074 mp_node v = NULL; 22075 new_number (vv); 22076 if (p != NULL) { 22077 restore_cur_exp = false; 22078 } else { 22079 p = mp_stash_cur_exp (mp); 22080 restore_cur_exp = true; 22081 } 22082 t = mp_type (p); 22083 if (t < mp_dependent) { /* no dep list, could be a capsule */ 22084 if (t != mp_vacuous && t != mp_known && value_node (p) != NULL) 22085 v = value_node (p); 22086 else 22087 number_clone (vv, value_number (p)); 22088 } else if (t < mp_independent) { 22089 v = (mp_node) dep_list ((mp_value_node) p); 22090 } 22091 @<Print an abbreviated value of |v| or |vv| with format depending on |t|@>; 22092 if (restore_cur_exp) 22093 mp_unstash_cur_exp (mp, p); 22094 free_number (vv); 22095} 22096 22097 22098@ @<Print an abbreviated value of |v| or |vv| with format depending on |t|@>= 22099switch (t) { 22100case mp_vacuous: 22101 mp_print (mp, "vacuous"); 22102 break; 22103case mp_boolean_type: 22104 if (number_to_boolean (vv) == mp_true_code) 22105 mp_print (mp, "true"); 22106 else 22107 mp_print (mp, "false"); 22108 break; 22109case unknown_types: 22110case mp_numeric_type: 22111 @<Display a variable that's been declared but not defined@>; 22112 break; 22113case mp_string_type: 22114 mp_print_char (mp, xord ('"')); 22115 mp_print_str (mp, value_str (p)); 22116 mp_print_char (mp, xord ('"')); 22117 break; 22118case mp_pen_type: 22119case mp_path_type: 22120case mp_picture_type: 22121 @<Display a complex type@>; 22122 break; 22123case mp_transform_type: 22124 if (number_zero (vv) && v == NULL) 22125 mp_print_type (mp, t); 22126 else 22127 @<Display a transform node@>; 22128 break; 22129case mp_color_type: 22130 if (number_zero (vv) && v == NULL) 22131 mp_print_type (mp, t); 22132 else 22133 @<Display a color node@>; 22134 break; 22135case mp_pair_type: 22136 if (number_zero (vv) && v == NULL) 22137 mp_print_type (mp, t); 22138 else 22139 @<Display a pair node@>; 22140 break; 22141case mp_cmykcolor_type: 22142 if (number_zero (vv) && v == NULL) 22143 mp_print_type (mp, t); 22144 else 22145 @<Display a cmykcolor node@>; 22146 break; 22147case mp_known: 22148 print_number (vv); 22149 break; 22150case mp_dependent: 22151case mp_proto_dependent: 22152 mp_print_dp (mp, t, (mp_value_node) v, verbosity); 22153 break; 22154case mp_independent: 22155 mp_print_variable_name (mp, p); 22156 break; 22157default: 22158 mp_confusion (mp, "exp"); 22159 break; 22160@:this can't happen exp}{\quad exp@> 22161} 22162 22163 22164@ @<Display big node item |v|@>= 22165{ 22166 if (mp_type (v) == mp_known) 22167 print_number (value_number (v)); 22168 else if (mp_type (v) == mp_independent) 22169 mp_print_variable_name (mp, v); 22170 else 22171 mp_print_dp (mp, mp_type (v), (mp_value_node) dep_list ((mp_value_node) v), 22172 verbosity); 22173} 22174 22175 22176@ In these cases, |v| starts as the big node. 22177 22178@<Display a pair node@>= 22179{ 22180 mp_node vvv = v; 22181 mp_print_char (mp, xord ('(')); 22182 /* clang: dereference of null pointer */ assert(vvv); 22183 v = x_part (vvv); 22184 @<Display big node item |v|@>; 22185 mp_print_char (mp, xord (',')); 22186 v = y_part (vvv); 22187 @<Display big node item |v|@>; 22188 mp_print_char (mp, xord (')')); 22189} 22190 22191 22192@ @<Display a transform node@>= 22193{ 22194 mp_node vvv = v; 22195 mp_print_char (mp, xord ('(')); 22196 /* clang: dereference of null pointer */ assert(vvv); 22197 v = tx_part (vvv); 22198 @<Display big node item |v|@>; 22199 mp_print_char (mp, xord (',')); 22200 v = ty_part (vvv); 22201 @<Display big node item |v|@>; 22202 mp_print_char (mp, xord (',')); 22203 v = xx_part (vvv); 22204 @<Display big node item |v|@>; 22205 mp_print_char (mp, xord (',')); 22206 v = xy_part (vvv); 22207 @<Display big node item |v|@>; 22208 mp_print_char (mp, xord (',')); 22209 v = yx_part (vvv); 22210 @<Display big node item |v|@>; 22211 mp_print_char (mp, xord (',')); 22212 v = yy_part (vvv); 22213 @<Display big node item |v|@>; 22214 mp_print_char (mp, xord (')')); 22215} 22216 22217 22218@ @<Display a color node@>= 22219{ 22220 mp_node vvv = v; 22221 mp_print_char (mp, xord ('(')); 22222 /* clang: dereference of null pointer */ assert(vvv); 22223 v = red_part (vvv); 22224 @<Display big node item |v|@>; 22225 mp_print_char (mp, xord (',')); 22226 v = green_part (vvv); 22227 @<Display big node item |v|@>; 22228 mp_print_char (mp, xord (',')); 22229 v = blue_part (vvv); 22230 @<Display big node item |v|@>; 22231 mp_print_char (mp, xord (')')); 22232} 22233 22234 22235@ @<Display a cmykcolor node@>= 22236{ 22237 mp_node vvv = v; 22238 mp_print_char (mp, xord ('(')); 22239 /* clang: dereference of null pointer */ assert(vvv); 22240 v = cyan_part (vvv); 22241 @<Display big node item |v|@>; 22242 mp_print_char (mp, xord (',')); 22243 v = magenta_part (vvv); 22244 @<Display big node item |v|@>; 22245 mp_print_char (mp, xord (',')); 22246 v = yellow_part (vvv); 22247 @<Display big node item |v|@>; 22248 mp_print_char (mp, xord (',')); 22249 v = black_part (vvv); 22250 @<Display big node item |v|@>; 22251 mp_print_char (mp, xord (')')); 22252} 22253 22254 22255@ Values of type \&{picture}, \&{path}, and \&{pen} are displayed verbosely 22256in the log file only, unless the user has given a positive value to 22257\\{tracingonline}. 22258 22259@<Display a complex type@>= 22260if (verbosity <= 1) { 22261 mp_print_type (mp, t); 22262} else { 22263 if (mp->selector == term_and_log) 22264 if (number_nonpositive (internal_value (mp_tracing_online))) { 22265 mp->selector = term_only; 22266 mp_print_type (mp, t); 22267 mp_print (mp, " (see the transcript file)"); 22268 mp->selector = term_and_log; 22269 }; 22270 switch (t) { 22271 case mp_pen_type: 22272 mp_print_pen (mp, value_knot (p), "", false); 22273 break; 22274 case mp_path_type: 22275 mp_print_path (mp, value_knot (p), "", false); 22276 break; 22277 case mp_picture_type: 22278 mp_print_edges (mp, v, "", false); 22279 break; 22280 default: 22281 break; 22282 } 22283} 22284 22285 22286@ @<Declare the procedure called |print_dp|@>= 22287static void mp_print_dp (MP mp, quarterword t, mp_value_node p, 22288 quarterword verbosity) { 22289 mp_value_node q; /* the node following |p| */ 22290 q = (mp_value_node) mp_link (p); 22291 if ((dep_info (q) == NULL) || (verbosity > 0)) 22292 mp_print_dependency (mp, p, t); 22293 else 22294 mp_print (mp, "linearform"); 22295} 22296 22297 22298@ The displayed name of a variable in a ring will not be a capsule unless 22299the ring consists entirely of capsules. 22300 22301@<Display a variable that's been declared but not defined@>= 22302{ 22303 mp_print_type (mp, t); 22304 if (v != NULL) { 22305 mp_print_char (mp, xord (' ')); 22306 while ((mp_name_type (v) == mp_capsule) && (v != p)) 22307 v = value_node (v); 22308 mp_print_variable_name (mp, v); 22309 }; 22310} 22311 22312 22313@ When errors are detected during parsing, it is often helpful to 22314display an expression just above the error message, using |disp_err| 22315just before |mp_error|. 22316 22317@<Declarations@>= 22318static void mp_disp_err (MP mp, mp_node p); 22319 22320@ @c 22321void mp_disp_err (MP mp, mp_node p) { 22322 if (mp->interaction == mp_error_stop_mode) 22323 wake_up_terminal(); 22324 mp_print_nl (mp, ">> "); 22325@.>>@>; 22326 mp_print_exp (mp, p, 1); /* ``medium verbose'' printing of the expression */ 22327} 22328 22329 22330@ If |cur_type| and |cur_exp| contain relevant information that should 22331be recycled, we will use the following procedure, which changes |cur_type| 22332to |known| and stores a given value in |cur_exp|. We can think of |cur_type| 22333and |cur_exp| as either alive or dormant after this has been done, 22334because |cur_exp| will not contain a pointer value. 22335 22336@ @c 22337void mp_flush_cur_exp (MP mp, mp_value v) { 22338 if (is_number(mp->cur_exp.data.n)) { 22339 free_number(mp->cur_exp.data.n); 22340 } 22341 switch (mp->cur_exp.type) { 22342 case unknown_types: 22343 case mp_transform_type: 22344 case mp_color_type: 22345 case mp_pair_type: 22346 case mp_dependent: 22347 case mp_proto_dependent: 22348 case mp_independent: 22349 case mp_cmykcolor_type: 22350 mp_recycle_value (mp, cur_exp_node ()); 22351 mp_free_value_node (mp, cur_exp_node ()); 22352 break; 22353 case mp_string_type: 22354 delete_str_ref (cur_exp_str ()); 22355 break; 22356 case mp_pen_type: 22357 case mp_path_type: 22358 mp_toss_knot_list (mp, cur_exp_knot ()); 22359 break; 22360 case mp_picture_type: 22361 delete_edge_ref (cur_exp_node ()); 22362 break; 22363 default: 22364 break; 22365 } 22366 mp->cur_exp = v; 22367 mp->cur_exp.type = mp_known; 22368} 22369 22370 22371@ There's a much more general procedure that is capable of releasing 22372the storage associated with any non-symbolic value packet. 22373 22374@<Declarations@>= 22375static void mp_recycle_value (MP mp, mp_node p); 22376 22377@ @c 22378static void mp_recycle_value (MP mp, mp_node p) { 22379 mp_variable_type t; /* a type code */ 22380 FUNCTION_TRACE2 ("mp_recycle_value(%p)\n", p); 22381 if (p==NULL || p==MP_VOID) 22382 return; 22383 t = mp_type (p); 22384 switch (t) { 22385 case mp_vacuous: 22386 case mp_boolean_type: 22387 case mp_known: 22388 case mp_numeric_type: 22389 break; 22390 case unknown_types: 22391 mp_ring_delete (mp, p); 22392 break; 22393 case mp_string_type: 22394 delete_str_ref (value_str (p)); 22395 break; 22396 case mp_path_type: 22397 case mp_pen_type: 22398 mp_toss_knot_list (mp, value_knot (p)); 22399 break; 22400 case mp_picture_type: 22401 delete_edge_ref (value_node (p)); 22402 break; 22403 case mp_cmykcolor_type: 22404 if (value_node (p) != NULL) { 22405 mp_recycle_value (mp, cyan_part (value_node (p))); 22406 mp_recycle_value (mp, magenta_part (value_node (p))); 22407 mp_recycle_value (mp, yellow_part (value_node (p))); 22408 mp_recycle_value (mp, black_part (value_node (p))); 22409 mp_free_value_node (mp, cyan_part (value_node (p))); 22410 mp_free_value_node (mp, magenta_part (value_node (p))); 22411 mp_free_value_node (mp, black_part (value_node (p))); 22412 mp_free_value_node (mp, yellow_part (value_node (p))); 22413 mp_free_node (mp, value_node (p), cmykcolor_node_size); 22414 } 22415 break; 22416 case mp_pair_type: 22417 if (value_node (p) != NULL) { 22418 mp_recycle_value (mp, x_part (value_node (p))); 22419 mp_recycle_value (mp, y_part (value_node (p))); 22420 mp_free_value_node (mp, x_part (value_node (p))); 22421 mp_free_value_node (mp, y_part (value_node (p))); 22422 mp_free_pair_node (mp, value_node (p)); 22423 } 22424 break; 22425 case mp_color_type: 22426 if (value_node (p) != NULL) { 22427 mp_recycle_value (mp, red_part (value_node (p))); 22428 mp_recycle_value (mp, green_part (value_node (p))); 22429 mp_recycle_value (mp, blue_part (value_node (p))); 22430 mp_free_value_node (mp, red_part (value_node (p))); 22431 mp_free_value_node (mp, green_part (value_node (p))); 22432 mp_free_value_node (mp, blue_part (value_node (p))); 22433 mp_free_node (mp, value_node (p), color_node_size); 22434 } 22435 break; 22436 case mp_transform_type: 22437 if (value_node (p) != NULL) { 22438 mp_recycle_value (mp, tx_part (value_node (p))); 22439 mp_recycle_value (mp, ty_part (value_node (p))); 22440 mp_recycle_value (mp, xx_part (value_node (p))); 22441 mp_recycle_value (mp, xy_part (value_node (p))); 22442 mp_recycle_value (mp, yx_part (value_node (p))); 22443 mp_recycle_value (mp, yy_part (value_node (p))); 22444 mp_free_value_node (mp, tx_part (value_node (p))); 22445 mp_free_value_node (mp, ty_part (value_node (p))); 22446 mp_free_value_node (mp, xx_part (value_node (p))); 22447 mp_free_value_node (mp, xy_part (value_node (p))); 22448 mp_free_value_node (mp, yx_part (value_node (p))); 22449 mp_free_value_node (mp, yy_part (value_node (p))); 22450 mp_free_node (mp, value_node (p), transform_node_size); 22451 } 22452 break; 22453 case mp_dependent: 22454 case mp_proto_dependent: 22455 /* Recycle a dependency list */ 22456 { 22457 mp_value_node qq = (mp_value_node) dep_list ((mp_value_node) p); 22458 while (dep_info (qq) != NULL) 22459 qq = (mp_value_node) mp_link (qq); 22460 set_mp_link (prev_dep ((mp_value_node) p), mp_link (qq)); 22461 set_prev_dep (mp_link (qq), prev_dep ((mp_value_node) p)); 22462 set_mp_link (qq, NULL); 22463 mp_flush_node_list (mp, (mp_node) dep_list ((mp_value_node) p)); 22464 } 22465 break; 22466 case mp_independent: 22467 @<Recycle an independent variable@>; 22468 break; 22469 case mp_token_list: 22470 case mp_structured: 22471 mp_confusion (mp, "recycle"); 22472 break; 22473 case mp_unsuffixed_macro: 22474 case mp_suffixed_macro: 22475 mp_delete_mac_ref (mp, value_node (p)); 22476 break; 22477 default: /* there are no other valid cases, but please the compiler */ 22478 break; 22479 } 22480 mp_type (p) = mp_undefined; 22481} 22482 22483@ When an independent variable disappears, it simply fades away, unless 22484something depends on it. In the latter case, a dependent variable whose 22485coefficient of dependence is maximal will take its place. 22486The relevant algorithm is due to Ignacio~A. Zabala, who implemented it 22487as part of his Ph.n->data. thesis (Stanford University, December 1982). 22488@^Zabala Salelles, Ignacio Andr\'es@> 22489 22490For example, suppose that variable $x$ is being recycled, and that the 22491only variables depending on~$x$ are $y=2x+a$ and $z=x+b$. In this case 22492we want to make $y$ independent and $z=.5y-.5a+b$; no other variables 22493will depend on~$y$. If $\\{tracingequations}>0$ in this situation, 22494we will print `\.{\#\#\# -2x=-y+a}'. 22495 22496There's a slight complication, however: An independent variable $x$ 22497can occur both in dependency lists and in proto-dependency lists. 22498This makes it necessary to be careful when deciding which coefficient 22499is maximal. 22500 22501Furthermore, this complication is not so slight when 22502a proto-dependent variable is chosen to become independent. For example, 22503suppose that $y=2x+100a$ is proto-dependent while $z=x+b$ is dependent; 22504then we must change $z=.5y-50a+b$ to a proto-dependency, because of the 22505large coefficient `50'. 22506 22507In order to deal with these complications without wasting too much time, 22508we shall link together the occurrences of~$x$ among all the linear 22509dependencies, maintaining separate lists for the dependent and 22510proto-dependent cases. 22511 22512@<Recycle an independent variable@>= 22513{ 22514 mp_value_node q, r, s; 22515 mp_node pp; /* link manipulation register */ 22516 mp_number v ; /* a value */ 22517 mp_number test; /* a temporary value */ 22518 new_number (test); 22519 new_number (v); 22520 if (t < mp_dependent) 22521 number_clone (v, value_number (p)); 22522 set_number_to_zero(mp->max_c[mp_dependent]); 22523 set_number_to_zero(mp->max_c[mp_proto_dependent]); 22524 mp->max_link[mp_dependent] = NULL; 22525 mp->max_link[mp_proto_dependent] = NULL; 22526 q = (mp_value_node) mp_link (mp->dep_head); 22527 while (q != mp->dep_head) { 22528 s = (mp_value_node) mp->temp_head; 22529 set_mp_link (s, dep_list (q)); 22530 while (1) { 22531 r = (mp_value_node) mp_link (s); 22532 if (dep_info (r) == NULL) 22533 break; 22534 if (dep_info (r) != p) { 22535 s = r; 22536 } else { 22537 t = mp_type (q); 22538 if (mp_link (s) == dep_list (q)) { /* reset the |dep_list| */ 22539 set_dep_list (q, mp_link (r)); 22540 } 22541 set_mp_link (s, mp_link (r)); 22542 set_dep_info (r, (mp_node) q); 22543 number_clone (test, dep_value (r)); 22544 number_abs (test); 22545 if (number_greater (test, mp->max_c[t])) { 22546 /* Record a new maximum coefficient of type |t| */ 22547 if (number_positive(mp->max_c[t])) { 22548 set_mp_link (mp->max_ptr[t], (mp_node) mp->max_link[t]); 22549 mp->max_link[t] = mp->max_ptr[t]; 22550 } 22551 number_clone (mp->max_c[t], test); 22552 mp->max_ptr[t] = r; 22553 } else { 22554 set_mp_link (r, (mp_node) mp->max_link[t]); 22555 mp->max_link[t] = r; 22556 } 22557 } 22558 } 22559 q = (mp_value_node) mp_link (r); 22560 } 22561 if (number_positive(mp->max_c[mp_dependent]) || number_positive(mp->max_c[mp_proto_dependent])) { 22562 /* Choose a dependent variable to take the place of the disappearing 22563 independent variable, and change all remaining dependencies 22564 accordingly */ 22565 mp_number test, ret; /* temporary use */ 22566 new_number (ret); 22567 new_number (test); 22568 number_clone (test, mp->max_c[mp_dependent]); 22569 number_divide_int (test, 4096); 22570 if (number_greaterequal(test, mp->max_c[mp_proto_dependent])) 22571 t = mp_dependent; 22572 else 22573 t = mp_proto_dependent; 22574 22575 /* Let |s=max_ptr[t]|. At this point we have $|value|(s)=\pm|max_c|[t]$, 22576 and |dep_info(s)| points to the dependent variable~|pp| of type~|t| from 22577 whose dependency list we have removed node~|s|. We must reinsert 22578 node~|s| into the dependency list, with coefficient $-1.0$, and with 22579 |pp| as the new independent variable. Since |pp| will have a larger serial 22580 number than any other variable, we can put node |s| at the head of the 22581 list. */ 22582 /* Determine the dependency list |s| to substitute for the independent 22583 variable~|p| */ 22584 22585 s = mp->max_ptr[t]; 22586 pp = (mp_node) dep_info (s); 22587 number_clone (v, dep_value (s)); 22588 if (t == mp_dependent) { 22589 set_dep_value (s, fraction_one_t); 22590 } else { 22591 set_dep_value (s, unity_t); 22592 } 22593 number_negate(dep_value(s)); 22594 r = (mp_value_node) dep_list ((mp_value_node) pp); 22595 set_mp_link (s, (mp_node) r); 22596 while (dep_info (r) != NULL) 22597 r = (mp_value_node) mp_link (r); 22598 q = (mp_value_node) mp_link (r); 22599 set_mp_link (r, NULL); 22600 set_prev_dep (q, prev_dep ((mp_value_node) pp)); 22601 set_mp_link (prev_dep ((mp_value_node) pp), (mp_node) q); 22602 mp_new_indep (mp, pp); 22603 if (cur_exp_node () == pp && mp->cur_exp.type == t) 22604 mp->cur_exp.type = mp_independent; 22605 if (number_positive (internal_value (mp_tracing_equations))) { 22606 /* Show the transformed dependency */ 22607 if (mp_interesting (mp, p)) { 22608 mp_begin_diagnostic (mp); 22609 mp_show_transformed_dependency(mp, v, t, p); 22610 mp_print_dependency (mp, s, t); 22611 mp_end_diagnostic (mp, false); 22612 } 22613 } 22614 22615 t = (quarterword) (mp_dependent + mp_proto_dependent - t); /* complement |t| */ 22616 if (number_positive(mp->max_c[t])) { 22617 /* we need to pick up an unchosen dependency */ 22618 set_mp_link (mp->max_ptr[t], (mp_node) mp->max_link[t]); 22619 mp->max_link[t] = mp->max_ptr[t]; 22620 } 22621 /* Finally, there are dependent and proto-dependent variables whose 22622 dependency lists must be brought up to date. */ 22623 if (t != mp_dependent) { 22624 /* Substitute new dependencies in place of |p| */ 22625 for (t = mp_dependent; t <= mp_proto_dependent; t=t+1) { 22626 r = mp->max_link[t]; 22627 while (r != NULL) { 22628 q = (mp_value_node) dep_info (r); 22629 number_clone (test, v); 22630 number_negate (test); 22631 make_fraction (ret, dep_value (r), test); 22632 set_dep_list (q, mp_p_plus_fq (mp, (mp_value_node) dep_list (q), ret, s, t, mp_dependent)); 22633 if (dep_list (q) == (mp_node) mp->dep_final) 22634 mp_make_known (mp, q, mp->dep_final); 22635 q = r; 22636 r = (mp_value_node) mp_link (r); 22637 mp_free_dep_node (mp, q); 22638 } 22639 } 22640 } else { 22641 /* Substitute new proto-dependencies in place of |p| */ 22642 for (t = mp_dependent; t <= mp_proto_dependent; t=t+1) { 22643 r = mp->max_link[t]; 22644 while (r != NULL) { 22645 q = (mp_value_node) dep_info (r); 22646 if (t == mp_dependent) { /* for safety's sake, we change |q| to |mp_proto_dependent| */ 22647 if (cur_exp_node () == (mp_node) q && mp->cur_exp.type == mp_dependent) 22648 mp->cur_exp.type = mp_proto_dependent; 22649 set_dep_list (q, mp_p_over_v (mp, (mp_value_node) dep_list (q), 22650 unity_t, mp_dependent, 22651 mp_proto_dependent)); 22652 mp_type (q) = mp_proto_dependent; 22653 fraction_to_round_scaled (dep_value (r)); 22654 } 22655 number_clone (test, v); 22656 number_negate (test); 22657 make_scaled (ret, dep_value (r), test); 22658 set_dep_list (q, mp_p_plus_fq (mp, (mp_value_node) dep_list (q), 22659 ret, s, 22660 mp_proto_dependent, 22661 mp_proto_dependent)); 22662 if (dep_list (q) == (mp_node) mp->dep_final) 22663 mp_make_known (mp, q, mp->dep_final); 22664 q = r; 22665 r = (mp_value_node) mp_link (r); 22666 mp_free_dep_node (mp, q); 22667 } 22668 } 22669 } 22670 mp_flush_node_list (mp, (mp_node) s); 22671 if (mp->fix_needed) 22672 mp_fix_dependencies (mp); 22673 check_arith(); 22674 free_number (ret); 22675 } 22676 free_number (v); 22677 free_number(test); 22678} 22679 22680@ @<Declarations@>= 22681static void mp_show_transformed_dependency(MP mp, mp_number v, mp_variable_type t, mp_node p); 22682 22683@ @c 22684static void mp_show_transformed_dependency(MP mp, mp_number v, mp_variable_type t, mp_node p) 22685{ 22686 mp_number vv; /* for temp use */ 22687 new_number (vv); 22688 mp_print_nl (mp, "### "); 22689 if (number_positive(v)) 22690 mp_print_char (mp, xord ('-')); 22691 if (t == mp_dependent) { 22692 number_clone (vv, mp->max_c[mp_dependent]); 22693 fraction_to_round_scaled (vv); 22694 } else { 22695 number_clone (vv, mp->max_c[mp_proto_dependent]); 22696 } 22697 if (!number_equal(vv, unity_t)) { 22698 print_number (vv); 22699 } 22700 mp_print_variable_name (mp, p); 22701 while (indep_scale (p) > 0) { 22702 mp_print (mp, "*4"); 22703 set_indep_scale(p, indep_scale(p)-2); 22704 } 22705 if (t == mp_dependent) 22706 mp_print_char (mp, xord ('=')); 22707 else 22708 mp_print (mp, " = "); 22709 free_number (vv); 22710} 22711 22712 22713@ The code for independency removal makes use of three non-symbolic arrays. 22714 22715@<Glob...@>= 22716mp_number max_c[mp_proto_dependent + 1]; /* max coefficient magnitude */ 22717mp_value_node max_ptr[mp_proto_dependent + 1]; /* where |p| occurs with |max_c| */ 22718mp_value_node max_link[mp_proto_dependent + 1]; /* other occurrences of |p| */ 22719 22720 22721@ @<Initialize table ... @>= 22722{ 22723 int i; 22724 for (i=0;i<mp_proto_dependent + 1;i++) { 22725 new_number (mp->max_c[i]); 22726 } 22727} 22728 22729@ @<Dealloc...@>= 22730{ 22731 int i; 22732 for (i=0;i<mp_proto_dependent + 1;i++) { 22733 free_number (mp->max_c[i]); 22734 } 22735} 22736 22737@ A global variable |var_flag| is set to a special command code 22738just before \MP\ calls |scan_expression|, if the expression should be 22739treated as a variable when this command code immediately follows. For 22740example, |var_flag| is set to |assignment| at the beginning of a 22741statement, because we want to know the {\sl location\/} of a variable at 22742the left of `\.{:=}', not the {\sl value\/} of that variable. 22743 22744The |scan_expression| subroutine calls |scan_tertiary|, 22745which calls |scan_secondary|, which calls |scan_primary|, which sets 22746|var_flag:=0|. In this way each of the scanning routines ``knows'' 22747when it has been called with a special |var_flag|, but |var_flag| is 22748usually zero. 22749 22750A variable preceding a command that equals |var_flag| is converted to a 22751token list rather than a value. Furthermore, an `\.{=}' sign following an 22752expression with |var_flag=assignment| is not considered to be a relation 22753that produces boolean expressions. 22754 22755 22756@<Glob...@>= 22757int var_flag; /* command that wants a variable */ 22758 22759@ @<Set init...@>= 22760mp->var_flag = 0; 22761 22762@* Parsing primary expressions. 22763The first parsing routine, |scan_primary|, is also the most complicated one, 22764since it involves so many different cases. But each case---with one 22765exception---is fairly simple by itself. 22766 22767When |scan_primary| begins, the first token of the primary to be scanned 22768should already appear in |cur_cmd|, |cur_mod|, and |cur_sym|. The values 22769of |cur_type| and |cur_exp| should be either dead or dormant, as explained 22770earlier. If |cur_cmd| is not between |min_primary_command| and 22771|max_primary_command|, inclusive, a syntax error will be signaled. 22772 22773Later we'll come to procedures that perform actual operations like 22774addition, square root, and so on; our purpose now is to do the parsing. 22775But we might as well mention those future procedures now, so that the 22776suspense won't be too bad: 22777 22778\smallskip 22779|do_nullary(c)| does primitive operations that have no operands (e.g., 22780`\&{true}' or `\&{pencircle}'); 22781 22782\smallskip 22783|do_unary(c)| applies a primitive operation to the current expression; 22784 22785\smallskip 22786|do_binary(p,c)| applies a primitive operation to the capsule~|p| 22787and the current expression. 22788 22789@<Declare the basic parsing subroutines@>= 22790static void check_for_mediation (MP mp); 22791void mp_scan_primary (MP mp) { 22792 mp_command_code my_var_flag; /* initial value of |my_var_flag| */ 22793 my_var_flag = mp->var_flag; 22794 mp->var_flag = 0; 22795RESTART: 22796 check_arith(); 22797 /* Supply diagnostic information, if requested */ 22798 if (mp->interrupt != 0) { 22799 if (mp->OK_to_interrupt) { 22800 mp_back_input (mp); 22801 check_interrupt; 22802 mp_get_x_next (mp); 22803 } 22804 } 22805 switch (cur_cmd()) { 22806 case mp_left_delimiter: 22807 { 22808 /* Scan a delimited primary */ 22809 mp_node p, q, r; /* for list manipulation */ 22810 mp_sym l_delim, r_delim; /* hash addresses of a delimiter pair */ 22811 l_delim = cur_sym(); 22812 r_delim = equiv_sym (cur_sym()); 22813 mp_get_x_next (mp); 22814 mp_scan_expression (mp); 22815 if ((cur_cmd() == mp_comma) && (mp->cur_exp.type >= mp_known)) { 22816 /* Scan the rest of a delimited set of numerics */ 22817 /* This code uses the fact that |red_part| and |green_part| 22818 are synonymous with |x_part| and |y_part|. */ 22819 p = mp_stash_cur_exp (mp); 22820 mp_get_x_next (mp); 22821 mp_scan_expression (mp); 22822 /* Make sure the second part of a pair or color has a numeric type */ 22823 if (mp->cur_exp.type < mp_known) { 22824 const char *hlp[] = { 22825 "I've started to scan a pair `(a,b)' or a color `(a,b,c)';", 22826 "but after finding a nice `a' I found a `b' that isn't", 22827 "of numeric type. So I've changed that part to zero.", 22828 "(The b that I didn't like appears above the error message.)", 22829 NULL }; 22830 mp_value new_expr; 22831 memset(&new_expr,0,sizeof(mp_value)); 22832 mp_disp_err(mp, NULL); 22833 new_number(new_expr.data.n); 22834 set_number_to_zero(new_expr.data.n); 22835 mp_back_error (mp,"Nonnumeric ypart has been replaced by 0", hlp, true); 22836 mp_get_x_next (mp); 22837 mp_flush_cur_exp (mp, new_expr); 22838 } 22839 22840 q = mp_get_value_node (mp); 22841 mp_name_type (q) = mp_capsule; 22842 if (cur_cmd() == mp_comma) { 22843 mp_init_color_node (mp, q); 22844 r = value_node (q); 22845 mp_stash_in (mp, y_part (r)); 22846 mp_unstash_cur_exp (mp, p); 22847 mp_stash_in (mp, x_part (r)); 22848 /* Scan the last of a triplet of numerics */ 22849 mp_get_x_next (mp); 22850 mp_scan_expression (mp); 22851 if (mp->cur_exp.type < mp_known) { 22852 mp_value new_expr; 22853 const char *hlp[] = { 22854 "I've just scanned a color `(a,b,c)' or cmykcolor(a,b,c,d); but the `c'", 22855 "isn't of numeric type. So I've changed that part to zero.", 22856 "(The c that I didn't like appears above the error message.)", 22857 NULL }; 22858 memset(&new_expr,0,sizeof(mp_value)); 22859 mp_disp_err(mp, NULL); 22860 new_number(new_expr.data.n); 22861 set_number_to_zero(new_expr.data.n); 22862 mp_back_error (mp,"Nonnumeric third part has been replaced by 0", hlp, true); 22863 mp_get_x_next (mp); 22864 mp_flush_cur_exp (mp, new_expr); 22865 } 22866 mp_stash_in (mp, blue_part (r)); 22867 22868 if (cur_cmd() == mp_comma) { 22869 mp_node t; /* a token */ 22870 mp_init_cmykcolor_node (mp, q); 22871 t = value_node (q); 22872 mp_type (cyan_part (t)) = mp_type (red_part (r)); 22873 set_value_number (cyan_part (t), value_number (red_part (r))); 22874 mp_type (magenta_part (t)) = mp_type (green_part (r)); 22875 set_value_number (magenta_part (t), value_number (green_part (r))); 22876 mp_type (yellow_part (t)) = mp_type (blue_part (r)); 22877 set_value_number (yellow_part (t), value_number (blue_part (r))); 22878 mp_recycle_value (mp, r); 22879 r = t; 22880 /* Scan the last of a quartet of numerics */ 22881 mp_get_x_next (mp); 22882 mp_scan_expression (mp); 22883 if (mp->cur_exp.type < mp_known) { 22884 const char *hlp[] = { 22885 "I've just scanned a cmykcolor `(c,m,y,k)'; but the `k' isn't", 22886 "of numeric type. So I've changed that part to zero.", 22887 "(The k that I didn't like appears above the error message.)", 22888 NULL }; 22889 mp_value new_expr; 22890 memset(&new_expr,0,sizeof(mp_value)); 22891 new_number(new_expr.data.n); 22892 mp_disp_err(mp, NULL); 22893 set_number_to_zero(new_expr.data.n); 22894 mp_back_error (mp,"Nonnumeric blackpart has been replaced by 0", hlp, true); 22895 mp_get_x_next (mp); 22896 mp_flush_cur_exp (mp, new_expr); 22897 } 22898 mp_stash_in (mp, black_part (r)); 22899 22900 } 22901 } else { 22902 mp_init_pair_node (mp, q); 22903 r = value_node (q); 22904 mp_stash_in (mp, y_part (r)); 22905 mp_unstash_cur_exp (mp, p); 22906 mp_stash_in (mp, x_part (r)); 22907 } 22908 mp_check_delimiter (mp, l_delim, r_delim); 22909 mp->cur_exp.type = mp_type (q); 22910 set_cur_exp_node (q); 22911 22912 } else { 22913 mp_check_delimiter (mp, l_delim, r_delim); 22914 } 22915 } 22916 break; 22917 case mp_begin_group: 22918 /* Scan a grouped primary */ 22919 /* The local variable |group_line| keeps track of the line 22920 where a \&{begingroup} command occurred; this will be useful 22921 in an error message if the group doesn't actually end. */ 22922 { 22923 integer group_line; /* where a group began */ 22924 group_line = mp_true_line (mp); 22925 if (number_positive (internal_value (mp_tracing_commands))) 22926 show_cur_cmd_mod; 22927 mp_save_boundary (mp); 22928 do { 22929 mp_do_statement (mp); /* ends with |cur_cmd>=semicolon| */ 22930 } while (cur_cmd() == mp_semicolon); 22931 if (cur_cmd() != mp_end_group) { 22932 char msg[256]; 22933 const char *hlp[] = { 22934 "I saw a `begingroup' back there that hasn't been matched", 22935 "by `endgroup'. So I've inserted `endgroup' now.", 22936 NULL }; 22937 mp_snprintf(msg, 256, "A group begun on line %d never ended", (int)group_line); 22938 mp_back_error (mp, msg, hlp, true); 22939 set_cur_cmd((mp_variable_type)mp_end_group); 22940 } 22941 mp_unsave (mp); 22942 /* this might change |cur_type|, if independent variables are recycled */ 22943 if (number_positive (internal_value (mp_tracing_commands))) 22944 show_cur_cmd_mod; 22945 } 22946 break; 22947 case mp_string_token: 22948 /* Scan a string constant */ 22949 mp->cur_exp.type = mp_string_type; 22950 set_cur_exp_str (cur_mod_str()); 22951 break; 22952 case mp_numeric_token: 22953 { 22954 /* Scan a primary that starts with a numeric token */ 22955 /* A numeric token might be a primary by itself, or it might be the 22956 numerator of a fraction composed solely of numeric tokens, or it might 22957 multiply the primary that follows (provided that the primary doesn't begin 22958 with a plus sign or a minus sign). The code here uses the facts that 22959 |max_primary_command=plus_or_minus| and 22960 |max_primary_command-1=numeric_token|. If a fraction is found that is less 22961 than unity, we try to retain higher precision when we use it in scalar 22962 multiplication. */ 22963 mp_number num, denom; /* for primaries that are fractions, like `1/2' */ 22964 new_number (num); 22965 new_number (denom); 22966 set_cur_exp_value_number (cur_mod_number()); 22967 mp->cur_exp.type = mp_known; 22968 mp_get_x_next (mp); 22969 if (cur_cmd() != mp_slash) { 22970 set_number_to_zero(num); 22971 set_number_to_zero(denom); 22972 } else { 22973 mp_get_x_next (mp); 22974 if (cur_cmd() != mp_numeric_token) { 22975 mp_back_input (mp); 22976 set_cur_cmd((mp_variable_type)mp_slash); 22977 set_cur_mod(mp_over); 22978 set_cur_sym(mp->frozen_slash); 22979 free_number (num); 22980 free_number (denom); 22981 goto DONE; 22982 } 22983 number_clone (num, cur_exp_value_number ()); 22984 number_clone (denom, cur_mod_number()); 22985 if (number_zero(denom)) { 22986 /* Protest division by zero */ 22987 const char *hlp[] = { "I'll pretend that you meant to divide by 1.", NULL }; 22988 mp_error (mp, "Division by zero", hlp, true); 22989 } else { 22990 mp_number ret; 22991 new_number (ret); 22992 make_scaled (ret, num, denom); 22993 set_cur_exp_value_number (ret); 22994 free_number (ret); 22995 } 22996 check_arith(); 22997 mp_get_x_next (mp); 22998 } 22999 if (cur_cmd() >= mp_min_primary_command) { 23000 if (cur_cmd() < mp_numeric_token) { /* in particular, |cur_cmd<>plus_or_minus| */ 23001 mp_node p; /* for list manipulation */ 23002 mp_number absnum, absdenom; 23003 new_number (absnum); 23004 new_number (absdenom); 23005 p = mp_stash_cur_exp (mp); 23006 mp_scan_primary (mp); 23007 number_clone (absnum, num); 23008 number_abs (absnum); 23009 number_clone (absdenom, denom); 23010 number_abs (absdenom); 23011 if (number_greaterequal(absnum, absdenom) || (mp->cur_exp.type < mp_color_type)) { 23012 mp_do_binary (mp, p, mp_times); 23013 } else { 23014 mp_frac_mult (mp, num, denom); 23015 mp_free_value_node (mp, p); 23016 } 23017 free_number (absnum); 23018 free_number (absdenom); 23019 } 23020 } 23021 free_number (num); 23022 free_number (denom); 23023 goto DONE; 23024 } 23025 break; 23026 case mp_nullary: 23027 /* Scan a nullary operation */ 23028 mp_do_nullary (mp, (quarterword) cur_mod()); 23029 break; 23030 case mp_unary: 23031 case mp_type_name: 23032 case mp_cycle: 23033 case mp_plus_or_minus: 23034 { 23035 /* Scan a unary operation */ 23036 quarterword c; /* a primitive operation code */ 23037 c = (quarterword) cur_mod(); 23038 mp_get_x_next (mp); 23039 mp_scan_primary (mp); 23040 mp_do_unary (mp, c); 23041 goto DONE; 23042 } 23043 break; 23044 case mp_primary_binary: 23045 { 23046 /* Scan a binary operation with `\&{of}' between its operands */ 23047 mp_node p; /* for list manipulation */ 23048 quarterword c; /* a primitive operation code */ 23049 c = (quarterword) cur_mod(); 23050 mp_get_x_next (mp); 23051 mp_scan_expression (mp); 23052 if (cur_cmd() != mp_of_token) { 23053 char msg[256]; 23054 mp_string sname; 23055 const char *hlp[] = { 23056 "I've got the first argument; will look now for the other.", 23057 NULL }; 23058 int old_setting = mp->selector; 23059 mp->selector = new_string; 23060 mp_print_cmd_mod (mp, mp_primary_binary, c); 23061 mp->selector = old_setting; 23062 sname = mp_make_string(mp); 23063 mp_snprintf (msg, 256, "Missing `of' has been inserted for %s", mp_str(mp, sname)); 23064 delete_str_ref(sname); 23065 mp_back_error (mp, msg, hlp, true); 23066 } 23067 p = mp_stash_cur_exp (mp); 23068 mp_get_x_next (mp); 23069 mp_scan_primary (mp); 23070 mp_do_binary (mp, p, c); 23071 goto DONE; 23072 } 23073 break; 23074 case mp_str_op: 23075 /* Convert a suffix to a string */ 23076 mp_get_x_next (mp); 23077 mp_scan_suffix (mp); 23078 mp->old_setting = mp->selector; 23079 mp->selector = new_string; 23080 mp_show_token_list (mp, cur_exp_node (), NULL, 100000, 0); 23081 mp_flush_token_list (mp, cur_exp_node ()); 23082 set_cur_exp_str (mp_make_string (mp)); 23083 mp->selector = mp->old_setting; 23084 mp->cur_exp.type = mp_string_type; 23085 goto DONE; 23086 break; 23087 case mp_internal_quantity: 23088 /* Scan an internal numeric quantity */ 23089 /* If an internal quantity appears all by itself on the left of an 23090 assignment, we return a token list of length one, containing the address 23091 of the internal quantity, with |name_type| equal to |mp_internal_sym|. 23092 (This accords with the conventions of the save stack, as described earlier.) */ 23093 { 23094 halfword qq = cur_mod(); 23095 if (my_var_flag == mp_assignment) { 23096 mp_get_x_next (mp); 23097 if (cur_cmd() == mp_assignment) { 23098 set_cur_exp_node (mp_get_symbolic_node (mp)); 23099 set_mp_sym_info (cur_exp_node (), qq); 23100 mp_name_type (cur_exp_node ()) = mp_internal_sym; 23101 mp->cur_exp.type = mp_token_list; 23102 goto DONE; 23103 } 23104 mp_back_input (mp); 23105 } 23106 if (internal_type (qq) == mp_string_type) { 23107 set_cur_exp_str (internal_string (qq)); 23108 } else { 23109 set_cur_exp_value_number (internal_value (qq)); 23110 } 23111 mp->cur_exp.type = internal_type (qq); 23112 } 23113 break; 23114 case mp_capsule_token: 23115 mp_make_exp_copy (mp, cur_mod_node()); 23116 break; 23117 case mp_tag_token: 23118 @<Scan a variable primary; |goto restart| if it turns out to be a macro@>; 23119 break; 23120 default: 23121 mp_bad_exp (mp, "A primary"); 23122 goto RESTART; 23123 break; 23124 } 23125 mp_get_x_next (mp); /* the routines |goto done| if they don't want this */ 23126DONE: 23127 check_for_mediation (mp); 23128} 23129 23130@ Expressions of the form `\.{a[b,c]}' are converted into 23131`\.{b+a*(c-b)}', without checking the types of \.b~or~\.c, 23132provided that \.a is numeric. 23133 23134@<Declare the basic parsing subroutines@>= 23135static void check_for_mediation (MP mp) { 23136 mp_node p, q, r; /* for list manipulation */ 23137 if (cur_cmd() == mp_left_bracket) { 23138 if (mp->cur_exp.type >= mp_known) { 23139 /* Scan a mediation construction */ 23140 p = mp_stash_cur_exp (mp); 23141 mp_get_x_next (mp); 23142 mp_scan_expression (mp); 23143 if (cur_cmd() != mp_comma) { 23144 /* Put the left bracket and the expression back to be rescanned */ 23145 /* The left bracket that we thought was introducing a subscript might have 23146 actually been the left bracket in a mediation construction like `\.{x[a,b]}'. 23147 So we don't issue an error message at this point; but we do want to back up 23148 so as to avoid any embarrassment about our incorrect assumption. */ 23149 mp_back_input (mp); /* that was the token following the current expression */ 23150 mp_back_expr (mp); 23151 set_cur_cmd((mp_variable_type)mp_left_bracket); 23152 set_cur_mod_number(zero_t); 23153 set_cur_sym(mp->frozen_left_bracket); 23154 mp_unstash_cur_exp (mp, p); 23155 } else { 23156 q = mp_stash_cur_exp (mp); 23157 mp_get_x_next (mp); 23158 mp_scan_expression (mp); 23159 if (cur_cmd() != mp_right_bracket) { 23160 const char *hlp[] = { 23161 "I've scanned an expression of the form `a[b,c',", 23162 "so a right bracket should have come next.", 23163 "I shall pretend that one was there.", 23164 NULL }; 23165 mp_back_error (mp, "Missing `]' has been inserted", hlp, true); 23166 } 23167 r = mp_stash_cur_exp (mp); 23168 mp_make_exp_copy (mp, q); 23169 mp_do_binary (mp, r, mp_minus); 23170 mp_do_binary (mp, p, mp_times); 23171 mp_do_binary (mp, q, mp_plus); 23172 mp_get_x_next (mp); 23173 } 23174 } 23175 } 23176} 23177 23178 23179@ Errors at the beginning of expressions are flagged by |bad_exp|. 23180 23181@c 23182static void mp_bad_exp (MP mp, const char *s) { 23183 char msg[256]; 23184 int save_flag; 23185 const char *hlp[] = { 23186 "I'm afraid I need some sort of value in order to continue,", 23187 "so I've tentatively inserted `0'. You may want to", 23188 "delete this zero and insert something else;", 23189 "see Chapter 27 of The METAFONTbook for an example.", 23190 NULL }; 23191@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>; 23192 { 23193 mp_string cm; 23194 int old_selector = mp->selector; 23195 mp->selector = new_string; 23196 mp_print_cmd_mod (mp, cur_cmd(), cur_mod()); 23197 mp->selector = old_selector; 23198 cm = mp_make_string(mp); 23199 mp_snprintf(msg, 256, "%s expression can't begin with `%s'", s, mp_str(mp, cm)); 23200 delete_str_ref(cm); 23201 } 23202 mp_back_input (mp); 23203 set_cur_sym(NULL); 23204 set_cur_cmd((mp_variable_type)mp_numeric_token); 23205 set_cur_mod_number (zero_t); 23206 mp_ins_error (mp, msg, hlp, true); 23207 save_flag = mp->var_flag; 23208 mp->var_flag = 0; 23209 mp_get_x_next (mp); 23210 mp->var_flag = save_flag; 23211} 23212 23213 23214@ The |stash_in| subroutine puts the current (numeric) expression into a field 23215within a ``big node.'' 23216 23217@c 23218static void mp_stash_in (MP mp, mp_node p) { 23219 mp_value_node q; /* temporary register */ 23220 mp_type (p) = mp->cur_exp.type; 23221 if (mp->cur_exp.type == mp_known) { 23222 set_value_number (p, cur_exp_value_number ()); 23223 } else { 23224 if (mp->cur_exp.type == mp_independent) { 23225 /* Stash an independent |cur_exp| into a big node */ 23226 /* In rare cases the current expression can become |independent|. There 23227 may be many dependency lists pointing to such an independent capsule, 23228 so we can't simply move it into place within a big node. Instead, 23229 we copy it, then recycle it. */ 23230 q = mp_single_dependency (mp, cur_exp_node ()); 23231 if (q == mp->dep_final) { 23232 mp_type (p) = mp_known; 23233 set_value_number (p, zero_t); 23234 mp_free_dep_node (mp, q); 23235 } else { 23236 mp_new_dep (mp, p, mp_dependent, q); 23237 } 23238 mp_recycle_value (mp, cur_exp_node ()); 23239 mp_free_value_node (mp, cur_exp_node ()); 23240 } else { 23241 set_dep_list ((mp_value_node) p, 23242 dep_list ((mp_value_node) cur_exp_node ())); 23243 set_prev_dep ((mp_value_node) p, 23244 prev_dep ((mp_value_node) cur_exp_node ())); 23245 set_mp_link (prev_dep ((mp_value_node) p), p); 23246 mp_free_dep_node (mp, (mp_value_node) cur_exp_node ()); 23247 } 23248 } 23249 mp->cur_exp.type = mp_vacuous; 23250} 23251 23252@ The most difficult part of |scan_primary| has been saved for last, since 23253it was necessary to build up some confidence first. We can now face the task 23254of scanning a variable. 23255 23256As we scan a variable, we build a token list containing the relevant 23257names and subscript values, simultaneously following along in the 23258``collective'' structure to see if we are actually dealing with a macro 23259instead of a value. 23260 23261The local variables |pre_head| and |post_head| will point to the beginning 23262of the prefix and suffix lists; |tail| will point to the end of the list 23263that is currently growing. 23264 23265Another local variable, |tt|, contains partial information about the 23266declared type of the variable-so-far. If |tt>=mp_unsuffixed_macro|, the 23267relation |tt=mp_type(q)| will always hold. If |tt=undefined|, the routine 23268doesn't bother to update its information about type. And if 23269|undefined<tt<mp_unsuffixed_macro|, the precise value of |tt| isn't critical. 23270 23271@ @<Scan a variable primary...@>= 23272{ 23273 mp_node p, q; /* for list manipulation */ 23274 mp_node t; /* a token */ 23275 mp_node pre_head, post_head, tail; /* prefix and suffix list variables */ 23276 quarterword tt; /* approximation to the type of the variable-so-far */ 23277 mp_node macro_ref = 0; /* reference count for a suffixed macro */ 23278 pre_head = mp_get_symbolic_node (mp); 23279 tail = pre_head; 23280 post_head = NULL; 23281 tt = mp_vacuous; 23282 while (1) { 23283 t = mp_cur_tok (mp); 23284 mp_link (tail) = t; 23285 if (tt != mp_undefined) { 23286 /* Find the approximate type |tt| and corresponding~|q| */ 23287 /* Every time we call |get_x_next|, there's a chance that the variable we've 23288 been looking at will disappear. Thus, we cannot safely keep |q| pointing 23289 into the variable structure; we need to start searching from the root each 23290 time. */ 23291 mp_sym qq; 23292 p = mp_link (pre_head); 23293 qq = mp_sym_sym (p); 23294 tt = mp_undefined; 23295 if (eq_type (qq) % mp_outer_tag == mp_tag_token) { 23296 q = equiv_node (qq); 23297 if (q == NULL) 23298 goto DONE2; 23299 while (1) { 23300 p = mp_link (p); 23301 if (p == NULL) { 23302 tt = mp_type (q); 23303 goto DONE2; 23304 } 23305 if (mp_type (q) != mp_structured) 23306 goto DONE2; 23307 q = mp_link (attr_head (q)); /* the |collective_subscript| attribute */ 23308 if (mp_type (p) == mp_symbol_node) { /* it's not a subscript */ 23309 do { 23310 q = mp_link (q); 23311 } while (!(hashloc (q) >= mp_sym_sym (p))); 23312 if (hashloc (q) > mp_sym_sym (p)) 23313 goto DONE2; 23314 } 23315 } 23316 } 23317 DONE2: 23318 23319 if (tt >= mp_unsuffixed_macro) { 23320 /* Either begin an unsuffixed macro call or 23321 prepare for a suffixed one */ 23322 mp_link (tail) = NULL; 23323 if (tt > mp_unsuffixed_macro) { /* |tt=mp_suffixed_macro| */ 23324 post_head = mp_get_symbolic_node (mp); 23325 tail = post_head; 23326 mp_link (tail) = t; 23327 tt = mp_undefined; 23328 macro_ref = value_node (q); 23329 add_mac_ref (macro_ref); 23330 } else { 23331 /* Set up unsuffixed macro call and |goto restart| */ 23332 /* The only complication associated with macro calling is that the prefix 23333 and ``at'' parameters must be packaged in an appropriate list of lists. */ 23334 p = mp_get_symbolic_node (mp); 23335 set_mp_sym_sym (pre_head, mp_link (pre_head)); 23336 mp_link (pre_head) = p; 23337 set_mp_sym_sym (p, t); 23338 mp_macro_call (mp, value_node (q), pre_head, NULL); 23339 mp_get_x_next (mp); 23340 goto RESTART; 23341 } 23342 } 23343 } 23344 mp_get_x_next (mp); 23345 tail = t; 23346 if (cur_cmd() == mp_left_bracket) { 23347 /* Scan for a subscript; replace |cur_cmd| by |numeric_token| if found */ 23348 mp_get_x_next (mp); 23349 mp_scan_expression (mp); 23350 if (cur_cmd() != mp_right_bracket) { 23351 /* Put the left bracket and the expression back to be rescanned */ 23352 /* The left bracket that we thought was introducing a subscript might have 23353 actually been the left bracket in a mediation construction like `\.{x[a,b]}'. 23354 So we don't issue an error message at this point; but we do want to back up 23355 so as to avoid any embarrassment about our incorrect assumption. */ 23356 mp_back_input (mp); /* that was the token following the current expression */ 23357 mp_back_expr (mp); 23358 set_cur_cmd((mp_variable_type)mp_left_bracket); 23359 set_cur_mod_number(zero_t); 23360 set_cur_sym(mp->frozen_left_bracket); 23361 23362 } else { 23363 if (mp->cur_exp.type != mp_known) 23364 mp_bad_subscript (mp); 23365 set_cur_cmd((mp_variable_type)mp_numeric_token); 23366 set_cur_mod_number(cur_exp_value_number ()); 23367 set_cur_sym(NULL); 23368 } 23369 } 23370 if (cur_cmd() > mp_max_suffix_token) 23371 break; 23372 if (cur_cmd() < mp_min_suffix_token) 23373 break; 23374 } /* now |cur_cmd| is |internal_quantity|, |tag_token|, or |numeric_token| */ 23375 /* Handle unusual cases that masquerade as variables, and |goto restart| or 23376 |goto done| if appropriate; otherwise make a copy of the variable and |goto done| */ 23377 /* If the variable does exist, we also need to check 23378 for a few other special cases before deciding that a plain old ordinary 23379 variable has, indeed, been scanned. */ 23380 if (post_head != NULL) { 23381 /* Set up suffixed macro call and |goto restart| */ 23382 /* If the ``variable'' that turned out to be a suffixed macro no longer exists, 23383 we don't care, because we have reserved a pointer (|macro_ref|) to its 23384 token list. */ 23385 mp_back_input (mp); 23386 p = mp_get_symbolic_node (mp); 23387 q = mp_link (post_head); 23388 set_mp_sym_sym (pre_head, mp_link (pre_head)); 23389 mp_link (pre_head) = post_head; 23390 set_mp_sym_sym (post_head, q); 23391 mp_link (post_head) = p; 23392 set_mp_sym_sym (p, mp_link (q)); 23393 mp_link (q) = NULL; 23394 mp_macro_call (mp, macro_ref, pre_head, NULL); 23395 decr_mac_ref (macro_ref); 23396 mp_get_x_next (mp); 23397 goto RESTART; 23398 } 23399 q = mp_link (pre_head); 23400 mp_free_symbolic_node (mp, pre_head); 23401 if (cur_cmd() == my_var_flag) { 23402 mp->cur_exp.type = mp_token_list; 23403 set_cur_exp_node (q); 23404 goto DONE; 23405 } 23406 p = mp_find_variable (mp, q); 23407 if (p != NULL) { 23408 mp_make_exp_copy (mp, p); 23409 } else { 23410 mp_value new_expr; 23411 const char *hlp[] = { 23412 "While I was evaluating the suffix of this variable,", 23413 "something was redefined, and it's no longer a variable!", 23414 "In order to get back on my feet, I've inserted `0' instead.", 23415 NULL }; 23416 char *msg = mp_obliterated (mp, q); 23417 memset(&new_expr,0,sizeof(mp_value)); 23418 new_number(new_expr.data.n); 23419 set_number_to_zero(new_expr.data.n); 23420 mp_back_error (mp, msg, hlp, true); 23421 free(msg); 23422 mp_get_x_next (mp); 23423 mp_flush_cur_exp (mp, new_expr); 23424 } 23425 mp_flush_node_list (mp, q); 23426 goto DONE; 23427} 23428 23429 23430@ Here's a routine that puts the current expression back to be read again. 23431 23432@c 23433static void mp_back_expr (MP mp) { 23434 mp_node p; /* capsule token */ 23435 p = mp_stash_cur_exp (mp); 23436 mp_link (p) = NULL; 23437 back_list (p); 23438} 23439 23440 23441@ Unknown subscripts lead to the following error message. 23442 23443@c 23444static void mp_bad_subscript (MP mp) { 23445 mp_value new_expr; 23446 const char *hlp[] = { 23447 "A bracketed subscript must have a known numeric value;", 23448 "unfortunately, what I found was the value that appears just", 23449 "above this error message. So I'll try a zero subscript.", 23450 NULL }; 23451 memset(&new_expr,0,sizeof(mp_value)); 23452 new_number(new_expr.data.n); 23453 mp_disp_err(mp, NULL); 23454 mp_error (mp, "Improper subscript has been replaced by zero", hlp, true); 23455@.Improper subscript...@>; 23456 mp_flush_cur_exp (mp, new_expr); 23457} 23458 23459 23460@ How do things stand now? Well, we have scanned an entire variable name, 23461including possible subscripts and/or attributes; |cur_cmd|, |cur_mod|, and 23462|cur_sym| represent the token that follows. If |post_head=NULL|, a 23463token list for this variable name starts at |mp_link(pre_head)|, with all 23464subscripts evaluated. But if |post_head<>NULL|, the variable turned out 23465to be a suffixed macro; |pre_head| is the head of the prefix list, while 23466|post_head| is the head of a token list containing both `\.{\AT!}' and 23467the suffix. 23468 23469Our immediate problem is to see if this variable still exists. (Variable 23470structures can change drastically whenever we call |get_x_next|; users 23471aren't supposed to do this, but the fact that it is possible means that 23472we must be cautious.) 23473 23474The following procedure creates an error message for when a variable 23475unexpectedly disappears. 23476 23477@c 23478static char *mp_obliterated (MP mp, mp_node q) { 23479 char msg[256]; 23480 mp_string sname; 23481 int old_setting = mp->selector; 23482 mp->selector = new_string; 23483 mp_show_token_list (mp, q, NULL, 1000, 0); 23484 sname = mp_make_string(mp); 23485 mp->selector = old_setting; 23486 mp_snprintf(msg, 256, "Variable %s has been obliterated", mp_str(mp, sname)); 23487@.Variable...obliterated@>; 23488 delete_str_ref(sname); 23489 return xstrdup(msg); 23490} 23491 23492 23493@ Our remaining job is simply to make a copy of the value that has been 23494found. Some cases are harder than others, but complexity arises solely 23495because of the multiplicity of possible cases. 23496 23497@<Declare the procedure called |make_exp_copy|@>= 23498@<Declare subroutines needed by |make_exp_copy|@>; 23499static void mp_make_exp_copy (MP mp, mp_node p) { 23500 mp_node t; /* register(s) for list manipulation */ 23501 mp_value_node q; 23502RESTART: 23503 mp->cur_exp.type = mp_type (p); 23504 switch (mp->cur_exp.type) { 23505 case mp_vacuous: 23506 case mp_boolean_type: 23507 case mp_known: 23508 set_cur_exp_value_number (value_number (p)); 23509 break; 23510 case unknown_types: 23511 t = mp_new_ring_entry (mp, p); 23512 set_cur_exp_node (t); 23513 break; 23514 case mp_string_type: 23515 set_cur_exp_str (value_str (p)); 23516 break; 23517 case mp_picture_type: 23518 set_cur_exp_node (value_node (p)); 23519 add_edge_ref (cur_exp_node ()); 23520 break; 23521 case mp_pen_type: 23522 set_cur_exp_knot (copy_pen (value_knot (p))); 23523 break; 23524 case mp_path_type: 23525 set_cur_exp_knot (mp_copy_path (mp, value_knot (p))); 23526 break; 23527 case mp_transform_type: 23528 case mp_color_type: 23529 case mp_cmykcolor_type: 23530 case mp_pair_type: 23531 /* Copy the big node |p| */ 23532 /* The most tedious case arises when the user refers to a 23533 \&{pair}, \&{color}, or \&{transform} variable; we must copy several fields, 23534 each of which can be |independent|, |dependent|, |mp_proto_dependent|, 23535 or |known|. */ 23536 if (value_node (p) == NULL) { 23537 switch (mp_type (p)) { 23538 case mp_pair_type: 23539 mp_init_pair_node (mp, p); 23540 break; 23541 case mp_color_type: 23542 mp_init_color_node (mp, p); 23543 break; 23544 case mp_cmykcolor_type: 23545 mp_init_cmykcolor_node (mp, p); 23546 break; 23547 case mp_transform_type: 23548 mp_init_transform_node (mp, p); 23549 break; 23550 default: /* there are no other valid cases, but please the compiler */ 23551 break; 23552 } 23553 } 23554 t = mp_get_value_node (mp); 23555 mp_name_type (t) = mp_capsule; 23556 q = (mp_value_node)value_node (p); 23557 switch (mp->cur_exp.type) { 23558 case mp_pair_type: 23559 mp_init_pair_node (mp, t); 23560 mp_install (mp, y_part (value_node (t)), y_part (q)); 23561 mp_install (mp, x_part (value_node (t)), x_part (q)); 23562 break; 23563 case mp_color_type: 23564 mp_init_color_node (mp, t); 23565 mp_install (mp, blue_part (value_node (t)), blue_part (q)); 23566 mp_install (mp, green_part (value_node (t)), green_part (q)); 23567 mp_install (mp, red_part (value_node (t)), red_part (q)); 23568 break; 23569 case mp_cmykcolor_type: 23570 mp_init_cmykcolor_node (mp, t); 23571 mp_install (mp, black_part (value_node (t)), black_part (q)); 23572 mp_install (mp, yellow_part (value_node (t)), yellow_part (q)); 23573 mp_install (mp, magenta_part (value_node (t)), magenta_part (q)); 23574 mp_install (mp, cyan_part (value_node (t)), cyan_part (q)); 23575 break; 23576 case mp_transform_type: 23577 mp_init_transform_node (mp, t); 23578 mp_install (mp, yy_part (value_node (t)), yy_part (q)); 23579 mp_install (mp, yx_part (value_node (t)), yx_part (q)); 23580 mp_install (mp, xy_part (value_node (t)), xy_part (q)); 23581 mp_install (mp, xx_part (value_node (t)), xx_part (q)); 23582 mp_install (mp, ty_part (value_node (t)), ty_part (q)); 23583 mp_install (mp, tx_part (value_node (t)), tx_part (q)); 23584 break; 23585 default: /* there are no other valid cases, but please the compiler */ 23586 break; 23587 } 23588 set_cur_exp_node (t); 23589 break; 23590 case mp_dependent: 23591 case mp_proto_dependent: 23592 mp_encapsulate (mp, 23593 mp_copy_dep_list (mp, 23594 (mp_value_node) dep_list ((mp_value_node) 23595 p))); 23596 break; 23597 case mp_numeric_type: 23598 mp_new_indep (mp, p); 23599 goto RESTART; 23600 break; 23601 case mp_independent: 23602 q = mp_single_dependency (mp, p); 23603 if (q == mp->dep_final) { 23604 mp->cur_exp.type = mp_known; 23605 set_cur_exp_value_number (zero_t); 23606 mp_free_dep_node (mp, q); 23607 } else { 23608 mp->cur_exp.type = mp_dependent; 23609 mp_encapsulate (mp, q); 23610 } 23611 break; 23612 default: 23613 mp_confusion (mp, "copy"); 23614@:this can't happen copy}{\quad copy@>; 23615 break; 23616 } 23617} 23618 23619 23620@ The |encapsulate| subroutine assumes that |dep_final| is the 23621tail of dependency list~|p|. 23622 23623@<Declare subroutines needed by |make_exp_copy|@>= 23624static void mp_encapsulate (MP mp, mp_value_node p) { 23625 mp_node q = mp_get_value_node (mp); 23626 FUNCTION_TRACE2 ("mp_encapsulate(%p)\n", p); 23627 mp_name_type (q) = mp_capsule; 23628 mp_new_dep (mp, q, mp->cur_exp.type, p); 23629 set_cur_exp_node (q); 23630} 23631 23632@ The |install| procedure copies a numeric field~|q| into field~|r| of 23633a big node that will be part of a capsule. 23634 23635@<Declare subroutines needed by |make_exp_copy|@>= 23636static void mp_install (MP mp, mp_node r, mp_node q) { 23637 mp_value_node p; /* temporary register */ 23638 if (mp_type (q) == mp_known) { 23639 mp_type (r) = mp_known; 23640 set_value_number (r, value_number (q)); 23641 } else if (mp_type (q) == mp_independent) { 23642 p = mp_single_dependency (mp, q); 23643 if (p == mp->dep_final) { 23644 mp_type (r) = mp_known; 23645 set_value_number (r, zero_t); 23646 mp_free_dep_node (mp, p); 23647 } else { 23648 mp_new_dep (mp, r, mp_dependent, p); 23649 } 23650 } else { 23651 mp_new_dep (mp, r, mp_type (q), 23652 mp_copy_dep_list (mp, (mp_value_node) dep_list ((mp_value_node) 23653 q))); 23654 } 23655} 23656 23657 23658@ Here is a comparatively simple routine that is used to scan the 23659\&{suffix} parameters of a macro. 23660 23661@<Declare the basic parsing subroutines@>= 23662static void mp_scan_suffix (MP mp) { 23663 mp_node h, t; /* head and tail of the list being built */ 23664 mp_node p; /* temporary register */ 23665 h = mp_get_symbolic_node (mp); 23666 t = h; 23667 while (1) { 23668 if (cur_cmd() == mp_left_bracket) { 23669 /* Scan a bracketed subscript and set |cur_cmd:=numeric_token| */ 23670 mp_get_x_next (mp); 23671 mp_scan_expression (mp); 23672 if (mp->cur_exp.type != mp_known) 23673 mp_bad_subscript (mp); 23674 if (cur_cmd() != mp_right_bracket) { 23675 const char *hlp[] = { 23676 "I've seen a `[' and a subscript value, in a suffix,", 23677 "so a right bracket should have come next.", 23678 "I shall pretend that one was there.", 23679 NULL }; 23680 mp_back_error (mp, "Missing `]' has been inserted", hlp, true); 23681 } 23682 set_cur_cmd((mp_variable_type)mp_numeric_token); 23683 set_cur_mod_number(cur_exp_value_number ()); 23684 23685 } 23686 if (cur_cmd() == mp_numeric_token) { 23687 mp_number arg1; 23688 new_number (arg1); 23689 number_clone (arg1, cur_mod_number()); 23690 p = mp_new_num_tok (mp, arg1); 23691 free_number (arg1); 23692 } else if ((cur_cmd() == mp_tag_token) || (cur_cmd() == mp_internal_quantity)) { 23693 p = mp_get_symbolic_node (mp); 23694 set_mp_sym_sym (p, cur_sym()); 23695 mp_name_type (p) = cur_sym_mod(); 23696 } else { 23697 break; 23698 } 23699 mp_link (t) = p; 23700 t = p; 23701 mp_get_x_next (mp); 23702 } 23703 set_cur_exp_node (mp_link (h)); 23704 mp_free_symbolic_node (mp, h); 23705 mp->cur_exp.type = mp_token_list; 23706} 23707 23708@* Parsing secondary and higher expressions. 23709 23710After the intricacies of |scan_primary|\kern-1pt, 23711the |scan_secondary| routine is 23712refreshingly simple. It's not trivial, but the operations are relatively 23713straightforward; the main difficulty is, again, that expressions and data 23714structures might change drastically every time we call |get_x_next|, so a 23715cautious approach is mandatory. For example, a macro defined by 23716\&{primarydef} might have disappeared by the time its second argument has 23717been scanned; we solve this by increasing the reference count of its token 23718list, so that the macro can be called even after it has been clobbered. 23719 23720@<Declare the basic parsing subroutines@>= 23721static void mp_scan_secondary (MP mp) { 23722 mp_node p; /* for list manipulation */ 23723 halfword c, d; /* operation codes or modifiers */ 23724 mp_node cc = NULL; 23725 mp_sym mac_name = NULL; /* token defined with \&{primarydef} */ 23726RESTART: 23727 if ((cur_cmd() < mp_min_primary_command) || 23728 (cur_cmd() > mp_max_primary_command)) 23729 mp_bad_exp (mp, "A secondary"); 23730@.A secondary expression...@>; 23731 mp_scan_primary (mp); 23732CONTINUE: 23733 if (cur_cmd() <= mp_max_secondary_command && 23734 cur_cmd() >= mp_min_secondary_command) { 23735 p = mp_stash_cur_exp (mp); 23736 d = cur_cmd(); 23737 c = cur_mod(); 23738 if (d == mp_secondary_primary_macro) { 23739 cc = cur_mod_node(); 23740 mac_name = cur_sym(); 23741 add_mac_ref (cc); 23742 } 23743 mp_get_x_next (mp); 23744 mp_scan_primary (mp); 23745 if (d != mp_secondary_primary_macro) { 23746 mp_do_binary (mp, p, c); 23747 } else { 23748 mp_back_input (mp); 23749 mp_binary_mac (mp, p, cc, mac_name); 23750 decr_mac_ref (cc); 23751 mp_get_x_next (mp); 23752 goto RESTART; 23753 } 23754 goto CONTINUE; 23755 } 23756} 23757 23758 23759@ The following procedure calls a macro that has two parameters, 23760|p| and |cur_exp|. 23761 23762@c 23763static void mp_binary_mac (MP mp, mp_node p, mp_node c, mp_sym n) { 23764 mp_node q, r; /* nodes in the parameter list */ 23765 q = mp_get_symbolic_node (mp); 23766 r = mp_get_symbolic_node (mp); 23767 mp_link (q) = r; 23768 set_mp_sym_sym (q, p); 23769 set_mp_sym_sym (r, mp_stash_cur_exp (mp)); 23770 mp_macro_call (mp, c, q, n); 23771} 23772 23773 23774@ The next procedure, |scan_tertiary|, is pretty much the same deal. 23775 23776@<Declare the basic parsing subroutines@>= 23777static void mp_scan_tertiary (MP mp) { 23778 mp_node p; /* for list manipulation */ 23779 halfword c, d; /* operation codes or modifiers */ 23780 mp_node cc = NULL; 23781 mp_sym mac_name = NULL; /* token defined with \&{secondarydef} */ 23782RESTART: 23783 if ((cur_cmd() < mp_min_primary_command) || 23784 (cur_cmd() > mp_max_primary_command)) 23785 mp_bad_exp (mp, "A tertiary"); 23786@.A tertiary expression...@>; 23787 mp_scan_secondary (mp); 23788CONTINUE: 23789 if (cur_cmd() <= mp_max_tertiary_command) { 23790 if (cur_cmd() >= mp_min_tertiary_command) { 23791 p = mp_stash_cur_exp (mp); 23792 c = cur_mod(); 23793 d = cur_cmd(); 23794 if (d == mp_tertiary_secondary_macro) { 23795 cc = cur_mod_node(); 23796 mac_name = cur_sym(); 23797 add_mac_ref (cc); 23798 } 23799 mp_get_x_next (mp); 23800 mp_scan_secondary (mp); 23801 if (d != mp_tertiary_secondary_macro) { 23802 mp_do_binary (mp, p, c); 23803 } else { 23804 mp_back_input (mp); 23805 mp_binary_mac (mp, p, cc, mac_name); 23806 decr_mac_ref (cc); 23807 mp_get_x_next (mp); 23808 goto RESTART; 23809 } 23810 goto CONTINUE; 23811 } 23812 } 23813} 23814 23815 23816@ Finally we reach the deepest level in our quartet of parsing routines. 23817This one is much like the others; but it has an extra complication from 23818paths, which materialize here. 23819 23820@<Declare the basic parsing subroutines@>= 23821static int mp_scan_path (MP mp); 23822static void mp_scan_expression (MP mp) { 23823 int my_var_flag; /* initial value of |var_flag| */ 23824 my_var_flag = mp->var_flag; 23825 check_expansion_depth(); 23826RESTART: 23827 if ((cur_cmd() < mp_min_primary_command) || 23828 (cur_cmd() > mp_max_primary_command)) 23829 mp_bad_exp (mp, "An"); 23830@.An expression...@>; 23831 mp_scan_tertiary (mp); 23832CONTINUE: 23833 if (cur_cmd() <= mp_max_expression_command) { 23834 if (cur_cmd() >= mp_min_expression_command) { 23835 if ((cur_cmd() != mp_equals) || (my_var_flag != mp_assignment)) { 23836 mp_node p; /* for list manipulation */ 23837 mp_node cc = NULL; 23838 halfword c; 23839 halfword d; /* operation codes or modifiers */ 23840 mp_sym mac_name; /* token defined with \&{tertiarydef} */ 23841 mac_name = NULL; 23842 p = mp_stash_cur_exp (mp); 23843 d = cur_cmd(); 23844 c = cur_mod(); 23845 if (d == mp_expression_tertiary_macro) { 23846 cc = cur_mod_node(); 23847 mac_name = cur_sym(); 23848 add_mac_ref (cc); 23849 } 23850 if ((d < mp_ampersand) || ((d == mp_ampersand) && 23851 ((mp_type (p) == mp_pair_type) 23852 || (mp_type (p) == mp_path_type)))) { 23853 /* Scan a path construction operation; but |return| if |p| has the wrong type */ 23854 23855 mp_unstash_cur_exp (mp, p); 23856 if (!mp_scan_path(mp)) { 23857 mp->expand_depth_count--; 23858 return; 23859 } 23860 } else { 23861 mp_get_x_next (mp); 23862 mp_scan_tertiary (mp); 23863 if (d != mp_expression_tertiary_macro) { 23864 mp_do_binary (mp, p, c); 23865 } else { 23866 mp_back_input (mp); 23867 mp_binary_mac (mp, p, cc, mac_name); 23868 decr_mac_ref (cc); 23869 mp_get_x_next (mp); 23870 goto RESTART; 23871 } 23872 } 23873 goto CONTINUE; 23874 } 23875 } 23876 } 23877 mp->expand_depth_count--; 23878} 23879 23880 23881@ The reader should review the data structure conventions for paths before 23882hoping to understand the next part of this code. 23883 23884@d min_tension three_quarter_unit_t 23885 23886@<Declare the basic parsing subroutines@>= 23887static void force_valid_tension_setting(MP mp) { 23888 if ((mp->cur_exp.type != mp_known) || number_less(cur_exp_value_number (), min_tension)) { 23889 mp_value new_expr; 23890 const char *hlp[] = { 23891 "The expression above should have been a number >=3/4.", 23892 NULL }; 23893 memset(&new_expr,0,sizeof(mp_value)); 23894 new_number(new_expr.data.n); 23895 mp_disp_err(mp, NULL); 23896 number_clone(new_expr.data.n, unity_t); 23897 mp_back_error (mp, "Improper tension has been set to 1", hlp, true); 23898 mp_get_x_next (mp); 23899 mp_flush_cur_exp (mp, new_expr); 23900 } 23901} 23902static int mp_scan_path (MP mp) { 23903 mp_knot path_p, path_q, r; 23904 mp_knot pp, qq; 23905 halfword d; /* operation code or modifier */ 23906 boolean cycle_hit; /* did a path expression just end with `\&{cycle}'? */ 23907 mp_number x, y; /* explicit coordinates or tension at a path join */ 23908 int t; /* knot type following a path join */ 23909 t = 0; 23910 cycle_hit = false; 23911 /* Convert the left operand, |p|, into a partial path ending at~|q|; 23912 but |return| if |p| doesn't have a suitable type */ 23913 if (mp->cur_exp.type == mp_pair_type) 23914 path_p = mp_pair_to_knot (mp); 23915 else if (mp->cur_exp.type == mp_path_type) 23916 path_p = cur_exp_knot (); 23917 else 23918 return 0; 23919 path_q = path_p; 23920 while (mp_next_knot (path_q) != path_p) 23921 path_q = mp_next_knot (path_q); 23922 if (mp_left_type (path_p) != mp_endpoint) { /* open up a cycle */ 23923 r = mp_copy_knot (mp, path_p); 23924 mp_next_knot (path_q) = r; 23925 path_q = r; 23926 } 23927 mp_left_type (path_p) = mp_open; 23928 mp_right_type (path_q) = mp_open; 23929 23930 new_number (y); 23931 new_number (x); 23932 23933CONTINUE_PATH: 23934 /* Determine the path join parameters; 23935 but |goto finish_path| if there's only a direction specifier */ 23936 /* At this point |cur_cmd| is either |ampersand|, |left_brace|, or |path_join|. */ 23937 23938 if (cur_cmd() == mp_left_brace) { 23939 /* Put the pre-join direction information into node |q| */ 23940 /* At this point |mp_right_type(q)| is usually |open|, but it may have been 23941 set to some other value by a previous operation. We must maintain 23942 the value of |mp_right_type(q)| in cases such as 23943 `\.{..\{curl2\}z\{0,0\}..}'. */ 23944 t = mp_scan_direction (mp); 23945 if (t != mp_open) { 23946 mp_right_type (path_q) = (unsigned short) t; 23947 number_clone(path_q->right_given, cur_exp_value_number ()); 23948 if (mp_left_type (path_q) == mp_open) { 23949 mp_left_type (path_q) = (unsigned short) t; 23950 number_clone(path_q->left_given, cur_exp_value_number ()); 23951 } /* note that |left_given(q)=left_curl(q)| */ 23952 } 23953 } 23954 d = cur_cmd(); 23955 if (d == mp_path_join) { 23956 /* Determine the tension and/or control points */ 23957 mp_get_x_next (mp); 23958 if (cur_cmd() == mp_tension) { 23959 /* Set explicit tensions */ 23960 mp_get_x_next (mp); 23961 set_number_from_scaled (y, cur_cmd()); 23962 if (cur_cmd() == mp_at_least) 23963 mp_get_x_next (mp); 23964 mp_scan_primary (mp); 23965 force_valid_tension_setting(mp); 23966 if (number_to_scaled (y) == mp_at_least) { 23967 if (is_number(cur_exp_value_number())) 23968 number_negate (cur_exp_value_number()); 23969 } 23970 number_clone(path_q->right_tension, cur_exp_value_number ()); 23971 if (cur_cmd() == mp_and_command) { 23972 mp_get_x_next (mp); 23973 set_number_from_scaled (y, cur_cmd()); 23974 if (cur_cmd() == mp_at_least) 23975 mp_get_x_next (mp); 23976 mp_scan_primary (mp); 23977 force_valid_tension_setting(mp); 23978 if (number_to_scaled (y) == mp_at_least) { 23979 if (is_number(cur_exp_value_number())) 23980 number_negate (cur_exp_value_number()); 23981 } 23982 } 23983 number_clone (y, cur_exp_value_number ()); 23984 23985 } else if (cur_cmd() == mp_controls) { 23986 /* Set explicit control points */ 23987 mp_right_type (path_q) = mp_explicit; 23988 t = mp_explicit; 23989 mp_get_x_next (mp); 23990 mp_scan_primary (mp); 23991 mp_known_pair (mp); 23992 number_clone (path_q->right_x, mp->cur_x); 23993 number_clone (path_q->right_y, mp->cur_y); 23994 if (cur_cmd() != mp_and_command) { 23995 number_clone (x, path_q->right_x); 23996 number_clone (y, path_q->right_y); 23997 } else { 23998 mp_get_x_next (mp); 23999 mp_scan_primary (mp); 24000 mp_known_pair (mp); 24001 number_clone (x, mp->cur_x); 24002 number_clone (y, mp->cur_y); 24003 } 24004 24005 } else { 24006 set_number_to_unity(path_q->right_tension); 24007 set_number_to_unity(y); 24008 mp_back_input (mp); /* default tension */ 24009 goto DONE; 24010 }; 24011 if (cur_cmd() != mp_path_join) { 24012 const char *hlp[] = { "A path join command should end with two dots.", NULL}; 24013 mp_back_error (mp, "Missing `..' has been inserted", hlp, true); 24014 } 24015 DONE: 24016 ; 24017 } else if (d != mp_ampersand) { 24018 goto FINISH_PATH; 24019 } 24020 mp_get_x_next (mp); 24021 if (cur_cmd() == mp_left_brace) { 24022 /* Put the post-join direction information into |x| and |t| */ 24023 /* Since |left_tension| and |mp_left_y| share the same position in knot nodes, 24024 and since |left_given| is similarly equivalent to |left_x|, we use 24025 |x| and |y| to hold the given direction and tension information when 24026 there are no explicit control points. */ 24027 t = mp_scan_direction (mp); 24028 if (mp_right_type (path_q) != mp_explicit) 24029 number_clone (x, cur_exp_value_number ()); 24030 else 24031 t = mp_explicit; /* the direction information is superfluous */ 24032 24033 } else if (mp_right_type (path_q) != mp_explicit) { 24034 t = mp_open; 24035 set_number_to_zero(x); 24036 } 24037 24038 if (cur_cmd() == mp_cycle) { 24039 /* Get ready to close a cycle */ 24040 /* If a person tries to define an entire path by saying `\.{(x,y)\&cycle}', 24041 we silently change the specification to `\.{(x,y)..cycle}', since a cycle 24042 shouldn't have length zero. */ 24043 cycle_hit = true; 24044 mp_get_x_next (mp); 24045 pp = path_p; 24046 qq = path_p; 24047 if (d == mp_ampersand) { 24048 if (path_p == path_q) { 24049 d = mp_path_join; 24050 set_number_to_unity(path_q->right_tension); 24051 set_number_to_unity(y); 24052 } 24053 } 24054 } else { 24055 mp_scan_tertiary (mp); 24056 /* Convert the right operand, |cur_exp|, 24057 into a partial path from |pp| to~|qq| */ 24058 if (mp->cur_exp.type != mp_path_type) 24059 pp = mp_pair_to_knot (mp); 24060 else 24061 pp = cur_exp_knot (); 24062 qq = pp; 24063 while (mp_next_knot (qq) != pp) 24064 qq = mp_next_knot (qq); 24065 if (mp_left_type (pp) != mp_endpoint) { /* open up a cycle */ 24066 r = mp_copy_knot (mp, pp); 24067 mp_next_knot (qq) = r; 24068 qq = r; 24069 } 24070 mp_left_type (pp) = mp_open; 24071 mp_right_type (qq) = mp_open; 24072 } 24073 /* Join the partial paths and reset |p| and |q| to the head and tail 24074 of the result */ 24075 if (d == mp_ampersand) { 24076 if (!(number_equal (path_q->x_coord, pp->x_coord)) || 24077 !(number_equal (path_q->y_coord, pp->y_coord))) { 24078 const char *hlp[] = { 24079 "When you join paths `p&q', the ending point of p", 24080 "must be exactly equal to the starting point of q.", 24081 "So I'm going to pretend that you said `p..q' instead.", 24082 NULL }; 24083 mp_back_error (mp, "Paths don't touch; `&' will be changed to `..'", hlp, true); 24084@.Paths don't touch@>; 24085 mp_get_x_next (mp); 24086 d = mp_path_join; 24087 set_number_to_unity (path_q->right_tension); 24088 set_number_to_unity (y); 24089 } 24090 } 24091 /* Plug an opening in |mp_right_type(pp)|, if possible */ 24092 if (mp_right_type (pp) == mp_open) { 24093 if ((t == mp_curl) || (t == mp_given)) { 24094 mp_right_type (pp) = (unsigned short) t; 24095 number_clone (pp->right_given, x); 24096 } 24097 } 24098 if (d == mp_ampersand) { 24099 /* Splice independent paths together */ 24100 if (mp_left_type (path_q) == mp_open) 24101 if (mp_right_type (path_q) == mp_open) { 24102 mp_left_type (path_q) = mp_curl; 24103 set_number_to_unity(path_q->left_curl); 24104 } 24105 if (mp_right_type (pp) == mp_open) 24106 if (t == mp_open) { 24107 mp_right_type (pp) = mp_curl; 24108 set_number_to_unity(pp->right_curl); 24109 } 24110 mp_right_type (path_q) = mp_right_type (pp); 24111 mp_next_knot (path_q) = mp_next_knot (pp); 24112 number_clone (path_q->right_x, pp->right_x); 24113 number_clone (path_q->right_y, pp->right_y); 24114 mp_xfree (pp); 24115 if (qq == pp) 24116 qq = path_q; 24117 24118 } else { 24119 /* Plug an opening in |mp_right_type(q)|, if possible */ 24120 if (mp_right_type (path_q) == mp_open) { 24121 if ((mp_left_type (path_q) == mp_curl) || (mp_left_type (path_q) == mp_given)) { 24122 mp_right_type (path_q) = mp_left_type (path_q); 24123 number_clone(path_q->right_given, path_q->left_given); 24124 } 24125 } 24126 24127 mp_next_knot (path_q) = pp; 24128 number_clone (pp->left_y, y); 24129 if (t != mp_open) { 24130 number_clone (pp->left_x, x); 24131 mp_left_type (pp) = (unsigned short) t; 24132 }; 24133 } 24134 path_q = qq; 24135 24136 if (cur_cmd() >= mp_min_expression_command) 24137 if (cur_cmd() <= mp_ampersand) 24138 if (!cycle_hit) 24139 goto CONTINUE_PATH; 24140FINISH_PATH: 24141 /* Choose control points for the path and put the result into |cur_exp| */ 24142 if (cycle_hit) { 24143 if (d == mp_ampersand) 24144 path_p = path_q; 24145 } else { 24146 mp_left_type (path_p) = mp_endpoint; 24147 if (mp_right_type (path_p) == mp_open) { 24148 mp_right_type (path_p) = mp_curl; 24149 set_number_to_unity(path_p->right_curl); 24150 } 24151 mp_right_type (path_q) = mp_endpoint; 24152 if (mp_left_type (path_q) == mp_open) { 24153 mp_left_type (path_q) = mp_curl; 24154 set_number_to_unity(path_q->left_curl); 24155 } 24156 mp_next_knot (path_q) = path_p; 24157 } 24158 mp_make_choices (mp, path_p); 24159 mp->cur_exp.type = mp_path_type; 24160 set_cur_exp_knot (path_p); 24161 24162 free_number (x); 24163 free_number (y); 24164 return 1; 24165} 24166 24167 24168@ A pair of numeric values is changed into a knot node for a one-point path 24169when \MP\ discovers that the pair is part of a path. 24170 24171@c 24172static mp_knot mp_pair_to_knot (MP mp) { /* convert a pair to a knot with two endpoints */ 24173 mp_knot q; /* the new node */ 24174 q = mp_new_knot(mp); 24175 mp_left_type (q) = mp_endpoint; 24176 mp_right_type (q) = mp_endpoint; 24177 mp_originator (q) = mp_metapost_user; 24178 mp_next_knot (q) = q; 24179 mp_known_pair (mp); 24180 number_clone (q->x_coord, mp->cur_x); 24181 number_clone (q->y_coord, mp->cur_y); 24182 return q; 24183} 24184 24185 24186@ The |known_pair| subroutine sets |cur_x| and |cur_y| to the components 24187of the current expression, assuming that the current expression is a 24188pair of known numerics. Unknown components are zeroed, and the 24189current expression is flushed. 24190 24191@<Declarations@>= 24192static void mp_known_pair (MP mp); 24193 24194@ @c 24195void mp_known_pair (MP mp) { 24196 mp_value new_expr; 24197 mp_node p; /* the pair node */ 24198 memset(&new_expr,0,sizeof(mp_value)); 24199 new_number(new_expr.data.n); 24200 if (mp->cur_exp.type != mp_pair_type) { 24201 const char *hlp[] = { 24202 "I need x and y numbers for this part of the path.", 24203 "The value I found (see above) was no good;", 24204 "so I'll try to keep going by using zero instead.", 24205 "(Chapter 27 of The METAFONTbook explains that", 24206 "you might want to type `I ??" "?' now.)", 24207 NULL }; 24208 mp_disp_err(mp, NULL); 24209 mp_back_error (mp, "Undefined coordinates have been replaced by (0,0)", hlp, true); 24210 mp_get_x_next (mp); 24211 mp_flush_cur_exp (mp, new_expr); 24212 set_number_to_zero(mp->cur_x); 24213 set_number_to_zero(mp->cur_y); 24214 } else { 24215 p = value_node (cur_exp_node ()); 24216 /* Make sure that both |x| and |y| parts of |p| are known; 24217 copy them into |cur_x| and |cur_y| */ 24218 if (mp_type (x_part (p)) == mp_known) { 24219 number_clone(mp->cur_x, value_number (x_part (p))); 24220 } else { 24221 const char *hlp[] = { 24222 "I need a `known' x value for this part of the path.", 24223 "The value I found (see above) was no good;", 24224 "so I'll try to keep going by using zero instead.", 24225 "(Chapter 27 of The METAFONTbook explains that", 24226 "you might want to type `I ??" "?' now.)", 24227 NULL }; 24228 mp_disp_err (mp, x_part (p)); 24229 mp_back_error (mp, "Undefined x coordinate has been replaced by 0", hlp, true); 24230 mp_get_x_next (mp); 24231 mp_recycle_value (mp, x_part (p)); 24232 set_number_to_zero(mp->cur_x); 24233 } 24234 if (mp_type (y_part (p)) == mp_known) { 24235 number_clone(mp->cur_y, value_number (y_part (p))); 24236 } else { 24237 const char *hlp[] = { 24238 "I need a `known' y value for this part of the path.", 24239 "The value I found (see above) was no good;", 24240 "so I'll try to keep going by using zero instead.", 24241 "(Chapter 27 of The METAFONTbook explains that", 24242 "you might want to type `I ??" "?' now.)", 24243 NULL }; 24244 mp_disp_err (mp, y_part (p)); 24245 mp_back_error (mp, "Undefined y coordinate has been replaced by 0", hlp, true); 24246 mp_get_x_next (mp); 24247 mp_recycle_value (mp, y_part (p)); 24248 set_number_to_zero(mp->cur_y); 24249 } 24250 mp_flush_cur_exp (mp, new_expr); 24251 } 24252} 24253 24254@ The |scan_direction| subroutine looks at the directional information 24255that is enclosed in braces, and also scans ahead to the following character. 24256A type code is returned, either |open| (if the direction was $(0,0)$), 24257or |curl| (if the direction was a curl of known value |cur_exp|), or 24258|given| (if the direction is given by the |angle| value that now 24259appears in |cur_exp|). 24260 24261There's nothing difficult about this subroutine, but the program is rather 24262lengthy because a variety of potential errors need to be nipped in the bud. 24263 24264@c 24265static quarterword mp_scan_direction (MP mp) { 24266 int t; /* the type of information found */ 24267 mp_get_x_next (mp); 24268 if (cur_cmd() == mp_curl_command) { 24269 /* Scan a curl specification */ 24270 mp_get_x_next (mp); 24271 mp_scan_expression (mp); 24272 if ((mp->cur_exp.type != mp_known) || (number_negative(cur_exp_value_number ()))) { 24273 mp_value new_expr; 24274 const char *hlp[] = { "A curl must be a known, nonnegative number.", NULL }; 24275 memset(&new_expr,0,sizeof(mp_value)); 24276 new_number(new_expr.data.n); 24277 set_number_to_unity(new_expr.data.n); 24278 mp_disp_err(mp, NULL); 24279 mp_back_error (mp, "Improper curl has been replaced by 1", hlp, true); 24280 mp_get_x_next (mp); 24281 mp_flush_cur_exp (mp, new_expr); 24282 } 24283 t = mp_curl; 24284 24285 } else { 24286 /* Scan a given direction */ 24287 mp_scan_expression (mp); 24288 if (mp->cur_exp.type > mp_pair_type) { 24289 /* Get given directions separated by commas */ 24290 mp_number xx; 24291 new_number(xx); 24292 if (mp->cur_exp.type != mp_known) { 24293 mp_value new_expr; 24294 const char *hlp[] = { 24295 "I need a `known' x value for this part of the path.", 24296 "The value I found (see above) was no good;", 24297 "so I'll try to keep going by using zero instead.", 24298 "(Chapter 27 of The METAFONTbook explains that", 24299 "you might want to type `I ??" "?' now.)", 24300 NULL }; 24301 memset(&new_expr,0,sizeof(mp_value)); 24302 new_number(new_expr.data.n); 24303 set_number_to_zero(new_expr.data.n); 24304 mp_disp_err(mp, NULL); 24305 mp_back_error (mp, "Undefined x coordinate has been replaced by 0", hlp, true); 24306 mp_get_x_next (mp); 24307 mp_flush_cur_exp (mp, new_expr); 24308 } 24309 number_clone(xx, cur_exp_value_number ()); 24310 if (cur_cmd() != mp_comma) { 24311 const char *hlp[] = { 24312 "I've got the x coordinate of a path direction;", 24313 "will look for the y coordinate next.", 24314 NULL }; 24315 mp_back_error (mp, "Missing `,' has been inserted", hlp, true); 24316 } 24317 mp_get_x_next (mp); 24318 mp_scan_expression (mp); 24319 if (mp->cur_exp.type != mp_known) { 24320 mp_value new_expr; 24321 const char *hlp[] = { 24322 "I need a `known' y value for this part of the path.", 24323 "The value I found (see above) was no good;", 24324 "so I'll try to keep going by using zero instead.", 24325 "(Chapter 27 of The METAFONTbook explains that", 24326 "you might want to type `I ??" "?' now.)", 24327 NULL }; 24328 memset(&new_expr,0,sizeof(mp_value)); 24329 new_number(new_expr.data.n); 24330 set_number_to_zero(new_expr.data.n); 24331 mp_disp_err(mp, NULL); 24332 mp_back_error (mp, "Undefined y coordinate has been replaced by 0", hlp, true); 24333 mp_get_x_next (mp); 24334 mp_flush_cur_exp (mp, new_expr); 24335 } 24336 number_clone(mp->cur_y, cur_exp_value_number ()); 24337 number_clone(mp->cur_x, xx); 24338 free_number(xx); 24339 24340 } else { 24341 mp_known_pair (mp); 24342 } 24343 if (number_zero(mp->cur_x) && number_zero(mp->cur_y)) 24344 t = mp_open; 24345 else { 24346 mp_number narg; 24347 new_angle (narg); 24348 n_arg (narg, mp->cur_x, mp->cur_y); 24349 t = mp_given; 24350 set_cur_exp_value_number (narg); 24351 free_number (narg); 24352 } 24353 } 24354 if (cur_cmd() != mp_right_brace) { 24355 const char *hlp[] = { 24356 "I've scanned a direction spec for part of a path,", 24357 "so a right brace should have come next.", 24358 "I shall pretend that one was there.", 24359 NULL }; 24360 mp_back_error (mp, "Missing `}' has been inserted", hlp, true); 24361 } 24362 mp_get_x_next (mp); 24363 return (quarterword) t; 24364} 24365 24366 24367@ Finally, we sometimes need to scan an expression whose value is 24368supposed to be either |true_code| or |false_code|. 24369 24370@d mp_get_boolean(mp) do { 24371 mp_get_x_next (mp); 24372 mp_scan_expression (mp); 24373 if (mp->cur_exp.type != mp_boolean_type) { 24374 do_boolean_error(mp); 24375 } 24376} while (0) 24377 24378@<Declare the basic parsing subroutines@>= 24379static void do_boolean_error (MP mp) { 24380 mp_value new_expr; 24381 const char *hlp[] = { 24382 "The expression shown above should have had a definite", 24383 "true-or-false value. I'm changing it to `false'.", 24384 NULL }; 24385 memset(&new_expr,0,sizeof(mp_value)); 24386 new_number(new_expr.data.n); 24387 mp_disp_err(mp, NULL); 24388 set_number_from_boolean (new_expr.data.n, mp_false_code); 24389 mp_back_error (mp, "Undefined condition will be treated as `false'", hlp, true); 24390 mp_get_x_next (mp); 24391 mp_flush_cur_exp (mp, new_expr); 24392 mp->cur_exp.type = mp_boolean_type; 24393} 24394 24395@ @<Declarations@>= 24396static void do_boolean_error (MP mp); 24397 24398@* Doing the operations. 24399The purpose of parsing is primarily to permit people to avoid piles of 24400parentheses. But the real work is done after the structure of an expression 24401has been recognized; that's when new expressions are generated. We 24402turn now to the guts of \MP, which handles individual operators that 24403have come through the parsing mechanism. 24404 24405We'll start with the easy ones that take no operands, then work our way 24406up to operators with one and ultimately two arguments. In other words, 24407we will write the three procedures |do_nullary|, |do_unary|, and |do_binary| 24408that are invoked periodically by the expression scanners. 24409 24410First let's make sure that all of the primitive operators are in the 24411hash table. Although |scan_primary| and its relatives made use of the 24412\\{cmd} code for these operators, the \\{do} routines base everything 24413on the \\{mod} code. For example, |do_binary| doesn't care whether the 24414operation it performs is a |primary_binary| or |secondary_binary|, etc. 24415 24416@<Put each...@>= 24417mp_primitive (mp, "true", mp_nullary, mp_true_code); 24418@:true_}{\&{true} primitive@>; 24419mp_primitive (mp, "false", mp_nullary, mp_false_code); 24420@:false_}{\&{false} primitive@>; 24421mp_primitive (mp, "nullpicture", mp_nullary, mp_null_picture_code); 24422@:null_picture_}{\&{nullpicture} primitive@>; 24423mp_primitive (mp, "nullpen", mp_nullary, mp_null_pen_code); 24424@:null_pen_}{\&{nullpen} primitive@>; 24425mp_primitive (mp, "readstring", mp_nullary, mp_read_string_op); 24426@:read_string_}{\&{readstring} primitive@>; 24427mp_primitive (mp, "pencircle", mp_nullary, mp_pen_circle); 24428@:pen_circle_}{\&{pencircle} primitive@>; 24429mp_primitive (mp, "normaldeviate", mp_nullary, mp_normal_deviate); 24430@:normal_deviate_}{\&{normaldeviate} primitive@>; 24431mp_primitive (mp, "readfrom", mp_unary, mp_read_from_op); 24432@:read_from_}{\&{readfrom} primitive@>; 24433mp_primitive (mp, "closefrom", mp_unary, mp_close_from_op); 24434@:close_from_}{\&{closefrom} primitive@>; 24435mp_primitive (mp, "odd", mp_unary, mp_odd_op); 24436@:odd_}{\&{odd} primitive@>; 24437mp_primitive (mp, "known", mp_unary, mp_known_op); 24438@:known_}{\&{known} primitive@>; 24439mp_primitive (mp, "unknown", mp_unary, mp_unknown_op); 24440@:unknown_}{\&{unknown} primitive@>; 24441mp_primitive (mp, "not", mp_unary, mp_not_op); 24442@:not_}{\&{not} primitive@>; 24443mp_primitive (mp, "decimal", mp_unary, mp_decimal); 24444@:decimal_}{\&{decimal} primitive@>; 24445mp_primitive (mp, "reverse", mp_unary, mp_reverse); 24446@:reverse_}{\&{reverse} primitive@>; 24447mp_primitive (mp, "makepath", mp_unary, mp_make_path_op); 24448@:make_path_}{\&{makepath} primitive@>; 24449mp_primitive (mp, "makepen", mp_unary, mp_make_pen_op); 24450@:make_pen_}{\&{makepen} primitive@>; 24451mp_primitive (mp, "oct", mp_unary, mp_oct_op); 24452@:oct_}{\&{oct} primitive@>; 24453mp_primitive (mp, "hex", mp_unary, mp_hex_op); 24454@:hex_}{\&{hex} primitive@>; 24455mp_primitive (mp, "ASCII", mp_unary, mp_ASCII_op); 24456@:ASCII_}{\&{ASCII} primitive@>; 24457mp_primitive (mp, "char", mp_unary, mp_char_op); 24458@:char_}{\&{char} primitive@>; 24459mp_primitive (mp, "length", mp_unary, mp_length_op); 24460@:length_}{\&{length} primitive@>; 24461mp_primitive (mp, "turningnumber", mp_unary, mp_turning_op); 24462@:turning_number_}{\&{turningnumber} primitive@>; 24463mp_primitive (mp, "xpart", mp_unary, mp_x_part); 24464@:x_part_}{\&{xpart} primitive@>; 24465mp_primitive (mp, "ypart", mp_unary, mp_y_part); 24466@:y_part_}{\&{ypart} primitive@>; 24467mp_primitive (mp, "xxpart", mp_unary, mp_xx_part); 24468@:xx_part_}{\&{xxpart} primitive@>; 24469mp_primitive (mp, "xypart", mp_unary, mp_xy_part); 24470@:xy_part_}{\&{xypart} primitive@>; 24471mp_primitive (mp, "yxpart", mp_unary, mp_yx_part); 24472@:yx_part_}{\&{yxpart} primitive@>; 24473mp_primitive (mp, "yypart", mp_unary, mp_yy_part); 24474@:yy_part_}{\&{yypart} primitive@>; 24475mp_primitive (mp, "redpart", mp_unary, mp_red_part); 24476@:red_part_}{\&{redpart} primitive@>; 24477mp_primitive (mp, "greenpart", mp_unary, mp_green_part); 24478@:green_part_}{\&{greenpart} primitive@>; 24479mp_primitive (mp, "bluepart", mp_unary, mp_blue_part); 24480@:blue_part_}{\&{bluepart} primitive@>; 24481mp_primitive (mp, "cyanpart", mp_unary, mp_cyan_part); 24482@:cyan_part_}{\&{cyanpart} primitive@>; 24483mp_primitive (mp, "magentapart", mp_unary, mp_magenta_part); 24484@:magenta_part_}{\&{magentapart} primitive@>; 24485mp_primitive (mp, "yellowpart", mp_unary, mp_yellow_part); 24486@:yellow_part_}{\&{yellowpart} primitive@>; 24487mp_primitive (mp, "blackpart", mp_unary, mp_black_part); 24488@:black_part_}{\&{blackpart} primitive@>; 24489mp_primitive (mp, "greypart", mp_unary, mp_grey_part); 24490@:grey_part_}{\&{greypart} primitive@>; 24491mp_primitive (mp, "colormodel", mp_unary, mp_color_model_part); 24492@:color_model_part_}{\&{colormodel} primitive@>; 24493mp_primitive (mp, "fontpart", mp_unary, mp_font_part); 24494@:font_part_}{\&{fontpart} primitive@>; 24495mp_primitive (mp, "textpart", mp_unary, mp_text_part); 24496@:text_part_}{\&{textpart} primitive@>; 24497mp_primitive (mp, "prescriptpart", mp_unary, mp_prescript_part); 24498@:prescript_part_}{\&{prescriptpart} primitive@>; 24499mp_primitive (mp, "postscriptpart", mp_unary, mp_postscript_part); 24500@:postscript_part_}{\&{postscriptpart} primitive@>; 24501mp_primitive (mp, "pathpart", mp_unary, mp_path_part); 24502@:path_part_}{\&{pathpart} primitive@>; 24503mp_primitive (mp, "penpart", mp_unary, mp_pen_part); 24504@:pen_part_}{\&{penpart} primitive@>; 24505mp_primitive (mp, "dashpart", mp_unary, mp_dash_part); 24506@:dash_part_}{\&{dashpart} primitive@>; 24507mp_primitive (mp, "sqrt", mp_unary, mp_sqrt_op); 24508@:sqrt_}{\&{sqrt} primitive@>; 24509mp_primitive (mp, "mexp", mp_unary, mp_m_exp_op); 24510@:m_exp_}{\&{mexp} primitive@>; 24511mp_primitive (mp, "mlog", mp_unary, mp_m_log_op); 24512@:m_log_}{\&{mlog} primitive@>; 24513mp_primitive (mp, "sind", mp_unary, mp_sin_d_op); 24514@:sin_d_}{\&{sind} primitive@>; 24515mp_primitive (mp, "cosd", mp_unary, mp_cos_d_op); 24516@:cos_d_}{\&{cosd} primitive@>; 24517mp_primitive (mp, "floor", mp_unary, mp_floor_op); 24518@:floor_}{\&{floor} primitive@>; 24519mp_primitive (mp, "uniformdeviate", mp_unary, mp_uniform_deviate); 24520@:uniform_deviate_}{\&{uniformdeviate} primitive@>; 24521mp_primitive (mp, "charexists", mp_unary, mp_char_exists_op); 24522@:char_exists_}{\&{charexists} primitive@>; 24523mp_primitive (mp, "fontsize", mp_unary, mp_font_size); 24524@:font_size_}{\&{fontsize} primitive@>; 24525mp_primitive (mp, "llcorner", mp_unary, mp_ll_corner_op); 24526@:ll_corner_}{\&{llcorner} primitive@>; 24527mp_primitive (mp, "lrcorner", mp_unary, mp_lr_corner_op); 24528@:lr_corner_}{\&{lrcorner} primitive@>; 24529mp_primitive (mp, "ulcorner", mp_unary, mp_ul_corner_op); 24530@:ul_corner_}{\&{ulcorner} primitive@>; 24531mp_primitive (mp, "urcorner", mp_unary, mp_ur_corner_op); 24532@:ur_corner_}{\&{urcorner} primitive@>; 24533mp_primitive (mp, "arclength", mp_unary, mp_arc_length); 24534@:arc_length_}{\&{arclength} primitive@>; 24535mp_primitive (mp, "angle", mp_unary, mp_angle_op); 24536@:angle_}{\&{angle} primitive@>; 24537mp_primitive (mp, "cycle", mp_cycle, mp_cycle_op); 24538@:cycle_}{\&{cycle} primitive@>; 24539mp_primitive (mp, "stroked", mp_unary, mp_stroked_op); 24540@:stroked_}{\&{stroked} primitive@>; 24541mp_primitive (mp, "filled", mp_unary, mp_filled_op); 24542@:filled_}{\&{filled} primitive@>; 24543mp_primitive (mp, "textual", mp_unary, mp_textual_op); 24544@:textual_}{\&{textual} primitive@>; 24545mp_primitive (mp, "clipped", mp_unary, mp_clipped_op); 24546@:clipped_}{\&{clipped} primitive@>; 24547mp_primitive (mp, "bounded", mp_unary, mp_bounded_op); 24548@:bounded_}{\&{bounded} primitive@>; 24549mp_primitive (mp, "+", mp_plus_or_minus, mp_plus); 24550@:+ }{\.{+} primitive@>; 24551mp_primitive (mp, "-", mp_plus_or_minus, mp_minus); 24552@:- }{\.{-} primitive@>; 24553mp_primitive (mp, "*", mp_secondary_binary, mp_times); 24554@:* }{\.{*} primitive@>; 24555mp_primitive (mp, "/", mp_slash, mp_over); 24556mp->frozen_slash = mp_frozen_primitive (mp, "/", mp_slash, mp_over); 24557@:/ }{\.{/} primitive@>; 24558mp_primitive (mp, "++", mp_tertiary_binary, mp_pythag_add); 24559@:++_}{\.{++} primitive@>; 24560mp_primitive (mp, "+-+", mp_tertiary_binary, mp_pythag_sub); 24561@:+-+_}{\.{+-+} primitive@>; 24562mp_primitive (mp, "or", mp_tertiary_binary, mp_or_op); 24563@:or_}{\&{or} primitive@>; 24564mp_primitive (mp, "and", mp_and_command, mp_and_op); 24565@:and_}{\&{and} primitive@>; 24566mp_primitive (mp, "<", mp_expression_binary, mp_less_than); 24567@:< }{\.{<} primitive@>; 24568mp_primitive (mp, "<=", mp_expression_binary, mp_less_or_equal); 24569@:<=_}{\.{<=} primitive@>; 24570mp_primitive (mp, ">", mp_expression_binary, mp_greater_than); 24571@:> }{\.{>} primitive@>; 24572mp_primitive (mp, ">=", mp_expression_binary, mp_greater_or_equal); 24573@:>=_}{\.{>=} primitive@>; 24574mp_primitive (mp, "=", mp_equals, mp_equal_to); 24575@:= }{\.{=} primitive@>; 24576mp_primitive (mp, "<>", mp_expression_binary, mp_unequal_to); 24577@:<>_}{\.{<>} primitive@>; 24578mp_primitive (mp, "substring", mp_primary_binary, mp_substring_of); 24579@:substring_}{\&{substring} primitive@>; 24580mp_primitive (mp, "subpath", mp_primary_binary, mp_subpath_of); 24581@:subpath_}{\&{subpath} primitive@>; 24582mp_primitive (mp, "directiontime", mp_primary_binary, mp_direction_time_of); 24583@:direction_time_}{\&{directiontime} primitive@>; 24584mp_primitive (mp, "point", mp_primary_binary, mp_point_of); 24585@:point_}{\&{point} primitive@>; 24586mp_primitive (mp, "precontrol", mp_primary_binary, mp_precontrol_of); 24587@:precontrol_}{\&{precontrol} primitive@>; 24588mp_primitive (mp, "postcontrol", mp_primary_binary, mp_postcontrol_of); 24589@:postcontrol_}{\&{postcontrol} primitive@>; 24590mp_primitive (mp, "penoffset", mp_primary_binary, mp_pen_offset_of); 24591@:pen_offset_}{\&{penoffset} primitive@>; 24592mp_primitive (mp, "arctime", mp_primary_binary, mp_arc_time_of); 24593@:arc_time_of_}{\&{arctime} primitive@>; 24594mp_primitive (mp, "mpversion", mp_nullary, mp_version); 24595@:mp_verison_}{\&{mpversion} primitive@>; 24596mp_primitive (mp, "&", mp_ampersand, mp_concatenate); 24597@:!!!}{\.{\&} primitive@>; 24598mp_primitive (mp, "rotated", mp_secondary_binary, mp_rotated_by); 24599@:rotated_}{\&{rotated} primitive@>; 24600mp_primitive (mp, "slanted", mp_secondary_binary, mp_slanted_by); 24601@:slanted_}{\&{slanted} primitive@>; 24602mp_primitive (mp, "scaled", mp_secondary_binary, mp_scaled_by); 24603@:scaled_}{\&{scaled} primitive@>; 24604mp_primitive (mp, "shifted", mp_secondary_binary, mp_shifted_by); 24605@:shifted_}{\&{shifted} primitive@>; 24606mp_primitive (mp, "transformed", mp_secondary_binary, mp_transformed_by); 24607@:transformed_}{\&{transformed} primitive@>; 24608mp_primitive (mp, "xscaled", mp_secondary_binary, mp_x_scaled); 24609@:x_scaled_}{\&{xscaled} primitive@>; 24610mp_primitive (mp, "yscaled", mp_secondary_binary, mp_y_scaled); 24611@:y_scaled_}{\&{yscaled} primitive@>; 24612mp_primitive (mp, "zscaled", mp_secondary_binary, mp_z_scaled); 24613@:z_scaled_}{\&{zscaled} primitive@>; 24614mp_primitive (mp, "infont", mp_secondary_binary, mp_in_font); 24615@:in_font_}{\&{infont} primitive@>; 24616mp_primitive (mp, "intersectiontimes", mp_tertiary_binary, mp_intersect); 24617@:intersection_times_}{\&{intersectiontimes} primitive@>; 24618mp_primitive (mp, "envelope", mp_primary_binary, mp_envelope_of); 24619@:envelope_}{\&{envelope} primitive@>; 24620mp_primitive (mp, "glyph", mp_primary_binary, mp_glyph_infont); 24621@:glyph_infont_}{\&{envelope} primitive@> 24622 24623 24624@ @<Cases of |print_cmd...@>= 24625case mp_nullary: 24626case mp_unary: 24627case mp_primary_binary: 24628case mp_secondary_binary: 24629case mp_tertiary_binary: 24630case mp_expression_binary: 24631case mp_cycle: 24632case mp_plus_or_minus: 24633case mp_slash: 24634case mp_ampersand: 24635case mp_equals: 24636case mp_and_command: 24637mp_print_op (mp, (quarterword) m); 24638break; 24639 24640@ OK, let's look at the simplest \\{do} procedure first. 24641 24642@c 24643@<Declare nullary action procedure@>; 24644static void mp_do_nullary (MP mp, quarterword c) { 24645 check_arith(); 24646 if (number_greater (internal_value (mp_tracing_commands), two_t)) 24647 mp_show_cmd_mod (mp, mp_nullary, c); 24648 switch (c) { 24649 case mp_true_code: 24650 case mp_false_code: 24651 mp->cur_exp.type = mp_boolean_type; 24652 set_cur_exp_value_boolean (c); 24653 break; 24654 case mp_null_picture_code: 24655 mp->cur_exp.type = mp_picture_type; 24656 set_cur_exp_node ((mp_node)mp_get_edge_header_node (mp)); 24657 mp_init_edges (mp, (mp_edge_header_node)cur_exp_node ()); 24658 break; 24659 case mp_null_pen_code: 24660 mp->cur_exp.type = mp_pen_type; 24661 set_cur_exp_knot (mp_get_pen_circle (mp, zero_t)); 24662 break; 24663 case mp_normal_deviate: 24664 { 24665 mp_number r; 24666 new_number (r); 24667 /*mp_norm_rand (mp, &r);*/ 24668 m_norm_rand (r); 24669 mp->cur_exp.type = mp_known; 24670 set_cur_exp_value_number (r); 24671 free_number (r); 24672 } 24673 break; 24674 case mp_pen_circle: 24675 mp->cur_exp.type = mp_pen_type; 24676 set_cur_exp_knot (mp_get_pen_circle (mp, unity_t)); 24677 break; 24678 case mp_version: 24679 mp->cur_exp.type = mp_string_type; 24680 set_cur_exp_str (mp_intern (mp, metapost_version)); 24681 break; 24682 case mp_read_string_op: 24683 /* Read a string from the terminal */ 24684 if (mp->noninteractive || mp->interaction <= mp_nonstop_mode) 24685 mp_fatal_error (mp, "*** (cannot readstring in nonstop modes)"); 24686 mp_begin_file_reading (mp); 24687 name = is_read; 24688 limit = start; 24689 prompt_input (""); 24690 mp_finish_read (mp); 24691 break; 24692 } /* there are no other cases */ 24693 check_arith(); 24694} 24695 24696 24697@ @<Declare nullary action procedure@>= 24698static void mp_finish_read (MP mp) { /* copy |buffer| line to |cur_exp| */ 24699 size_t k; 24700 str_room (((int) mp->last - (int) start)); 24701 for (k = (size_t) start; k < mp->last; k++) { 24702 append_char (mp->buffer[k]); 24703 } 24704 mp_end_file_reading (mp); 24705 mp->cur_exp.type = mp_string_type; 24706 set_cur_exp_str (mp_make_string (mp)); 24707} 24708 24709 24710@ Things get a bit more interesting when there's an operand. The 24711operand to |do_unary| appears in |cur_type| and |cur_exp|. 24712 24713This complicated if test makes sure that any |bounds| or |clip| 24714picture objects that get passed into \&{within} do not raise an 24715error when queried using the color part primitives (this is needed 24716for backward compatibility) . 24717 24718@d cur_pic_item mp_link(edge_list(cur_exp_node())) 24719@d pict_color_type(A) ((cur_pic_item!=NULL) && 24720 ((!has_color(cur_pic_item)) 24721 || 24722 (((mp_color_model(cur_pic_item)==A) 24723 || 24724 ((mp_color_model(cur_pic_item)==mp_uninitialized_model) && 24725 (number_to_scaled (internal_value(mp_default_color_model))/number_to_scaled (unity_t))==(A)))))) 24726 24727@d boolean_reset(A) if ( (A) ) set_cur_exp_value_boolean(mp_true_code); else set_cur_exp_value_boolean(mp_false_code) 24728 24729@d type_range(A,B) { 24730 if ( (mp->cur_exp.type>=(A)) && (mp->cur_exp.type<=(B)) ) 24731 set_number_from_boolean (new_expr.data.n, mp_true_code); 24732 else 24733 set_number_from_boolean (new_expr.data.n, mp_false_code); 24734 mp_flush_cur_exp(mp, new_expr); 24735 mp->cur_exp.type=mp_boolean_type; 24736 } 24737@d type_test(A) { 24738 if ( mp->cur_exp.type==(mp_variable_type)(A) ) 24739 set_number_from_boolean (new_expr.data.n, mp_true_code); 24740 else 24741 set_number_from_boolean (new_expr.data.n, mp_false_code); 24742 mp_flush_cur_exp(mp, new_expr); 24743 mp->cur_exp.type=mp_boolean_type; 24744 } 24745 24746 24747@c 24748@<Declare unary action procedures@>; 24749static void mp_do_unary (MP mp, quarterword c) { 24750 mp_node p; /* for list manipulation */ 24751 mp_value new_expr; 24752 check_arith(); 24753 if (number_greater (internal_value (mp_tracing_commands), two_t)) { 24754 /* Trace the current unary operation */ 24755 mp_begin_diagnostic (mp); 24756 mp_print_nl (mp, "{"); 24757 mp_print_op (mp, c); 24758 mp_print_char (mp, xord ('(')); 24759 mp_print_exp (mp, NULL, 0); /* show the operand, but not verbosely */ 24760 mp_print (mp, ")}"); 24761 mp_end_diagnostic (mp, false); 24762 } 24763 switch (c) { 24764 case mp_plus: 24765 if (mp->cur_exp.type < mp_color_type) 24766 mp_bad_unary (mp, mp_plus); 24767 break; 24768 case mp_minus: 24769 negate_cur_expr(mp); 24770 break; 24771 case mp_not_op: 24772 if (mp->cur_exp.type != mp_boolean_type) { 24773 mp_bad_unary (mp, mp_not_op); 24774 } else { 24775 halfword bb; 24776 if (cur_exp_value_boolean () == mp_true_code) 24777 bb = mp_false_code; 24778 else 24779 bb = mp_true_code; 24780 set_cur_exp_value_boolean (bb); 24781 } 24782 break; 24783 case mp_sqrt_op: 24784 case mp_m_exp_op: 24785 case mp_m_log_op: 24786 case mp_sin_d_op: 24787 case mp_cos_d_op: 24788 case mp_floor_op: 24789 case mp_uniform_deviate: 24790 case mp_odd_op: 24791 case mp_char_exists_op: 24792 if (mp->cur_exp.type != mp_known) { 24793 mp_bad_unary (mp, c); 24794 } else { 24795 switch (c) { 24796 case mp_sqrt_op: 24797 { 24798 mp_number r1; 24799 new_number (r1); 24800 square_rt (r1, cur_exp_value_number ()); 24801 set_cur_exp_value_number (r1); 24802 free_number (r1); 24803 } 24804 break; 24805 case mp_m_exp_op: 24806 { 24807 mp_number r1; 24808 new_number (r1); 24809 m_exp (r1, cur_exp_value_number ()); 24810 set_cur_exp_value_number (r1); 24811 free_number (r1); 24812 } 24813 break; 24814 case mp_m_log_op: 24815 { 24816 mp_number r1; 24817 new_number (r1); 24818 m_log (r1, cur_exp_value_number ()); 24819 set_cur_exp_value_number (r1); 24820 free_number (r1); 24821 } 24822 break; 24823 case mp_sin_d_op: 24824 case mp_cos_d_op: 24825 { 24826 mp_number n_sin, n_cos, arg1, arg2; 24827 new_number (arg1); 24828 new_number (arg2); 24829 new_fraction (n_sin); 24830 new_fraction (n_cos); /* results computed by |n_sin_cos| */ 24831 number_clone (arg1, cur_exp_value_number()); 24832 number_clone (arg2, unity_t); 24833 number_multiply_int (arg2, 360); 24834 number_modulo (arg1, arg2); 24835 convert_scaled_to_angle (arg1); 24836 n_sin_cos (arg1, n_cos, n_sin); 24837 if (c == mp_sin_d_op) { 24838 fraction_to_round_scaled (n_sin); 24839 set_cur_exp_value_number (n_sin); 24840 } else { 24841 fraction_to_round_scaled (n_cos); 24842 set_cur_exp_value_number (n_cos); 24843 } 24844 free_number (arg1); 24845 free_number (arg2); 24846 free_number (n_sin); 24847 free_number (n_cos); 24848 } 24849 break; 24850 case mp_floor_op: 24851 { 24852 mp_number vvx; 24853 new_number (vvx); 24854 number_clone (vvx, cur_exp_value_number ()); 24855 floor_scaled (vvx); 24856 set_cur_exp_value_number (vvx); 24857 free_number (vvx); 24858 } 24859 break; 24860 case mp_uniform_deviate: 24861 { 24862 mp_number vvx; 24863 new_number (vvx); 24864 mp_unif_rand (mp, &vvx, cur_exp_value_number ()); 24865 set_cur_exp_value_number (vvx); 24866 free_number (vvx); 24867 } 24868 break; 24869 case mp_odd_op: 24870 { 24871 integer vvx = odd (round_unscaled (cur_exp_value_number ())); 24872 boolean_reset (vvx); 24873 mp->cur_exp.type = mp_boolean_type; 24874 } 24875 break; 24876 case mp_char_exists_op: 24877 /* Determine if a character has been shipped out */ 24878 set_cur_exp_value_scaled (round_unscaled (cur_exp_value_number ()) % 256); 24879 if (number_negative(cur_exp_value_number ())) { 24880 halfword vv = number_to_scaled(cur_exp_value_number ()); 24881 set_cur_exp_value_scaled (vv + 256); 24882 } 24883 boolean_reset (mp->char_exists[number_to_scaled(cur_exp_value_number ())]); 24884 mp->cur_exp.type = mp_boolean_type; 24885 break; 24886 } /* there are no other cases */ 24887 } 24888 break; 24889 case mp_angle_op: 24890 if (mp_nice_pair (mp, cur_exp_node (), mp->cur_exp.type)) { 24891 mp_number narg; 24892 memset(&new_expr,0,sizeof(mp_value)); 24893 new_number(new_expr.data.n); 24894 new_angle (narg); 24895 p = value_node (cur_exp_node ()); 24896 n_arg (narg, value_number (x_part (p)), value_number (y_part (p))); 24897 number_clone (new_expr.data.n, narg); 24898 convert_angle_to_scaled (new_expr.data.n); 24899 free_number (narg); 24900 mp_flush_cur_exp (mp, new_expr); 24901 } else { 24902 mp_bad_unary (mp, mp_angle_op); 24903 } 24904 break; 24905 case mp_x_part: 24906 case mp_y_part: 24907 if ((mp->cur_exp.type == mp_pair_type) 24908 || (mp->cur_exp.type == mp_transform_type)) 24909 mp_take_part (mp, c); 24910 else if (mp->cur_exp.type == mp_picture_type) 24911 mp_take_pict_part (mp, c); 24912 else 24913 mp_bad_unary (mp, c); 24914 break; 24915 case mp_xx_part: 24916 case mp_xy_part: 24917 case mp_yx_part: 24918 case mp_yy_part: 24919 if (mp->cur_exp.type == mp_transform_type) 24920 mp_take_part (mp, c); 24921 else if (mp->cur_exp.type == mp_picture_type) 24922 mp_take_pict_part (mp, c); 24923 else 24924 mp_bad_unary (mp, c); 24925 break; 24926 case mp_red_part: 24927 case mp_green_part: 24928 case mp_blue_part: 24929 if (mp->cur_exp.type == mp_color_type) 24930 mp_take_part (mp, c); 24931 else if (mp->cur_exp.type == mp_picture_type) { 24932 if pict_color_type 24933 (mp_rgb_model) mp_take_pict_part (mp, c); 24934 else 24935 mp_bad_color_part (mp, c); 24936 } else 24937 mp_bad_unary (mp, c); 24938 break; 24939 case mp_cyan_part: 24940 case mp_magenta_part: 24941 case mp_yellow_part: 24942 case mp_black_part: 24943 if (mp->cur_exp.type == mp_cmykcolor_type) 24944 mp_take_part (mp, c); 24945 else if (mp->cur_exp.type == mp_picture_type) { 24946 if pict_color_type 24947 (mp_cmyk_model) mp_take_pict_part (mp, c); 24948 else 24949 mp_bad_color_part (mp, c); 24950 } else 24951 mp_bad_unary (mp, c); 24952 break; 24953 case mp_grey_part: 24954 if (mp->cur_exp.type == mp_known); 24955 else if (mp->cur_exp.type == mp_picture_type) { 24956 if pict_color_type 24957 (mp_grey_model) mp_take_pict_part (mp, c); 24958 else 24959 mp_bad_color_part (mp, c); 24960 } else 24961 mp_bad_unary (mp, c); 24962 break; 24963 case mp_color_model_part: 24964 if (mp->cur_exp.type == mp_picture_type) 24965 mp_take_pict_part (mp, c); 24966 else 24967 mp_bad_unary (mp, c); 24968 break; 24969 case mp_font_part: 24970 case mp_text_part: 24971 case mp_path_part: 24972 case mp_pen_part: 24973 case mp_dash_part: 24974 case mp_prescript_part: 24975 case mp_postscript_part: 24976 if (mp->cur_exp.type == mp_picture_type) 24977 mp_take_pict_part (mp, c); 24978 else 24979 mp_bad_unary (mp, c); 24980 break; 24981 case mp_char_op: 24982 if (mp->cur_exp.type != mp_known) { 24983 mp_bad_unary (mp, mp_char_op); 24984 } else { 24985 int vv = round_unscaled (cur_exp_value_number ()) % 256; 24986 set_cur_exp_value_scaled (vv); 24987 mp->cur_exp.type = mp_string_type; 24988 if (number_negative(cur_exp_value_number ())) { 24989 vv = number_to_scaled(cur_exp_value_number ()) + 256; 24990 set_cur_exp_value_scaled (vv); 24991 } 24992 { 24993 unsigned char ss[2]; 24994 ss[0] = (unsigned char) number_to_scaled(cur_exp_value_number ()); 24995 ss[1] = '\0'; 24996 set_cur_exp_str (mp_rtsl (mp, (char *) ss, 1)); 24997 } 24998 } 24999 break; 25000 case mp_decimal: 25001 if (mp->cur_exp.type != mp_known) { 25002 mp_bad_unary (mp, mp_decimal); 25003 } else { 25004 mp->old_setting = mp->selector; 25005 mp->selector = new_string; 25006 print_number (cur_exp_value_number ()); 25007 set_cur_exp_str (mp_make_string (mp)); 25008 mp->selector = mp->old_setting; 25009 mp->cur_exp.type = mp_string_type; 25010 } 25011 break; 25012 case mp_oct_op: 25013 case mp_hex_op: 25014 case mp_ASCII_op: 25015 if (mp->cur_exp.type != mp_string_type) 25016 mp_bad_unary (mp, c); 25017 else 25018 mp_str_to_num (mp, c); 25019 break; 25020 case mp_font_size: 25021 if (mp->cur_exp.type != mp_string_type) { 25022 mp_bad_unary (mp, mp_font_size); 25023 } else { 25024 /* Find the design size of the font whose name is |cur_exp| */ 25025 /* One simple application of |find_font| is the implementation of the |font_size| 25026 operator that gets the design size for a given font name. */ 25027 memset(&new_expr,0,sizeof(mp_value)); 25028 new_number(new_expr.data.n); 25029 set_number_from_scaled (new_expr.data.n, 25030 (mp->font_dsize[mp_find_font (mp, mp_str (mp, cur_exp_str ()))] + 8) / 16); 25031 mp_flush_cur_exp (mp, new_expr); 25032 } 25033 break; 25034 case mp_length_op: 25035 /* The length operation is somewhat unusual in that it applies to a variety 25036 of different types of operands. */ 25037 switch (mp->cur_exp.type) { 25038 case mp_string_type: 25039 memset(&new_expr,0,sizeof(mp_value)); 25040 new_number(new_expr.data.n); 25041 number_clone (new_expr.data.n, unity_t); 25042 number_multiply_int(new_expr.data.n, cur_exp_str ()->len); 25043 mp_flush_cur_exp (mp, new_expr); 25044 break; 25045 case mp_path_type: 25046 memset(&new_expr,0,sizeof(mp_value)); 25047 new_number(new_expr.data.n); 25048 mp_path_length (mp, &new_expr.data.n); 25049 mp_flush_cur_exp (mp, new_expr); 25050 break; 25051 case mp_known: 25052 set_cur_exp_value_number (cur_exp_value_number ()); 25053 number_abs (cur_exp_value_number ()); 25054 break; 25055 case mp_picture_type: 25056 memset(&new_expr,0,sizeof(mp_value)); 25057 new_number(new_expr.data.n); 25058 mp_pict_length (mp, &new_expr.data.n); 25059 mp_flush_cur_exp (mp, new_expr); 25060 break; 25061 default: 25062 if (mp_nice_pair (mp, cur_exp_node (), mp->cur_exp.type)) { 25063 memset(&new_expr,0,sizeof(mp_value)); 25064 new_number(new_expr.data.n); 25065 pyth_add (new_expr.data.n, value_number (x_part (value_node (cur_exp_node ()))), 25066 value_number (y_part (value_node (cur_exp_node ())))); 25067 mp_flush_cur_exp (mp, new_expr); 25068 } else 25069 mp_bad_unary (mp, c); 25070 break; 25071 } 25072 break; 25073 case mp_turning_op: 25074 if (mp->cur_exp.type == mp_pair_type) { 25075 memset(&new_expr,0,sizeof(mp_value)); 25076 new_number(new_expr.data.n); 25077 set_number_to_zero(new_expr.data.n); 25078 mp_flush_cur_exp (mp, new_expr); 25079 } else if (mp->cur_exp.type != mp_path_type) { 25080 mp_bad_unary (mp, mp_turning_op); 25081 } else if (mp_left_type (cur_exp_knot ()) == mp_endpoint) { 25082 memset(&new_expr,0,sizeof(mp_value)); 25083 new_number(new_expr.data.n); 25084 new_expr.data.p = NULL; 25085 mp_flush_cur_exp (mp, new_expr); /* not a cyclic path */ 25086 } else { 25087 memset(&new_expr,0,sizeof(mp_value)); 25088 new_number(new_expr.data.n); 25089 mp_turn_cycles_wrapper (mp, &new_expr.data.n, cur_exp_knot ()); 25090 mp_flush_cur_exp (mp, new_expr); 25091 } 25092 break; 25093 case mp_boolean_type: 25094 memset(&new_expr,0,sizeof(mp_value)); 25095 new_number(new_expr.data.n); 25096 type_range (mp_boolean_type, mp_unknown_boolean); 25097 break; 25098 case mp_string_type: 25099 memset(&new_expr,0,sizeof(mp_value)); 25100 new_number(new_expr.data.n); 25101 type_range (mp_string_type, mp_unknown_string); 25102 break; 25103 case mp_pen_type: 25104 memset(&new_expr,0,sizeof(mp_value)); 25105 new_number(new_expr.data.n); 25106 type_range (mp_pen_type, mp_unknown_pen); 25107 break; 25108 case mp_path_type: 25109 memset(&new_expr,0,sizeof(mp_value)); 25110 new_number(new_expr.data.n); 25111 type_range (mp_path_type, mp_unknown_path); 25112 break; 25113 case mp_picture_type: 25114 memset(&new_expr,0,sizeof(mp_value)); 25115 new_number(new_expr.data.n); 25116 type_range (mp_picture_type, mp_unknown_picture); 25117 break; 25118 case mp_transform_type: 25119 case mp_color_type: 25120 case mp_cmykcolor_type: 25121 case mp_pair_type: 25122 memset(&new_expr,0,sizeof(mp_value)); 25123 new_number(new_expr.data.n); 25124 type_test (c); 25125 break; 25126 case mp_numeric_type: 25127 memset(&new_expr,0,sizeof(mp_value)); 25128 new_number(new_expr.data.n); 25129 type_range (mp_known, mp_independent); 25130 break; 25131 case mp_known_op: 25132 case mp_unknown_op: 25133 mp_test_known (mp, c); 25134 break; 25135 case mp_cycle_op: 25136 memset(&new_expr,0,sizeof(mp_value)); 25137 new_number(new_expr.data.n); 25138 if (mp->cur_exp.type != mp_path_type) 25139 set_number_from_boolean (new_expr.data.n, mp_false_code); 25140 else if (mp_left_type (cur_exp_knot ()) != mp_endpoint) 25141 set_number_from_boolean (new_expr.data.n, mp_true_code); 25142 else 25143 set_number_from_boolean (new_expr.data.n, mp_false_code); 25144 mp_flush_cur_exp (mp, new_expr); 25145 mp->cur_exp.type = mp_boolean_type; 25146 break; 25147 case mp_arc_length: 25148 if (mp->cur_exp.type == mp_pair_type) 25149 mp_pair_to_path (mp); 25150 if (mp->cur_exp.type != mp_path_type) { 25151 mp_bad_unary (mp, mp_arc_length); 25152 } else { 25153 memset(&new_expr,0,sizeof(mp_value)); 25154 new_number(new_expr.data.n); 25155 mp_get_arc_length (mp, &new_expr.data.n, cur_exp_knot ()); 25156 mp_flush_cur_exp (mp, new_expr); 25157 } 25158 break; 25159 case mp_filled_op: 25160 case mp_stroked_op: 25161 case mp_textual_op: 25162 case mp_clipped_op: 25163 case mp_bounded_op: 25164 /* Here we use the fact that |c-filled_op+fill_code| is the desired graphical 25165 object |type|. */ 25166@^data structure assumptions@> 25167 memset(&new_expr,0,sizeof(mp_value)); 25168 new_number(new_expr.data.n); 25169 if (mp->cur_exp.type != mp_picture_type) { 25170 set_number_from_boolean (new_expr.data.n, mp_false_code); 25171 } else if (mp_link (edge_list (cur_exp_node ())) == NULL) { 25172 set_number_from_boolean (new_expr.data.n, mp_false_code); 25173 } else if (mp_type (mp_link (edge_list (cur_exp_node ()))) == 25174 (mp_variable_type) (c + mp_fill_node_type - mp_filled_op)) { 25175 set_number_from_boolean (new_expr.data.n, mp_true_code); 25176 } else { 25177 set_number_from_boolean (new_expr.data.n, mp_false_code); 25178 } 25179 mp_flush_cur_exp (mp, new_expr); 25180 mp->cur_exp.type = mp_boolean_type; 25181 break; 25182 case mp_make_pen_op: 25183 if (mp->cur_exp.type == mp_pair_type) 25184 mp_pair_to_path (mp); 25185 if (mp->cur_exp.type != mp_path_type) 25186 mp_bad_unary (mp, mp_make_pen_op); 25187 else { 25188 mp->cur_exp.type = mp_pen_type; 25189 set_cur_exp_knot (mp_make_pen (mp, cur_exp_knot (), true)); 25190 } 25191 break; 25192 case mp_make_path_op: 25193 if (mp->cur_exp.type != mp_pen_type) { 25194 mp_bad_unary (mp, mp_make_path_op); 25195 } else { 25196 mp->cur_exp.type = mp_path_type; 25197 mp_make_path (mp, cur_exp_knot ()); 25198 } 25199 break; 25200 case mp_reverse: 25201 if (mp->cur_exp.type == mp_path_type) { 25202 mp_knot pk = mp_htap_ypoc (mp, cur_exp_knot ()); 25203 if (mp_right_type (pk) == mp_endpoint) 25204 pk = mp_next_knot (pk); 25205 mp_toss_knot_list (mp, cur_exp_knot ()); 25206 set_cur_exp_knot (pk); 25207 } else if (mp->cur_exp.type == mp_pair_type) { 25208 mp_pair_to_path (mp); 25209 } else { 25210 mp_bad_unary (mp, mp_reverse); 25211 } 25212 break; 25213 case mp_ll_corner_op: 25214 if (!mp_get_cur_bbox (mp)) 25215 mp_bad_unary (mp, mp_ll_corner_op); 25216 else 25217 mp_pair_value (mp, mp_minx, mp_miny); 25218 break; 25219 case mp_lr_corner_op: 25220 if (!mp_get_cur_bbox (mp)) 25221 mp_bad_unary (mp, mp_lr_corner_op); 25222 else 25223 mp_pair_value (mp, mp_maxx, mp_miny); 25224 break; 25225 case mp_ul_corner_op: 25226 if (!mp_get_cur_bbox (mp)) 25227 mp_bad_unary (mp, mp_ul_corner_op); 25228 else 25229 mp_pair_value (mp, mp_minx, mp_maxy); 25230 break; 25231 case mp_ur_corner_op: 25232 if (!mp_get_cur_bbox (mp)) 25233 mp_bad_unary (mp, mp_ur_corner_op); 25234 else 25235 mp_pair_value (mp, mp_maxx, mp_maxy); 25236 break; 25237 case mp_read_from_op: 25238 case mp_close_from_op: 25239 if (mp->cur_exp.type != mp_string_type) 25240 mp_bad_unary (mp, c); 25241 else 25242 mp_do_read_or_close (mp, c); 25243 break; 25244 25245 } /* there are no other cases */ 25246 check_arith(); 25247} 25248 25249 25250@ The |nice_pair| function returns |true| if both components of a pair 25251are known. 25252 25253@<Declare unary action procedures@>= 25254static boolean mp_nice_pair (MP mp, mp_node p, quarterword t) { 25255 (void) mp; 25256 if (t == mp_pair_type) { 25257 p = value_node (p); 25258 if (mp_type (x_part (p)) == mp_known) 25259 if (mp_type (y_part (p)) == mp_known) 25260 return true; 25261 } 25262 return false; 25263} 25264 25265 25266@ The |nice_color_or_pair| function is analogous except that it also accepts 25267fully known colors. 25268 25269@<Declare unary action procedures@>= 25270static boolean mp_nice_color_or_pair (MP mp, mp_node p, quarterword t) { 25271 mp_node q; 25272 (void) mp; 25273 switch (t) { 25274 case mp_pair_type: 25275 q = value_node (p); 25276 if (mp_type (x_part (q)) == mp_known) 25277 if (mp_type (y_part (q)) == mp_known) 25278 return true; 25279 break; 25280 case mp_color_type: 25281 q = value_node (p); 25282 if (mp_type (red_part (q)) == mp_known) 25283 if (mp_type (green_part (q)) == mp_known) 25284 if (mp_type (blue_part (q)) == mp_known) 25285 return true; 25286 break; 25287 case mp_cmykcolor_type: 25288 q = value_node (p); 25289 if (mp_type (cyan_part (q)) == mp_known) 25290 if (mp_type (magenta_part (q)) == mp_known) 25291 if (mp_type (yellow_part (q)) == mp_known) 25292 if (mp_type (black_part (q)) == mp_known) 25293 return true; 25294 break; 25295 } 25296 return false; 25297} 25298 25299 25300@ @<Declare unary action...@>= 25301static void mp_print_known_or_unknown_type (MP mp, quarterword t, mp_node v) { 25302 mp_print_char (mp, xord ('(')); 25303 if (t > mp_known) 25304 mp_print (mp, "unknown numeric"); 25305 else { 25306 if ((t == mp_pair_type) || (t == mp_color_type) || (t == mp_cmykcolor_type)) 25307 if (!mp_nice_color_or_pair (mp, v, t)) 25308 mp_print (mp, "unknown "); 25309 mp_print_type (mp, t); 25310 } 25311 mp_print_char (mp, xord (')')); 25312} 25313 25314 25315@ @<Declare unary action...@>= 25316static void mp_bad_unary (MP mp, quarterword c) { 25317 char msg[256]; 25318 mp_string sname; 25319 int old_setting = mp->selector; 25320 const char *hlp[] = { 25321 "I'm afraid I don't know how to apply that operation to that", 25322 "particular type. Continue, and I'll simply return the", 25323 "argument (shown above) as the result of the operation.", 25324 NULL }; 25325 mp->selector = new_string; 25326 mp_print_op (mp, c); 25327 mp_print_known_or_unknown_type (mp, mp->cur_exp.type, cur_exp_node ()); 25328 sname = mp_make_string(mp); 25329 mp->selector = old_setting; 25330 mp_snprintf (msg, 256, "Not implemented: %s", mp_str(mp, sname)); 25331 delete_str_ref(sname); 25332 mp_disp_err(mp, NULL); 25333 mp_back_error (mp, msg, hlp, true); 25334@.Not implemented...@>; 25335 mp_get_x_next (mp); 25336} 25337 25338 25339 25340@ Negation is easy except when the current expression 25341is of type |independent|, or when it is a pair with one or more 25342|independent| components. 25343 25344@<Declare unary action...@>= 25345static void mp_negate_dep_list (MP mp, mp_value_node p) { 25346 (void) mp; 25347 while (1) { 25348 number_negate (dep_value (p)); 25349 if (dep_info (p) == NULL) 25350 return; 25351 p = (mp_value_node) mp_link (p); 25352 } 25353} 25354 25355 25356@ It is tempting to argue that the negative of an independent variable 25357is an independent variable, hence we don't have to do anything when 25358negating it. The fallacy is that other dependent variables pointing 25359to the current expression must change the sign of their 25360coefficients if we make no change to the current expression. 25361 25362Instead, we work around the problem by copying the current expression 25363and recycling it afterwards (cf.~the |stash_in| routine). 25364 25365@d negate_value(A) if (mp_type (A) == mp_known) { 25366 set_value_number(A, (value_number (A))); /* to clear the rest */ 25367 number_negate (value_number (A)); 25368 } else { 25369 mp_negate_dep_list (mp, (mp_value_node) dep_list ((mp_value_node) A)); 25370 } 25371 25372@<Declare unary action...@>= 25373static void negate_cur_expr(MP mp) { 25374 mp_node p, q, r; /* for list manipulation */ 25375 switch (mp->cur_exp.type) { 25376 case mp_color_type: 25377 case mp_cmykcolor_type: 25378 case mp_pair_type: 25379 case mp_independent: 25380 q = cur_exp_node (); 25381 mp_make_exp_copy (mp, q); 25382 if (mp->cur_exp.type == mp_dependent) { 25383 mp_negate_dep_list (mp, (mp_value_node) dep_list ((mp_value_node) 25384 cur_exp_node ())); 25385 } else if (mp->cur_exp.type <= mp_pair_type) { 25386 /* |mp_color_type| |mp_cmykcolor_type|, or |mp_pair_type| */ 25387 p = value_node (cur_exp_node ()); 25388 switch (mp->cur_exp.type) { 25389 case mp_pair_type: 25390 r = x_part (p); 25391 negate_value (r); 25392 r = y_part (p); 25393 negate_value (r); 25394 break; 25395 case mp_color_type: 25396 r = red_part (p); 25397 negate_value (r); 25398 r = green_part (p); 25399 negate_value (r); 25400 r = blue_part (p); 25401 negate_value (r); 25402 break; 25403 case mp_cmykcolor_type: 25404 r = cyan_part (p); 25405 negate_value (r); 25406 r = magenta_part (p); 25407 negate_value (r); 25408 r = yellow_part (p); 25409 negate_value (r); 25410 r = black_part (p); 25411 negate_value (r); 25412 break; 25413 default: /* there are no other valid cases, but please the compiler */ 25414 break; 25415 } 25416 } /* if |cur_type=mp_known| then |cur_exp=0| */ 25417 mp_recycle_value (mp, q); 25418 mp_free_value_node (mp, q); 25419 break; 25420 case mp_dependent: 25421 case mp_proto_dependent: 25422 mp_negate_dep_list (mp, (mp_value_node) dep_list ((mp_value_node) 25423 cur_exp_node ())); 25424 break; 25425 case mp_known: 25426 if (is_number(cur_exp_value_number())) 25427 number_negate (cur_exp_value_number()); 25428 break; 25429 default: 25430 mp_bad_unary (mp, mp_minus); 25431 break; 25432 } 25433} 25434 25435@ If the current expression is a pair, but the context wants it to 25436be a path, we call |pair_to_path|. 25437 25438@<Declare unary action...@>= 25439static void mp_pair_to_path (MP mp) { 25440 set_cur_exp_knot (mp_pair_to_knot (mp)); 25441 mp->cur_exp.type = mp_path_type; 25442} 25443 25444 25445 25446@ @<Declarations@>= 25447static void mp_bad_color_part (MP mp, quarterword c); 25448 25449@ @c 25450static void mp_bad_color_part (MP mp, quarterword c) { 25451 mp_node p; /* the big node */ 25452 mp_value new_expr; 25453 char msg[256]; 25454 int old_setting; 25455 mp_string sname; 25456 const char *hlp[] = { 25457 "You can only ask for the redpart, greenpart, bluepart of a rgb object,", 25458 "the cyanpart, magentapart, yellowpart or blackpart of a cmyk object, ", 25459 "or the greypart of a grey object. No mixing and matching, please.", 25460 NULL }; 25461 memset(&new_expr,0,sizeof(mp_value)); 25462 new_number(new_expr.data.n); 25463 p = mp_link (edge_list (cur_exp_node ())); 25464 mp_disp_err(mp, NULL); 25465 old_setting = mp->selector; 25466 mp->selector = new_string; 25467 mp_print_op (mp, c); 25468 sname = mp_make_string(mp); 25469 mp->selector = old_setting; 25470@.Wrong picture color model...@>; 25471 if (mp_color_model (p) == mp_grey_model) 25472 mp_snprintf (msg, 256, "Wrong picture color model: %s of grey object", mp_str(mp, sname)); 25473 else if (mp_color_model (p) == mp_cmyk_model) 25474 mp_snprintf (msg, 256, "Wrong picture color model: %s of cmyk object", mp_str(mp, sname)); 25475 else if (mp_color_model (p) == mp_rgb_model) 25476 mp_snprintf (msg, 256, "Wrong picture color model: %s of rgb object", mp_str(mp, sname)); 25477 else if (mp_color_model (p) == mp_no_model) 25478 mp_snprintf (msg, 256, "Wrong picture color model: %s of marking object", mp_str(mp, sname)); 25479 else 25480 mp_snprintf (msg, 256, "Wrong picture color model: %s of defaulted object", mp_str(mp, sname)); 25481 delete_str_ref(sname); 25482 mp_error (mp, msg, hlp, true); 25483 if (c == mp_black_part) 25484 number_clone (new_expr.data.n, unity_t); 25485 else 25486 set_number_to_zero(new_expr.data.n); 25487 mp_flush_cur_exp (mp, new_expr); 25488} 25489 25490 25491@ In the following procedure, |cur_exp| points to a capsule, which points to 25492a big node. We want to delete all but one part of the big node. 25493 25494@<Declare unary action...@>= 25495static void mp_take_part (MP mp, quarterword c) { 25496 mp_node p; /* the big node */ 25497 p = value_node (cur_exp_node ()); 25498 set_value_node (mp->temp_val, p); 25499 mp_type (mp->temp_val) = mp->cur_exp.type; 25500 mp_link (p) = mp->temp_val; 25501 mp_free_value_node (mp, cur_exp_node ()); 25502 switch (c) { 25503 case mp_x_part: 25504 if (mp->cur_exp.type == mp_pair_type) 25505 mp_make_exp_copy (mp, x_part (p)); 25506 else 25507 mp_make_exp_copy (mp, tx_part (p)); 25508 break; 25509 case mp_y_part: 25510 if (mp->cur_exp.type == mp_pair_type) 25511 mp_make_exp_copy (mp, y_part (p)); 25512 else 25513 mp_make_exp_copy (mp, ty_part (p)); 25514 break; 25515 case mp_xx_part: 25516 mp_make_exp_copy (mp, xx_part (p)); 25517 break; 25518 case mp_xy_part: 25519 mp_make_exp_copy (mp, xy_part (p)); 25520 break; 25521 case mp_yx_part: 25522 mp_make_exp_copy (mp, yx_part (p)); 25523 break; 25524 case mp_yy_part: 25525 mp_make_exp_copy (mp, yy_part (p)); 25526 break; 25527 case mp_red_part: 25528 mp_make_exp_copy (mp, red_part (p)); 25529 break; 25530 case mp_green_part: 25531 mp_make_exp_copy (mp, green_part (p)); 25532 break; 25533 case mp_blue_part: 25534 mp_make_exp_copy (mp, blue_part (p)); 25535 break; 25536 case mp_cyan_part: 25537 mp_make_exp_copy (mp, cyan_part (p)); 25538 break; 25539 case mp_magenta_part: 25540 mp_make_exp_copy (mp, magenta_part (p)); 25541 break; 25542 case mp_yellow_part: 25543 mp_make_exp_copy (mp, yellow_part (p)); 25544 break; 25545 case mp_black_part: 25546 mp_make_exp_copy (mp, black_part (p)); 25547 break; 25548 } 25549 mp_recycle_value (mp, mp->temp_val); 25550} 25551 25552 25553@ @<Initialize table entries@>= 25554mp->temp_val = mp_get_value_node (mp); 25555mp_name_type (mp->temp_val) = mp_capsule; 25556 25557@ @<Free table entries@>= 25558mp_free_value_node (mp, mp->temp_val); 25559 25560 25561@ @<Declarations@>= 25562static mp_edge_header_node mp_scale_edges (MP mp, mp_number se_sf, mp_edge_header_node se_pic); 25563 25564@ @<Declare unary action...@>= 25565static void mp_take_pict_part (MP mp, quarterword c) { 25566 mp_node p; /* first graphical object in |cur_exp| */ 25567 mp_value new_expr; 25568 memset(&new_expr,0,sizeof(mp_value)); 25569 new_number(new_expr.data.n); 25570 p = mp_link (edge_list (cur_exp_node ())); 25571 if (p != NULL) { 25572 switch (c) { 25573 case mp_x_part: 25574 case mp_y_part: 25575 case mp_xx_part: 25576 case mp_xy_part: 25577 case mp_yx_part: 25578 case mp_yy_part: 25579 if (mp_type (p) == mp_text_node_type) { 25580 mp_text_node p0 = (mp_text_node)p; 25581 switch (c) { 25582 case mp_x_part: 25583 number_clone(new_expr.data.n, p0->tx); 25584 break; 25585 case mp_y_part: 25586 number_clone(new_expr.data.n, p0->ty); 25587 break; 25588 case mp_xx_part: 25589 number_clone(new_expr.data.n, p0->txx); 25590 break; 25591 case mp_xy_part: 25592 number_clone(new_expr.data.n, p0->txy); 25593 break; 25594 case mp_yx_part: 25595 number_clone(new_expr.data.n, p0->tyx); 25596 break; 25597 case mp_yy_part: 25598 number_clone(new_expr.data.n, p0->tyy); 25599 break; 25600 } 25601 mp_flush_cur_exp (mp, new_expr); 25602 } else 25603 goto NOT_FOUND; 25604 break; 25605 case mp_red_part: 25606 case mp_green_part: 25607 case mp_blue_part: 25608 if (has_color (p)) { 25609 switch (c) { 25610 case mp_red_part: 25611 number_clone(new_expr.data.n,((mp_stroked_node)p)->red); 25612 break; 25613 case mp_green_part: 25614 number_clone(new_expr.data.n,((mp_stroked_node)p)->green); 25615 break; 25616 case mp_blue_part: 25617 number_clone(new_expr.data.n,((mp_stroked_node)p)->blue); 25618 break; 25619 } 25620 mp_flush_cur_exp (mp, new_expr); 25621 } else 25622 goto NOT_FOUND; 25623 break; 25624 case mp_cyan_part: 25625 case mp_magenta_part: 25626 case mp_yellow_part: 25627 case mp_black_part: 25628 if (has_color (p)) { 25629 if (mp_color_model (p) == mp_uninitialized_model && c == mp_black_part) { 25630 set_number_to_unity(new_expr.data.n); 25631 } else { 25632 switch (c) { 25633 case mp_cyan_part: 25634 number_clone(new_expr.data.n,((mp_stroked_node)p)->cyan); 25635 break; 25636 case mp_magenta_part: 25637 number_clone(new_expr.data.n,((mp_stroked_node)p)->magenta); 25638 break; 25639 case mp_yellow_part: 25640 number_clone(new_expr.data.n,((mp_stroked_node)p)->yellow); 25641 break; 25642 case mp_black_part: 25643 number_clone(new_expr.data.n,((mp_stroked_node)p)->black); 25644 break; 25645 } 25646 } 25647 mp_flush_cur_exp (mp, new_expr); 25648 } else 25649 goto NOT_FOUND; 25650 break; 25651 case mp_grey_part: 25652 if (has_color (p)) { 25653 number_clone(new_expr.data.n,((mp_stroked_node)p)->grey); 25654 mp_flush_cur_exp (mp, new_expr); 25655 } else 25656 goto NOT_FOUND; 25657 break; 25658 case mp_color_model_part: 25659 if (has_color (p)) { 25660 if (mp_color_model (p) == mp_uninitialized_model) { 25661 number_clone (new_expr.data.n, internal_value (mp_default_color_model)); 25662 } else { 25663 number_clone (new_expr.data.n, unity_t); 25664 number_multiply_int (new_expr.data.n, mp_color_model (p)); 25665 } 25666 mp_flush_cur_exp (mp, new_expr); 25667 } else 25668 goto NOT_FOUND; 25669 break; 25670 case mp_text_part: 25671 if (mp_type (p) != mp_text_node_type) 25672 goto NOT_FOUND; 25673 else { 25674 new_expr.data.str = mp_text_p (p); 25675 add_str_ref (new_expr.data.str); 25676 mp_flush_cur_exp (mp, new_expr); 25677 mp->cur_exp.type = mp_string_type; 25678 }; 25679 break; 25680 case mp_prescript_part: 25681 if (!has_color (p)) { 25682 goto NOT_FOUND; 25683 } else { 25684 if (mp_pre_script(p)) { 25685 new_expr.data.str = mp_pre_script(p); 25686 add_str_ref (new_expr.data.str); 25687 } else { 25688 new_expr.data.str = mp_rts(mp,""); 25689 } 25690 mp_flush_cur_exp (mp, new_expr); 25691 mp->cur_exp.type = mp_string_type; 25692 }; 25693 break; 25694 case mp_postscript_part: 25695 if (!has_color (p)) { 25696 goto NOT_FOUND; 25697 } else { 25698 if (mp_post_script(p)) { 25699 new_expr.data.str = mp_post_script(p); 25700 add_str_ref (new_expr.data.str); 25701 } else { 25702 new_expr.data.str = mp_rts(mp,""); 25703 } 25704 mp_flush_cur_exp (mp, new_expr); 25705 mp->cur_exp.type = mp_string_type; 25706 }; 25707 break; 25708 case mp_font_part: 25709 if (mp_type (p) != mp_text_node_type) 25710 goto NOT_FOUND; 25711 else { 25712 new_expr.data.str = mp_rts (mp, mp->font_name[mp_font_n (p)]); 25713 add_str_ref (new_expr.data.str); 25714 mp_flush_cur_exp (mp, new_expr); 25715 mp->cur_exp.type = mp_string_type; 25716 }; 25717 break; 25718 case mp_path_part: 25719 if (mp_type (p) == mp_text_node_type) { 25720 goto NOT_FOUND; 25721 } else if (is_stop (p)) { 25722 mp_confusion (mp, "pict"); 25723 } else { 25724 new_expr.data.node = NULL; 25725 switch (mp_type (p)) { 25726 case mp_fill_node_type: 25727 new_expr.data.p = mp_copy_path (mp, mp_path_p ((mp_fill_node) p)); 25728 break; 25729 case mp_stroked_node_type: 25730 new_expr.data.p = mp_copy_path (mp, mp_path_p ((mp_stroked_node) p)); 25731 break; 25732 case mp_start_bounds_node_type: 25733 new_expr.data.p = mp_copy_path (mp, mp_path_p ((mp_start_bounds_node) p)); 25734 break; 25735 case mp_start_clip_node_type: 25736 new_expr.data.p = mp_copy_path (mp, mp_path_p ((mp_start_clip_node) p)); 25737 break; 25738 default: 25739 assert (0); 25740 break; 25741 } 25742 mp_flush_cur_exp (mp, new_expr); 25743 mp->cur_exp.type = mp_path_type; 25744 } 25745 break; 25746 case mp_pen_part: 25747 if (!has_pen (p)) { 25748 goto NOT_FOUND; 25749 } else { 25750 switch (mp_type (p)) { 25751 case mp_fill_node_type: 25752 if (mp_pen_p ((mp_fill_node) p) == NULL) 25753 goto NOT_FOUND; 25754 else { 25755 new_expr.data.p = copy_pen (mp_pen_p ((mp_fill_node) p)); 25756 mp_flush_cur_exp (mp, new_expr); 25757 mp->cur_exp.type = mp_pen_type; 25758 } 25759 break; 25760 case mp_stroked_node_type: 25761 if (mp_pen_p ((mp_stroked_node) p) == NULL) 25762 goto NOT_FOUND; 25763 else { 25764 new_expr.data.p = copy_pen (mp_pen_p ((mp_stroked_node) p)); 25765 mp_flush_cur_exp (mp, new_expr); 25766 mp->cur_exp.type = mp_pen_type; 25767 } 25768 break; 25769 default: 25770 assert (0); 25771 break; 25772 } 25773 } 25774 break; 25775 case mp_dash_part: 25776 if (mp_type (p) != mp_stroked_node_type) { 25777 goto NOT_FOUND; 25778 } else { 25779 if (mp_dash_p (p) == NULL) { 25780 goto NOT_FOUND; 25781 } else { 25782 add_edge_ref (mp_dash_p (p)); 25783 new_expr.data.node = (mp_node)mp_scale_edges (mp, ((mp_stroked_node)p)->dash_scale, 25784 (mp_edge_header_node)mp_dash_p (p)); 25785 mp_flush_cur_exp (mp, new_expr); 25786 mp->cur_exp.type = mp_picture_type; 25787 } 25788 } 25789 break; 25790 } /* all cases have been enumerated */ 25791 return; 25792 }; 25793NOT_FOUND: 25794 /* Convert the current expression to a NULL value appropriate for |c| */ 25795 switch (c) { 25796 case mp_text_part: 25797 case mp_font_part: 25798 case mp_prescript_part: 25799 case mp_postscript_part: 25800 new_expr.data.str = mp_rts(mp,""); 25801 mp_flush_cur_exp (mp, new_expr); 25802 mp->cur_exp.type = mp_string_type; 25803 break; 25804 case mp_path_part: 25805 new_expr.data.p = mp_new_knot (mp); 25806 mp_flush_cur_exp (mp, new_expr); 25807 mp_left_type (cur_exp_knot ()) = mp_endpoint; 25808 mp_right_type (cur_exp_knot ()) = mp_endpoint; 25809 mp_next_knot (cur_exp_knot ()) = cur_exp_knot (); 25810 set_number_to_zero(cur_exp_knot ()->x_coord); 25811 set_number_to_zero(cur_exp_knot ()->y_coord); 25812 mp_originator (cur_exp_knot ()) = mp_metapost_user; 25813 mp->cur_exp.type = mp_path_type; 25814 break; 25815 case mp_pen_part: 25816 new_expr.data.p = mp_get_pen_circle (mp, zero_t); 25817 mp_flush_cur_exp (mp, new_expr); 25818 mp->cur_exp.type = mp_pen_type; 25819 break; 25820 case mp_dash_part: 25821 new_expr.data.node = (mp_node)mp_get_edge_header_node (mp); 25822 mp_flush_cur_exp (mp, new_expr); 25823 mp_init_edges (mp, (mp_edge_header_node)cur_exp_node ()); 25824 mp->cur_exp.type = mp_picture_type; 25825 break; 25826 default: 25827 set_number_to_zero(new_expr.data.n); 25828 mp_flush_cur_exp (mp, new_expr); 25829 break; 25830 } 25831} 25832 25833@ @<Declare unary action...@>= 25834static void mp_str_to_num (MP mp, quarterword c) { /* converts a string to a number */ 25835 integer n; /* accumulator */ 25836 ASCII_code m; /* current character */ 25837 unsigned k; /* index into |str_pool| */ 25838 int b; /* radix of conversion */ 25839 boolean bad_char; /* did the string contain an invalid digit? */ 25840 mp_value new_expr; 25841 memset(&new_expr,0,sizeof(mp_value)); 25842 new_number(new_expr.data.n); 25843 if (c == mp_ASCII_op) { 25844 if (cur_exp_str ()->len == 0) 25845 n = -1; 25846 else 25847 n = cur_exp_str ()->str[0]; 25848 } else { 25849 if (c == mp_oct_op) 25850 b = 8; 25851 else 25852 b = 16; 25853 n = 0; 25854 bad_char = false; 25855 for (k = 0; k < cur_exp_str ()->len; k++) { 25856 m = (ASCII_code) (*(cur_exp_str ()->str + k)); 25857 if ((m >= '0') && (m <= '9')) 25858 m = (ASCII_code) (m - '0'); 25859 else if ((m >= 'A') && (m <= 'F')) 25860 m = (ASCII_code) (m - 'A' + 10); 25861 else if ((m >= 'a') && (m <= 'f')) 25862 m = (ASCII_code) (m - 'a' + 10); 25863 else { 25864 bad_char = true; 25865 m = 0; 25866 }; 25867 if ((int) m >= b) { 25868 bad_char = true; 25869 m = 0; 25870 }; 25871 if (n < 32768 / b) 25872 n = n * b + m; 25873 else 25874 n = 32767; 25875 } 25876 /* Give error messages if |bad_char| or |n>=4096| */ 25877 if (bad_char) { 25878 const char *hlp[] = {"I zeroed out characters that weren't hex digits.", NULL}; 25879 if (c == mp_oct_op) { 25880 hlp[0] = "I zeroed out characters that weren't in the range 0..7."; 25881 } 25882 mp_disp_err(mp, NULL); 25883 mp_back_error (mp, "String contains illegal digits", hlp, true); 25884 mp_get_x_next (mp); 25885 } 25886 if ((n > 4095)) { /* todo, this is scaled specific */ 25887 if (number_positive (internal_value (mp_warning_check))) { 25888 char msg[256]; 25889 const char *hlp[] = { 25890 "I have trouble with numbers greater than 4095; watch out.", 25891 "(Set warningcheck:=0 to suppress this message.)", 25892 NULL }; 25893 mp_snprintf (msg, 256,"Number too large (%d)", (int)n); 25894 mp_back_error (mp, msg, hlp, true); 25895 mp_get_x_next (mp); 25896 } 25897 } 25898 } 25899 number_clone (new_expr.data.n, unity_t); 25900 number_multiply_int(new_expr.data.n, n); 25901 mp_flush_cur_exp (mp, new_expr); 25902} 25903 25904@ @<Declare unary action...@>= 25905static void mp_path_length (MP mp, mp_number *n) { /* computes the length of the current path */ 25906 mp_knot p; /* traverser */ 25907 set_number_to_zero (*n); 25908 p = cur_exp_knot (); 25909 if (mp_left_type (p) == mp_endpoint) { 25910 number_substract(*n, unity_t); /* -unity */ 25911 } 25912 do { 25913 p = mp_next_knot (p); 25914 number_add(*n, unity_t); 25915 } while (p != cur_exp_knot ()); 25916} 25917 25918 25919@ @<Declare unary action...@>= 25920static void mp_pict_length (MP mp, mp_number *n) { 25921 /* counts interior components in picture |cur_exp| */ 25922 mp_node p; /* traverser */ 25923 set_number_to_zero (*n); 25924 p = mp_link (edge_list (cur_exp_node ())); 25925 if (p != NULL) { 25926 if (is_start_or_stop (p)) 25927 if (mp_skip_1component (mp, p) == NULL) 25928 p = mp_link (p); 25929 while (p != NULL) { 25930 if ( ! is_start_or_stop(p) ) 25931 p = mp_link(p); 25932 else if ( ! is_stop(p)) 25933 p = mp_skip_1component(mp, p); 25934 else 25935 return; 25936 number_add(*n, unity_t); 25937 } 25938 } 25939} 25940 25941 25942@ The function |an_angle| returns the value of the |angle| primitive, or $0$ if the 25943argument is |origin|. 25944 25945@<Declare unary action...@>= 25946static void mp_an_angle (MP mp, mp_number *ret, mp_number xpar, mp_number ypar) { 25947 set_number_to_zero (*ret); 25948 if ((!(number_zero(xpar) && number_zero(ypar)))) { 25949 n_arg (*ret, xpar, ypar); 25950 } 25951} 25952 25953 25954@ The actual turning number is (for the moment) computed in a C function 25955that receives eight integers corresponding to the four controlling points, 25956and returns a single angle. Besides those, we have to account for discrete 25957moves at the actual points. 25958 25959@d mp_floor(a) ((a)>=0 ? (int)(a) : -(int)(-(a))) 25960@d bezier_error (720*(256*256*16))+1 25961@d mp_sign(v) ((v)>0 ? 1 : ((v)<0 ? -1 : 0 )) 25962@d mp_out(A) (double)((A)/16) 25963 25964@<Declare unary action...@>= 25965static void mp_bezier_slope (MP mp, mp_number *ret, mp_number AX, mp_number AY, mp_number BX, 25966 mp_number BY, mp_number CX, mp_number CY, mp_number DX, 25967 mp_number DY); 25968 25969@ @c 25970static void mp_bezier_slope (MP mp, mp_number *ret, mp_number AX, mp_number AY, mp_number BX, 25971 mp_number BY, mp_number CX, mp_number CY, mp_number DX, 25972 mp_number DY) { 25973 double a, b, c; 25974 mp_number deltax, deltay; 25975 double ax, ay, bx, by, cx, cy, dx, dy; 25976 mp_number xi, xo, xm; 25977 double res = 0; 25978 ax = number_to_double (AX); 25979 ay = number_to_double (AY); 25980 bx = number_to_double (BX); 25981 by = number_to_double (BY); 25982 cx = number_to_double (CX); 25983 cy = number_to_double (CY); 25984 dx = number_to_double (DX); 25985 dy = number_to_double (DY); 25986 new_number (deltax); 25987 new_number (deltay); 25988 set_number_from_substraction(deltax, BX, AX); 25989 set_number_from_substraction(deltay, BY, AY); 25990 if (number_zero(deltax) && number_zero(deltay)) { 25991 set_number_from_substraction(deltax, CX, AX); 25992 set_number_from_substraction(deltay, CY, AY); 25993 } 25994 if (number_zero(deltax) && number_zero(deltay)) { 25995 set_number_from_substraction(deltax, DX, AX); 25996 set_number_from_substraction(deltay, DY, AY); 25997 } 25998 new_number (xi); 25999 new_number (xm); 26000 new_number (xo); 26001 mp_an_angle (mp, &xi, deltax, deltay); 26002 set_number_from_substraction(deltax, CX, BX); 26003 set_number_from_substraction(deltay, CY, BY); 26004 mp_an_angle (mp, &xm, deltax, deltay); /* !!! never used? */ 26005 set_number_from_substraction(deltax, DX, CX); 26006 set_number_from_substraction(deltay, DY, CY); 26007 if (number_zero(deltax) && number_zero(deltay)) { 26008 set_number_from_substraction(deltax, DX, BX); 26009 set_number_from_substraction(deltay, DY, BY); 26010 } 26011 if (number_zero(deltax) && number_zero(deltay)) { 26012 set_number_from_substraction(deltax, DX, AX); 26013 set_number_from_substraction(deltay, DY, AY); 26014 } 26015 mp_an_angle (mp, &xo, deltax, deltay); 26016 a = (bx - ax) * (cy - by) - (cx - bx) * (by - ay); /* a = (bp-ap)x(cp-bp); */ 26017 b = (bx - ax) * (dy - cy) - (by - ay) * (dx - cx);; /* b = (bp-ap)x(dp-cp); */ 26018 c = (cx - bx) * (dy - cy) - (dx - cx) * (cy - by); /* c = (cp-bp)x(dp-cp); */ 26019 if ((a == 0) && (c == 0)) { 26020 res = (b == 0 ? 0 : (mp_out (number_to_double(xo)) - mp_out (number_to_double(xi)))); 26021 } else if ((a == 0) || (c == 0)) { 26022 if ((mp_sign (b) == mp_sign (a)) || (mp_sign (b) == mp_sign (c))) { 26023 res = mp_out (number_to_double(xo)) - mp_out (number_to_double(xi)); /* ? */ 26024 if (res < -180.0) 26025 res += 360.0; 26026 else if (res > 180.0) 26027 res -= 360.0; 26028 } else { 26029 res = mp_out (number_to_double(xo)) - mp_out (number_to_double(xi)); /* ? */ 26030 } 26031 } else if ((mp_sign (a) * mp_sign (c)) < 0) { 26032 res = mp_out (number_to_double(xo)) - mp_out (number_to_double(xi)); /* ? */ 26033 if (res < -180.0) 26034 res += 360.0; 26035 else if (res > 180.0) 26036 res -= 360.0; 26037 } else { 26038 if (mp_sign (a) == mp_sign (b)) { 26039 res = mp_out (number_to_double(xo)) - mp_out (number_to_double(xi)); /* ? */ 26040 if (res < -180.0) 26041 res += 360.0; 26042 else if (res > 180.0) 26043 res -= 360.0; 26044 } else { 26045 if ((b * b) == (4 * a * c)) { 26046 res = (double) bezier_error; 26047 } else if ((b * b) < (4 * a * c)) { 26048 res = mp_out (number_to_double(xo)) - mp_out (number_to_double(xi)); /* ? */ 26049 if (res <= 0.0 && res > -180.0) 26050 res += 360.0; 26051 else if (res >= 0.0 && res < 180.0) 26052 res -= 360.0; 26053 } else { 26054 res = mp_out (number_to_double(xo)) - mp_out (number_to_double(xi)); 26055 if (res < -180.0) 26056 res += 360.0; 26057 else if (res > 180.0) 26058 res -= 360.0; 26059 } 26060 } 26061 } 26062 free_number (deltax); 26063 free_number (deltay); 26064 free_number (xi); 26065 free_number (xo); 26066 free_number (xm); 26067 set_number_from_double(*ret, res); 26068 convert_scaled_to_angle (*ret); 26069} 26070 26071 26072@ 26073@d p_nextnext mp_next_knot(mp_next_knot(p)) 26074@d p_next mp_next_knot(p) 26075 26076@<Declare unary action...@>= 26077static void mp_turn_cycles (MP mp, mp_number *turns, mp_knot c) { 26078 mp_angle res, ang; /* the angles of intermediate results */ 26079 mp_knot p; /* for running around the path */ 26080 mp_number xp, yp; /* coordinates of next point */ 26081 mp_number x, y; /* helper coordinates */ 26082 mp_number arg1, arg2; 26083 mp_angle in_angle, out_angle; /* helper angles */ 26084 mp_angle seven_twenty_deg_t, neg_one_eighty_deg_t; 26085 unsigned old_setting; /* saved |selector| setting */ 26086 set_number_to_zero(*turns); 26087 new_number(arg1); 26088 new_number(arg2); 26089 new_number(xp); 26090 new_number(yp); 26091 new_number(x); 26092 new_number(y); 26093 new_angle(in_angle); 26094 new_angle(out_angle); 26095 new_angle(ang); 26096 new_angle(res); 26097 new_angle(seven_twenty_deg_t); 26098 new_angle(neg_one_eighty_deg_t); 26099 number_clone(seven_twenty_deg_t, three_sixty_deg_t); 26100 number_double(seven_twenty_deg_t); 26101 number_clone(neg_one_eighty_deg_t, one_eighty_deg_t); 26102 number_negate(neg_one_eighty_deg_t); 26103 p = c; 26104 old_setting = mp->selector; 26105 mp->selector = term_only; 26106 if (number_greater (internal_value (mp_tracing_commands), unity_t)) { 26107 mp_begin_diagnostic (mp); 26108 mp_print_nl (mp, ""); 26109 mp_end_diagnostic (mp, false); 26110 } 26111 do { 26112 number_clone (xp, p_next->x_coord); 26113 number_clone (yp, p_next->y_coord); 26114 mp_bezier_slope (mp, &ang, p->x_coord, p->y_coord, p->right_x, p->right_y, 26115 p_next->left_x, p_next->left_y, xp, yp); 26116 if (number_greater(ang, seven_twenty_deg_t)) { 26117 mp_error (mp, "Strange path", NULL, true); 26118 mp->selector = old_setting; 26119 set_number_to_zero(*turns); 26120 goto DONE; 26121 } 26122 number_add(res, ang); 26123 if (number_greater(res, one_eighty_deg_t)) { 26124 number_substract(res, three_sixty_deg_t); 26125 number_add(*turns, unity_t); 26126 } 26127 if (number_lessequal(res, neg_one_eighty_deg_t)) { 26128 number_add(res, three_sixty_deg_t); 26129 number_substract(*turns, unity_t); 26130 } 26131 /* incoming angle at next point */ 26132 number_clone (x, p_next->left_x); 26133 number_clone (y, p_next->left_y); 26134 if (number_equal(xp, x) && number_equal(yp, y)) { 26135 number_clone (x, p->right_x); 26136 number_clone (y, p->right_y); 26137 } 26138 if (number_equal(xp, x) && number_equal(yp, y)) { 26139 number_clone (x, p->x_coord); 26140 number_clone (y, p->y_coord); 26141 } 26142 set_number_from_substraction(arg1, xp, x); 26143 set_number_from_substraction(arg2, yp, y); 26144 mp_an_angle (mp, &in_angle, arg1, arg2); 26145 /* outgoing angle at next point */ 26146 number_clone (x, p_next->right_x); 26147 number_clone (y, p_next->right_y); 26148 if (number_equal(xp, x) && number_equal(yp, y)) { 26149 number_clone (x, p_nextnext->left_x); 26150 number_clone (y, p_nextnext->left_y); 26151 } 26152 if (number_equal(xp, x) && number_equal(yp, y)) { 26153 number_clone (x, p_nextnext->x_coord); 26154 number_clone (y, p_nextnext->y_coord); 26155 } 26156 set_number_from_substraction(arg1, x, xp); 26157 set_number_from_substraction(arg2, y, yp); 26158 mp_an_angle (mp, &out_angle, arg1, arg2); 26159 set_number_from_substraction(ang, out_angle, in_angle); 26160 mp_reduce_angle (mp, &ang); 26161 if (number_nonzero(ang)) { 26162 number_add(res, ang); 26163 if (number_greaterequal(res, one_eighty_deg_t)) { 26164 number_substract(res, three_sixty_deg_t); 26165 number_add(*turns, unity_t); 26166 } 26167 if (number_lessequal(res, neg_one_eighty_deg_t)) { 26168 number_add(res, three_sixty_deg_t); 26169 number_substract(*turns, unity_t); 26170 } 26171 } 26172 p = mp_next_knot (p); 26173 } while (p != c); 26174 mp->selector = old_setting; 26175DONE: 26176 free_number(xp); 26177 free_number(yp); 26178 free_number(x); 26179 free_number(y); 26180 free_number(seven_twenty_deg_t); 26181 free_number(neg_one_eighty_deg_t); 26182 free_number(in_angle); 26183 free_number(out_angle); 26184 free_number(ang); 26185 free_number(res); 26186 free_number(arg1); 26187 free_number(arg2); 26188} 26189 26190@ @<Declare unary action...@>= 26191static void mp_turn_cycles_wrapper (MP mp, mp_number *ret, mp_knot c) { 26192 if (mp_next_knot (c) == c) { 26193 /* one-knot paths always have a turning number of 1 */ 26194 set_number_to_unity(*ret); 26195 } else { 26196 mp_turn_cycles (mp, ret, c); 26197 } 26198} 26199 26200@ @<Declare unary action procedures@>= 26201static void mp_test_known (MP mp, quarterword c) { 26202 int b; /* is the current expression known? */ 26203 mp_node p; /* location in a big node */ 26204 mp_value new_expr; 26205 memset(&new_expr,0,sizeof(mp_value)); 26206 new_number(new_expr.data.n); 26207 b = mp_false_code; 26208 switch (mp->cur_exp.type) { 26209 case mp_vacuous: 26210 case mp_boolean_type: 26211 case mp_string_type: 26212 case mp_pen_type: 26213 case mp_path_type: 26214 case mp_picture_type: 26215 case mp_known: 26216 b = mp_true_code; 26217 break; 26218 case mp_transform_type: 26219 p = value_node (cur_exp_node ()); 26220 if (mp_type (tx_part (p)) != mp_known) 26221 break; 26222 if (mp_type (ty_part (p)) != mp_known) 26223 break; 26224 if (mp_type (xx_part (p)) != mp_known) 26225 break; 26226 if (mp_type (xy_part (p)) != mp_known) 26227 break; 26228 if (mp_type (yx_part (p)) != mp_known) 26229 break; 26230 if (mp_type (yy_part (p)) != mp_known) 26231 break; 26232 b = mp_true_code; 26233 break; 26234 case mp_color_type: 26235 p = value_node (cur_exp_node ()); 26236 if (mp_type (red_part (p)) != mp_known) 26237 break; 26238 if (mp_type (green_part (p)) != mp_known) 26239 break; 26240 if (mp_type (blue_part (p)) != mp_known) 26241 break; 26242 b = mp_true_code; 26243 break; 26244 case mp_cmykcolor_type: 26245 p = value_node (cur_exp_node ()); 26246 if (mp_type (cyan_part (p)) != mp_known) 26247 break; 26248 if (mp_type (magenta_part (p)) != mp_known) 26249 break; 26250 if (mp_type (yellow_part (p)) != mp_known) 26251 break; 26252 if (mp_type (black_part (p)) != mp_known) 26253 break; 26254 b = mp_true_code; 26255 break; 26256 case mp_pair_type: 26257 p = value_node (cur_exp_node ()); 26258 if (mp_type (x_part (p)) != mp_known) 26259 break; 26260 if (mp_type (y_part (p)) != mp_known) 26261 break; 26262 b = mp_true_code; 26263 break; 26264 default: 26265 break; 26266 } 26267 if (c == mp_known_op) { 26268 set_number_from_boolean (new_expr.data.n, b); 26269 } else { 26270 if (b==mp_true_code) { 26271 set_number_from_boolean (new_expr.data.n, mp_false_code); 26272 } else { 26273 set_number_from_boolean (new_expr.data.n, mp_true_code); 26274 } 26275 } 26276 mp_flush_cur_exp (mp, new_expr); 26277 cur_exp_node() = NULL; /* !! do not replace with |set_cur_exp_node()| !! */ 26278 mp->cur_exp.type = mp_boolean_type; 26279} 26280 26281@ The |pair_value| routine changes the current expression to a 26282given ordered pair of values. 26283 26284@<Declare unary action procedures@>= 26285static void mp_pair_value (MP mp, mp_number x, mp_number y) { 26286 mp_node p; /* a pair node */ 26287 mp_value new_expr; 26288 mp_number x1, y1; 26289 new_number(x1); 26290 new_number(y1); 26291 number_clone (x1, x); 26292 number_clone (y1, y); 26293 memset(&new_expr,0,sizeof(mp_value)); 26294 new_number(new_expr.data.n); 26295 p = mp_get_value_node (mp); 26296 new_expr.type = mp_type (p); 26297 new_expr.data.node = p; 26298 mp_flush_cur_exp (mp, new_expr); 26299 mp->cur_exp.type = mp_pair_type; 26300 mp_name_type (p) = mp_capsule; 26301 mp_init_pair_node (mp, p); 26302 p = value_node (p); 26303 mp_type (x_part (p)) = mp_known; 26304 set_value_number (x_part (p), x1); 26305 mp_type (y_part (p)) = mp_known; 26306 set_value_number (y_part (p), y1); 26307 free_number(x1); 26308 free_number(y1); 26309} 26310 26311 26312@ Here is a function that sets |minx|, |maxx|, |miny|, |maxy| to the bounding 26313box of the current expression. The boolean result is |false| if the expression 26314has the wrong type. 26315 26316@<Declare unary action procedures@>= 26317static boolean mp_get_cur_bbox (MP mp) { 26318 switch (mp->cur_exp.type) { 26319 case mp_picture_type: 26320 { 26321 mp_edge_header_node p0 = (mp_edge_header_node)cur_exp_node (); 26322 mp_set_bbox (mp, p0, true); 26323 if (number_greater(p0->minx, p0->maxx)) { 26324 set_number_to_zero(mp_minx); 26325 set_number_to_zero(mp_maxx); 26326 set_number_to_zero(mp_miny); 26327 set_number_to_zero(mp_maxy); 26328 } else { 26329 number_clone (mp_minx, p0->minx); 26330 number_clone (mp_maxx, p0->maxx); 26331 number_clone (mp_miny, p0->miny); 26332 number_clone (mp_maxy, p0->maxy); 26333 } 26334 } 26335 break; 26336 case mp_path_type: 26337 mp_path_bbox (mp, cur_exp_knot ()); 26338 break; 26339 case mp_pen_type: 26340 mp_pen_bbox (mp, cur_exp_knot ()); 26341 break; 26342 default: 26343 return false; 26344 } 26345 return true; 26346} 26347 26348 26349@ Here is a routine that interprets |cur_exp| as a file name and tries to read 26350a line from the file or to close the file. 26351 26352@<Declare unary action procedures@>= 26353static void mp_do_read_or_close (MP mp, quarterword c) { 26354 mp_value new_expr; 26355 readf_index n, n0; /* indices for searching |rd_fname| */ 26356 memset(&new_expr,0,sizeof(mp_value)); 26357 new_number(new_expr.data.n); 26358 /* Find the |n| where |rd_fname[n]=cur_exp|; if |cur_exp| must be inserted, 26359 call |start_read_input| and |goto found| or |not_found| */ 26360 /* Free slots in the |rd_file| and |rd_fname| arrays are marked with NULL's in 26361 |rd_fname|. */ 26362 { 26363 char *fn; 26364 n = mp->read_files; 26365 n0 = mp->read_files; 26366 fn = mp_xstrdup (mp, mp_str (mp, cur_exp_str ())); 26367 while (mp_xstrcmp (fn, mp->rd_fname[n]) != 0) { 26368 if (n > 0) { 26369 decr (n); 26370 } else if (c == mp_close_from_op) { 26371 goto CLOSE_FILE; 26372 } else { 26373 if (n0 == mp->read_files) { 26374 if (mp->read_files < mp->max_read_files) { 26375 incr (mp->read_files); 26376 } else { 26377 void **rd_file; 26378 char **rd_fname; 26379 readf_index l, k; 26380 l = mp->max_read_files + (mp->max_read_files / 4); 26381 rd_file = xmalloc ((l + 1), sizeof (void *)); 26382 rd_fname = xmalloc ((l + 1), sizeof (char *)); 26383 for (k = 0; k <= l; k++) { 26384 if (k <= mp->max_read_files) { 26385 rd_file[k] = mp->rd_file[k]; 26386 rd_fname[k] = mp->rd_fname[k]; 26387 } else { 26388 rd_file[k] = 0; 26389 rd_fname[k] = NULL; 26390 } 26391 } 26392 xfree (mp->rd_file); 26393 xfree (mp->rd_fname); 26394 mp->max_read_files = l; 26395 mp->rd_file = rd_file; 26396 mp->rd_fname = rd_fname; 26397 } 26398 } 26399 n = n0; 26400 if (mp_start_read_input (mp, fn, n)) 26401 goto FOUND; 26402 else 26403 goto NOT_FOUND; 26404 } 26405 if (mp->rd_fname[n] == NULL) { 26406 n0 = n; 26407 } 26408 } 26409 if (c == mp_close_from_op) { 26410 (mp->close_file) (mp, mp->rd_file[n]); 26411 goto NOT_FOUND; 26412 } 26413 } 26414 mp_begin_file_reading (mp); 26415 name = is_read; 26416 if (mp_input_ln (mp, mp->rd_file[n])) 26417 goto FOUND; 26418 mp_end_file_reading (mp); 26419NOT_FOUND: 26420 /* Record the end of file and set |cur_exp| to a dummy value */ 26421 xfree (mp->rd_fname[n]); 26422 mp->rd_fname[n] = NULL; 26423 if (n == mp->read_files - 1) 26424 mp->read_files = n; 26425 if (c == mp_close_from_op) 26426 goto CLOSE_FILE; 26427 new_expr.data.str = mp->eof_line; 26428 add_str_ref (new_expr.data.str); 26429 mp_flush_cur_exp (mp, new_expr); 26430 mp->cur_exp.type = mp_string_type; 26431 return; 26432CLOSE_FILE: 26433 mp_flush_cur_exp (mp, new_expr); 26434 mp->cur_exp.type = mp_vacuous; 26435 return; 26436FOUND: 26437 mp_flush_cur_exp (mp, new_expr); 26438 mp_finish_read (mp); 26439} 26440 26441@ The string denoting end-of-file is a one-byte string at position zero, by definition. 26442I have to cheat a little here because 26443 26444@<Glob...@>= 26445mp_string eof_line; 26446 26447@ @<Set init...@>= 26448mp->eof_line = mp_rtsl (mp, "\0", 1); 26449mp->eof_line->refs = MAX_STR_REF; 26450 26451@ Finally, we have the operations that combine a capsule~|p| 26452with the current expression. 26453 26454Several of the binary operations are potentially complicated by the 26455fact that |independent| values can sneak into capsules. For example, 26456we've seen an instance of this difficulty in the unary operation 26457of negation. In order to reduce the number of cases that need to be 26458handled, we first change the two operands (if necessary) 26459to rid them of |independent| components. The original operands are 26460put into capsules called |old_p| and |old_exp|, which will be 26461recycled after the binary operation has been safely carried out. 26462 26463@d binary_return { mp_finish_binary(mp, old_p, old_exp); return; } 26464 26465@c 26466@<Declare binary action procedures@>; 26467static void mp_finish_binary (MP mp, mp_node old_p, mp_node old_exp) { 26468 check_arith(); 26469 /* Recycle any sidestepped |independent| capsules */ 26470 if (old_p != NULL) { 26471 mp_recycle_value (mp, old_p); 26472 mp_free_value_node (mp, old_p); 26473 } 26474 if (old_exp != NULL) { 26475 mp_recycle_value (mp, old_exp); 26476 mp_free_value_node (mp, old_exp); 26477 } 26478} 26479static void mp_do_binary (MP mp, mp_node p, integer c) { 26480 mp_node q, r, rr; /* for list manipulation */ 26481 mp_node old_p, old_exp; /* capsules to recycle */ 26482 mp_value new_expr; 26483 check_arith(); 26484 if (number_greater (internal_value (mp_tracing_commands), two_t)) { 26485 /* Trace the current binary operation */ 26486 mp_begin_diagnostic (mp); 26487 mp_print_nl (mp, "{("); 26488 mp_print_exp (mp, p, 0); /* show the operand, but not verbosely */ 26489 mp_print_char (mp, xord (')')); 26490 mp_print_op (mp, (quarterword) c); 26491 mp_print_char (mp, xord ('(')); 26492 mp_print_exp (mp, NULL, 0); 26493 mp_print (mp, ")}"); 26494 mp_end_diagnostic (mp, false); 26495 } 26496 /* Sidestep |independent| cases in capsule |p| */ 26497 /* A big node is considered to be ``tarnished'' if it contains at least one 26498 independent component. We will define a simple function called `|tarnished|' 26499 that returns |NULL| if and only if its argument is not tarnished. */ 26500 switch (mp_type (p)) { 26501 case mp_transform_type: 26502 case mp_color_type: 26503 case mp_cmykcolor_type: 26504 case mp_pair_type: 26505 old_p = mp_tarnished (mp, p); 26506 break; 26507 case mp_independent: 26508 old_p = MP_VOID; 26509 break; 26510 default: 26511 old_p = NULL; 26512 break; 26513 } 26514 if (old_p != NULL) { 26515 q = mp_stash_cur_exp (mp); 26516 old_p = p; 26517 mp_make_exp_copy (mp, old_p); 26518 p = mp_stash_cur_exp (mp); 26519 mp_unstash_cur_exp (mp, q); 26520 } 26521 26522 /* Sidestep |independent| cases in the current expression */ 26523 switch (mp->cur_exp.type) { 26524 case mp_transform_type: 26525 case mp_color_type: 26526 case mp_cmykcolor_type: 26527 case mp_pair_type: 26528 old_exp = mp_tarnished (mp, cur_exp_node ()); 26529 break; 26530 case mp_independent: 26531 old_exp = MP_VOID; 26532 break; 26533 default: 26534 old_exp = NULL; 26535 break; 26536 } 26537 if (old_exp != NULL) { 26538 old_exp = cur_exp_node (); 26539 mp_make_exp_copy (mp, old_exp); 26540 } 26541 26542 switch (c) { 26543 case mp_plus: 26544 case mp_minus: 26545 /* Add or subtract the current expression from |p| */ 26546 if ((mp->cur_exp.type < mp_color_type) || (mp_type (p) < mp_color_type)) { 26547 mp_bad_binary (mp, p, (quarterword) c); 26548 } else { 26549 quarterword cc = (quarterword)c; 26550 if ((mp->cur_exp.type > mp_pair_type) && (mp_type (p) > mp_pair_type)) { 26551 mp_add_or_subtract (mp, p, NULL, cc); 26552 } else { 26553 if (mp->cur_exp.type != mp_type (p)) { 26554 mp_bad_binary (mp, p, cc); 26555 } else { 26556 q = value_node (p); 26557 r = value_node (cur_exp_node ()); 26558 switch (mp->cur_exp.type) { 26559 case mp_pair_type: 26560 mp_add_or_subtract (mp, x_part (q), x_part (r),cc); 26561 mp_add_or_subtract (mp, y_part (q), y_part (r),cc); 26562 break; 26563 case mp_color_type: 26564 mp_add_or_subtract (mp, red_part (q), red_part (r),cc); 26565 mp_add_or_subtract (mp, green_part (q), green_part (r),cc); 26566 mp_add_or_subtract (mp, blue_part (q), blue_part (r),cc); 26567 break; 26568 case mp_cmykcolor_type: 26569 mp_add_or_subtract (mp, cyan_part (q), cyan_part (r),cc); 26570 mp_add_or_subtract (mp, magenta_part (q), magenta_part (r),cc); 26571 mp_add_or_subtract (mp, yellow_part (q), yellow_part (r),cc); 26572 mp_add_or_subtract (mp, black_part (q), black_part (r),cc); 26573 break; 26574 case mp_transform_type: 26575 mp_add_or_subtract (mp, tx_part (q), tx_part (r),cc); 26576 mp_add_or_subtract (mp, ty_part (q), ty_part (r),cc); 26577 mp_add_or_subtract (mp, xx_part (q), xx_part (r),cc); 26578 mp_add_or_subtract (mp, xy_part (q), xy_part (r),cc); 26579 mp_add_or_subtract (mp, yx_part (q), yx_part (r),cc); 26580 mp_add_or_subtract (mp, yy_part (q), yy_part (r),cc); 26581 break; 26582 default: /* there are no other valid cases, but please the compiler */ 26583 break; 26584 } 26585 } 26586 } 26587 } 26588 break; 26589 case mp_less_than: 26590 case mp_less_or_equal: 26591 case mp_greater_than: 26592 case mp_greater_or_equal: 26593 case mp_equal_to: 26594 case mp_unequal_to: 26595 check_arith(); /* at this point |arith_error| should be |false|? */ 26596 if ((mp->cur_exp.type > mp_pair_type) && (mp_type (p) > mp_pair_type)) { 26597 mp_add_or_subtract (mp, p, NULL, mp_minus); /* |cur_exp:=(p)-cur_exp| */ 26598 } else if (mp->cur_exp.type != mp_type (p)) { 26599 mp_bad_binary (mp, p, (quarterword) c); 26600 goto DONE; 26601 } else if (mp->cur_exp.type == mp_string_type) { 26602 memset(&new_expr,0,sizeof(mp_value)); 26603 new_number(new_expr.data.n); 26604 set_number_from_scaled (new_expr.data.n, mp_str_vs_str (mp, value_str (p), cur_exp_str ())); 26605 mp_flush_cur_exp (mp, new_expr); 26606 } else if ((mp->cur_exp.type == mp_unknown_string) || 26607 (mp->cur_exp.type == mp_unknown_boolean)) { 26608 /* Check if unknowns have been equated */ 26609 /* When two unknown strings are in the same ring, we know that they are 26610 equal. Otherwise, we don't know whether they are equal or not, so we 26611 make no change. */ 26612 q = value_node (cur_exp_node ()); 26613 while ((q != cur_exp_node ()) && (q != p)) 26614 q = value_node (q); 26615 if (q == p) { 26616 memset(&new_expr,0,sizeof(mp_value)); 26617 new_number(new_expr.data.n); 26618 set_cur_exp_node (NULL); 26619 mp_flush_cur_exp (mp, new_expr); 26620 } 26621 26622 } else if ((mp->cur_exp.type <= mp_pair_type) 26623 && (mp->cur_exp.type >= mp_transform_type)) { 26624 /* Reduce comparison of big nodes to comparison of scalars */ 26625 /* In the following, the |while| loops exist just so that |break| can be used, 26626 each loop runs exactly once. */ 26627 quarterword part_type; 26628 q = value_node (p); 26629 r = value_node (cur_exp_node ()); 26630 part_type = 0; 26631 switch (mp->cur_exp.type) { 26632 case mp_pair_type: 26633 while (part_type==0) { 26634 rr = x_part (r); 26635 part_type = mp_x_part; 26636 mp_add_or_subtract (mp, x_part (q), rr, mp_minus); 26637 if (mp_type (rr) != mp_known || ! number_zero(value_number (rr))) 26638 break; 26639 rr = y_part (r); 26640 part_type = mp_y_part; 26641 mp_add_or_subtract (mp, y_part (q), rr, mp_minus); 26642 if (mp_type (rr) != mp_known || !number_zero(value_number (rr))) 26643 break; 26644 } 26645 mp_take_part (mp, part_type); 26646 break; 26647 case mp_color_type: 26648 while (part_type==0) { 26649 rr = red_part (r); 26650 part_type = mp_red_part; 26651 mp_add_or_subtract (mp, red_part (q), rr, mp_minus); 26652 if (mp_type (rr) != mp_known || ! number_zero(value_number (rr))) 26653 break; 26654 rr = green_part (r); 26655 part_type = mp_green_part; 26656 mp_add_or_subtract (mp, green_part (q), rr, mp_minus); 26657 if (mp_type (rr) != mp_known || !number_zero(value_number (rr))) 26658 break; 26659 rr = blue_part (r); 26660 part_type = mp_blue_part; 26661 mp_add_or_subtract (mp, blue_part (q), rr, mp_minus); 26662 if (mp_type (rr) != mp_known || !number_zero(value_number (rr))) 26663 break; 26664 } 26665 mp_take_part (mp, part_type); 26666 break; 26667 case mp_cmykcolor_type: 26668 while (part_type==0) { 26669 rr = cyan_part (r); 26670 part_type = mp_cyan_part; 26671 mp_add_or_subtract (mp, cyan_part (q), rr, mp_minus); 26672 if (mp_type (rr) != mp_known || !number_zero(value_number (rr))) 26673 break; 26674 rr = magenta_part (r); 26675 part_type = mp_magenta_part; 26676 mp_add_or_subtract (mp, magenta_part (q), rr, mp_minus); 26677 if (mp_type (rr) != mp_known || !number_zero(value_number (rr))) 26678 break; 26679 rr = yellow_part (r); 26680 part_type = mp_yellow_part; 26681 mp_add_or_subtract (mp, yellow_part (q), rr, mp_minus); 26682 if (mp_type (rr) != mp_known || !number_zero(value_number (rr))) 26683 break; 26684 rr = black_part (r); 26685 part_type = mp_black_part; 26686 mp_add_or_subtract (mp, black_part (q), rr, mp_minus); 26687 if (mp_type (rr) != mp_known || !number_zero(value_number (rr))) 26688 break; 26689 } 26690 mp_take_part (mp, part_type); 26691 break; 26692 case mp_transform_type: 26693 while (part_type==0) { 26694 rr = tx_part (r); 26695 part_type = mp_x_part; 26696 mp_add_or_subtract (mp, tx_part (q), rr, mp_minus); 26697 if (mp_type (rr) != mp_known || !number_zero(value_number (rr))) 26698 break; 26699 rr = ty_part (r); 26700 part_type = mp_y_part; 26701 mp_add_or_subtract (mp, ty_part (q), rr, mp_minus); 26702 if (mp_type (rr) != mp_known || !number_zero(value_number (rr))) 26703 break; 26704 rr = xx_part (r); 26705 part_type = mp_xx_part; 26706 mp_add_or_subtract (mp, xx_part (q), rr, mp_minus); 26707 if (mp_type (rr) != mp_known || !number_zero(value_number (rr))) 26708 break; 26709 rr = xy_part (r); 26710 part_type = mp_xy_part; 26711 mp_add_or_subtract (mp, xy_part (q), rr, mp_minus); 26712 if (mp_type (rr) != mp_known || !number_zero(value_number (rr))) 26713 break; 26714 rr = yx_part (r); 26715 part_type = mp_yx_part; 26716 mp_add_or_subtract (mp, yx_part (q), rr, mp_minus); 26717 if (mp_type (rr) != mp_known || !number_zero(value_number (rr))) 26718 break; 26719 rr = yy_part (r); 26720 part_type = mp_yy_part; 26721 mp_add_or_subtract (mp, yy_part (q), rr, mp_minus); 26722 if (mp_type (rr) != mp_known || !number_zero(value_number (rr))) 26723 break; 26724 } 26725 mp_take_part (mp, part_type); 26726 break; 26727 default: 26728 assert (0); /* todo: |mp->cur_exp.type>mp_transform_node_type| ? */ 26729 break; 26730 } 26731 26732 } else if (mp->cur_exp.type == mp_boolean_type) { 26733 memset(&new_expr,0,sizeof(mp_value)); 26734 new_number(new_expr.data.n); 26735 set_number_from_boolean (new_expr.data.n, number_to_scaled(cur_exp_value_number ()) - 26736 number_to_scaled (value_number (p))); 26737 mp_flush_cur_exp (mp, new_expr); 26738 } else { 26739 mp_bad_binary (mp, p, (quarterword) c); 26740 goto DONE; 26741 } 26742 /* Compare the current expression with zero */ 26743 if (mp->cur_exp.type != mp_known) { 26744 const char *hlp[] = { 26745 "Oh dear. I can\'t decide if the expression above is positive,", 26746 "negative, or zero. So this comparison test won't be `true'.", 26747 NULL }; 26748 if (mp->cur_exp.type < mp_known) { 26749 mp_disp_err (mp, p); 26750 hlp[0] = "The quantities shown above have not been equated."; 26751 hlp[1] = NULL; 26752 } 26753 mp_disp_err(mp, NULL); 26754 memset(&new_expr,0,sizeof(mp_value)); 26755 new_number(new_expr.data.n); 26756 set_number_from_boolean (new_expr.data.n, mp_false_code); 26757 mp_back_error (mp,"Unknown relation will be considered false", hlp, true); 26758 @.Unknown relation...@>; 26759 mp_get_x_next (mp); 26760 mp_flush_cur_exp (mp, new_expr); 26761 } else { 26762 switch (c) { 26763 case mp_less_than: 26764 boolean_reset (number_negative(cur_exp_value_number ())); 26765 break; 26766 case mp_less_or_equal: 26767 boolean_reset (number_nonpositive(cur_exp_value_number ())); 26768 break; 26769 case mp_greater_than: 26770 boolean_reset (number_positive(cur_exp_value_number ())); 26771 break; 26772 case mp_greater_or_equal: 26773 boolean_reset (number_nonnegative(cur_exp_value_number ())); 26774 break; 26775 case mp_equal_to: 26776 boolean_reset (number_zero(cur_exp_value_number ())); 26777 break; 26778 case mp_unequal_to: 26779 boolean_reset (number_nonzero(cur_exp_value_number ())); 26780 break; 26781 }; /* there are no other cases */ 26782 } 26783 mp->cur_exp.type = mp_boolean_type; 26784 DONE: 26785 mp->arith_error = false; /* ignore overflow in comparisons */ 26786 break; 26787 case mp_and_op: 26788 case mp_or_op: 26789 /* Here we use the sneaky fact that |and_op-false_code=or_op-true_code| */ 26790 if ((mp_type (p) != mp_boolean_type) || (mp->cur_exp.type != mp_boolean_type)) 26791 mp_bad_binary (mp, p, (quarterword) c); 26792 else if (number_to_boolean (p->data.n) == c + mp_false_code - mp_and_op) { 26793 set_cur_exp_value_boolean (number_to_boolean (p->data.n)); 26794 } 26795 break; 26796 case mp_times: 26797 if ((mp->cur_exp.type < mp_color_type) || (mp_type (p) < mp_color_type)) { 26798 mp_bad_binary (mp, p, mp_times); 26799 } else if ((mp->cur_exp.type == mp_known) || (mp_type (p) == mp_known)) { 26800 /* Multiply when at least one operand is known */ 26801 mp_number vv; 26802 new_fraction (vv); 26803 if (mp_type (p) == mp_known) { 26804 number_clone(vv, value_number (p)); 26805 mp_free_value_node (mp, p); 26806 } else { 26807 number_clone(vv, cur_exp_value_number ()); 26808 mp_unstash_cur_exp (mp, p); 26809 } 26810 if (mp->cur_exp.type == mp_known) { 26811 mp_number ret; 26812 new_number (ret); 26813 take_scaled (ret, cur_exp_value_number (), vv); 26814 set_cur_exp_value_number (ret); 26815 free_number (ret); 26816 } else if (mp->cur_exp.type == mp_pair_type) { 26817 mp_dep_mult (mp, (mp_value_node) x_part (value_node (cur_exp_node ())), vv, true); 26818 mp_dep_mult (mp, (mp_value_node) y_part (value_node (cur_exp_node ())), vv, true); 26819 } else if (mp->cur_exp.type == mp_color_type) { 26820 mp_dep_mult (mp, (mp_value_node) red_part (value_node (cur_exp_node ())), vv, true); 26821 mp_dep_mult (mp, (mp_value_node) green_part (value_node (cur_exp_node ())), vv, true); 26822 mp_dep_mult (mp, (mp_value_node) blue_part (value_node (cur_exp_node ())), vv, true); 26823 } else if (mp->cur_exp.type == mp_cmykcolor_type) { 26824 mp_dep_mult (mp, (mp_value_node) cyan_part (value_node (cur_exp_node ())), vv, true); 26825 mp_dep_mult (mp, (mp_value_node) magenta_part (value_node (cur_exp_node ())), vv, true); 26826 mp_dep_mult (mp, (mp_value_node) yellow_part (value_node (cur_exp_node ())), vv, true); 26827 mp_dep_mult (mp, (mp_value_node) black_part (value_node (cur_exp_node ())), vv, true); 26828 } else { 26829 mp_dep_mult (mp, NULL, vv, true); 26830 } 26831 free_number (vv); 26832 binary_return; 26833 26834 } else if ((mp_nice_color_or_pair (mp, p, mp_type (p)) 26835 && (mp->cur_exp.type > mp_pair_type)) 26836 || (mp_nice_color_or_pair (mp, cur_exp_node (), mp->cur_exp.type) 26837 && (mp_type (p) > mp_pair_type))) { 26838 mp_hard_times (mp, p); 26839 binary_return; 26840 } else { 26841 mp_bad_binary (mp, p, mp_times); 26842 } 26843 break; 26844 case mp_over: 26845 if ((mp->cur_exp.type != mp_known) || (mp_type (p) < mp_color_type)) { 26846 mp_bad_binary (mp, p, mp_over); 26847 } else { 26848 mp_number v_n; 26849 new_number (v_n); 26850 number_clone (v_n, cur_exp_value_number ()); 26851 mp_unstash_cur_exp (mp, p); 26852 if (number_zero(v_n)) { 26853 /* Squeal about division by zero */ 26854 const char *hlp[] = { 26855 "You're trying to divide the quantity shown above the error", 26856 "message by zero. I'm going to divide it by one instead.", 26857 NULL }; 26858 mp_disp_err(mp, NULL); 26859 mp_back_error (mp, "Division by zero", hlp, true); 26860 mp_get_x_next (mp); 26861 26862 } else { 26863 if (mp->cur_exp.type == mp_known) { 26864 mp_number ret; 26865 new_number (ret); 26866 make_scaled (ret, cur_exp_value_number (), v_n); 26867 set_cur_exp_value_number (ret); 26868 free_number (ret); 26869 } else if (mp->cur_exp.type == mp_pair_type) { 26870 mp_dep_div (mp, (mp_value_node) x_part (value_node (cur_exp_node ())), 26871 v_n); 26872 mp_dep_div (mp, (mp_value_node) y_part (value_node (cur_exp_node ())), 26873 v_n); 26874 } else if (mp->cur_exp.type == mp_color_type) { 26875 mp_dep_div (mp, 26876 (mp_value_node) red_part (value_node (cur_exp_node ())), 26877 v_n); 26878 mp_dep_div (mp, 26879 (mp_value_node) green_part (value_node (cur_exp_node ())), 26880 v_n); 26881 mp_dep_div (mp, 26882 (mp_value_node) blue_part (value_node (cur_exp_node ())), 26883 v_n); 26884 } else if (mp->cur_exp.type == mp_cmykcolor_type) { 26885 mp_dep_div (mp, 26886 (mp_value_node) cyan_part (value_node (cur_exp_node ())), 26887 v_n); 26888 mp_dep_div (mp, (mp_value_node) 26889 magenta_part (value_node (cur_exp_node ())), v_n); 26890 mp_dep_div (mp, (mp_value_node) 26891 yellow_part (value_node (cur_exp_node ())), v_n); 26892 mp_dep_div (mp, 26893 (mp_value_node) black_part (value_node (cur_exp_node ())), 26894 v_n); 26895 } else { 26896 mp_dep_div (mp, NULL, v_n); 26897 } 26898 } 26899 free_number(v_n); 26900 binary_return; 26901 } 26902 break; 26903 case mp_pythag_add: 26904 case mp_pythag_sub: 26905 if ((mp->cur_exp.type == mp_known) && (mp_type (p) == mp_known)) { 26906 mp_number r; 26907 new_number (r); 26908 if (c == mp_pythag_add) { 26909 pyth_add (r, value_number (p), cur_exp_value_number ()); 26910 } else { 26911 pyth_sub (r, value_number (p), cur_exp_value_number ()); 26912 } 26913 set_cur_exp_value_number (r); 26914 free_number (r); 26915 } else 26916 mp_bad_binary (mp, p, (quarterword) c); 26917 break; 26918 case mp_rotated_by: 26919 case mp_slanted_by: 26920 case mp_scaled_by: 26921 case mp_shifted_by: 26922 case mp_transformed_by: 26923 case mp_x_scaled: 26924 case mp_y_scaled: 26925 case mp_z_scaled: 26926 /* The next few sections of the program deal with affine transformations 26927 of coordinate data. */ 26928 if (mp_type (p) == mp_path_type) { 26929 path_trans ((quarterword) c, p); 26930 binary_return; 26931 } else if (mp_type (p) == mp_pen_type) { 26932 pen_trans ((quarterword) c, p); 26933 set_cur_exp_knot (mp_convex_hull (mp, cur_exp_knot ())); 26934 /* rounding error could destroy convexity */ 26935 binary_return; 26936 } else if ((mp_type (p) == mp_pair_type) || (mp_type (p) == mp_transform_type)) { 26937 mp_big_trans (mp, p, (quarterword) c); 26938 } else if (mp_type (p) == mp_picture_type) { 26939 mp_do_edges_trans (mp, p, (quarterword) c); 26940 binary_return; 26941 } else { 26942 mp_bad_binary (mp, p, (quarterword) c); 26943 } 26944 break; 26945 case mp_concatenate: 26946 if ((mp->cur_exp.type == mp_string_type) && (mp_type (p) == mp_string_type)) { 26947 mp_string str = mp_cat (mp, value_str (p), cur_exp_str()); 26948 delete_str_ref (cur_exp_str ()) ; 26949 set_cur_exp_str (str); 26950 } else 26951 mp_bad_binary (mp, p, mp_concatenate); 26952 break; 26953 case mp_substring_of: 26954 if (mp_nice_pair (mp, p, mp_type (p)) && (mp->cur_exp.type == mp_string_type)) { 26955 mp_string str = mp_chop_string (mp, 26956 cur_exp_str (), 26957 round_unscaled (value_number (x_part (value_node(p)))), 26958 round_unscaled (value_number (y_part (value_node(p))))); 26959 delete_str_ref (cur_exp_str ()) ; 26960 set_cur_exp_str (str); 26961 } else 26962 mp_bad_binary (mp, p, mp_substring_of); 26963 break; 26964 case mp_subpath_of: 26965 if (mp->cur_exp.type == mp_pair_type) 26966 mp_pair_to_path (mp); 26967 if (mp_nice_pair (mp, p, mp_type (p)) && (mp->cur_exp.type == mp_path_type)) 26968 mp_chop_path (mp, value_node (p)); 26969 else 26970 mp_bad_binary (mp, p, mp_subpath_of); 26971 break; 26972 case mp_point_of: 26973 case mp_precontrol_of: 26974 case mp_postcontrol_of: 26975 if (mp->cur_exp.type == mp_pair_type) 26976 mp_pair_to_path (mp); 26977 if ((mp->cur_exp.type == mp_path_type) && (mp_type (p) == mp_known)) 26978 mp_find_point (mp, value_number (p), (quarterword) c); 26979 else 26980 mp_bad_binary (mp, p, (quarterword) c); 26981 break; 26982 case mp_pen_offset_of: 26983 if ((mp->cur_exp.type == mp_pen_type) && mp_nice_pair (mp, p, mp_type (p))) 26984 mp_set_up_offset (mp, value_node (p)); 26985 else 26986 mp_bad_binary (mp, p, mp_pen_offset_of); 26987 break; 26988 case mp_direction_time_of: 26989 if (mp->cur_exp.type == mp_pair_type) 26990 mp_pair_to_path (mp); 26991 if ((mp->cur_exp.type == mp_path_type) && mp_nice_pair (mp, p, mp_type (p))) 26992 mp_set_up_direction_time (mp, value_node (p)); 26993 else 26994 mp_bad_binary (mp, p, mp_direction_time_of); 26995 break; 26996 case mp_envelope_of: 26997 if ((mp_type (p) != mp_pen_type) || (mp->cur_exp.type != mp_path_type)) 26998 mp_bad_binary (mp, p, mp_envelope_of); 26999 else 27000 mp_set_up_envelope (mp, p); 27001 break; 27002 case mp_glyph_infont: 27003 if ((mp_type (p) != mp_string_type && 27004 mp_type (p) != mp_known) || (mp->cur_exp.type != mp_string_type)) 27005 mp_bad_binary (mp, p, mp_glyph_infont); 27006 else 27007 mp_set_up_glyph_infont (mp, p); 27008 break; 27009 case mp_arc_time_of: 27010 if (mp->cur_exp.type == mp_pair_type) 27011 mp_pair_to_path (mp); 27012 if ((mp->cur_exp.type == mp_path_type) && (mp_type (p) == mp_known)) { 27013 memset(&new_expr,0,sizeof(mp_value)); 27014 new_number(new_expr.data.n); 27015 mp_get_arc_time (mp, &new_expr.data.n, cur_exp_knot (), value_number (p)); 27016 mp_flush_cur_exp (mp, new_expr); 27017 } else { 27018 mp_bad_binary (mp, p, (quarterword) c); 27019 } 27020 break; 27021 case mp_intersect: 27022 if (mp_type (p) == mp_pair_type) { 27023 q = mp_stash_cur_exp (mp); 27024 mp_unstash_cur_exp (mp, p); 27025 mp_pair_to_path (mp); 27026 p = mp_stash_cur_exp (mp); 27027 mp_unstash_cur_exp (mp, q); 27028 } 27029 if (mp->cur_exp.type == mp_pair_type) 27030 mp_pair_to_path (mp); 27031 if ((mp->cur_exp.type == mp_path_type) && (mp_type (p) == mp_path_type)) { 27032 mp_number arg1, arg2; 27033 new_number (arg1); 27034 new_number (arg2); 27035 mp_path_intersection (mp, value_knot (p), cur_exp_knot ()); 27036 number_clone (arg1, mp->cur_t); 27037 number_clone (arg2, mp->cur_tt); 27038 mp_pair_value (mp, arg1, arg2); 27039 free_number (arg1); 27040 free_number (arg2); 27041 } else { 27042 mp_bad_binary (mp, p, mp_intersect); 27043 } 27044 break; 27045 case mp_in_font: 27046 if ((mp->cur_exp.type != mp_string_type) || mp_type (p) != mp_string_type) { 27047 mp_bad_binary (mp, p, mp_in_font); 27048 } else { 27049 mp_do_infont (mp, p); 27050 binary_return; 27051 } 27052 break; 27053 } /* there are no other cases */ 27054 mp_recycle_value (mp, p); 27055 mp_free_value_node (mp, p); /* |return| to avoid this */ 27056 mp_finish_binary (mp, old_p, old_exp); 27057} 27058 27059 27060@ @<Declare binary action...@>= 27061static void mp_bad_binary (MP mp, mp_node p, quarterword c) { 27062 char msg[256]; 27063 mp_string sname; 27064 int old_setting = mp->selector; 27065 const char *hlp[] = { 27066 "I'm afraid I don't know how to apply that operation to that", 27067 "combination of types. Continue, and I'll return the second", 27068 "argument (see above) as the result of the operation.", 27069 NULL }; 27070 mp->selector = new_string; 27071 if (c >= mp_min_of) 27072 mp_print_op (mp, c); 27073 mp_print_known_or_unknown_type (mp, mp_type (p), p); 27074 if (c >= mp_min_of) 27075 mp_print (mp, "of"); 27076 else 27077 mp_print_op (mp, c); 27078 mp_print_known_or_unknown_type (mp, mp->cur_exp.type, cur_exp_node ()); 27079 sname = mp_make_string(mp); 27080 mp->selector = old_setting; 27081 mp_snprintf (msg, 256, "Not implemented: %s", mp_str(mp, sname)); 27082@.Not implemented...@>; 27083 delete_str_ref(sname); 27084 mp_disp_err (mp, p); 27085 mp_disp_err (mp, NULL); 27086 mp_back_error (mp, msg, hlp, true); 27087 mp_get_x_next (mp); 27088} 27089static void mp_bad_envelope_pen (MP mp) { 27090 const char *hlp[] = { 27091 "I'm afraid I don't know how to apply that operation to that", 27092 "combination of types. Continue, and I'll return the second", 27093 "argument (see above) as the result of the operation.", 27094 NULL }; 27095 mp_disp_err (mp, NULL); 27096 mp_disp_err (mp, NULL); 27097 mp_back_error (mp, "Not implemented: envelope(elliptical pen)of(path)", hlp, true); 27098@.Not implemented...@>; 27099 mp_get_x_next (mp); 27100} 27101 27102@ @<Declare binary action...@>= 27103static mp_node mp_tarnished (MP mp, mp_node p) { 27104 mp_node q; /* beginning of the big node */ 27105 mp_node r; /* moving value node pointer */ 27106 (void) mp; 27107 q = value_node (p); 27108 switch (mp_type (p)) { 27109 case mp_pair_type: 27110 r = x_part (q); 27111 if (mp_type (r) == mp_independent) 27112 return MP_VOID; 27113 r = y_part (q); 27114 if (mp_type (r) == mp_independent) 27115 return MP_VOID; 27116 break; 27117 case mp_color_type: 27118 r = red_part (q); 27119 if (mp_type (r) == mp_independent) 27120 return MP_VOID; 27121 r = green_part (q); 27122 if (mp_type (r) == mp_independent) 27123 return MP_VOID; 27124 r = blue_part (q); 27125 if (mp_type (r) == mp_independent) 27126 return MP_VOID; 27127 break; 27128 case mp_cmykcolor_type: 27129 r = cyan_part (q); 27130 if (mp_type (r) == mp_independent) 27131 return MP_VOID; 27132 r = magenta_part (q); 27133 if (mp_type (r) == mp_independent) 27134 return MP_VOID; 27135 r = yellow_part (q); 27136 if (mp_type (r) == mp_independent) 27137 return MP_VOID; 27138 r = black_part (q); 27139 if (mp_type (r) == mp_independent) 27140 return MP_VOID; 27141 break; 27142 case mp_transform_type: 27143 r = tx_part (q); 27144 if (mp_type (r) == mp_independent) 27145 return MP_VOID; 27146 r = ty_part (q); 27147 if (mp_type (r) == mp_independent) 27148 return MP_VOID; 27149 r = xx_part (q); 27150 if (mp_type (r) == mp_independent) 27151 return MP_VOID; 27152 r = xy_part (q); 27153 if (mp_type (r) == mp_independent) 27154 return MP_VOID; 27155 r = yx_part (q); 27156 if (mp_type (r) == mp_independent) 27157 return MP_VOID; 27158 r = yy_part (q); 27159 if (mp_type (r) == mp_independent) 27160 return MP_VOID; 27161 break; 27162 default: /* there are no other valid cases, but please the compiler */ 27163 break; 27164 } 27165 return NULL; 27166} 27167 27168@ The first argument to |add_or_subtract| is the location of a value node 27169in a capsule or pair node that will soon be recycled. The second argument 27170is either a location within a pair or transform node of |cur_exp|, 27171or it is NULL (which means that |cur_exp| itself should be the second 27172argument). The third argument is either |plus| or |minus|. 27173 27174The sum or difference of the numeric quantities will replace the second 27175operand. Arithmetic overflow may go undetected; users aren't supposed to 27176be monkeying around with really big values. 27177@^overflow in arithmetic@> 27178 27179@<Declare binary action...@>= 27180@<Declare the procedure called |dep_finish|@>; 27181static void mp_add_or_subtract (MP mp, mp_node p, mp_node q, quarterword c) { 27182 mp_variable_type s, t; /* operand types */ 27183 mp_value_node r; /* dependency list traverser */ 27184 mp_value_node v = NULL; /* second operand value for dep lists */ 27185 mp_number vv; /* second operand value for known values */ 27186 new_number (vv); 27187 if (q == NULL) { 27188 t = mp->cur_exp.type; 27189 if (t < mp_dependent) 27190 number_clone (vv, cur_exp_value_number ()); 27191 else 27192 v = (mp_value_node) dep_list ((mp_value_node) cur_exp_node ()); 27193 } else { 27194 t = mp_type (q); 27195 if (t < mp_dependent) 27196 number_clone (vv, value_number (q)); 27197 else 27198 v = (mp_value_node) dep_list ((mp_value_node) q); 27199 } 27200 if (t == mp_known) { 27201 mp_value_node qq = (mp_value_node) q; 27202 if (c == mp_minus) 27203 number_negate (vv); 27204 if (mp_type (p) == mp_known) { 27205 slow_add (vv, value_number (p), vv); 27206 if (q == NULL) 27207 set_cur_exp_value_number (vv); 27208 else 27209 set_value_number (q, vv); 27210 free_number (vv); 27211 return; 27212 } 27213 /* Add a known value to the constant term of |dep_list(p)| */ 27214 r = (mp_value_node) dep_list ((mp_value_node) p); 27215 while (dep_info (r) != NULL) 27216 r = (mp_value_node) mp_link (r); 27217 slow_add (vv, dep_value (r), vv); 27218 set_dep_value (r, vv); 27219 if (qq == NULL) { 27220 qq = mp_get_dep_node (mp); 27221 set_cur_exp_node ((mp_node) qq); 27222 mp->cur_exp.type = mp_type (p); 27223 mp_name_type (qq) = mp_capsule; 27224 /* clang: never read: |q = (mp_node) qq;| */ 27225 } 27226 set_dep_list (qq, dep_list ((mp_value_node) p)); 27227 mp_type (qq) = mp_type (p); 27228 set_prev_dep (qq, prev_dep ((mp_value_node) p)); 27229 mp_link (prev_dep ((mp_value_node) p)) = (mp_node) qq; 27230 mp_type (p) = mp_known; /* this will keep the recycler from collecting non-garbage */ 27231 } else { 27232 if (c == mp_minus) 27233 mp_negate_dep_list (mp, v); 27234 /* Add operand |p| to the dependency list |v| */ 27235 /* We prefer |dependent| lists to |mp_proto_dependent| ones, because it is 27236 nice to retain the extra accuracy of |fraction| coefficients. 27237 But we have to handle both kinds, and mixtures too. */ 27238 if (mp_type (p) == mp_known) { 27239 /* Add the known |value(p)| to the constant term of |v| */ 27240 while (dep_info (v) != NULL) { 27241 v = (mp_value_node) mp_link (v); 27242 } 27243 slow_add (vv, value_number (p), dep_value (v)); 27244 set_dep_value (v, vv); 27245 } else { 27246 s = mp_type (p); 27247 r = (mp_value_node) dep_list ((mp_value_node) p); 27248 if (t == mp_dependent) { 27249 if (s == mp_dependent) { 27250 mp_number ret1, ret2; 27251 new_fraction (ret1); 27252 new_fraction (ret2); 27253 mp_max_coef (mp, &ret1, r); 27254 mp_max_coef (mp, &ret2, v); 27255 number_add (ret1, ret2); 27256 free_number (ret2); 27257 if (number_less (ret1, coef_bound_k)) { 27258 v = mp_p_plus_q (mp, v, r, mp_dependent); 27259 free_number (ret1); 27260 goto DONE; 27261 } 27262 free_number (ret1); 27263 } /* |fix_needed| will necessarily be false */ 27264 t = mp_proto_dependent; 27265 v = mp_p_over_v (mp, v, unity_t, mp_dependent, mp_proto_dependent); 27266 } 27267 if (s == mp_proto_dependent) 27268 v = mp_p_plus_q (mp, v, r, mp_proto_dependent); 27269 else 27270 v = mp_p_plus_fq (mp, v, unity_t, r, mp_proto_dependent, mp_dependent); 27271 DONE: 27272 /* Output the answer, |v| (which might have become |known|) */ 27273 if (q != NULL) { 27274 mp_dep_finish (mp, v, (mp_value_node) q, t); 27275 } else { 27276 mp->cur_exp.type = t; 27277 mp_dep_finish (mp, v, NULL, t); 27278 } 27279 } 27280 } 27281 free_number (vv); 27282} 27283 27284 27285@ Here's the current situation: The dependency list |v| of type |t| 27286should either be put into the current expression (if |q=NULL|) or 27287into location |q| within a pair node (otherwise). The destination (|cur_exp| 27288or |q|) formerly held a dependency list with the same 27289final pointer as the list |v|. 27290 27291@<Declare the procedure called |dep_finish|@>= 27292static void mp_dep_finish (MP mp, mp_value_node v, mp_value_node q, 27293 quarterword t) { 27294 mp_value_node p; /* the destination */ 27295 if (q == NULL) 27296 p = (mp_value_node) cur_exp_node (); 27297 else 27298 p = q; 27299 set_dep_list (p, v); 27300 mp_type (p) = t; 27301 if (dep_info (v) == NULL) { 27302 mp_number vv; /* the value, if it is |known| */ 27303 new_number (vv); 27304 number_clone (vv, value_number (v)); 27305 if (q == NULL) { 27306 mp_value new_expr; 27307 memset(&new_expr,0,sizeof(mp_value)); 27308 new_number(new_expr.data.n); 27309 number_clone (new_expr.data.n, vv); 27310 mp_flush_cur_exp (mp, new_expr); 27311 } else { 27312 mp_recycle_value (mp, (mp_node) p); 27313 mp_type (q) = mp_known; 27314 set_value_number (q, vv); 27315 } 27316 free_number (vv); 27317 } else if (q == NULL) { 27318 mp->cur_exp.type = t; 27319 } 27320 if (mp->fix_needed) 27321 mp_fix_dependencies (mp); 27322} 27323 27324@ @<Declare binary action...@>= 27325static void mp_dep_mult (MP mp, mp_value_node p, mp_number v, boolean v_is_scaled) { 27326 mp_value_node q; /* the dependency list being multiplied by |v| */ 27327 quarterword s, t; /* its type, before and after */ 27328 if (p == NULL) { 27329 q = (mp_value_node) cur_exp_node (); 27330 } else if (mp_type (p) != mp_known) { 27331 q = p; 27332 } else { 27333 { 27334 mp_number r1, arg1; 27335 new_number (arg1); 27336 number_clone (arg1, dep_value (p)); 27337 if (v_is_scaled) { 27338 new_number (r1); 27339 take_scaled (r1, arg1, v); 27340 } else { 27341 new_fraction (r1); 27342 take_fraction (r1, arg1, v); 27343 } 27344 set_dep_value (p, r1); 27345 free_number (r1); 27346 free_number (arg1); 27347 } 27348 return; 27349 } 27350 t = mp_type (q); 27351 q = (mp_value_node) dep_list (q); 27352 s = t; 27353 if (t == mp_dependent) { 27354 if (v_is_scaled) { 27355 mp_number ab_vs_cd; 27356 mp_number arg1, arg2; 27357 new_number (ab_vs_cd); 27358 new_number (arg2); 27359 new_fraction (arg1); 27360 mp_max_coef (mp, &arg1, q); 27361 number_clone (arg2, v); 27362 number_abs (arg2); 27363 ab_vs_cd (ab_vs_cd, arg1, arg2, coef_bound_minus_1, unity_t); 27364 free_number (arg1); 27365 free_number (arg2); 27366 if (number_nonnegative(ab_vs_cd)) { 27367 t = mp_proto_dependent; 27368 } 27369 free_number (ab_vs_cd); 27370 } 27371 } 27372 q = mp_p_times_v (mp, q, v, s, t, v_is_scaled); 27373 mp_dep_finish (mp, q, p, t); 27374} 27375 27376 27377@ Here is a routine that is similar to |times|; but it is invoked only 27378internally, when |v| is a |fraction| whose magnitude is at most~1, 27379and when |cur_type>=mp_color_type|. 27380 27381@c 27382static void mp_frac_mult (MP mp, mp_number n, mp_number d) { 27383 /* multiplies |cur_exp| by |n/d| */ 27384 mp_node old_exp; /* a capsule to recycle */ 27385 mp_number v; /* |n/d| */ 27386 new_fraction (v); 27387 if (number_greater (internal_value (mp_tracing_commands), two_t)) { 27388 @<Trace the fraction multiplication@>; 27389 } 27390 switch (mp->cur_exp.type) { 27391 case mp_transform_type: 27392 case mp_color_type: 27393 case mp_cmykcolor_type: 27394 case mp_pair_type: 27395 old_exp = mp_tarnished (mp, cur_exp_node ()); 27396 break; 27397 case mp_independent: 27398 old_exp = MP_VOID; 27399 break; 27400 default: 27401 old_exp = NULL; 27402 break; 27403 } 27404 if (old_exp != NULL) { 27405 old_exp = cur_exp_node (); 27406 mp_make_exp_copy (mp, old_exp); 27407 } 27408 make_fraction (v, n, d); 27409 if (mp->cur_exp.type == mp_known) { 27410 mp_number r1, arg1; 27411 new_fraction (r1); 27412 new_number (arg1); 27413 number_clone (arg1, cur_exp_value_number ()); 27414 take_fraction (r1, arg1, v); 27415 set_cur_exp_value_number (r1); 27416 free_number (r1); 27417 free_number (arg1); 27418 } else if (mp->cur_exp.type == mp_pair_type) { 27419 mp_dep_mult (mp, (mp_value_node) x_part (value_node (cur_exp_node ())), v, false); 27420 mp_dep_mult (mp, (mp_value_node) y_part (value_node (cur_exp_node ())), v, false); 27421 } else if (mp->cur_exp.type == mp_color_type) { 27422 mp_dep_mult (mp, (mp_value_node) red_part (value_node (cur_exp_node ())), v, false); 27423 mp_dep_mult (mp, (mp_value_node) green_part (value_node (cur_exp_node ())), v, false); 27424 mp_dep_mult (mp, (mp_value_node) blue_part (value_node (cur_exp_node ())), v, false); 27425 } else if (mp->cur_exp.type == mp_cmykcolor_type) { 27426 mp_dep_mult (mp, (mp_value_node) cyan_part (value_node (cur_exp_node ())), v, false); 27427 mp_dep_mult (mp, (mp_value_node) magenta_part (value_node (cur_exp_node ())), v, false); 27428 mp_dep_mult (mp, (mp_value_node) yellow_part (value_node (cur_exp_node ())), v, false); 27429 mp_dep_mult (mp, (mp_value_node) black_part (value_node (cur_exp_node ())), v, false); 27430 } else { 27431 mp_dep_mult (mp, NULL, v, false); 27432 } 27433 if (old_exp != NULL) { 27434 mp_recycle_value (mp, old_exp); 27435 mp_free_value_node (mp, old_exp); 27436 } 27437 free_number (v); 27438} 27439 27440 27441@ @<Trace the fraction multiplication@>= 27442{ 27443 mp_begin_diagnostic (mp); 27444 mp_print_nl (mp, "{("); 27445 print_number (n); 27446 mp_print_char (mp, xord ('/')); 27447 print_number (d); 27448 mp_print (mp, ")*("); 27449 mp_print_exp (mp, NULL, 0); 27450 mp_print (mp, ")}"); 27451 mp_end_diagnostic (mp, false); 27452} 27453 27454 27455@ The |hard_times| routine multiplies a nice color or pair by a dependency list. 27456 27457@<Declare binary action procedures@>= 27458static void mp_hard_times (MP mp, mp_node p) { 27459 mp_value_node q; /* a copy of the dependent variable |p| */ 27460 mp_value_node pp; /* for typecasting p */ 27461 mp_node r; /* a component of the big node for the nice color or pair */ 27462 mp_number v; /* the known value for |r| */ 27463 new_number (v); 27464 if (mp_type (p) <= mp_pair_type) { 27465 q = (mp_value_node) mp_stash_cur_exp (mp); 27466 mp_unstash_cur_exp (mp, p); 27467 p = (mp_node) q; 27468 } /* now |cur_type=mp_pair_type| or |cur_type=mp_color_type| or |cur_type=mp_cmykcolor_type| */ 27469 pp = (mp_value_node) p; 27470 if (mp->cur_exp.type == mp_pair_type) { 27471 r = x_part (value_node (cur_exp_node ())); 27472 number_clone(v, value_number (r)); 27473 mp_new_dep (mp, r, mp_type (pp), 27474 mp_copy_dep_list (mp, (mp_value_node) dep_list (pp))); 27475 mp_dep_mult (mp, (mp_value_node) r, v, true); 27476 r = y_part (value_node (cur_exp_node ())); 27477 number_clone(v, value_number (r)); 27478 mp_new_dep (mp, r, mp_type (pp), 27479 mp_copy_dep_list (mp, (mp_value_node) dep_list (pp))); 27480 mp_dep_mult (mp, (mp_value_node) r, v, true); 27481 } else if (mp->cur_exp.type == mp_color_type) { 27482 r = red_part (value_node (cur_exp_node ())); 27483 number_clone(v, value_number (r)); 27484 mp_new_dep (mp, r, mp_type (pp), 27485 mp_copy_dep_list (mp, (mp_value_node) dep_list (pp))); 27486 mp_dep_mult (mp, (mp_value_node) r, v, true); 27487 r = green_part (value_node (cur_exp_node ())); 27488 number_clone(v, value_number (r)); 27489 mp_new_dep (mp, r, mp_type (pp), 27490 mp_copy_dep_list (mp, (mp_value_node) dep_list (pp))); 27491 mp_dep_mult (mp, (mp_value_node) r, v, true); 27492 r = blue_part (value_node (cur_exp_node ())); 27493 number_clone(v, value_number (r)); 27494 mp_new_dep (mp, r, mp_type (pp), 27495 mp_copy_dep_list (mp, (mp_value_node) dep_list (pp))); 27496 mp_dep_mult (mp, (mp_value_node) r, v, true); 27497 } else if (mp->cur_exp.type == mp_cmykcolor_type) { 27498 r = cyan_part (value_node (cur_exp_node ())); 27499 number_clone(v, value_number (r)); 27500 mp_new_dep (mp, r, mp_type (pp), 27501 mp_copy_dep_list (mp, (mp_value_node) dep_list (pp))); 27502 mp_dep_mult (mp, (mp_value_node) r, v, true); 27503 r = yellow_part (value_node (cur_exp_node ())); 27504 number_clone(v, value_number (r)); 27505 mp_new_dep (mp, r, mp_type (pp), 27506 mp_copy_dep_list (mp, (mp_value_node) dep_list (pp))); 27507 mp_dep_mult (mp, (mp_value_node) r, v, true); 27508 r = magenta_part (value_node (cur_exp_node ())); 27509 number_clone(v, value_number (r)); 27510 mp_new_dep (mp, r, mp_type (pp), 27511 mp_copy_dep_list (mp, (mp_value_node) dep_list (pp))); 27512 mp_dep_mult (mp, (mp_value_node) r, v, true); 27513 r = black_part (value_node (cur_exp_node ())); 27514 number_clone(v, value_number (r)); 27515 mp_new_dep (mp, r, mp_type (pp), 27516 mp_copy_dep_list (mp, (mp_value_node) dep_list (pp))); 27517 mp_dep_mult (mp, (mp_value_node) r, v, true); 27518 } 27519 free_number (v); 27520} 27521 27522@ @<Declare binary action...@>= 27523static void mp_dep_div (MP mp, mp_value_node p, mp_number v) { 27524 mp_value_node q; /* the dependency list being divided by |v| */ 27525 quarterword s, t; /* its type, before and after */ 27526 if (p == NULL) 27527 q = (mp_value_node) cur_exp_node (); 27528 else if (mp_type (p) != mp_known) 27529 q = p; 27530 else { 27531 mp_number ret; 27532 new_number (ret); 27533 make_scaled (ret, value_number (p), v); 27534 set_value_number (p, ret); 27535 free_number (ret); 27536 return; 27537 } 27538 t = mp_type (q); 27539 q = (mp_value_node) dep_list (q); 27540 s = t; 27541 if (t == mp_dependent) { 27542 mp_number ab_vs_cd; 27543 mp_number arg1, arg2; 27544 new_number (ab_vs_cd); 27545 new_number (arg2); 27546 new_fraction (arg1); 27547 mp_max_coef (mp, &arg1, q); 27548 number_clone (arg2, v); 27549 number_abs (arg2); 27550 ab_vs_cd (ab_vs_cd, arg1, unity_t, coef_bound_minus_1, arg2); 27551 free_number (arg1); 27552 free_number (arg2); 27553 if (number_nonnegative(ab_vs_cd)) { 27554 t = mp_proto_dependent; 27555 } 27556 free_number (ab_vs_cd); 27557 } 27558 q = mp_p_over_v (mp, q, v, s, t); 27559 mp_dep_finish (mp, q, p, t); 27560} 27561 27562@ Let |c| be one of the eight transform operators. The procedure call 27563|set_up_trans(c)| first changes |cur_exp| to a transform that corresponds to 27564|c| and the original value of |cur_exp|. (In particular, |cur_exp| doesn't 27565change at all if |c=transformed_by|.) 27566 27567Then, if all components of the resulting transform are |known|, they are 27568moved to the global variables |txx|, |txy|, |tyx|, |tyy|, |tx|, |ty|; 27569and |cur_exp| is changed to the known value zero. 27570 27571@<Declare binary action...@>= 27572static void mp_set_up_trans (MP mp, quarterword c) { 27573 mp_node p, q, r; /* list manipulation registers */ 27574 mp_value new_expr; 27575 memset(&new_expr,0,sizeof(mp_value)); 27576 if ((c != mp_transformed_by) || (mp->cur_exp.type != mp_transform_type)) { 27577 /* Put the current transform into |cur_exp| */ 27578 const char *hlp[] = { 27579 "The expression shown above has the wrong type,", 27580 "so I can\'t transform anything using it.", 27581 "Proceed, and I'll omit the transformation.", 27582 NULL }; 27583 p = mp_stash_cur_exp (mp); 27584 set_cur_exp_node (mp_id_transform (mp)); 27585 mp->cur_exp.type = mp_transform_type; 27586 q = value_node (cur_exp_node ()); 27587 switch (c) { 27588 @<For each of the eight cases, change the relevant fields of |cur_exp| 27589 and |goto done|; 27590 but do nothing if capsule |p| doesn't have the appropriate type@>; 27591 }; /* there are no other cases */ 27592 mp_disp_err (mp, p); 27593 mp_back_error (mp, "Improper transformation argument", hlp, true); 27594 mp_get_x_next (mp); 27595 DONE: 27596 mp_recycle_value (mp, p); 27597 mp_free_value_node (mp, p); 27598 27599 } 27600 /* If the current transform is entirely known, stash it in global variables; 27601 otherwise |return| */ 27602 q = value_node (cur_exp_node ()); 27603 if (mp_type (tx_part (q)) != mp_known) 27604 return; 27605 if (mp_type (ty_part (q)) != mp_known) 27606 return; 27607 if (mp_type (xx_part (q)) != mp_known) 27608 return; 27609 if (mp_type (xy_part (q)) != mp_known) 27610 return; 27611 if (mp_type (yx_part (q)) != mp_known) 27612 return; 27613 if (mp_type (yy_part (q)) != mp_known) 27614 return; 27615 number_clone(mp->txx, value_number (xx_part (q))); 27616 number_clone(mp->txy, value_number (xy_part (q))); 27617 number_clone(mp->tyx, value_number (yx_part (q))); 27618 number_clone(mp->tyy, value_number (yy_part (q))); 27619 number_clone(mp->tx, value_number (tx_part (q))); 27620 number_clone(mp->ty, value_number (ty_part (q))); 27621 new_number(new_expr.data.n); 27622 set_number_to_zero (new_expr.data.n); 27623 mp_flush_cur_exp (mp, new_expr); 27624} 27625 27626 27627@ @<Glob...@>= 27628mp_number txx; 27629mp_number txy; 27630mp_number tyx; 27631mp_number tyy; 27632mp_number tx; 27633mp_number ty; /* current transform coefficients */ 27634 27635@ @<Initialize table...@>= 27636new_number(mp->txx); 27637new_number(mp->txy); 27638new_number(mp->tyx); 27639new_number(mp->tyy); 27640new_number(mp->tx); 27641new_number(mp->ty); 27642 27643@ @<Free table...@>= 27644free_number(mp->txx); 27645free_number(mp->txy); 27646free_number(mp->tyx); 27647free_number(mp->tyy); 27648free_number(mp->tx); 27649free_number(mp->ty); 27650 27651 27652@ @<For each of the eight cases...@>= 27653case mp_rotated_by: 27654if (mp_type (p) == mp_known) 27655 @<Install sines and cosines, then |goto done|@>; 27656break; 27657case mp_slanted_by: 27658if (mp_type (p) > mp_pair_type) { 27659 mp_install (mp, xy_part (q), p); 27660 goto DONE; 27661} 27662break; 27663case mp_scaled_by: 27664if (mp_type (p) > mp_pair_type) { 27665 mp_install (mp, xx_part (q), p); 27666 mp_install (mp, yy_part (q), p); 27667 goto DONE; 27668} 27669break; 27670case mp_shifted_by: 27671if (mp_type (p) == mp_pair_type) { 27672 r = value_node (p); 27673 mp_install (mp, tx_part (q), x_part (r)); 27674 mp_install (mp, ty_part (q), y_part (r)); 27675 goto DONE; 27676} 27677break; 27678case mp_x_scaled: 27679if (mp_type (p) > mp_pair_type) { 27680 mp_install (mp, xx_part (q), p); 27681 goto DONE; 27682} 27683break; 27684case mp_y_scaled: 27685if (mp_type (p) > mp_pair_type) { 27686 mp_install (mp, yy_part (q), p); 27687 goto DONE; 27688} 27689break; 27690case mp_z_scaled: 27691if (mp_type (p) == mp_pair_type) 27692 @<Install a complex multiplier, then |goto done|@>; 27693break; 27694case mp_transformed_by: 27695break; 27696 27697 27698@ @<Install sines and cosines, then |goto done|@>= 27699{ 27700 mp_number n_sin, n_cos, arg1, arg2; 27701 new_number (arg1); 27702 new_number (arg2); 27703 new_fraction (n_sin); 27704 new_fraction (n_cos); /* results computed by |n_sin_cos| */ 27705 number_clone (arg2, unity_t); 27706 number_clone (arg1, value_number (p)); 27707 number_multiply_int (arg2, 360); 27708 number_modulo (arg1, arg2); 27709 convert_scaled_to_angle (arg1); 27710 n_sin_cos (arg1, n_cos, n_sin); 27711 fraction_to_round_scaled (n_sin); 27712 fraction_to_round_scaled (n_cos); 27713 set_value_number (xx_part (q), n_cos); 27714 set_value_number (yx_part (q), n_sin); 27715 set_value_number (xy_part (q), value_number (yx_part (q))); 27716 number_negate (value_number (xy_part (q))); 27717 set_value_number (yy_part (q), value_number (xx_part (q))); 27718 free_number (arg1); 27719 free_number (arg2); 27720 free_number (n_sin); 27721 free_number (n_cos); 27722 goto DONE; 27723} 27724 27725 27726@ @<Install a complex multiplier, then |goto done|@>= 27727{ 27728 r = value_node (p); 27729 mp_install (mp, xx_part (q), x_part (r)); 27730 mp_install (mp, yy_part (q), x_part (r)); 27731 mp_install (mp, yx_part (q), y_part (r)); 27732 if (mp_type (y_part (r)) == mp_known) { 27733 set_value_number (y_part (r), value_number (y_part (r))); 27734 number_negate (value_number (y_part (r))); 27735 } else { 27736 mp_negate_dep_list (mp, (mp_value_node) dep_list ((mp_value_node) 27737 y_part (r))); 27738 } 27739 mp_install (mp, xy_part (q), y_part (r)); 27740 goto DONE; 27741} 27742 27743 27744@ Procedure |set_up_known_trans| is like |set_up_trans|, but it 27745insists that the transformation be entirely known. 27746 27747@<Declare binary action...@>= 27748static void mp_set_up_known_trans (MP mp, quarterword c) { 27749 mp_set_up_trans (mp, c); 27750 if (mp->cur_exp.type != mp_known) { 27751 mp_value new_expr; 27752 const char *hlp[] = { 27753 "I'm unable to apply a partially specified transformation", 27754 "except to a fully known pair or transform.", 27755 "Proceed, and I'll omit the transformation.", 27756 NULL }; 27757 memset(&new_expr,0,sizeof(mp_value)); 27758 new_number(new_expr.data.n); 27759 mp_disp_err(mp, NULL); 27760 set_number_to_zero (new_expr.data.n); 27761 mp_back_error (mp,"Transform components aren't all known", hlp, true); 27762 mp_get_x_next (mp); 27763 mp_flush_cur_exp (mp, new_expr); 27764 set_number_to_unity(mp->txx); 27765 set_number_to_zero(mp->txy); 27766 set_number_to_zero(mp->tyx); 27767 set_number_to_unity(mp->tyy); 27768 set_number_to_zero(mp->tx); 27769 set_number_to_zero(mp->ty); 27770 } 27771} 27772 27773 27774@ Here's a procedure that applies the transform |txx..ty| to a pair of 27775coordinates in locations |p| and~|q|. 27776 27777@<Declare binary action...@>= 27778static void mp_number_trans (MP mp, mp_number *p, mp_number *q) { 27779 mp_number r1, r2, v; 27780 new_number (r1); 27781 new_number (r2); 27782 new_number (v); 27783 take_scaled (r1, *p, mp->txx); 27784 take_scaled (r2, *q, mp->txy); 27785 number_add (r1, r2); 27786 set_number_from_addition(v, r1, mp->tx); 27787 take_scaled (r1, *p, mp->tyx); 27788 take_scaled (r2, *q, mp->tyy); 27789 number_add (r1, r2); 27790 set_number_from_addition(*q, r1, mp->ty); 27791 number_clone(*p,v); 27792 free_number (r1); 27793 free_number (r2); 27794 free_number(v); 27795} 27796 27797 27798@ The simplest transformation procedure applies a transform to all 27799coordinates of a path. The |path_trans(c)(p)| macro applies 27800a transformation defined by |cur_exp| and the transform operator |c| 27801to the path~|p|. 27802 27803@d path_trans(A,B) { mp_set_up_known_trans(mp, (A)); 27804 mp_unstash_cur_exp(mp, (B)); 27805 mp_do_path_trans(mp, cur_exp_knot()); } 27806 27807@<Declare binary action...@>= 27808static void mp_do_path_trans (MP mp, mp_knot p) { 27809 mp_knot q; /* list traverser */ 27810 q = p; 27811 do { 27812 if (mp_left_type (q) != mp_endpoint) 27813 mp_number_trans (mp, &q->left_x, &q->left_y); 27814 mp_number_trans (mp, &q->x_coord, &q->y_coord); 27815 if (mp_right_type (q) != mp_endpoint) 27816 mp_number_trans (mp, &q->right_x, &q->right_y); 27817 q = mp_next_knot (q); 27818 } while (q != p); 27819} 27820 27821 27822@ Transforming a pen is very similar, except that there are no |mp_left_type| 27823and |mp_right_type| fields. 27824 27825@d pen_trans(A,B) { mp_set_up_known_trans(mp, (A)); 27826 mp_unstash_cur_exp(mp, (B)); 27827 mp_do_pen_trans(mp, cur_exp_knot()); } 27828 27829@<Declare binary action...@>= 27830static void mp_do_pen_trans (MP mp, mp_knot p) { 27831 mp_knot q; /* list traverser */ 27832 if (pen_is_elliptical (p)) { 27833 mp_number_trans (mp, &p->left_x, &p->left_y); 27834 mp_number_trans (mp, &p->right_x, &p->right_y); 27835 } 27836 q = p; 27837 do { 27838 mp_number_trans (mp, &q->x_coord, &q->y_coord); 27839 q = mp_next_knot (q); 27840 } while (q != p); 27841} 27842 27843 27844@ The next transformation procedure applies to edge structures. It will do 27845any transformation, but the results may be substandard if the picture contains 27846text that uses downloaded bitmap fonts. The binary action procedure is 27847|do_edges_trans|, but we also need a function that just scales a picture. 27848That routine is |scale_edges|. Both it and the underlying routine |edges_trans| 27849should be thought of as procedures that update an edge structure |h|, except 27850that they have to return a (possibly new) structure because of the need to call 27851|private_edges|. 27852 27853@<Declare binary action...@>= 27854static mp_edge_header_node mp_edges_trans (MP mp, mp_edge_header_node h) { 27855 mp_node q; /* the object being transformed */ 27856 mp_dash_node r, s; /* for list manipulation */ 27857 mp_number sx, sy; /* saved transformation parameters */ 27858 mp_number sqdet; /* square root of determinant for |dash_scale| */ 27859 mp_number sgndet; /* sign of the determinant */ 27860 h = mp_private_edges (mp, h); 27861 new_number(sx); 27862 new_number(sy); 27863 new_number(sqdet); 27864 new_number(sgndet); 27865 mp_sqrt_det (mp, &sqdet, mp->txx, mp->txy, mp->tyx, mp->tyy); 27866 ab_vs_cd (sgndet, mp->txx, mp->tyy, mp->txy, mp->tyx); 27867 if (dash_list (h) != mp->null_dash) { 27868 @<Try to transform the dash list of |h|@>; 27869 } 27870 @<Make the bounding box of |h| unknown if it can't be updated properly 27871 without scanning the whole structure@>; 27872 q = mp_link (edge_list (h)); 27873 while (q != NULL) { 27874 @<Transform graphical object |q|@>; 27875 q = mp_link (q); 27876 } 27877 free_number (sx); 27878 free_number (sy); 27879 free_number (sqdet); 27880 free_number(sgndet); 27881 return h; 27882} 27883static void mp_do_edges_trans (MP mp, mp_node p, quarterword c) { 27884 mp_set_up_known_trans (mp, c); 27885 set_value_node (p, (mp_node)mp_edges_trans (mp, (mp_edge_header_node)value_node (p))); 27886 mp_unstash_cur_exp (mp, p); 27887} 27888static mp_edge_header_node mp_scale_edges (MP mp, mp_number se_sf, mp_edge_header_node se_pic) { 27889 number_clone(mp->txx, se_sf); 27890 number_clone(mp->tyy, se_sf); 27891 set_number_to_zero(mp->txy); 27892 set_number_to_zero(mp->tyx); 27893 set_number_to_zero(mp->tx); 27894 set_number_to_zero(mp->ty); 27895 return mp_edges_trans (mp, se_pic); 27896} 27897 27898 27899@ @<Try to transform the dash list of |h|@>= 27900if (number_nonzero(mp->txy) || number_nonzero(mp->tyx) || 27901 number_nonzero(mp->ty) || number_nonequalabs (mp->txx, mp->tyy)) { 27902 mp_flush_dash_list (mp, h); 27903} else { 27904 mp_number abs_tyy, ret; 27905 new_number (abs_tyy); 27906 if (number_negative(mp->txx)) { 27907 @<Reverse the dash list of |h|@>; 27908 } 27909 @<Scale the dash list by |txx| and shift it by |tx|@>; 27910 number_clone(abs_tyy, mp->tyy); 27911 number_abs (abs_tyy); 27912 new_number (ret); 27913 take_scaled (ret, h->dash_y, abs_tyy); 27914 number_clone(h->dash_y, ret); 27915 free_number (ret); 27916 free_number (abs_tyy); 27917} 27918 27919 27920@ @<Reverse the dash list of |h|@>= 27921{ 27922 r = dash_list (h); 27923 set_dash_list (h, mp->null_dash); 27924 while (r != mp->null_dash) { 27925 s = r; 27926 r = (mp_dash_node)mp_link (r); 27927 number_swap(s->start_x, s->stop_x ); 27928 mp_link (s) = (mp_node)dash_list (h); 27929 set_dash_list (h, s); 27930 } 27931} 27932 27933 27934@ @<Scale the dash list by |txx| and shift it by |tx|@>= 27935r = dash_list (h); 27936{ 27937 mp_number arg1; 27938 new_number (arg1); 27939 while (r != mp->null_dash) { 27940 take_scaled (arg1, r->start_x, mp->txx); 27941 set_number_from_addition(r->start_x, arg1, mp->tx); 27942 take_scaled (arg1, r->stop_x, mp->txx); 27943 set_number_from_addition(r->stop_x, arg1, mp->tx); 27944 r = (mp_dash_node)mp_link (r); 27945 } 27946 free_number (arg1); 27947} 27948 27949 27950@ @<Make the bounding box of |h| unknown if it can't be updated properly...@>= 27951if (number_zero(mp->txx) && number_zero(mp->tyy)) { 27952 @<Swap the $x$ and $y$ parameters in the bounding box of |h|@>; 27953} else if (number_nonzero(mp->txy) || number_nonzero(mp->tyx)) { 27954 mp_init_bbox (mp, h); 27955 goto DONE1; 27956} 27957if (number_lessequal (h->minx, h->maxx)) { 27958 @<Scale the bounding box by |txx+txy| and |tyx+tyy|; then shift by 27959 |(tx,ty)|@>; 27960} 27961DONE1: 27962 27963 27964@ @<Swap the $x$ and $y$ parameters in the bounding box of |h|@>= 27965{ 27966 number_swap(h->minx, h->miny); 27967 number_swap(h->maxx, h->maxy); 27968} 27969 27970 27971@ The sum ``|txx+txy|'' is whichever of |txx| or |txy| is nonzero. The other 27972sum is similar. 27973 27974@<Scale the bounding box by |txx+txy| and |tyx+tyy|; then shift...@>= 27975{ 27976 mp_number tot, ret; 27977 new_number(tot); 27978 new_number (ret); 27979 set_number_from_addition(tot,mp->txx,mp->txy); 27980 take_scaled (ret, h->minx, tot); 27981 set_number_from_addition(h->minx,ret, mp->tx); 27982 take_scaled (ret, h->maxx, tot); 27983 set_number_from_addition(h->maxx,ret, mp->tx); 27984 27985 set_number_from_addition(tot,mp->tyx,mp->tyy); 27986 take_scaled (ret, h->miny, tot); 27987 set_number_from_addition(h->miny, ret, mp->ty); 27988 take_scaled (ret, h->maxy, tot); 27989 set_number_from_addition(h->maxy, ret, mp->ty); 27990 27991 set_number_from_addition(tot, mp->txx, mp->txy); 27992 if (number_negative(tot)) { 27993 number_swap(h->minx, h->maxx); 27994 } 27995 set_number_from_addition(tot, mp->tyx, mp->tyy); 27996 if (number_negative(tot)) { 27997 number_swap(h->miny, h->maxy); 27998 } 27999 free_number (ret); 28000 free_number (tot); 28001} 28002 28003 28004@ Now we ready for the main task of transforming the graphical objects in edge 28005structure~|h|. 28006 28007@<Transform graphical object |q|@>= 28008switch (mp_type (q)) { 28009case mp_fill_node_type: 28010 { 28011 mp_fill_node qq = (mp_fill_node) q; 28012 mp_do_path_trans (mp, mp_path_p (qq)); 28013 @<Transform |mp_pen_p(qq)|, making sure polygonal pens stay counter-clockwise@>; 28014 } 28015 break; 28016case mp_stroked_node_type: 28017 { 28018 mp_stroked_node qq = (mp_stroked_node) q; 28019 mp_do_path_trans (mp, mp_path_p (qq)); 28020 @<Transform |mp_pen_p(qq)|, making sure polygonal pens stay counter-clockwise@>; 28021 } 28022 break; 28023case mp_start_clip_node_type: 28024 mp_do_path_trans (mp, mp_path_p ((mp_start_clip_node) q)); 28025 break; 28026case mp_start_bounds_node_type: 28027 mp_do_path_trans (mp, mp_path_p ((mp_start_bounds_node) q)); 28028 break; 28029case mp_text_node_type: 28030 @<Transform the compact transformation@>; 28031 break; 28032case mp_stop_clip_node_type: 28033case mp_stop_bounds_node_type: 28034 break; 28035default: /* there are no other valid cases, but please the compiler */ 28036 break; 28037} 28038 28039 28040@ Note that the shift parameters |(tx,ty)| apply only to the path being stroked. 28041The |dash_scale| has to be adjusted to scale the dash lengths in |mp_dash_p(q)| 28042since the \ps\ output procedures will try to compensate for the transformation 28043we are applying to |mp_pen_p(q)|. Since this compensation is based on the square 28044root of the determinant, |sqdet| is the appropriate factor. 28045 28046We pass the mptrap test only if |dash_scale| is not adjusted, nowadays 28047(backend is changed?) 28048 28049@<Transform |mp_pen_p(qq)|, making sure...@>= 28050if (mp_pen_p (qq) != NULL) { 28051 number_clone(sx, mp->tx); 28052 number_clone(sy, mp->ty); 28053 set_number_to_zero(mp->tx); 28054 set_number_to_zero(mp->ty); 28055 mp_do_pen_trans (mp, mp_pen_p (qq)); 28056 if (number_nonzero(sqdet) 28057 && ((mp_type (q) == mp_stroked_node_type) && (mp_dash_p (q) != NULL))) { 28058 mp_number ret; 28059 new_number (ret); 28060 take_scaled (ret, ((mp_stroked_node)q)->dash_scale, sqdet); 28061 number_clone(((mp_stroked_node)q)->dash_scale, ret); 28062 free_number (ret); 28063 } 28064 if (!pen_is_elliptical (mp_pen_p (qq))) 28065 if (number_negative(sgndet)) 28066 mp_pen_p (qq) = mp_make_pen (mp, mp_copy_path (mp, mp_pen_p (qq)), true); 28067 /* this unreverses the pen */ 28068 number_clone(mp->tx, sx); 28069 number_clone(mp->ty, sy); 28070} 28071 28072@ @<Transform the compact transformation@>= 28073mp_number_trans (mp, &((mp_text_node)q)->tx, &((mp_text_node)q)->ty); 28074number_clone(sx, mp->tx); 28075number_clone(sy, mp->ty); 28076set_number_to_zero(mp->tx); 28077set_number_to_zero(mp->ty); 28078mp_number_trans (mp, &((mp_text_node)q)->txx, &((mp_text_node)q)->tyx); 28079mp_number_trans (mp, &((mp_text_node)q)->txy, &((mp_text_node)q)->tyy); 28080number_clone(mp->tx, sx); 28081number_clone(mp->ty, sy) 28082 28083@ The hard cases of transformation occur when big nodes are involved, 28084and when some of their components are unknown. 28085 28086@<Declare binary action...@>= 28087@<Declare subroutines needed by |big_trans|@>; 28088static void mp_big_trans (MP mp, mp_node p, quarterword c) { 28089 mp_node q, r, pp, qq; /* list manipulation registers */ 28090 q = value_node (p); 28091 if (mp_type (q) == mp_pair_node_type) { 28092 if (mp_type (x_part (q)) != mp_known || 28093 mp_type (y_part (q)) != mp_known) { 28094 @<Transform an unknown big node and |return|@>; 28095 } 28096 } else { /* |mp_transform_type| */ 28097 if (mp_type (tx_part (q)) != mp_known || 28098 mp_type (ty_part (q)) != mp_known || 28099 mp_type (xx_part (q)) != mp_known || 28100 mp_type (xy_part (q)) != mp_known || 28101 mp_type (yx_part (q)) != mp_known || 28102 mp_type (yy_part (q)) != mp_known) { 28103 @<Transform an unknown big node and |return|@>; 28104 } 28105 } 28106 @<Transform a known big node@>; 28107} /* node |p| will now be recycled by |do_binary| */ 28108 28109 28110@ @<Transform an unknown big node and |return|@>= 28111{ 28112 mp_set_up_known_trans (mp, c); 28113 mp_make_exp_copy (mp, p); 28114 r = value_node (cur_exp_node ()); 28115 if (mp->cur_exp.type == mp_transform_type) { 28116 mp_bilin1 (mp, yy_part (r), mp->tyy, xy_part (q), mp->tyx, zero_t); 28117 mp_bilin1 (mp, yx_part (r), mp->tyy, xx_part (q), mp->tyx, zero_t); 28118 mp_bilin1 (mp, xy_part (r), mp->txx, yy_part (q), mp->txy, zero_t); 28119 mp_bilin1 (mp, xx_part (r), mp->txx, yx_part (q), mp->txy, zero_t); 28120 } 28121 mp_bilin1 (mp, y_part (r), mp->tyy, x_part (q), mp->tyx, mp->ty); 28122 mp_bilin1 (mp, x_part (r), mp->txx, y_part (q), mp->txy, mp->tx); 28123 return; 28124} 28125 28126 28127@ Let |p| point to a value field inside a big node of |cur_exp|, 28128and let |q| point to a another value field. The |bilin1| procedure 28129replaces |p| by $p\cdot t+q\cdot u+\delta$. 28130 28131@<Declare subroutines needed by |big_trans|@>= 28132static void mp_bilin1 (MP mp, mp_node p, mp_number t, mp_node q, 28133 mp_number u, mp_number delta_orig) { 28134 mp_number delta; 28135 new_number (delta); 28136 number_clone (delta, delta_orig); 28137 if (!number_equal(t, unity_t)) { 28138 mp_dep_mult (mp, (mp_value_node) p, t, true); 28139 } 28140 if (number_nonzero(u)) { 28141 if (mp_type (q) == mp_known) { 28142 mp_number tmp; 28143 new_number (tmp); 28144 take_scaled (tmp, value_number (q), u); 28145 number_add (delta, tmp); 28146 free_number (tmp); 28147 } else { 28148 /* Ensure that |type(p)=mp_proto_dependent| */ 28149 if (mp_type (p) != mp_proto_dependent) { 28150 if (mp_type (p) == mp_known) { 28151 mp_new_dep (mp, p, mp_type (p), mp_const_dependency (mp, value_number (p))); 28152 } else { 28153 set_dep_list ((mp_value_node) p, 28154 mp_p_times_v (mp, 28155 (mp_value_node) dep_list ((mp_value_node) 28156 p), unity_t, 28157 mp_dependent, mp_proto_dependent, true)); 28158 } 28159 mp_type (p) = mp_proto_dependent; 28160 } 28161 set_dep_list ((mp_value_node) p, 28162 mp_p_plus_fq (mp, 28163 (mp_value_node) dep_list ((mp_value_node) p), u, 28164 (mp_value_node) dep_list ((mp_value_node) q), 28165 mp_proto_dependent, mp_type (q))); 28166 } 28167 } 28168 if (mp_type (p) == mp_known) { 28169 set_value_number (p, value_number (p)); 28170 number_add (value_number (p), delta); 28171 } else { 28172 mp_number tmp; 28173 mp_value_node r; /* list traverser */ 28174 new_number (tmp); 28175 r = (mp_value_node) dep_list ((mp_value_node) p); 28176 while (dep_info (r) != NULL) 28177 r = (mp_value_node) mp_link (r); 28178 number_clone (tmp, value_number(r)); 28179 number_add (delta, tmp); 28180 if (r != (mp_value_node) dep_list ((mp_value_node) p)) 28181 set_value_number (r, delta); 28182 else { 28183 mp_recycle_value (mp, p); 28184 mp_type (p) = mp_known; 28185 set_value_number (p, delta); 28186 } 28187 free_number (tmp); 28188 } 28189 if (mp->fix_needed) 28190 mp_fix_dependencies (mp); 28191 free_number (delta); 28192} 28193 28194 28195@ @<Transform a known big node@>= 28196mp_set_up_trans (mp, c); 28197if (mp->cur_exp.type == mp_known) { 28198 @<Transform known by known@>; 28199} else { 28200 pp = mp_stash_cur_exp (mp); 28201 qq = value_node (pp); 28202 mp_make_exp_copy (mp, p); 28203 r = value_node (cur_exp_node ()); 28204 if (mp->cur_exp.type == mp_transform_type) { 28205 mp_bilin2 (mp, yy_part (r), yy_part (qq), value_number (xy_part (q)), 28206 yx_part (qq), NULL); 28207 mp_bilin2 (mp, yx_part (r), yy_part (qq), value_number (xx_part (q)), 28208 yx_part (qq), NULL); 28209 mp_bilin2 (mp, xy_part (r), xx_part (qq), value_number (yy_part (q)), 28210 xy_part (qq), NULL); 28211 mp_bilin2 (mp, xx_part (r), xx_part (qq), value_number (yx_part (q)), 28212 xy_part (qq), NULL); 28213 } 28214 mp_bilin2 (mp, y_part (r), yy_part (qq), value_number (x_part (q)), 28215 yx_part (qq), y_part (qq)); 28216 mp_bilin2 (mp, x_part (r), xx_part (qq), value_number (y_part (q)), 28217 xy_part (qq), x_part (qq)); 28218 mp_recycle_value (mp, pp); 28219 mp_free_value_node (mp, pp); 28220} 28221 28222 28223@ Let |p| be a |mp_proto_dependent| value whose dependency list ends 28224at |dep_final|. The following procedure adds |v| times another 28225numeric quantity to~|p|. 28226 28227@<Declare subroutines needed by |big_trans|@>= 28228static void mp_add_mult_dep (MP mp, mp_value_node p, mp_number v, mp_node r) { 28229 if (mp_type (r) == mp_known) { 28230 mp_number ret; 28231 new_number (ret); 28232 take_scaled (ret, value_number (r), v); 28233 set_dep_value (mp->dep_final, dep_value (mp->dep_final)); 28234 number_add (dep_value (mp->dep_final), ret); 28235 free_number (ret); 28236 } else { 28237 set_dep_list (p, 28238 mp_p_plus_fq (mp, (mp_value_node) dep_list (p), v, 28239 (mp_value_node) dep_list ((mp_value_node) r), 28240 mp_proto_dependent, mp_type (r))); 28241 if (mp->fix_needed) 28242 mp_fix_dependencies (mp); 28243 } 28244} 28245 28246 28247@ The |bilin2| procedure is something like |bilin1|, but with known 28248and unknown quantities reversed. Parameter |p| points to a value field 28249within the big node for |cur_exp|; and |type(p)=mp_known|. Parameters 28250|t| and~|u| point to value fields elsewhere; so does parameter~|q|, 28251unless it is |NULL| (which stands for zero). Location~|p| will be 28252replaced by $p\cdot t+v\cdot u+q$. 28253 28254@<Declare subroutines needed by |big_trans|@>= 28255static void mp_bilin2 (MP mp, mp_node p, mp_node t, mp_number v, 28256 mp_node u, mp_node q) { 28257 mp_number vv; /* temporary storage for |value(p)| */ 28258 new_number (vv); 28259 number_clone (vv, value_number (p)); 28260 mp_new_dep (mp, p, mp_proto_dependent, mp_const_dependency (mp, zero_t)); /* this sets |dep_final| */ 28261 if (number_nonzero(vv)) { 28262 mp_add_mult_dep (mp, (mp_value_node) p, vv, t); /* |dep_final| doesn't change */ 28263 } 28264 if (number_nonzero(v)) { 28265 mp_number arg1; 28266 new_number (arg1); 28267 number_clone (arg1, v); 28268 mp_add_mult_dep (mp, (mp_value_node) p, arg1, u); 28269 free_number (arg1); 28270 } 28271 if (q != NULL) 28272 mp_add_mult_dep (mp, (mp_value_node) p, unity_t, q); 28273 if (dep_list ((mp_value_node) p) == (mp_node) mp->dep_final) { 28274 number_clone (vv, dep_value (mp->dep_final)); 28275 mp_recycle_value (mp, p); 28276 mp_type (p) = mp_known; 28277 set_value_number (p, vv); 28278 } 28279 free_number (vv); 28280} 28281 28282 28283@ @<Transform known by known@>= 28284{ 28285 mp_make_exp_copy (mp, p); 28286 r = value_node (cur_exp_node ()); 28287 if (mp->cur_exp.type == mp_transform_type) { 28288 mp_bilin3 (mp, yy_part (r), mp->tyy, value_number (xy_part (q)), mp->tyx, zero_t); 28289 mp_bilin3 (mp, yx_part (r), mp->tyy, value_number (xx_part (q)), mp->tyx, zero_t); 28290 mp_bilin3 (mp, xy_part (r), mp->txx, value_number (yy_part (q)), mp->txy, zero_t); 28291 mp_bilin3 (mp, xx_part (r), mp->txx, value_number (yx_part (q)), mp->txy, zero_t); 28292 } 28293 mp_bilin3 (mp, y_part (r), mp->tyy, value_number (x_part (q)), mp->tyx, mp->ty); 28294 mp_bilin3 (mp, x_part (r), mp->txx, value_number (y_part (q)), mp->txy, mp->tx); 28295} 28296 28297 28298@ Finally, in |bilin3| everything is |known|. 28299 28300@<Declare subroutines needed by |big_trans|@>= 28301static void mp_bilin3 (MP mp, mp_node p, mp_number t, 28302 mp_number v, mp_number u, mp_number delta_orig) { 28303 mp_number delta; 28304 mp_number tmp; 28305 new_number (tmp); 28306 new_number (delta); 28307 number_clone (delta, delta_orig); 28308 if (!number_equal(t, unity_t)) { 28309 take_scaled (tmp, value_number (p), t); 28310 } else { 28311 number_clone (tmp, value_number (p)); 28312 } 28313 number_add (delta, tmp); 28314 if (number_nonzero(u)) { 28315 mp_number ret; 28316 new_number (ret); 28317 take_scaled (ret, v, u); 28318 set_value_number (p, delta); 28319 number_add (value_number (p), ret); 28320 free_number (ret); 28321 } else 28322 set_value_number (p, delta); 28323 free_number (tmp); 28324 free_number (delta); 28325} 28326 28327 28328@ @<Declare binary action...@>= 28329static void mp_chop_path (MP mp, mp_node p) { 28330 mp_knot q; /* a knot in the original path */ 28331 mp_knot pp, qq, rr, ss; /* link variables for copies of path nodes */ 28332 mp_number a, b; /* indices for chopping */ 28333 mp_number l; 28334 boolean reversed; /* was |a>b|? */ 28335 new_number (a); 28336 new_number (b); 28337 new_number (l); 28338 mp_path_length (mp, &l); 28339 number_clone (a, value_number (x_part (p))); 28340 number_clone (b, value_number (y_part (p))); 28341 if (number_lessequal(a, b)) { 28342 reversed = false; 28343 } else { 28344 reversed = true; 28345 number_swap (a, b); 28346 } 28347 /* Dispense with the cases |a<0| and/or |b>l| */ 28348 if (number_negative(a)) { 28349 if (mp_left_type (cur_exp_knot ()) == mp_endpoint) { 28350 set_number_to_zero(a); 28351 if (number_negative(b)) 28352 set_number_to_zero(b); 28353 } else { 28354 do { 28355 number_add (a, l); 28356 number_add (b, l); 28357 } while (number_negative(a)); /* a cycle always has length |l>0| */ 28358 } 28359 } 28360 if (number_greater (b, l)) { 28361 if (mp_left_type (cur_exp_knot ()) == mp_endpoint) { 28362 number_clone (b, l); 28363 if (number_greater (a, l)) 28364 number_clone(a, l); 28365 } else { 28366 while (number_greaterequal (a, l)) { 28367 number_substract (a, l); 28368 number_substract (b, l); 28369 } 28370 } 28371 } 28372 28373 q = cur_exp_knot (); 28374 while (number_greaterequal(a, unity_t)) { 28375 q = mp_next_knot (q); 28376 number_substract(a, unity_t); 28377 number_substract(b, unity_t); 28378 } 28379 if (number_equal(b, a)) { 28380 /* Construct a path from |pp| to |qq| of length zero */ 28381 if (number_positive (a)) { 28382 mp_number arg1; 28383 new_number (arg1); 28384 number_clone (arg1, a); 28385 convert_scaled_to_fraction (arg1); 28386 mp_split_cubic (mp, q, arg1); 28387 free_number (arg1); 28388 q = mp_next_knot (q); 28389 } 28390 pp = mp_copy_knot (mp, q); 28391 qq = pp; 28392 28393 } else { 28394 /* Construct a path from |pp| to |qq| of length $\lceil b\rceil$ */ 28395 pp = mp_copy_knot (mp, q); 28396 qq = pp; 28397 do { 28398 q = mp_next_knot (q); 28399 rr = qq; 28400 qq = mp_copy_knot (mp, q); 28401 mp_next_knot (rr) = qq; 28402 number_substract (b, unity_t); 28403 } while (number_positive (b)); 28404 if (number_positive (a)) { 28405 mp_number arg1; 28406 new_number (arg1); 28407 ss = pp; 28408 number_clone (arg1, a); 28409 convert_scaled_to_fraction (arg1); 28410 mp_split_cubic (mp, ss, arg1); 28411 free_number (arg1); 28412 pp = mp_next_knot (ss); 28413 mp_toss_knot (mp, ss); 28414 if (rr == ss) { 28415 mp_number arg1, arg2; 28416 new_number (arg1); 28417 new_number (arg2); 28418 set_number_from_substraction (arg1, unity_t, a); 28419 number_clone (arg2, b); 28420 make_scaled (b, arg2, arg1); 28421 free_number (arg1); 28422 free_number (arg2); 28423 rr = pp; 28424 } 28425 } 28426 if (number_negative (b)) { 28427 mp_number arg1; 28428 new_number (arg1); 28429 set_number_from_addition (arg1, b, unity_t); 28430 convert_scaled_to_fraction (arg1); 28431 mp_split_cubic (mp, rr, arg1); 28432 free_number (arg1); 28433 mp_toss_knot (mp, qq); 28434 qq = mp_next_knot (rr); 28435 } 28436 28437 } 28438 mp_left_type (pp) = mp_endpoint; 28439 mp_right_type (qq) = mp_endpoint; 28440 mp_next_knot (qq) = pp; 28441 mp_toss_knot_list (mp, cur_exp_knot ()); 28442 if (reversed) { 28443 set_cur_exp_knot (mp_next_knot (mp_htap_ypoc (mp, pp))); 28444 mp_toss_knot_list (mp, pp); 28445 } else { 28446 set_cur_exp_knot (pp); 28447 } 28448 free_number (l); 28449 free_number (a); 28450 free_number (b); 28451} 28452 28453 28454@ @<Declare binary action...@>= 28455static void mp_set_up_offset (MP mp, mp_node p) { 28456 mp_find_offset (mp, value_number (x_part (p)), value_number (y_part (p)), 28457 cur_exp_knot ()); 28458 mp_pair_value (mp, mp->cur_x, mp->cur_y); 28459} 28460static void mp_set_up_direction_time (MP mp, mp_node p) { 28461 mp_value new_expr; 28462 memset(&new_expr,0,sizeof(mp_value)); 28463 new_number (new_expr.data.n); 28464 mp_find_direction_time (mp, &new_expr.data.n, value_number (x_part (p)), 28465 value_number (y_part (p)), 28466 cur_exp_knot ()); 28467 mp_flush_cur_exp (mp, new_expr); 28468} 28469static void mp_set_up_envelope (MP mp, mp_node p) { 28470 unsigned char ljoin, lcap; 28471 mp_number miterlim; 28472 mp_knot q = mp_copy_path (mp, cur_exp_knot ()); /* the original path */ 28473 new_number(miterlim); 28474 /* TODO: accept elliptical pens for straight paths */ 28475 if (pen_is_elliptical (value_knot (p))) { 28476 mp_bad_envelope_pen (mp); 28477 set_cur_exp_knot (q); 28478 mp->cur_exp.type = mp_path_type; 28479 return; 28480 } 28481 if (number_greater (internal_value (mp_linejoin), unity_t)) 28482 ljoin = 2; 28483 else if (number_positive (internal_value (mp_linejoin))) 28484 ljoin = 1; 28485 else 28486 ljoin = 0; 28487 if (number_greater (internal_value (mp_linecap), unity_t)) 28488 lcap = 2; 28489 else if (number_positive (internal_value (mp_linecap))) 28490 lcap = 1; 28491 else 28492 lcap = 0; 28493 if (number_less (internal_value (mp_miterlimit), unity_t)) 28494 set_number_to_unity(miterlim); 28495 else 28496 number_clone(miterlim, internal_value (mp_miterlimit)); 28497 set_cur_exp_knot (mp_make_envelope 28498 (mp, q, value_knot (p), ljoin, lcap, miterlim)); 28499 mp->cur_exp.type = mp_path_type; 28500} 28501 28502 28503@ This is pretty straightfoward. The one silly thing is that 28504the output of |mp_ps_do_font_charstring| has to be un-exported. 28505 28506@<Declare binary action...@>= 28507static void mp_set_up_glyph_infont (MP mp, mp_node p) { 28508 mp_edge_object *h = NULL; 28509 mp_ps_font *f = NULL; 28510 char *n = mp_str (mp, cur_exp_str ()); 28511 f = mp_ps_font_parse (mp, (int) mp_find_font (mp, n)); 28512 if (f != NULL) { 28513 if (mp_type (p) == mp_known) { 28514 int v = round_unscaled (value_number (p)); 28515 if (v < 0 || v > 255) { 28516 char msg[256]; 28517 mp_snprintf (msg, 256, "glyph index too high (%d)", v); 28518 mp_error (mp, msg, NULL, true); 28519 } else { 28520 h = mp_ps_font_charstring (mp, f, v); 28521 } 28522 } else { 28523 n = mp_str (mp, value_str (p)); 28524 h = mp_ps_do_font_charstring (mp, f, n); 28525 } 28526 mp_ps_font_free (mp, f); 28527 } 28528 if (h != NULL) { 28529 set_cur_exp_node ((mp_node)mp_gr_import (mp, h)); 28530 } else { 28531 set_cur_exp_node ((mp_node)mp_get_edge_header_node (mp)); 28532 mp_init_edges (mp, (mp_edge_header_node)cur_exp_node ()); 28533 } 28534 mp->cur_exp.type = mp_picture_type; 28535} 28536 28537 28538@ @<Declare binary action...@>= 28539static void mp_find_point (MP mp, mp_number v_orig, quarterword c) { 28540 mp_knot p; /* the path */ 28541 mp_number n; /* its length */ 28542 mp_number v; 28543 new_number (v); 28544 new_number (n); 28545 number_clone (v, v_orig); 28546 p = cur_exp_knot (); 28547 if (mp_left_type (p) == mp_endpoint) { 28548 set_number_to_unity (n); 28549 number_negate (n); 28550 } else { 28551 set_number_to_zero (n); 28552 } 28553 do { 28554 p = mp_next_knot (p); 28555 number_add (n, unity_t); 28556 } while (p != cur_exp_knot ()); 28557 if (number_zero (n)) { 28558 set_number_to_zero(v); 28559 } else if (number_negative(v)) { 28560 if (mp_left_type (p) == mp_endpoint) { 28561 set_number_to_zero(v); 28562 } else { 28563 /* |v = n - 1 - ((-v - 1) % n) 28564 == - ((-v - 1) % n) - 1 + n| */ 28565 number_negate (v); 28566 number_add_scaled (v, -1); 28567 number_modulo (v, n); 28568 number_negate (v); 28569 number_add_scaled (v, -1); 28570 number_add (v, n); 28571 } 28572 } else if (number_greater(v, n)) { 28573 if (mp_left_type (p) == mp_endpoint) 28574 number_clone (v, n); 28575 else 28576 number_modulo (v, n); 28577 } 28578 p = cur_exp_knot (); 28579 while (number_greaterequal(v, unity_t)) { 28580 p = mp_next_knot (p); 28581 number_substract (v, unity_t); 28582 } 28583 if (number_nonzero(v)) { 28584 /* Insert a fractional node by splitting the cubic */ 28585 convert_scaled_to_fraction (v); 28586 mp_split_cubic (mp, p, v); 28587 p = mp_next_knot (p); 28588 } 28589 /* Set the current expression to the desired path coordinates */ 28590 switch (c) { 28591 case mp_point_of: 28592 mp_pair_value (mp, p->x_coord, p->y_coord); 28593 break; 28594 case mp_precontrol_of: 28595 if (mp_left_type (p) == mp_endpoint) 28596 mp_pair_value (mp, p->x_coord, p->y_coord); 28597 else 28598 mp_pair_value (mp, p->left_x, p->left_y); 28599 break; 28600 case mp_postcontrol_of: 28601 if (mp_right_type (p) == mp_endpoint) 28602 mp_pair_value (mp, p->x_coord, p->y_coord); 28603 else 28604 mp_pair_value (mp, p->right_x, p->right_y); 28605 break; 28606 } /* there are no other cases */ 28607 free_number (v); 28608 free_number (n); 28609} 28610 28611@ Function |new_text_node| owns the reference count for its second argument 28612(the text string) but not its first (the font name). 28613 28614@<Declare binary action...@>= 28615static void mp_do_infont (MP mp, mp_node p) { 28616 mp_edge_header_node q; 28617 mp_value new_expr; 28618 memset(&new_expr,0,sizeof(mp_value)); 28619 new_number(new_expr.data.n); 28620 q = mp_get_edge_header_node (mp); 28621 mp_init_edges (mp, q); 28622 add_str_ref (cur_exp_str()); 28623 mp_link (obj_tail (q)) = 28624 mp_new_text_node (mp, mp_str (mp, cur_exp_str ()), value_str (p)); 28625 obj_tail (q) = mp_link (obj_tail (q)); 28626 mp_free_value_node (mp, p); 28627 new_expr.data.node = (mp_node)q; 28628 mp_flush_cur_exp (mp, new_expr); 28629 mp->cur_exp.type = mp_picture_type; 28630} 28631 28632 28633@* Statements and commands. 28634The chief executive of \MP\ is the |do_statement| routine, which 28635contains the master switch that causes all the various pieces of \MP\ 28636to do their things, in the right order. 28637 28638In a sense, this is the grand climax of the program: It applies all the 28639tools that we have worked so hard to construct. In another sense, this is 28640the messiest part of the program: It necessarily refers to other pieces 28641of code all over the place, so that a person can't fully understand what is 28642going on without paging back and forth to be reminded of conventions that 28643are defined elsewhere. We are now at the hub of the web. 28644 28645The structure of |do_statement| itself is quite simple. The first token 28646of the statement is fetched using |get_x_next|. If it can be the first 28647token of an expression, we look for an equation, an assignment, or a 28648title. Otherwise we use a \&{case} construction to branch at high speed to 28649the appropriate routine for various and sundry other types of commands, 28650each of which has an ``action procedure'' that does the necessary work. 28651 28652The program uses the fact that 28653$$\hbox{|min_primary_command=max_statement_command=type_name|}$$ 28654to interpret a statement that starts with, e.g., `\&{string}', 28655as a type declaration rather than a boolean expression. 28656 28657@c 28658static void worry_about_bad_statement (MP mp); 28659static void flush_unparsable_junk_after_statement (MP mp); 28660void mp_do_statement (MP mp) { /* governs \MP's activities */ 28661 mp->cur_exp.type = mp_vacuous; 28662 mp_get_x_next (mp); 28663 if (cur_cmd() > mp_max_primary_command) { 28664 worry_about_bad_statement (mp); 28665 } else if (cur_cmd() > mp_max_statement_command) { 28666 /* Do an equation, assignment, title, or 28667 `$\langle\,$expression$\,\rangle\,$\&{endgroup}'; */ 28668 /* The most important statements begin with expressions */ 28669 mp_value new_expr; 28670 mp->var_flag = mp_assignment; 28671 mp_scan_expression (mp); 28672 if (cur_cmd() < mp_end_group) { 28673 if (cur_cmd() == mp_equals) 28674 mp_do_equation (mp); 28675 else if (cur_cmd() == mp_assignment) 28676 mp_do_assignment (mp); 28677 else if (mp->cur_exp.type == mp_string_type) { 28678 /* Do a title */ 28679 if (number_positive (internal_value (mp_tracing_titles))) { 28680 mp_print_nl (mp, ""); 28681 mp_print_str (mp, cur_exp_str ()); 28682 update_terminal(); 28683 } 28684 } else if (mp->cur_exp.type != mp_vacuous) { 28685 const char *hlp[] = { 28686 "I couldn't find an `=' or `:=' after the", 28687 "expression that is shown above this error message,", 28688 "so I guess I'll just ignore it and carry on.", 28689 NULL }; 28690 mp_disp_err(mp, NULL); 28691 mp_back_error (mp, "Isolated expression", hlp, true); 28692 mp_get_x_next (mp); 28693 } 28694 memset(&new_expr,0,sizeof(mp_value)); 28695 new_number(new_expr.data.n); 28696 set_number_to_zero (new_expr.data.n); 28697 mp_flush_cur_exp (mp, new_expr); 28698 mp->cur_exp.type = mp_vacuous; 28699 } 28700 } else { 28701 /* Do a statement that doesn't begin with an expression */ 28702 /* If |do_statement| ends with |cur_cmd=end_group|, we should have 28703 |cur_type=mp_vacuous| unless the statement was simply an expression; 28704 in the latter case, |cur_type| and |cur_exp| should represent that 28705 expression. */ 28706 if (number_positive (internal_value (mp_tracing_commands))) 28707 show_cur_cmd_mod; 28708 switch (cur_cmd()) { 28709 case mp_type_name: 28710 mp_do_type_declaration (mp); 28711 break; 28712 case mp_macro_def: 28713 if (cur_mod() > var_def) 28714 mp_make_op_def (mp); 28715 else if (cur_mod() > end_def) 28716 mp_scan_def (mp); 28717 break; 28718 case mp_random_seed: 28719 mp_do_random_seed (mp); 28720 break; 28721 case mp_mode_command: 28722 mp_print_ln (mp); 28723 mp->interaction = cur_mod(); 28724 initialize_print_selector(); 28725 if (mp->log_opened) 28726 mp->selector = mp->selector + 2; 28727 mp_get_x_next (mp); 28728 break; 28729 case mp_protection_command: 28730 mp_do_protection (mp); 28731 break; 28732 case mp_delimiters: 28733 mp_def_delims (mp); 28734 break; 28735 case mp_save_command: 28736 do { 28737 mp_get_symbol (mp); 28738 mp_save_variable (mp, cur_sym()); 28739 mp_get_x_next (mp); 28740 } while (cur_cmd() == mp_comma); 28741 break; 28742 case mp_interim_command: 28743 mp_do_interim (mp); 28744 break; 28745 case mp_let_command: 28746 mp_do_let (mp); 28747 break; 28748 case mp_new_internal: 28749 mp_do_new_internal (mp); 28750 break; 28751 case mp_show_command: 28752 mp_do_show_whatever (mp); 28753 break; 28754 case mp_add_to_command: 28755 mp_do_add_to (mp); 28756 break; 28757 case mp_bounds_command: 28758 mp_do_bounds (mp); 28759 break; 28760 case mp_ship_out_command: 28761 mp_do_ship_out (mp); 28762 break; 28763 case mp_every_job_command: 28764 mp_get_symbol (mp); 28765 mp->start_sym = cur_sym(); 28766 mp_get_x_next (mp); 28767 break; 28768 case mp_message_command: 28769 mp_do_message (mp); 28770 break; 28771 case mp_write_command: 28772 mp_do_write (mp); 28773 break; 28774 case mp_tfm_command: 28775 mp_do_tfm_command (mp); 28776 break; 28777 case mp_special_command: 28778 if (cur_mod() == 0) 28779 mp_do_special (mp); 28780 else if (cur_mod() == 1) 28781 mp_do_mapfile (mp); 28782 else 28783 mp_do_mapline (mp); 28784 break; 28785 default: 28786 break; /* make the compiler happy */ 28787 } 28788 mp->cur_exp.type = mp_vacuous; 28789 } 28790 if (cur_cmd() < mp_semicolon) 28791 flush_unparsable_junk_after_statement(mp); 28792 mp->error_count = 0; 28793} 28794 28795 28796@ @<Declarations@>= 28797@<Declare action procedures for use by |do_statement|@> 28798 28799 28800@ The only command codes |>max_primary_command| that can be present 28801at the beginning of a statement are |semicolon| and higher; these 28802occur when the statement is null. 28803 28804@c 28805static void worry_about_bad_statement (MP mp) { 28806 if (cur_cmd() < mp_semicolon) { 28807 char msg[256]; 28808 mp_string sname; 28809 int old_setting = mp->selector; 28810 const char *hlp[] = { 28811 "I was looking for the beginning of a new statement.", 28812 "If you just proceed without changing anything, I'll ignore", 28813 "everything up to the next `;'. Please insert a semicolon", 28814 "now in front of anything that you don't want me to delete.", 28815 "(See Chapter 27 of The METAFONTbook for an example.)", 28816 NULL }; 28817 mp->selector = new_string; 28818 mp_print_cmd_mod (mp, cur_cmd(), cur_mod()); 28819 sname = mp_make_string(mp); 28820 mp->selector = old_setting; 28821 mp_snprintf (msg, 256, "A statement can't begin with `%s'", mp_str(mp, sname)); 28822 delete_str_ref(sname); 28823 mp_back_error (mp, msg, hlp, true); 28824 mp_get_x_next (mp); 28825 } 28826} 28827 28828 28829@ The help message printed here says that everything is flushed up to 28830a semicolon, but actually the commands |end_group| and |stop| will 28831also terminate a statement. 28832 28833@c 28834static void flush_unparsable_junk_after_statement (MP mp) 28835{ 28836 const char *hlp[] = { 28837 "I've just read as much of that statement as I could fathom,", 28838 "so a semicolon should have been next. It's very puzzling...", 28839 "but I'll try to get myself back together, by ignoring", 28840 "everything up to the next `;'. Please insert a semicolon", 28841 "now in front of anything that you don't want me to delete.", 28842 "(See Chapter 27 of The METAFONTbook for an example.)", 28843 NULL }; 28844 mp_back_error (mp, "Extra tokens will be flushed", hlp, true); 28845 mp->scanner_status = flushing; 28846 do { 28847 get_t_next (mp); 28848 if (cur_cmd() == mp_string_token) { 28849 delete_str_ref (cur_mod_str()); 28850 } 28851 } while (!mp_end_of_statement); /* |cur_cmd=semicolon|, |end_group|, or |stop| */ 28852 mp->scanner_status = normal; 28853} 28854 28855 28856 28857@ Equations and assignments are performed by the pair of mutually recursive 28858@^recursion@> 28859routines |do_equation| and |do_assignment|. These routines are called when 28860|cur_cmd=equals| and when |cur_cmd=assignment|, respectively; the left-hand 28861side is in |cur_type| and |cur_exp|, while the right-hand side is yet 28862to be scanned. After the routines are finished, |cur_type| and |cur_exp| 28863will be equal to the right-hand side (which will normally be equal 28864to the left-hand side). 28865 28866@<Declarations@>= 28867@<Declare the procedure called |make_eq|@>; 28868static void mp_do_equation (MP mp); 28869 28870@ @c 28871static void trace_equation (MP mp, mp_node lhs) { 28872 mp_begin_diagnostic (mp); 28873 mp_print_nl (mp, "{("); 28874 mp_print_exp (mp, lhs, 0); 28875 mp_print (mp, ")=("); 28876 mp_print_exp (mp, NULL, 0); 28877 mp_print (mp, ")}"); 28878 mp_end_diagnostic (mp, false); 28879} 28880void mp_do_equation (MP mp) { 28881 mp_node lhs; /* capsule for the left-hand side */ 28882 lhs = mp_stash_cur_exp (mp); 28883 mp_get_x_next (mp); 28884 mp->var_flag = mp_assignment; 28885 mp_scan_expression (mp); 28886 if (cur_cmd() == mp_equals) 28887 mp_do_equation (mp); 28888 else if (cur_cmd() == mp_assignment) 28889 mp_do_assignment (mp); 28890 if (number_greater (internal_value (mp_tracing_commands), two_t)) { 28891 trace_equation(mp, lhs); 28892 } 28893 if (mp->cur_exp.type == mp_unknown_path) { 28894 if (mp_type (lhs) == mp_pair_type) { 28895 mp_node p; /* temporary register */ 28896 p = mp_stash_cur_exp (mp); 28897 mp_unstash_cur_exp (mp, lhs); 28898 lhs = p; 28899 } /* in this case |make_eq| will change the pair to a path */ 28900 } 28901 mp_make_eq (mp, lhs); /* equate |lhs| to |(cur_type,cur_exp)| */ 28902} 28903 28904 28905@ And |do_assignment| is similar to |do_equation|: 28906 28907@<Declarations@>= 28908static void mp_do_assignment (MP mp); 28909 28910@ @c 28911static void bad_lhs (MP mp) { 28912 const char *hlp[] = { 28913 "I didn't find a variable name at the left of the `:=',", 28914 "so I'm going to pretend that you said `=' instead.", 28915 NULL }; 28916 mp_disp_err(mp, NULL); 28917 mp_error (mp, "Improper `:=' will be changed to `='", hlp, true); 28918 mp_do_equation (mp); 28919} 28920static void bad_internal_assignment (MP mp, mp_node lhs) { 28921 char msg[256]; 28922 const char *hlp[] = { 28923 "I can\'t set this internal quantity to anything but a known", 28924 "numeric value, so I'll have to ignore this assignment.", 28925 NULL }; 28926 mp_disp_err(mp, NULL); 28927 if (internal_type (mp_sym_info (lhs)) == mp_known) { 28928 mp_snprintf (msg, 256, "Internal quantity `%s' must receive a known numeric value", 28929 internal_name (mp_sym_info (lhs))); 28930 } else { 28931 mp_snprintf (msg, 256, "Internal quantity `%s' must receive a known string", 28932 internal_name (mp_sym_info (lhs))); 28933 hlp[1] = "string, so I'll have to ignore this assignment."; 28934 } 28935 mp_back_error (mp, msg, hlp, true); 28936 mp_get_x_next (mp); 28937} 28938static void forbidden_internal_assignment (MP mp, mp_node lhs) { 28939 char msg[256]; 28940 const char *hlp[] = { 28941 "I can\'t set this internal quantity to anything just yet", 28942 "(it is read-only), so I'll have to ignore this assignment.", 28943 NULL }; 28944 mp_snprintf (msg, 256, "Internal quantity `%s' is read-only", 28945 internal_name (mp_sym_info (lhs))); 28946 mp_back_error (mp, msg, hlp, true); 28947 mp_get_x_next (mp); 28948} 28949static void bad_internal_assignment_precision (MP mp, mp_node lhs, mp_number min, mp_number max) { 28950 char msg[256]; 28951 char s[256]; 28952 const char *hlp[] = { 28953 "Precision values are limited by the current numbersystem.", 28954 NULL, 28955 NULL } ; 28956 mp_snprintf (msg, 256, "Bad '%s' has been ignored", internal_name (mp_sym_info (lhs))); 28957 mp_snprintf (s, 256, "Currently I am using '%s'; the allowed precision range is [%s,%s].", 28958 mp_str (mp, internal_string (mp_number_system)), number_tostring(min), number_tostring(max)); 28959 hlp[1] = s; 28960 mp_back_error (mp, msg, hlp, true); 28961 mp_get_x_next (mp); 28962} 28963static void bad_expression_assignment (MP mp, mp_node lhs) { 28964 const char *hlp[] = { 28965 "It seems you did a nasty thing---probably by accident,", 28966 "but nevertheless you nearly hornswoggled me...", 28967 "While I was evaluating the right-hand side of this", 28968 "command, something happened, and the left-hand side", 28969 "is no longer a variable! So I won't change anything.", 28970 NULL }; 28971 char *msg = mp_obliterated (mp, lhs); 28972 mp_back_error (mp, msg, hlp, true); 28973 free(msg); 28974 mp_get_x_next (mp); 28975} 28976static void trace_assignment (MP mp, mp_node lhs) { 28977 mp_begin_diagnostic (mp); 28978 mp_print_nl (mp, "{"); 28979 if (mp_name_type (lhs) == mp_internal_sym) 28980 mp_print (mp, internal_name (mp_sym_info (lhs))); 28981 else 28982 mp_show_token_list (mp, lhs, NULL, 1000, 0); 28983 mp_print (mp, ":="); 28984 mp_print_exp (mp, NULL, 0); 28985 mp_print_char (mp, xord ('}')); 28986 mp_end_diagnostic (mp, false); 28987} 28988void mp_do_assignment (MP mp) { 28989 if (mp->cur_exp.type != mp_token_list) { 28990 bad_lhs(mp); 28991 } else { 28992 mp_node lhs; /* token list for the left-hand side */ 28993 lhs = cur_exp_node (); 28994 mp->cur_exp.type = mp_vacuous; 28995 mp_get_x_next (mp); 28996 mp->var_flag = mp_assignment; 28997 mp_scan_expression (mp); 28998 if (cur_cmd() == mp_equals) 28999 mp_do_equation (mp); 29000 else if (cur_cmd() == mp_assignment) 29001 mp_do_assignment (mp); 29002 if (number_greater (internal_value (mp_tracing_commands), two_t)) { 29003 trace_assignment (mp, lhs); 29004 } 29005 if (mp_name_type (lhs) == mp_internal_sym) { 29006 /* Assign the current expression to an internal variable */ 29007 if ((mp->cur_exp.type == mp_known || mp->cur_exp.type == mp_string_type) 29008 && (internal_type (mp_sym_info (lhs)) == mp->cur_exp.type)) { 29009 if(mp_sym_info (lhs) == mp_number_system) { 29010 forbidden_internal_assignment (mp, lhs); 29011 } else if (mp_sym_info (lhs) == mp_number_precision) { 29012 if (!(mp->cur_exp.type == mp_known && 29013 (!number_less(cur_exp_value_number(), precision_min)) && 29014 (!number_greater(cur_exp_value_number(), precision_max)) 29015 )) { 29016 bad_internal_assignment_precision(mp, lhs, precision_min, precision_max); 29017 } else { 29018 set_internal_from_cur_exp(mp_sym_info (lhs)); 29019 set_precision(); 29020 } 29021 } else { 29022 set_internal_from_cur_exp(mp_sym_info (lhs)); 29023 } 29024 } else { 29025 bad_internal_assignment (mp, lhs); 29026 } 29027 } else { 29028 /* Assign the current expression to the variable |lhs| */ 29029 mp_node p; /* where the left-hand value is stored */ 29030 mp_node q; /* temporary capsule for the right-hand value */ 29031 p = mp_find_variable (mp, lhs); 29032 if (p != NULL) { 29033 q = mp_stash_cur_exp (mp); 29034 mp->cur_exp.type = mp_und_type (mp, p); 29035 mp_recycle_value (mp, p); 29036 mp_type (p) = mp->cur_exp.type; 29037 set_value_number (p, zero_t); 29038 mp_make_exp_copy (mp, p); 29039 p = mp_stash_cur_exp (mp); 29040 mp_unstash_cur_exp (mp, q); 29041 mp_make_eq (mp, p); 29042 } else { 29043 bad_expression_assignment(mp, lhs); 29044 } 29045 } 29046 mp_flush_node_list (mp, lhs); 29047 } 29048} 29049 29050 29051@ And now we get to the nitty-gritty. The |make_eq| procedure is given 29052a pointer to a capsule that is to be equated to the current expression. 29053 29054@<Declare the procedure called |make_eq|@>= 29055static void mp_make_eq (MP mp, mp_node lhs); 29056 29057@ 29058@c 29059static void announce_bad_equation (MP mp, mp_node lhs) { 29060 char msg[256]; 29061 const char *hlp[] = { 29062 "I'm sorry, but I don't know how to make such things equal.", 29063 "(See the two expressions just above the error message.)", 29064 NULL }; 29065 mp_snprintf(msg, 256, "Equation cannot be performed (%s=%s)", 29066 (mp_type (lhs) <= mp_pair_type ? mp_type_string (mp_type (lhs)) : "numeric"), 29067 (mp->cur_exp.type <= mp_pair_type ? mp_type_string (mp->cur_exp.type) : "numeric")); 29068 mp_disp_err (mp, lhs); 29069 mp_disp_err(mp, NULL); 29070 mp_back_error (mp, msg, hlp, true); 29071 mp_get_x_next (mp); 29072} 29073static void exclaim_inconsistent_equation (MP mp) { 29074 const char *hlp[] = { 29075 "The equation I just read contradicts what was said before.", 29076 "But don't worry; continue and I'll just ignore it.", 29077 NULL }; 29078 mp_back_error (mp,"Inconsistent equation", hlp, true); 29079 mp_get_x_next (mp); 29080} 29081static void exclaim_redundant_or_inconsistent_equation (MP mp) { 29082 const char *hlp[] = { 29083 "An equation between already-known quantities can't help.", 29084 "But don't worry; continue and I'll just ignore it.", 29085 NULL }; 29086 mp_back_error (mp, "Redundant or inconsistent equation", hlp, true); 29087 mp_get_x_next (mp); 29088} 29089static void report_redundant_or_inconsistent_equation (MP mp, mp_node lhs, mp_number v) { 29090 if (mp->cur_exp.type <= mp_string_type) { 29091 if (mp->cur_exp.type == mp_string_type) { 29092 if (mp_str_vs_str (mp, value_str (lhs), cur_exp_str ()) != 0) { 29093 exclaim_inconsistent_equation(mp); 29094 } else { 29095 exclaim_redundant_equation(mp); 29096 } 29097 } else if (!number_equal (v, cur_exp_value_number ())) { 29098 exclaim_inconsistent_equation(mp); 29099 } else { 29100 exclaim_redundant_equation(mp); 29101 } 29102 } else { 29103 exclaim_redundant_or_inconsistent_equation (mp); 29104 } 29105} 29106 29107void mp_make_eq (MP mp, mp_node lhs) { 29108 mp_value new_expr; 29109 mp_variable_type t; /* type of the left-hand side */ 29110 mp_number v; /* value of the left-hand side */ 29111 memset(&new_expr,0,sizeof(mp_value)); 29112 new_number (v); 29113RESTART: 29114 t = mp_type (lhs); 29115 if (t <= mp_pair_type) 29116 number_clone (v, value_number (lhs)); 29117 /* For each type |t|, make an equation or complain if |cur_type| 29118 is incompatible with~|t| */ 29119 switch (t) { 29120 case mp_boolean_type: 29121 case mp_string_type: 29122 case mp_pen_type: 29123 case mp_path_type: 29124 case mp_picture_type: 29125 if (mp->cur_exp.type == t + unknown_tag) { 29126 new_number(new_expr.data.n); 29127 if (t==mp_boolean_type) { 29128 number_clone (new_expr.data.n, v); 29129 } else if (t==mp_string_type) { 29130 new_expr.data.str = value_str(lhs); 29131 } else if (t==mp_picture_type) { 29132 new_expr.data.node = value_node(lhs); 29133 } else { /* pen or path */ 29134 new_expr.data.p = value_knot(lhs); 29135 } 29136 mp_nonlinear_eq (mp, new_expr, cur_exp_node (), false); 29137 mp_unstash_cur_exp (mp, cur_exp_node ()); 29138 } else if (mp->cur_exp.type == t) { 29139 report_redundant_or_inconsistent_equation(mp, lhs, v); 29140 } else { 29141 announce_bad_equation(mp, lhs); 29142 } 29143 break; 29144 case unknown_types: 29145 if (mp->cur_exp.type == t - unknown_tag) { 29146 mp_nonlinear_eq (mp, mp->cur_exp, lhs, true); 29147 } else if (mp->cur_exp.type == t) { 29148 mp_ring_merge (mp, lhs, cur_exp_node ()); 29149 } else if (mp->cur_exp.type == mp_pair_type) { 29150 if (t == mp_unknown_path) { 29151 mp_pair_to_path (mp); 29152 goto RESTART; 29153 } 29154 } else { 29155 announce_bad_equation(mp, lhs); 29156 } 29157 break; 29158 case mp_transform_type: 29159 case mp_color_type: 29160 case mp_cmykcolor_type: 29161 case mp_pair_type: 29162 if (mp->cur_exp.type == t) { 29163 /* Do multiple equations */ 29164 mp_node q = value_node (cur_exp_node ()); 29165 mp_node p = value_node (lhs); 29166 switch (t) { 29167 case mp_transform_type: 29168 mp_try_eq (mp, yy_part (p), yy_part (q)); 29169 mp_try_eq (mp, yx_part (p), yx_part (q)); 29170 mp_try_eq (mp, xy_part (p), xy_part (q)); 29171 mp_try_eq (mp, xx_part (p), xx_part (q)); 29172 mp_try_eq (mp, ty_part (p), ty_part (q)); 29173 mp_try_eq (mp, tx_part (p), tx_part (q)); 29174 break; 29175 case mp_color_type: 29176 mp_try_eq (mp, blue_part (p), blue_part (q)); 29177 mp_try_eq (mp, green_part (p), green_part (q)); 29178 mp_try_eq (mp, red_part (p), red_part (q)); 29179 break; 29180 case mp_cmykcolor_type: 29181 mp_try_eq (mp, black_part (p), black_part (q)); 29182 mp_try_eq (mp, yellow_part (p), yellow_part (q)); 29183 mp_try_eq (mp, magenta_part (p), magenta_part (q)); 29184 mp_try_eq (mp, cyan_part (p), cyan_part (q)); 29185 break; 29186 case mp_pair_type: 29187 mp_try_eq (mp, y_part (p), y_part (q)); 29188 mp_try_eq (mp, x_part (p), x_part (q)); 29189 break; 29190 default: /* there are no other valid cases, but please the compiler */ 29191 break; 29192 } 29193 } else { 29194 announce_bad_equation(mp, lhs); 29195 } 29196 break; 29197 case mp_known: 29198 case mp_dependent: 29199 case mp_proto_dependent: 29200 case mp_independent: 29201 if (mp->cur_exp.type >= mp_known) { 29202 mp_try_eq (mp, lhs, NULL); 29203 } else { 29204 announce_bad_equation(mp, lhs); 29205 } 29206 break; 29207 case mp_vacuous: 29208 announce_bad_equation(mp, lhs); 29209 break; 29210 default: /* there are no other valid cases, but please the compiler */ 29211 announce_bad_equation(mp, lhs); 29212 break; 29213 } 29214 check_arith(); 29215 mp_recycle_value (mp, lhs); 29216 free_number (v); 29217 mp_free_value_node (mp, lhs); 29218} 29219 29220@ The first argument to |try_eq| is the location of a value node 29221in a capsule that will soon be recycled. The second argument is 29222either a location within a pair or transform node pointed to by 29223|cur_exp|, or it is |NULL| (which means that |cur_exp| itself 29224serves as the second argument). The idea is to leave |cur_exp| unchanged, 29225but to equate the two operands. 29226 29227@<Declarations@>= 29228static void mp_try_eq (MP mp, mp_node l, mp_node r); 29229 29230@ 29231@d equation_threshold_k ((math_data *)mp->math)->equation_threshold_t 29232 29233@c 29234static void deal_with_redundant_or_inconsistent_equation(MP mp, mp_value_node p, mp_node r) { 29235 mp_number absp; 29236 new_number (absp); 29237 number_clone (absp, value_number (p)); 29238 number_abs (absp); 29239 if (number_greater (absp, equation_threshold_k)) { /* off by .001 or more */ 29240 char msg[256]; 29241 const char *hlp[] = { 29242 "The equation I just read contradicts what was said before.", 29243 "But don't worry; continue and I'll just ignore it.", 29244 NULL }; 29245 mp_snprintf (msg, 256, "Inconsistent equation (off by %s)", number_tostring (value_number (p))); 29246 mp_back_error (mp, msg, hlp, true); 29247 mp_get_x_next (mp); 29248 } else if (r == NULL) { 29249 exclaim_redundant_equation(mp); 29250 } 29251 free_number (absp); 29252 mp_free_dep_node (mp, p); 29253} 29254 29255void mp_try_eq (MP mp, mp_node l, mp_node r) { 29256 mp_value_node p; /* dependency list for right operand minus left operand */ 29257 mp_variable_type t; /* the type of list |p| */ 29258 mp_value_node q; /* the constant term of |p| is here */ 29259 mp_value_node pp; /* dependency list for right operand */ 29260 mp_variable_type tt; /* the type of list |pp| */ 29261 boolean copied; /* have we copied a list that ought to be recycled? */ 29262 /* Remove the left operand from its container, negate it, and 29263 put it into dependency list~|p| with constant term~|q| */ 29264 t = mp_type (l); 29265 if (t == mp_known) { 29266 mp_number arg1; 29267 new_number (arg1); 29268 number_clone (arg1, value_number(l)); 29269 number_negate (arg1); 29270 t = mp_dependent; 29271 p = mp_const_dependency (mp, arg1); 29272 q = p; 29273 free_number (arg1); 29274 } else if (t == mp_independent) { 29275 t = mp_dependent; 29276 p = mp_single_dependency (mp, l); 29277 number_negate(dep_value (p)); 29278 q = mp->dep_final; 29279 } else { 29280 mp_value_node ll = (mp_value_node) l; 29281 p = (mp_value_node) dep_list (ll); 29282 q = p; 29283 while (1) { 29284 number_negate(dep_value (q)); 29285 if (dep_info (q) == NULL) 29286 break; 29287 q = (mp_value_node) mp_link (q); 29288 } 29289 mp_link (prev_dep (ll)) = mp_link (q); 29290 set_prev_dep ((mp_value_node) mp_link (q), prev_dep (ll)); 29291 mp_type (ll) = mp_known; 29292 } 29293 29294 /* Add the right operand to list |p| */ 29295 if (r == NULL) { 29296 if (mp->cur_exp.type == mp_known) { 29297 number_add (value_number (q), cur_exp_value_number ()); 29298 goto DONE1; 29299 } else { 29300 tt = mp->cur_exp.type; 29301 if (tt == mp_independent) 29302 pp = mp_single_dependency (mp, cur_exp_node ()); 29303 else 29304 pp = (mp_value_node) dep_list ((mp_value_node) cur_exp_node ()); 29305 } 29306 } else { 29307 if (mp_type (r) == mp_known) { 29308 number_add (dep_value (q), value_number (r)); 29309 goto DONE1; 29310 } else { 29311 tt = mp_type (r); 29312 if (tt == mp_independent) 29313 pp = mp_single_dependency (mp, r); 29314 else 29315 pp = (mp_value_node) dep_list ((mp_value_node) r); 29316 } 29317 } 29318 if (tt != mp_independent) { 29319 copied = false; 29320 } else { 29321 copied = true; 29322 tt = mp_dependent; 29323 } 29324 /* Add dependency list |pp| of type |tt| to dependency list~|p| of type~|t| */ 29325 mp->watch_coefs = false; 29326 if (t == tt) { 29327 p = mp_p_plus_q (mp, p, pp, (quarterword) t); 29328 } else if (t == mp_proto_dependent) { 29329 p = mp_p_plus_fq (mp, p, unity_t, pp, mp_proto_dependent, mp_dependent); 29330 } else { 29331 mp_number x; 29332 new_number (x); 29333 q = p; 29334 while (dep_info (q) != NULL) { 29335 number_clone (x, dep_value (q)); 29336 fraction_to_round_scaled (x); 29337 set_dep_value (q, x); 29338 q = (mp_value_node) mp_link (q); 29339 } 29340 free_number (x); 29341 t = mp_proto_dependent; 29342 p = mp_p_plus_q (mp, p, pp, (quarterword) t); 29343 } 29344 mp->watch_coefs = true; 29345 29346 if (copied) 29347 mp_flush_node_list (mp, (mp_node) pp); 29348 DONE1: 29349 29350 if (dep_info (p) == NULL) { 29351 deal_with_redundant_or_inconsistent_equation(mp, p, r); 29352 } else { 29353 mp_linear_eq (mp, p, (quarterword) t); 29354 if (r == NULL && mp->cur_exp.type != mp_known) { 29355 if (mp_type (cur_exp_node ()) == mp_known) { 29356 mp_node pp = cur_exp_node (); 29357 set_cur_exp_value_number (value_number (pp)); 29358 mp->cur_exp.type = mp_known; 29359 mp_free_value_node (mp, pp); 29360 } 29361 } 29362 } 29363} 29364 29365@ Our next goal is to process type declarations. For this purpose it's 29366convenient to have a procedure that scans a $\langle\,$declared 29367variable$\,\rangle$ and returns the corresponding token list. After the 29368following procedure has acted, the token after the declared variable 29369will have been scanned, so it will appear in |cur_cmd|, |cur_mod|, 29370and~|cur_sym|. 29371 29372@<Declarations@>= 29373static mp_node mp_scan_declared_variable (MP mp); 29374 29375@ @c 29376mp_node mp_scan_declared_variable (MP mp) { 29377 mp_sym x; /* hash address of the variable's root */ 29378 mp_node h, t; /* head and tail of the token list to be returned */ 29379 mp_get_symbol (mp); 29380 x = cur_sym(); 29381 if (cur_cmd() != mp_tag_token) 29382 mp_clear_symbol (mp, x, false); 29383 h = mp_get_symbolic_node (mp); 29384 set_mp_sym_sym (h, x); 29385 t = h; 29386 while (1) { 29387 mp_get_x_next (mp); 29388 if (cur_sym() == NULL) 29389 break; 29390 if (cur_cmd() != mp_tag_token) { 29391 if (cur_cmd() != mp_internal_quantity) { 29392 if (cur_cmd() == mp_left_bracket) { 29393 /* Descend past a collective subscript */ 29394 /* If the subscript isn't collective, we don't accept it as part of the 29395 declared variable. */ 29396 mp_sym ll = cur_sym(); /* hash address of left bracket */ 29397 mp_get_x_next (mp); 29398 if (cur_cmd() == mp_right_bracket) { 29399 set_cur_sym(collective_subscript); 29400 } else { 29401 mp_back_input (mp); 29402 set_cur_sym(ll); 29403 set_cur_cmd((mp_variable_type)mp_left_bracket); 29404 break; 29405 } 29406 } else { 29407 break; 29408 } 29409 } 29410 } 29411 mp_link (t) = mp_get_symbolic_node (mp); 29412 t = mp_link (t); 29413 set_mp_sym_sym (t, cur_sym()); 29414 mp_name_type (t) = cur_sym_mod(); 29415 } 29416 if ((eq_type (x) % mp_outer_tag) != mp_tag_token) 29417 mp_clear_symbol (mp, x, false); 29418 if (equiv_node (x) == NULL) 29419 mp_new_root (mp, x); 29420 return h; 29421} 29422 29423 29424@ Type declarations are introduced by the following primitive operations. 29425 29426@<Put each...@>= 29427mp_primitive (mp, "numeric", mp_type_name, mp_numeric_type); 29428@:numeric_}{\&{numeric} primitive@>; 29429mp_primitive (mp, "string", mp_type_name, mp_string_type); 29430@:string_}{\&{string} primitive@>; 29431mp_primitive (mp, "boolean", mp_type_name, mp_boolean_type); 29432@:boolean_}{\&{boolean} primitive@>; 29433mp_primitive (mp, "path", mp_type_name, mp_path_type); 29434@:path_}{\&{path} primitive@>; 29435mp_primitive (mp, "pen", mp_type_name, mp_pen_type); 29436@:pen_}{\&{pen} primitive@>; 29437mp_primitive (mp, "picture", mp_type_name, mp_picture_type); 29438@:picture_}{\&{picture} primitive@>; 29439mp_primitive (mp, "transform", mp_type_name, mp_transform_type); 29440@:transform_}{\&{transform} primitive@>; 29441mp_primitive (mp, "color", mp_type_name, mp_color_type); 29442@:color_}{\&{color} primitive@>; 29443mp_primitive (mp, "rgbcolor", mp_type_name, mp_color_type); 29444@:color_}{\&{rgbcolor} primitive@>; 29445mp_primitive (mp, "cmykcolor", mp_type_name, mp_cmykcolor_type); 29446@:color_}{\&{cmykcolor} primitive@>; 29447mp_primitive (mp, "pair", mp_type_name, mp_pair_type); 29448@:pair_}{\&{pair} primitive@> 29449 29450 29451@ @<Cases of |print_cmd...@>= 29452case mp_type_name: 29453mp_print_type (mp, (quarterword) m); 29454break; 29455 29456@ Now we are ready to handle type declarations, assuming that a 29457|type_name| has just been scanned. 29458 29459@<Declare action procedures for use by |do_statement|@>= 29460static void mp_do_type_declaration (MP mp); 29461 29462@ @c 29463static void flush_spurious_symbols_after_declared_variable(MP mp); 29464void mp_do_type_declaration (MP mp) { 29465 integer t; /* the type being declared */ 29466 mp_node p; /* token list for a declared variable */ 29467 mp_node q; /* value node for the variable */ 29468 if (cur_mod() >= mp_transform_type) 29469 t = (quarterword) cur_mod(); 29470 else 29471 t = (quarterword) (cur_mod() + unknown_tag); 29472 do { 29473 p = mp_scan_declared_variable (mp); 29474 mp_flush_variable (mp, equiv_node (mp_sym_sym (p)), mp_link (p), false); 29475 q = mp_find_variable (mp, p); 29476 if (q != NULL) { 29477 mp_type (q) = t; 29478 set_value_number (q, zero_t); /* todo: this was |null| */ 29479 } else { 29480 const char *hlp[] = { 29481 "You can't use, e.g., `numeric foo[]' after `vardef foo'.", 29482 "Proceed, and I'll ignore the illegal redeclaration.", 29483 NULL }; 29484 mp_back_error (mp, "Declared variable conflicts with previous vardef", hlp, true); 29485 mp_get_x_next (mp); 29486 } 29487 mp_flush_node_list (mp, p); 29488 if (cur_cmd() < mp_comma) { 29489 flush_spurious_symbols_after_declared_variable(mp); 29490 } 29491 } while (!mp_end_of_statement); 29492} 29493 29494 29495@ 29496@c 29497static void flush_spurious_symbols_after_declared_variable (MP mp) 29498{ 29499 const char *hlp[] = { 29500 "Variables in declarations must consist entirely of", 29501 "names and collective subscripts, e.g., `x[]a'.", 29502 "Are you trying to use a reserved word in a variable name?", 29503 "I'm going to discard the junk I found here,", 29504 "up to the next comma or the end of the declaration.", 29505 NULL }; 29506 if (cur_cmd() == mp_numeric_token) 29507 hlp[2] = "Explicit subscripts like `x15a' aren't permitted."; 29508 mp_back_error (mp, "Illegal suffix of declared variable will be flushed", hlp, true); 29509 mp_get_x_next (mp); 29510 mp->scanner_status = flushing; 29511 do { 29512 get_t_next (mp); 29513 @<Decrease the string reference count...@>; 29514 } while (cur_cmd() < mp_comma); /* break on either |end_of_statement| or |comma| */ 29515 mp->scanner_status = normal; 29516} 29517 29518 29519@ \MP's |main_control| procedure just calls |do_statement| repeatedly 29520until coming to the end of the user's program. 29521Each execution of |do_statement| concludes with 29522|cur_cmd=semicolon|, |end_group|, or |stop|. 29523 29524@c 29525static void mp_main_control (MP mp) { 29526 do { 29527 mp_do_statement (mp); 29528 if (cur_cmd() == mp_end_group) { 29529 mp_value new_expr; 29530 const char *hlp[] = { 29531 "I'm not currently working on a `begingroup',", 29532 "so I had better not try to end anything.", 29533 NULL }; 29534 memset(&new_expr,0,sizeof(mp_value)); 29535 new_number(new_expr.data.n); 29536 mp_error (mp, "Extra `endgroup'", hlp, true); 29537 mp_flush_cur_exp (mp, new_expr); 29538 } 29539 } while (cur_cmd() != mp_stop); 29540} 29541int mp_run (MP mp) { 29542 if (mp->history < mp_fatal_error_stop) { 29543 xfree (mp->jump_buf); 29544 mp->jump_buf = malloc (sizeof (jmp_buf)); 29545 if (mp->jump_buf == NULL || setjmp (*(mp->jump_buf)) != 0) 29546 return mp->history; 29547 mp_main_control (mp); /* come to life */ 29548 mp_final_cleanup (mp); /* prepare for death */ 29549 mp_close_files_and_terminate (mp); 29550 } 29551 return mp->history; 29552} 29553 29554 29555@ This function allows setting of internals from an external 29556source (like the command line or a controlling application). 29557 29558It accepts two |char *|'s, even for numeric assignments when 29559it calls |atoi| to get an integer from the start of the string. 29560 29561@c 29562void mp_set_internal (MP mp, char *n, char *v, int isstring) { 29563 size_t l = strlen (n); 29564 char err[256]; 29565 const char *errid = NULL; 29566 if (l > 0) { 29567 mp_sym p = mp_id_lookup (mp, n, l, false); 29568 if (p == NULL) { 29569 errid = "variable does not exist"; 29570 } else { 29571 if (eq_type (p) == mp_internal_quantity) { 29572 if ((internal_type (equiv (p)) == mp_string_type) && (isstring)) { 29573 set_internal_string (equiv (p), mp_rts (mp, v)); 29574 } else if ((internal_type (equiv (p)) == mp_known) && (!isstring)) { 29575 int test = atoi (v); 29576 if (test > 16383 && mp->math_mode==mp_math_scaled_mode) { 29577 errid = "value is too large"; 29578 } else if (test < -16383 && mp->math_mode==mp_math_scaled_mode) { 29579 errid = "value is too small"; 29580 } else { 29581 set_internal_from_number (equiv (p), unity_t); 29582 number_multiply_int (internal_value(equiv (p)), test); 29583 } 29584 } else { 29585 errid = "value has the wrong type"; 29586 } 29587 } else { 29588 errid = "variable is not an internal"; 29589 } 29590 } 29591 } 29592 if (errid != NULL) { 29593 if (isstring) { 29594 mp_snprintf (err, 256, "%s=\"%s\": %s, assignment ignored.", n, v, errid); 29595 } else { 29596 mp_snprintf (err, 256, "%s=%d: %s, assignment ignored.", n, atoi (v), 29597 errid); 29598 } 29599 mp_warn (mp, err); 29600 } 29601} 29602 29603 29604@ @<Exported function headers@>= 29605void mp_set_internal (MP mp, char *n, char *v, int isstring); 29606 29607@ For |mp_execute|, we need to define a structure to store the 29608redirected input and output. This structure holds the five relevant 29609streams: the three informational output streams, the PostScript 29610generation stream, and the input stream. These streams have many 29611things in common, so it makes sense to give them their own structure 29612definition. 29613 29614\item{fptr} is a virtual file pointer 29615\item{data} is the data this stream holds 29616\item{cur} is a cursor pointing into |data| 29617\item{size} is the allocated length of the data stream 29618\item{used} is the actual length of the data stream 29619 29620There are small differences between input and output: |term_in| never 29621uses |used|, whereas the other four never use |cur|. 29622 29623The file |luatexdir/tex/texfileio.h| defines |term_in| as |stdin| and 29624|term_out| as |stdout|. Moreover |stdio.h| for MinGW defines |stdin| as 29625|(&_iob[0])| and |stdout| as |(&_iob[1])|. We must avoid all that. 29626 29627@<Exported types@>= 29628#undef term_in 29629#undef term_out 29630 29631typedef struct { 29632 void *fptr; 29633 char *data; 29634 char *cur; 29635 size_t size; 29636 size_t used; 29637} mp_stream; 29638typedef struct { 29639 mp_stream term_out; 29640 mp_stream error_out; 29641 mp_stream log_out; 29642 mp_stream ship_out; 29643 mp_stream term_in; 29644 struct mp_edge_object *edges; 29645} mp_run_data; 29646 29647@ We need a function to clear an output stream, this is called at the 29648beginning of |mp_execute|. We also need one for destroying an output 29649stream, this is called just before a stream is (re)opened. 29650 29651@c 29652static void mp_reset_stream (mp_stream * str) { 29653 xfree (str->data); 29654 str->cur = NULL; 29655 str->size = 0; 29656 str->used = 0; 29657} 29658static void mp_free_stream (mp_stream * str) { 29659 xfree (str->fptr); 29660 mp_reset_stream (str); 29661} 29662 29663 29664@ @<Declarations@>= 29665static void mp_reset_stream (mp_stream * str); 29666static void mp_free_stream (mp_stream * str); 29667 29668@ The global instance contains a pointer instead of the actual structure 29669even though it is essentially static, because that makes it is easier to move 29670the object around. 29671 29672@<Global ...@>= 29673mp_run_data run_data; 29674 29675@ Another type is needed: the indirection will overload some of the 29676file pointer objects in the instance (but not all). For clarity, an 29677indirect object is used that wraps a |FILE *|. 29678 29679@<Types ... @>= 29680typedef struct File { 29681 FILE *f; 29682} File; 29683 29684@ Here are all of the functions that need to be overloaded for |mp_execute|. 29685 29686@<Declarations@>= 29687static void *mplib_open_file (MP mp, const char *fname, const char *fmode, 29688 int ftype); 29689static int mplib_get_char (void *f, mp_run_data * mplib_data); 29690static void mplib_unget_char (void *f, mp_run_data * mplib_data, int c); 29691static char *mplib_read_ascii_file (MP mp, void *ff, size_t * size); 29692static void mplib_write_ascii_file (MP mp, void *ff, const char *s); 29693static void mplib_read_binary_file (MP mp, void *ff, void **data, 29694 size_t * size); 29695static void mplib_write_binary_file (MP mp, void *ff, void *s, size_t size); 29696static void mplib_close_file (MP mp, void *ff); 29697static int mplib_eof_file (MP mp, void *ff); 29698static void mplib_flush_file (MP mp, void *ff); 29699static void mplib_shipout_backend (MP mp, void *h); 29700 29701@ The |xmalloc(1,1)| calls make sure the stored indirection values are unique. 29702 29703@d reset_stream(a) do { 29704 mp_reset_stream(&(a)); 29705 if (!ff->f) { 29706 ff->f = xmalloc(1,1); 29707 (a).fptr = ff->f; 29708 } } while (0) 29709 29710@c 29711static void *mplib_open_file (MP mp, const char *fname, const char *fmode, 29712 int ftype) { 29713 File *ff = xmalloc (1, sizeof (File)); 29714 mp_run_data *run = mp_rundata (mp); 29715 ff->f = NULL; 29716 if (ftype == mp_filetype_terminal) { 29717 if (fmode[0] == 'r') { 29718 if (!ff->f) { 29719 ff->f = xmalloc (1, 1); 29720 run->term_in.fptr = ff->f; 29721 } 29722 } else { 29723 reset_stream (run->term_out); 29724 } 29725 } else if (ftype == mp_filetype_error) { 29726 reset_stream (run->error_out); 29727 } else if (ftype == mp_filetype_log) { 29728 reset_stream (run->log_out); 29729 } else if (ftype == mp_filetype_postscript) { 29730 mp_free_stream (&(run->ship_out)); 29731 ff->f = xmalloc (1, 1); 29732 run->ship_out.fptr = ff->f; 29733 } else if (ftype == mp_filetype_bitmap) { 29734 mp_free_stream (&(run->ship_out)); 29735 ff->f = xmalloc (1, 1); 29736 run->ship_out.fptr = ff->f; 29737 } else { 29738 char realmode[3]; 29739 char *f = (mp->find_file) (mp, fname, fmode, ftype); 29740 if (f == NULL) 29741 return NULL; 29742 realmode[0] = *fmode; 29743 realmode[1] = 'b'; 29744 realmode[2] = 0; 29745 ff->f = fopen (f, realmode); 29746 free (f); 29747 if ((fmode[0] == 'r') && (ff->f == NULL)) { 29748 free (ff); 29749 return NULL; 29750 } 29751 } 29752 return ff; 29753} 29754static int mplib_get_char (void *f, mp_run_data * run) { 29755 int c; 29756 if (f == run->term_in.fptr && run->term_in.data != NULL) { 29757 if (run->term_in.size == 0) { 29758 if (run->term_in.cur != NULL) { 29759 run->term_in.cur = NULL; 29760 } else { 29761 xfree (run->term_in.data); 29762 } 29763 c = EOF; 29764 } else { 29765 run->term_in.size--; 29766 c = *(run->term_in.cur)++; 29767 } 29768 } else { 29769 c = fgetc (f); 29770 } 29771 return c; 29772} 29773static void mplib_unget_char (void *f, mp_run_data * run, int c) { 29774 if (f == run->term_in.fptr && run->term_in.cur != NULL) { 29775 run->term_in.size++; 29776 run->term_in.cur--; 29777 } else { 29778 ungetc (c, f); 29779 } 29780} 29781static char *mplib_read_ascii_file (MP mp, void *ff, size_t * size) { 29782 char *s = NULL; 29783 if (ff != NULL) { 29784 int c; 29785 size_t len = 0, lim = 128; 29786 mp_run_data *run = mp_rundata (mp); 29787 FILE *f = ((File *) ff)->f; 29788 if (f == NULL) 29789 return NULL; 29790 *size = 0; 29791 c = mplib_get_char (f, run); 29792 if (c == EOF) 29793 return NULL; 29794 s = malloc (lim); 29795 if (s == NULL) 29796 return NULL; 29797 while (c != EOF && c != '\n' && c != '\r') { 29798 if (len >= (lim - 1)) { 29799 s = xrealloc (s, (lim + (lim >> 2)), 1); 29800 if (s == NULL) 29801 return NULL; 29802 lim += (lim >> 2); 29803 } 29804 s[len++] = (char) c; 29805 c = mplib_get_char (f, run); 29806 } 29807 if (c == '\r') { 29808 c = mplib_get_char (f, run); 29809 if (c != EOF && c != '\n') 29810 mplib_unget_char (f, run, c); 29811 } 29812 s[len] = 0; 29813 *size = len; 29814 } 29815 return s; 29816} 29817static void mp_append_string (MP mp, mp_stream * a, const char *b) { 29818 size_t l = strlen (b) + 1; /* don't forget the trailing |'\0'| */ 29819 if ((a->used + l) >= a->size) { 29820 a->size += 256 + (a->size) / 5 + l; 29821 a->data = xrealloc (a->data, a->size, 1); 29822 } 29823 memcpy (a->data + a->used, b, l); 29824 a->used += (l-1); 29825} 29826static void mp_append_data (MP mp, mp_stream * a, void *b, size_t l) { 29827 if ((a->used + l) >= a->size) { 29828 a->size += 256 + (a->size) / 5 + l; 29829 a->data = xrealloc (a->data, a->size, 1); 29830 } 29831 memcpy (a->data + a->used, b, l); 29832 a->used += l; 29833} 29834static void mplib_write_ascii_file (MP mp, void *ff, const char *s) { 29835 if (ff != NULL) { 29836 void *f = ((File *) ff)->f; 29837 mp_run_data *run = mp_rundata (mp); 29838 if (f != NULL) { 29839 if (f == run->term_out.fptr) { 29840 mp_append_string (mp, &(run->term_out), s); 29841 } else if (f == run->error_out.fptr) { 29842 mp_append_string (mp, &(run->error_out), s); 29843 } else if (f == run->log_out.fptr) { 29844 mp_append_string (mp, &(run->log_out), s); 29845 } else if (f == run->ship_out.fptr) { 29846 mp_append_string (mp, &(run->ship_out), s); 29847 } else { 29848 fprintf ((FILE *) f, "%s", s); 29849 } 29850 } 29851 } 29852} 29853static void mplib_read_binary_file (MP mp, void *ff, void **data, size_t * size) { 29854 (void) mp; 29855 if (ff != NULL) { 29856 size_t len = 0; 29857 FILE *f = ((File *) ff)->f; 29858 if (f != NULL) 29859 len = fread (*data, 1, *size, f); 29860 *size = len; 29861 } 29862} 29863static void mplib_write_binary_file (MP mp, void *ff, void *s, size_t size) { 29864 (void) mp; 29865 if (ff != NULL) { 29866 void *f = ((File *) ff)->f; 29867 mp_run_data *run = mp_rundata (mp); 29868 if (f != NULL) { 29869 if (f == run->ship_out.fptr) { 29870 mp_append_data (mp, &(run->ship_out), s, size); 29871 } else { 29872 (void) fwrite (s, size, 1, f); 29873 } 29874 } 29875 } 29876} 29877static void mplib_close_file (MP mp, void *ff) { 29878 if (ff != NULL) { 29879 mp_run_data *run = mp_rundata (mp); 29880 void *f = ((File *) ff)->f; 29881 if (f != NULL) { 29882 if (f != run->term_out.fptr 29883 && f != run->error_out.fptr 29884 && f != run->log_out.fptr 29885 && f != run->ship_out.fptr && f != run->term_in.fptr) { 29886 fclose (f); 29887 } 29888 } 29889 free (ff); 29890 } 29891} 29892static int mplib_eof_file (MP mp, void *ff) { 29893 if (ff != NULL) { 29894 mp_run_data *run = mp_rundata (mp); 29895 FILE *f = ((File *) ff)->f; 29896 if (f == NULL) 29897 return 1; 29898 if (f == run->term_in.fptr && run->term_in.data != NULL) { 29899 return (run->term_in.size == 0); 29900 } 29901 return feof (f); 29902 } 29903 return 1; 29904} 29905static void mplib_flush_file (MP mp, void *ff) { 29906 (void) mp; 29907 (void) ff; 29908 return; 29909} 29910static void mplib_shipout_backend (MP mp, void *voidh) { 29911 mp_edge_header_node h = (mp_edge_header_node) voidh; 29912 mp_edge_object *hh = mp_gr_export (mp, h); 29913 if (hh) { 29914 mp_run_data *run = mp_rundata (mp); 29915 if (run->edges == NULL) { 29916 run->edges = hh; 29917 } else { 29918 mp_edge_object *p = run->edges; 29919 while (p->next != NULL) { 29920 p = p->next; 29921 } 29922 p->next = hh; 29923 } 29924 } 29925} 29926 29927 29928@ This is where we fill them all in. 29929@<Prepare function pointers for non-interactive use@>= 29930{ 29931 mp->open_file = mplib_open_file; 29932 mp->close_file = mplib_close_file; 29933 mp->eof_file = mplib_eof_file; 29934 mp->flush_file = mplib_flush_file; 29935 mp->write_ascii_file = mplib_write_ascii_file; 29936 mp->read_ascii_file = mplib_read_ascii_file; 29937 mp->write_binary_file = mplib_write_binary_file; 29938 mp->read_binary_file = mplib_read_binary_file; 29939 mp->shipout_backend = mplib_shipout_backend; 29940} 29941 29942 29943@ Perhaps this is the most important API function in the library. 29944 29945@<Exported function ...@>= 29946extern mp_run_data *mp_rundata (MP mp); 29947 29948@ @c 29949mp_run_data *mp_rundata (MP mp) { 29950 return &(mp->run_data); 29951} 29952 29953 29954@ @<Dealloc ...@>= 29955mp_free_stream (&(mp->run_data.term_in)); 29956mp_free_stream (&(mp->run_data.term_out)); 29957mp_free_stream (&(mp->run_data.log_out)); 29958mp_free_stream (&(mp->run_data.error_out)); 29959mp_free_stream (&(mp->run_data.ship_out)); 29960 29961@ @<Finish non-interactive use@>= 29962xfree (mp->term_out); 29963xfree (mp->term_in); 29964xfree (mp->err_out); 29965 29966@ @<Start non-interactive work@>= 29967@<Initialize the output routines@>; 29968mp->input_ptr = 0; 29969mp->max_in_stack = file_bottom; 29970mp->in_open = file_bottom; 29971mp->open_parens = 0; 29972mp->max_buf_stack = 0; 29973mp->param_ptr = 0; 29974mp->max_param_stack = 0; 29975start = loc = 0; 29976iindex = file_bottom; 29977nloc = nstart = NULL; 29978mp->first = 0; 29979line = 0; 29980name = is_term; 29981mp->mpx_name[file_bottom] = absent; 29982mp->force_eof = false; 29983t_open_in(); 29984mp->scanner_status = normal; 29985if (!mp->ini_version) { 29986 if (!mp_load_preload_file (mp)) { 29987 mp->history = mp_fatal_error_stop; 29988 return mp->history; 29989 } 29990} 29991mp_fix_date_and_time (mp); 29992if (mp->random_seed == 0) 29993 mp->random_seed = 29994 (number_to_scaled (internal_value (mp_time)) / number_to_scaled (unity_t)) + number_to_scaled (internal_value (mp_day)); 29995init_randoms (mp->random_seed); 29996initialize_print_selector(); 29997mp_open_log_file (mp); 29998mp_set_job_id (mp); 29999mp_init_map_file (mp, mp->troff_mode); 30000mp->history = mp_spotless; /* ready to go! */ 30001if (mp->troff_mode) { 30002 number_clone (internal_value(mp_gtroffmode), unity_t); 30003 number_clone (internal_value(mp_prologues), unity_t); 30004} 30005@<Fix up |mp->internal[mp_job_name]|@>; 30006if (mp->start_sym != NULL) { /* insert the `\&{everyjob}' symbol */ 30007 set_cur_sym(mp->start_sym); 30008 mp_back_input (mp); 30009} 30010 30011@ @c 30012int mp_execute (MP mp, char *s, size_t l) { 30013 mp_reset_stream (&(mp->run_data.term_out)); 30014 mp_reset_stream (&(mp->run_data.log_out)); 30015 mp_reset_stream (&(mp->run_data.error_out)); 30016 mp_reset_stream (&(mp->run_data.ship_out)); 30017 if (mp->finished) { 30018 return mp->history; 30019 } else if (!mp->noninteractive) { 30020 mp->history = mp_fatal_error_stop; 30021 return mp->history; 30022 } 30023 if (mp->history < mp_fatal_error_stop) { 30024 xfree (mp->jump_buf); 30025 mp->jump_buf = malloc (sizeof (jmp_buf)); 30026 if (mp->jump_buf == NULL || setjmp (*(mp->jump_buf)) != 0) { 30027 return mp->history; 30028 } 30029 if (s == NULL) { /* this signals EOF */ 30030 mp_final_cleanup (mp); /* prepare for death */ 30031 mp_close_files_and_terminate (mp); 30032 return mp->history; 30033 } 30034 mp->tally = 0; 30035 mp->term_offset = 0; 30036 mp->file_offset = 0; 30037 /* Perhaps some sort of warning here when |data| is not 30038 * yet exhausted would be nice ... this happens after errors 30039 */ 30040 if (mp->run_data.term_in.data) 30041 xfree (mp->run_data.term_in.data); 30042 mp->run_data.term_in.data = xstrdup (s); 30043 mp->run_data.term_in.cur = mp->run_data.term_in.data; 30044 mp->run_data.term_in.size = l; 30045 if (mp->run_state == 0) { 30046 mp->selector = term_only; 30047 @<Start non-interactive work@>; 30048 } 30049 mp->run_state = 1; 30050 (void) mp_input_ln (mp, mp->term_in); 30051 mp_firm_up_the_line (mp); 30052 mp->buffer[limit] = xord ('%'); 30053 mp->first = (size_t) (limit + 1); 30054 loc = start; 30055 do { 30056 mp_do_statement (mp); 30057 } while (cur_cmd() != mp_stop); 30058 mp_final_cleanup (mp); 30059 mp_close_files_and_terminate (mp); 30060 } 30061 return mp->history; 30062} 30063 30064 30065@ This function cleans up 30066@c 30067int mp_finish (MP mp) { 30068 int history = 0; 30069 if (mp->finished || mp->history >= mp_fatal_error_stop) { 30070 history = mp->history; 30071 mp_free (mp); 30072 return history; 30073 } 30074 xfree (mp->jump_buf); 30075 mp->jump_buf = malloc (sizeof (jmp_buf)); 30076 if (mp->jump_buf == NULL || setjmp (*(mp->jump_buf)) != 0) { 30077 history = mp->history; 30078 } else { 30079 history = mp->history; 30080 mp_final_cleanup (mp); /* prepare for death */ 30081 } 30082 mp_close_files_and_terminate (mp); 30083 mp_free (mp); 30084 return history; 30085} 30086 30087 30088@ People may want to know the library version 30089@c 30090char *mp_metapost_version (void) { 30091 return mp_strdup (metapost_version); 30092} 30093void mp_show_library_versions (void) { 30094 fprintf(stdout, "Compiled with cairo %s; using %s\n", CAIRO_VERSION_STRING, cairo_version_string()); 30095 fprintf(stdout, "Compiled with pixman %s; using %s\n", PIXMAN_VERSION_STRING, pixman_version_string()); 30096 fprintf(stdout, "Compiled with libpng %s; using %s\n", PNG_LIBPNG_VER_STRING, png_libpng_ver); 30097 fprintf(stdout, "Compiled with zlib %s; using %s\n", ZLIB_VERSION, zlibVersion()); 30098 fprintf(stdout, "Compiled with mpfr %s; using %s\n", MPFR_VERSION_STRING, mpfr_get_version()); 30099 fprintf(stdout, "Compiled with gmp %d.%d.%d; using %s\n\n", __GNU_MP_VERSION, __GNU_MP_VERSION_MINOR, __GNU_MP_VERSION_PATCHLEVEL, gmp_version); 30100} 30101 30102@ @<Exported function headers@>= 30103int mp_run (MP mp); 30104int mp_execute (MP mp, char *s, size_t l); 30105int mp_finish (MP mp); 30106char *mp_metapost_version (void);void mp_show_library_versions (void); 30107 30108@ @<Put each...@>= 30109mp_primitive (mp, "end", mp_stop, 0); 30110@:end_}{\&{end} primitive@>; 30111mp_primitive (mp, "dump", mp_stop, 1); 30112mp->frozen_dump = mp_frozen_primitive (mp, "dump", mp_stop, 1); 30113@:dump_}{\&{dump} primitive@> 30114 30115 30116@ @<Cases of |print_cmd...@>= 30117case mp_stop: 30118if (cur_mod() == 0) 30119 mp_print (mp, "end"); 30120else 30121 mp_print (mp, "dump"); 30122break; 30123 30124@* Commands. 30125Let's turn now to statements that are classified as ``commands'' because 30126of their imperative nature. We'll begin with simple ones, so that it 30127will be clear how to hook command processing into the |do_statement| routine; 30128then we'll tackle the tougher commands. 30129 30130Here's one of the simplest: 30131 30132@ @<Declare action procedures for use by |do_statement|@>= 30133static void mp_do_random_seed (MP mp); 30134@ @c 30135void mp_do_random_seed (MP mp) { 30136 mp_value new_expr; 30137 memset(&new_expr,0,sizeof(mp_value)); 30138 new_number(new_expr.data.n); 30139 mp_get_x_next (mp); 30140 if (cur_cmd() != mp_assignment) { 30141 const char *hlp[] = { "Always say `randomseed:=<numeric expression>'.", NULL }; 30142 mp_back_error (mp, "Missing `:=' has been inserted", hlp, true); 30143@.Missing `:='@>; 30144 }; 30145 mp_get_x_next (mp); 30146 mp_scan_expression (mp); 30147 if (mp->cur_exp.type != mp_known) { 30148 const char *hlp[] = { 30149 "Your expression was too random for me to handle,", 30150 "so I won't change the random seed just now.", 30151 NULL }; 30152 mp_disp_err(mp, NULL); 30153 mp_back_error (mp, "Unknown value will be ignored", hlp, true); 30154@.Unknown value...ignored@>; 30155 mp_get_x_next (mp); 30156 mp_flush_cur_exp (mp, new_expr); 30157 } else { 30158 @<Initialize the random seed to |cur_exp|@>; 30159 } 30160} 30161 30162 30163@ @<Initialize the random seed to |cur_exp|@>= 30164{ 30165 init_randoms (number_to_scaled(cur_exp_value_number ())); 30166 if (mp->selector >= log_only && mp->selector < write_file) { 30167 mp->old_setting = mp->selector; 30168 mp->selector = log_only; 30169 mp_print_nl (mp, "{randomseed:="); 30170 print_number (cur_exp_value_number ()); 30171 mp_print_char (mp, xord ('}')); 30172 mp_print_nl (mp, ""); 30173 mp->selector = mp->old_setting; 30174 } 30175} 30176 30177 30178@ And here's another simple one (somewhat different in flavor): 30179 30180@ @<Put each...@>= 30181mp_primitive (mp, "batchmode", mp_mode_command, mp_batch_mode); 30182@:mp_batch_mode_}{\&{batchmode} primitive@>; 30183mp_primitive (mp, "nonstopmode", mp_mode_command, mp_nonstop_mode); 30184@:mp_nonstop_mode_}{\&{nonstopmode} primitive@>; 30185mp_primitive (mp, "scrollmode", mp_mode_command, mp_scroll_mode); 30186@:mp_scroll_mode_}{\&{scrollmode} primitive@>; 30187mp_primitive (mp, "errorstopmode", mp_mode_command, mp_error_stop_mode); 30188@:mp_error_stop_mode_}{\&{errorstopmode} primitive@> 30189 30190 30191@ @<Cases of |print_cmd_mod|...@>= 30192case mp_mode_command: 30193switch (m) { 30194case mp_batch_mode: 30195 mp_print (mp, "batchmode"); 30196 break; 30197case mp_nonstop_mode: 30198 mp_print (mp, "nonstopmode"); 30199 break; 30200case mp_scroll_mode: 30201 mp_print (mp, "scrollmode"); 30202 break; 30203default: 30204 mp_print (mp, "errorstopmode"); 30205 break; 30206} 30207break; 30208 30209@ The `\&{inner}' and `\&{outer}' commands are only slightly harder. 30210 30211@ @<Put each...@>= 30212mp_primitive (mp, "inner", mp_protection_command, 0); 30213@:inner_}{\&{inner} primitive@>; 30214mp_primitive (mp, "outer", mp_protection_command, 1); 30215@:outer_}{\&{outer} primitive@> 30216 30217 30218@ @<Cases of |print_cmd...@>= 30219case mp_protection_command: 30220if (m == 0) 30221 mp_print (mp, "inner"); 30222else 30223 mp_print (mp, "outer"); 30224break; 30225 30226@ @<Declare action procedures for use by |do_statement|@>= 30227static void mp_do_protection (MP mp); 30228 30229@ @c 30230void mp_do_protection (MP mp) { 30231 int m; /* 0 to unprotect, 1 to protect */ 30232 halfword t; /* the |eq_type| before we change it */ 30233 m = cur_mod(); 30234 do { 30235 mp_get_symbol (mp); 30236 t = eq_type (cur_sym()); 30237 if (m == 0) { 30238 if (t >= mp_outer_tag) 30239 set_eq_type (cur_sym(), (t - mp_outer_tag)); 30240 } else if (t < mp_outer_tag) { 30241 set_eq_type (cur_sym(), (t + mp_outer_tag)); 30242 } 30243 mp_get_x_next (mp); 30244 } while (cur_cmd() == mp_comma); 30245} 30246 30247 30248@ \MP\ never defines the tokens `\.(' and `\.)' to be primitives, but 30249plain \MP\ begins with the declaration `\&{delimiters} \.{()}'. Such a 30250declaration assigns the command code |left_delimiter| to `\.{(}' and 30251|right_delimiter| to `\.{)}'; the |equiv| of each delimiter is the 30252hash address of its mate. 30253 30254@ @<Declare action procedures for use by |do_statement|@>= 30255static void mp_def_delims (MP mp); 30256 30257@ @c 30258void mp_def_delims (MP mp) { 30259 mp_sym l_delim, r_delim; /* the new delimiter pair */ 30260 mp_get_clear_symbol (mp); 30261 l_delim = cur_sym(); 30262 mp_get_clear_symbol (mp); 30263 r_delim = cur_sym(); 30264 set_eq_type (l_delim, mp_left_delimiter); 30265 set_equiv_sym (l_delim, r_delim); 30266 set_eq_type (r_delim, mp_right_delimiter); 30267 set_equiv_sym (r_delim, l_delim); 30268 mp_get_x_next (mp); 30269} 30270 30271 30272@ Here is a procedure that is called when \MP\ has reached a point 30273where some right delimiter is mandatory. 30274 30275@<Declarations@>= 30276static void mp_check_delimiter (MP mp, mp_sym l_delim, mp_sym r_delim); 30277 30278@ @c 30279void mp_check_delimiter (MP mp, mp_sym l_delim, mp_sym r_delim) { 30280 if (cur_cmd() == mp_right_delimiter) 30281 if (equiv_sym (cur_sym()) == l_delim) 30282 return; 30283 if (cur_sym() != r_delim) { 30284 char msg[256]; 30285 const char *hlp[] = { 30286 "I found no right delimiter to match a left one. So I've", 30287 "put one in, behind the scenes; this may fix the problem.", 30288 NULL }; 30289 mp_snprintf(msg, 256, "Missing `%s' has been inserted", mp_str (mp, text (r_delim))); 30290@.Missing `)'@>; 30291 mp_back_error (mp, msg, hlp, true); 30292 } else { 30293 char msg[256]; 30294 const char *hlp[] = { 30295 "Strange: This token has lost its former meaning!", 30296 "I'll read it as a right delimiter this time;", 30297 "but watch out, I'll probably miss it later.", 30298 NULL }; 30299 mp_snprintf(msg, 256, "The token `%s' is no longer a right delimiter", mp_str(mp, text (r_delim))); 30300@.The token...delimiter@>; 30301 mp_error (mp, msg, hlp, true); 30302 } 30303} 30304 30305 30306@ The next four commands save or change the values associated with tokens. 30307 30308@ @<Declare action procedures for use by |do_statement|@>= 30309static void mp_do_statement (MP mp); 30310static void mp_do_interim (MP mp); 30311 30312@ @c 30313void mp_do_interim (MP mp) { 30314 mp_get_x_next (mp); 30315 if (cur_cmd() != mp_internal_quantity) { 30316 char msg[256]; 30317 const char *hlp[] = { 30318 "Something like `tracingonline' should follow `interim'.", 30319 NULL }; 30320 mp_snprintf(msg, 256, "The token `%s' isn't an internal quantity", 30321 (cur_sym() == NULL ? "(%CAPSULE)" : mp_str(mp, text (cur_sym())))); 30322@.The token...quantity@>; 30323 mp_back_error (mp, msg, hlp, true); 30324 } else { 30325 mp_save_internal (mp, cur_mod()); 30326 mp_back_input (mp); 30327 } 30328 mp_do_statement (mp); 30329} 30330 30331 30332@ The following procedure is careful not to undefine the left-hand symbol 30333too soon, lest commands like `{\tt let x=x}' have a surprising effect. 30334 30335@<Declare action procedures for use by |do_statement|@>= 30336static void mp_do_let (MP mp); 30337 30338@ @c 30339void mp_do_let (MP mp) { 30340 mp_sym l; /* hash location of the left-hand symbol */ 30341 mp_get_symbol (mp); 30342 l = cur_sym(); 30343 mp_get_x_next (mp); 30344 if (cur_cmd() != mp_equals && cur_cmd() != mp_assignment) { 30345 const char *hlp[] = { 30346 "You should have said `let symbol = something'.", 30347 "But don't worry; I'll pretend that an equals sign", 30348 "was present. The next token I read will be `something'.", 30349 NULL }; 30350 mp_back_error (mp, "Missing `=' has been inserted", hlp, true); 30351@.Missing `='@>; 30352 } 30353 mp_get_symbol (mp); 30354 switch (cur_cmd()) { 30355 case mp_defined_macro: 30356 case mp_secondary_primary_macro: 30357 case mp_tertiary_secondary_macro: 30358 case mp_expression_tertiary_macro: 30359 add_mac_ref (cur_mod_node()); 30360 break; 30361 default: 30362 break; 30363 } 30364 mp_clear_symbol (mp, l, false); 30365 set_eq_type (l, cur_cmd()); 30366 if (cur_cmd() == mp_tag_token) 30367 set_equiv (l, 0); /* todo: this was |null| */ 30368 else if (cur_cmd() == mp_defined_macro || 30369 cur_cmd() == mp_secondary_primary_macro || 30370 cur_cmd() == mp_tertiary_secondary_macro || 30371 cur_cmd() == mp_expression_tertiary_macro) 30372 set_equiv_node (l, cur_mod_node()); 30373 else if (cur_cmd() == mp_left_delimiter || 30374 cur_cmd() == mp_right_delimiter) 30375 set_equiv_sym (l, equiv_sym (cur_sym())); 30376 else 30377 set_equiv (l, cur_mod()); 30378 mp_get_x_next (mp); 30379} 30380 30381 30382@ @<Declarations@>= 30383static void mp_do_new_internal (MP mp); 30384 30385@ @<Internal library ...@>= 30386void mp_grow_internals (MP mp, int l); 30387 30388@ @c 30389void mp_grow_internals (MP mp, int l) { 30390 mp_internal *internal; 30391 int k; 30392 if (l > max_halfword) { 30393 mp_confusion (mp, "out of memory space"); /* can't be reached */ 30394 } 30395 internal = xmalloc ((l + 1), sizeof (mp_internal)); 30396 for (k = 0; k <= l; k++) { 30397 if (k <= mp->max_internal) { 30398 memcpy (internal + k, mp->internal + k, sizeof (mp_internal)); 30399 } else { 30400 memset (internal + k, 0, sizeof (mp_internal)); 30401 new_number(((mp_internal *)(internal + k))->v.data.n); 30402 } 30403 } 30404 xfree (mp->internal); 30405 mp->internal = internal; 30406 mp->max_internal = l; 30407} 30408void mp_do_new_internal (MP mp) { 30409 int the_type = mp_known; 30410 mp_get_x_next (mp); 30411 if (cur_cmd() == mp_type_name && cur_mod() == mp_string_type) { 30412 the_type = mp_string_type; 30413 } else { 30414 if (!(cur_cmd() == mp_type_name && cur_mod() == mp_numeric_type)) { 30415 mp_back_input (mp); 30416 } 30417 } 30418 do { 30419 if (mp->int_ptr == mp->max_internal) { 30420 mp_grow_internals (mp, (mp->max_internal + (mp->max_internal / 4))); 30421 } 30422 mp_get_clear_symbol (mp); 30423 incr (mp->int_ptr); 30424 set_eq_type (cur_sym(), mp_internal_quantity); 30425 set_equiv (cur_sym(), mp->int_ptr); 30426 if (internal_name (mp->int_ptr) != NULL) 30427 xfree (internal_name (mp->int_ptr)); 30428 set_internal_name (mp->int_ptr, 30429 mp_xstrdup (mp, mp_str (mp, text (cur_sym())))); 30430 if (the_type == mp_string_type) { 30431 set_internal_string (mp->int_ptr, mp_rts(mp,"")); 30432 } else { 30433 set_number_to_zero (internal_value (mp->int_ptr)); 30434 } 30435 set_internal_type (mp->int_ptr, the_type); 30436 mp_get_x_next (mp); 30437 } while (cur_cmd() == mp_comma); 30438} 30439 30440 30441@ @<Dealloc variables@>= 30442for (k = 0; k <= mp->max_internal; k++) { 30443 free_number(mp->internal[k].v.data.n); 30444 xfree (internal_name (k)); 30445} 30446xfree (mp->internal); 30447 30448 30449@ The various `\&{show}' commands are distinguished by modifier fields 30450in the usual way. 30451 30452@d show_token_code 0 /* show the meaning of a single token */ 30453@d show_stats_code 1 /* show current memory and string usage */ 30454@d show_code 2 /* show a list of expressions */ 30455@d show_var_code 3 /* show a variable and its descendents */ 30456@d show_dependencies_code 4 /* show dependent variables in terms of independents */ 30457 30458@<Put each...@>= 30459mp_primitive (mp, "showtoken", mp_show_command, show_token_code); 30460@:show_token_}{\&{showtoken} primitive@>; 30461mp_primitive (mp, "showstats", mp_show_command, show_stats_code); 30462@:show_stats_}{\&{showstats} primitive@>; 30463mp_primitive (mp, "show", mp_show_command, show_code); 30464@:show_}{\&{show} primitive@>; 30465mp_primitive (mp, "showvariable", mp_show_command, show_var_code); 30466@:show_var_}{\&{showvariable} primitive@>; 30467mp_primitive (mp, "showdependencies", mp_show_command, show_dependencies_code); 30468@:show_dependencies_}{\&{showdependencies} primitive@> 30469 30470 30471@ @<Cases of |print_cmd...@>= 30472case mp_show_command: 30473switch (m) { 30474case show_token_code: 30475 mp_print (mp, "showtoken"); 30476 break; 30477case show_stats_code: 30478 mp_print (mp, "showstats"); 30479 break; 30480case show_code: 30481 mp_print (mp, "show"); 30482 break; 30483case show_var_code: 30484 mp_print (mp, "showvariable"); 30485 break; 30486default: 30487 mp_print (mp, "showdependencies"); 30488 break; 30489} 30490break; 30491 30492@ The value of |cur_mod| controls the |verbosity| in the |print_exp| routine: 30493if it's |show_code|, complicated structures are abbreviated, otherwise 30494they aren't. 30495 30496@<Declare action procedures for use by |do_statement|@>= 30497static void mp_do_show (MP mp); 30498 30499@ @c 30500void mp_do_show (MP mp) { 30501 mp_value new_expr; 30502 do { 30503 memset(&new_expr,0,sizeof(mp_value)); 30504 new_number(new_expr.data.n); 30505 mp_get_x_next (mp); 30506 mp_scan_expression (mp); 30507 mp_print_nl (mp, ">> "); 30508@.>>@>; 30509 mp_print_exp (mp, NULL, 2); 30510 mp_flush_cur_exp (mp, new_expr); 30511 } while (cur_cmd() == mp_comma); 30512} 30513 30514 30515@ @<Declare action procedures for use by |do_statement|@>= 30516static void mp_disp_token (MP mp); 30517 30518@ @c 30519void mp_disp_token (MP mp) { 30520 mp_print_nl (mp, "> "); 30521@.>\relax@>; 30522 if (cur_sym() == NULL) { 30523 @<Show a numeric or string or capsule token@>; 30524 } else { 30525 mp_print_text (cur_sym()); 30526 mp_print_char (mp, xord ('=')); 30527 if (eq_type (cur_sym()) >= mp_outer_tag) 30528 mp_print (mp, "(outer) "); 30529 mp_print_cmd_mod (mp, cur_cmd(), cur_mod()); 30530 if (cur_cmd() == mp_defined_macro) { 30531 mp_print_ln (mp); 30532 mp_show_macro (mp, cur_mod_node(), NULL, 100000); 30533 } /* this avoids recursion between |show_macro| and |print_cmd_mod| */ 30534@^recursion@> 30535 } 30536} 30537 30538 30539@ @<Show a numeric or string or capsule token@>= 30540{ 30541 if (cur_cmd() == mp_numeric_token) { 30542 print_number (cur_mod_number()); 30543 } else if (cur_cmd() == mp_capsule_token) { 30544 mp_print_capsule (mp, cur_mod_node()); 30545 } else { 30546 mp_print_char (mp, xord ('"')); 30547 mp_print_str (mp, cur_mod_str()); 30548 mp_print_char (mp, xord ('"')); 30549 delete_str_ref (cur_mod_str()); 30550 } 30551} 30552 30553 30554@ The following cases of |print_cmd_mod| might arise in connection 30555with |disp_token|, although they don't necessarily correspond to 30556primitive tokens. 30557 30558@<Cases of |print_cmd_...@>= 30559case mp_left_delimiter: 30560case mp_right_delimiter: 30561if (c == mp_left_delimiter) 30562 mp_print (mp, "left"); 30563else 30564 mp_print (mp, "right"); 30565#if 0 30566mp_print (mp, " delimiter that matches "); 30567mp_print_text (m); 30568#else 30569mp_print (mp, " delimiter"); 30570#endif 30571break; 30572case mp_tag_token: 30573if (m == 0) /* todo: this was |null| */ 30574 mp_print (mp, "tag"); 30575else 30576 mp_print (mp, "variable"); 30577break; 30578case mp_defined_macro: 30579mp_print (mp, "macro:"); 30580break; 30581case mp_secondary_primary_macro: 30582case mp_tertiary_secondary_macro: 30583case mp_expression_tertiary_macro: 30584 mp_print_cmd_mod(mp, mp_macro_def,c); 30585 mp_print(mp, "'d macro:"); 30586 mp_print_ln(mp); 30587 mp_show_token_list(mp, mp_link(mp_link(cur_mod_node())),0,1000,0); 30588 break; 30589case mp_repeat_loop: 30590mp_print (mp, "[repeat the loop]"); 30591break; 30592case mp_internal_quantity: 30593mp_print (mp, internal_name (m)); 30594break; 30595 30596 30597@ @<Declare action procedures for use by |do_statement|@>= 30598static void mp_do_show_token (MP mp); 30599 30600@ @c 30601void mp_do_show_token (MP mp) { 30602 do { 30603 get_t_next (mp); 30604 mp_disp_token (mp); 30605 mp_get_x_next (mp); 30606 } while (cur_cmd() == mp_comma); 30607} 30608 30609 30610@ @<Declare action procedures for use by |do_statement|@>= 30611static void mp_do_show_stats (MP mp); 30612 30613@ @c 30614void mp_do_show_stats (MP mp) { 30615 mp_print_nl (mp, "Memory usage "); 30616@.Memory usage...@>; 30617 mp_print_int (mp, (integer) mp->var_used); 30618 mp_print_ln (mp); 30619 mp_print_nl (mp, "String usage "); 30620 mp_print_int (mp, (int) mp->strs_in_use); 30621 mp_print_char (mp, xord ('&')); 30622 mp_print_int (mp, (int) mp->pool_in_use); 30623 mp_print_ln (mp); 30624 mp_get_x_next (mp); 30625} 30626 30627 30628@ Here's a recursive procedure that gives an abbreviated account 30629of a variable, for use by |do_show_var|. 30630 30631@<Declare action procedures for use by |do_statement|@>= 30632static void mp_disp_var (MP mp, mp_node p); 30633 30634@ @c 30635void mp_disp_var (MP mp, mp_node p) { 30636 mp_node q; /* traverses attributes and subscripts */ 30637 int n; /* amount of macro text to show */ 30638 if (mp_type (p) == mp_structured) { 30639 @<Descend the structure@>; 30640 } else if (mp_type (p) >= mp_unsuffixed_macro) { 30641 @<Display a variable macro@>; 30642 } else if (mp_type (p) != mp_undefined) { 30643 mp_print_nl (mp, ""); 30644 mp_print_variable_name (mp, p); 30645 mp_print_char (mp, xord ('=')); 30646 mp_print_exp (mp, p, 0); 30647 } 30648} 30649 30650 30651@ @<Descend the structure@>= 30652{ 30653 q = attr_head (p); 30654 do { 30655 mp_disp_var (mp, q); 30656 q = mp_link (q); 30657 } while (q != mp->end_attr); 30658 q = subscr_head (p); 30659 while (mp_name_type (q) == mp_subscr) { 30660 mp_disp_var (mp, q); 30661 q = mp_link (q); 30662 } 30663} 30664 30665 30666@ @<Display a variable macro@>= 30667{ 30668 mp_print_nl (mp, ""); 30669 mp_print_variable_name (mp, p); 30670 if (mp_type (p) > mp_unsuffixed_macro) 30671 mp_print (mp, "@@#"); /* |suffixed_macro| */ 30672 mp_print (mp, "=macro:"); 30673 if ((int) mp->file_offset >= mp->max_print_line - 20) 30674 n = 5; 30675 else 30676 n = mp->max_print_line - (int) mp->file_offset - 15; 30677 mp_show_macro (mp, value_node (p), NULL, n); 30678} 30679 30680 30681@ @<Declare action procedures for use by |do_statement|@>= 30682static void mp_do_show_var (MP mp); 30683 30684@ @c 30685void mp_do_show_var (MP mp) { 30686 do { 30687 get_t_next (mp); 30688 if (cur_sym() != NULL) 30689 if (cur_sym_mod() == 0) 30690 if (cur_cmd() == mp_tag_token) 30691 if (cur_mod() != 0 || cur_mod_node()!=NULL) { 30692 mp_disp_var (mp, cur_mod_node()); 30693 goto DONE; 30694 } 30695 mp_disp_token (mp); 30696 DONE: 30697 mp_get_x_next (mp); 30698 } while (cur_cmd() == mp_comma); 30699} 30700 30701 30702@ @<Declare action procedures for use by |do_statement|@>= 30703static void mp_do_show_dependencies (MP mp); 30704 30705@ @c 30706void mp_do_show_dependencies (MP mp) { 30707 mp_value_node p; /* link that runs through all dependencies */ 30708 p = (mp_value_node) mp_link (mp->dep_head); 30709 while (p != mp->dep_head) { 30710 if (mp_interesting (mp, (mp_node) p)) { 30711 mp_print_nl (mp, ""); 30712 mp_print_variable_name (mp, (mp_node) p); 30713 if (mp_type (p) == mp_dependent) 30714 mp_print_char (mp, xord ('=')); 30715 else 30716 mp_print (mp, " = "); /* extra spaces imply proto-dependency */ 30717 mp_print_dependency (mp, (mp_value_node) dep_list (p), mp_type (p)); 30718 } 30719 p = (mp_value_node) dep_list (p); 30720 while (dep_info (p) != NULL) 30721 p = (mp_value_node) mp_link (p); 30722 p = (mp_value_node) mp_link (p); 30723 } 30724 mp_get_x_next (mp); 30725} 30726 30727 30728@ Finally we are ready for the procedure that governs all of the 30729show commands. 30730 30731@<Declare action procedures for use by |do_statement|@>= 30732static void mp_do_show_whatever (MP mp); 30733 30734@ @c 30735void mp_do_show_whatever (MP mp) { 30736 if (mp->interaction == mp_error_stop_mode) 30737 wake_up_terminal(); 30738 switch (cur_mod()) { 30739 case show_token_code: 30740 mp_do_show_token (mp); 30741 break; 30742 case show_stats_code: 30743 mp_do_show_stats (mp); 30744 break; 30745 case show_code: 30746 mp_do_show (mp); 30747 break; 30748 case show_var_code: 30749 mp_do_show_var (mp); 30750 break; 30751 case show_dependencies_code: 30752 mp_do_show_dependencies (mp); 30753 break; 30754 } /* there are no other cases */ 30755 if (number_positive (internal_value (mp_showstopping))) { 30756 const char *hlp[] = { 30757 "This isn't an error message; I'm just showing something.", 30758 NULL }; 30759 if (mp->interaction < mp_error_stop_mode) { 30760 hlp[0] = NULL; 30761 decr (mp->error_count); 30762 } 30763 if (cur_cmd() == mp_semicolon) { 30764 mp_error (mp, "OK", hlp, true); 30765 } else { 30766 mp_back_error (mp, "OK", hlp, true); 30767 mp_get_x_next (mp); 30768 } 30769@.OK@>; 30770 } 30771} 30772 30773 30774@ The `\&{addto}' command needs the following additional primitives: 30775 30776@d double_path_code 0 /* command modifier for `\&{doublepath}' */ 30777@d contour_code 1 /* command modifier for `\&{contour}' */ 30778@d also_code 2 /* command modifier for `\&{also}' */ 30779 30780@ Pre and postscripts need two new identifiers: 30781 30782@d with_mp_pre_script 11 30783@d with_mp_post_script 13 30784 30785@<Put each...@>= 30786mp_primitive (mp, "doublepath", mp_thing_to_add, double_path_code); 30787@:double_path_}{\&{doublepath} primitive@>; 30788mp_primitive (mp, "contour", mp_thing_to_add, contour_code); 30789@:contour_}{\&{contour} primitive@>; 30790mp_primitive (mp, "also", mp_thing_to_add, also_code); 30791@:also_}{\&{also} primitive@>; 30792mp_primitive (mp, "withpen", mp_with_option, mp_pen_type); 30793@:with_pen_}{\&{withpen} primitive@>; 30794mp_primitive (mp, "dashed", mp_with_option, mp_picture_type); 30795@:dashed_}{\&{dashed} primitive@>; 30796mp_primitive (mp, "withprescript", mp_with_option, with_mp_pre_script); 30797@:with_mp_pre_script_}{\&{withprescript} primitive@>; 30798mp_primitive (mp, "withpostscript", mp_with_option, with_mp_post_script); 30799@:with_mp_post_script_}{\&{withpostscript} primitive@>; 30800mp_primitive (mp, "withoutcolor", mp_with_option, mp_no_model); 30801@:with_color_}{\&{withoutcolor} primitive@>; 30802mp_primitive (mp, "withgreyscale", mp_with_option, mp_grey_model); 30803@:with_color_}{\&{withgreyscale} primitive@>; 30804mp_primitive (mp, "withcolor", mp_with_option, mp_uninitialized_model); 30805@:with_color_}{\&{withcolor} primitive@> 30806/* \&{withrgbcolor} is an alias for \&{withcolor} */ 30807 mp_primitive (mp, "withrgbcolor", mp_with_option, mp_rgb_model); 30808@:with_color_}{\&{withrgbcolor} primitive@>; 30809mp_primitive (mp, "withcmykcolor", mp_with_option, mp_cmyk_model); 30810@:with_color_}{\&{withcmykcolor} primitive@> 30811 30812 30813@ @<Cases of |print_cmd...@>= 30814case mp_thing_to_add: 30815if (m == contour_code) 30816 mp_print (mp, "contour"); 30817else if (m == double_path_code) 30818 mp_print (mp, "doublepath"); 30819else 30820 mp_print (mp, "also"); 30821break; 30822case mp_with_option: 30823if (m == mp_pen_type) 30824 mp_print (mp, "withpen"); 30825else if (m == with_mp_pre_script) 30826 mp_print (mp, "withprescript"); 30827else if (m == with_mp_post_script) 30828 mp_print (mp, "withpostscript"); 30829else if (m == mp_no_model) 30830 mp_print (mp, "withoutcolor"); 30831else if (m == mp_rgb_model) 30832 mp_print (mp, "withrgbcolor"); 30833else if (m == mp_uninitialized_model) 30834 mp_print (mp, "withcolor"); 30835else if (m == mp_cmyk_model) 30836 mp_print (mp, "withcmykcolor"); 30837else if (m == mp_grey_model) 30838 mp_print (mp, "withgreyscale"); 30839else 30840 mp_print (mp, "dashed"); 30841break; 30842 30843@ The |scan_with_list| procedure parses a $\langle$with list$\rangle$ and 30844updates the list of graphical objects starting at |p|. Each $\langle$with 30845clause$\rangle$ updates all graphical objects whose |type| is compatible. 30846Other objects are ignored. 30847 30848@<Declare action procedures for use by |do_statement|@>= 30849static void mp_scan_with_list (MP mp, mp_node p); 30850 30851@ Forcing the color to be between |0| and |unity| here guarantees that no 30852picture will ever contain a color outside the legal range for \ps\ graphics. 30853 30854@d make_cp_a_colored_object() do { 30855 cp = p; 30856 while (cp != NULL) { 30857 if (has_color (cp)) 30858 break; 30859 cp = mp_link (cp); 30860 } 30861} while (0) 30862 30863@d clear_color(A) do { 30864 set_number_to_zero(((mp_stroked_node)(A))->cyan); 30865 set_number_to_zero(((mp_stroked_node)(A))->magenta); 30866 set_number_to_zero(((mp_stroked_node)(A))->yellow); 30867 set_number_to_zero(((mp_stroked_node)(A))->black); 30868 mp_color_model ((A)) = mp_uninitialized_model; 30869} while (0) 30870 30871@d set_color_val(A,B) do { 30872 number_clone(A, (B)); 30873 if (number_negative(A)) 30874 set_number_to_zero(A); 30875 if (number_greater(A,unity_t)) 30876 set_number_to_unity(A); 30877} while (0) 30878 30879@c 30880static int is_invalid_with_list (MP mp, mp_variable_type t) { 30881 return ((t == with_mp_pre_script) && (mp->cur_exp.type != mp_string_type)) || 30882 ((t == with_mp_post_script) && (mp->cur_exp.type != mp_string_type)) || 30883 ((t == (mp_variable_type) mp_uninitialized_model) && 30884 ((mp->cur_exp.type != mp_cmykcolor_type) 30885 && (mp->cur_exp.type != mp_color_type) 30886 && (mp->cur_exp.type != mp_known) 30887 && (mp->cur_exp.type != mp_boolean_type))) || ((t == (mp_variable_type) mp_cmyk_model) 30888 && (mp->cur_exp.type != 30889 mp_cmykcolor_type)) 30890 || ((t == (mp_variable_type) mp_rgb_model) && (mp->cur_exp.type != mp_color_type)) 30891 || ((t == (mp_variable_type) mp_grey_model) && (mp->cur_exp.type != mp_known)) 30892 || ((t == (mp_variable_type) mp_pen_type) && (mp->cur_exp.type != t)) 30893 || ((t == (mp_variable_type) mp_picture_type) && (mp->cur_exp.type != t)); 30894} 30895static void complain_invalid_with_list (MP mp, mp_variable_type t) { 30896 /* Complain about improper type */ 30897 mp_value new_expr; 30898 const char *hlp[] = { 30899 "Next time say `withpen <known pen expression>';", 30900 "I'll ignore the bad `with' clause and look for another.", 30901 NULL }; 30902 memset(&new_expr,0,sizeof(mp_value)); 30903 new_number(new_expr.data.n); 30904 mp_disp_err(mp, NULL); 30905 if (t == with_mp_pre_script) 30906 hlp[0] = "Next time say `withprescript <known string expression>';"; 30907 else if (t == with_mp_post_script) 30908 hlp[0] = "Next time say `withpostscript <known string expression>';"; 30909 else if (t == mp_picture_type) 30910 hlp[0] = "Next time say `dashed <known picture expression>';"; 30911 else if (t == (mp_variable_type) mp_uninitialized_model) 30912 hlp[0] = "Next time say `withcolor <known color expression>';"; 30913 else if (t == (mp_variable_type) mp_rgb_model) 30914 hlp[0] = "Next time say `withrgbcolor <known color expression>';"; 30915 else if (t == (mp_variable_type) mp_cmyk_model) 30916 hlp[0] = "Next time say `withcmykcolor <known cmykcolor expression>';"; 30917 else if (t == (mp_variable_type) mp_grey_model) 30918 hlp[0] = "Next time say `withgreyscale <known numeric expression>';";; 30919 mp_back_error (mp, "Improper type", hlp, true); 30920 mp_get_x_next (mp); 30921 mp_flush_cur_exp (mp, new_expr); 30922} 30923 30924void mp_scan_with_list (MP mp, mp_node p) { 30925 mp_variable_type t; /* |cur_mod| of the |with_option| (should match |cur_type|) */ 30926 mp_node q; /* for list manipulation */ 30927 mp_node cp, pp, dp, ap, bp; 30928 /* objects being updated; |void| initially; |NULL| to suppress update */ 30929 cp = MP_VOID; 30930 pp = MP_VOID; 30931 dp = MP_VOID; 30932 ap = MP_VOID; 30933 bp = MP_VOID; 30934 while (cur_cmd() == mp_with_option) { 30935 /* todo this is not very nice: the color models have their own enumeration */ 30936 t = (mp_variable_type) cur_mod(); 30937 mp_get_x_next (mp); 30938 if (t != (mp_variable_type) mp_no_model) 30939 mp_scan_expression (mp); 30940 if (is_invalid_with_list(mp, t)) { 30941 complain_invalid_with_list (mp, t); 30942 continue; 30943 } 30944 if (t == (mp_variable_type) mp_uninitialized_model) { 30945 mp_value new_expr; 30946 memset(&new_expr,0,sizeof(mp_value)); 30947 new_number(new_expr.data.n); 30948 if (cp == MP_VOID) 30949 make_cp_a_colored_object(); 30950 if (cp != NULL) { 30951 /* Transfer a color from the current expression to object~|cp| */ 30952 if (mp->cur_exp.type == mp_color_type) { 30953 /* Transfer a rgbcolor from the current expression to object~|cp| */ 30954 mp_stroked_node cp0 = (mp_stroked_node)cp; 30955 q = value_node (cur_exp_node ()); 30956 clear_color(cp0); 30957 mp_color_model (cp) = mp_rgb_model; 30958 set_color_val (cp0->red, value_number (red_part (q))); 30959 set_color_val (cp0->green, value_number (green_part (q))); 30960 set_color_val (cp0->blue, value_number (blue_part (q))); 30961 } else if (mp->cur_exp.type == mp_cmykcolor_type) { 30962 /* Transfer a cmykcolor from the current expression to object~|cp| */ 30963 mp_stroked_node cp0 = (mp_stroked_node)cp; 30964 q = value_node (cur_exp_node ()); 30965 set_color_val (cp0->cyan, value_number (cyan_part (q))); 30966 set_color_val (cp0->magenta, value_number (magenta_part (q))); 30967 set_color_val (cp0->yellow, value_number (yellow_part (q))); 30968 set_color_val (cp0->black, value_number (black_part (q))); 30969 mp_color_model (cp) = mp_cmyk_model; 30970 } else if (mp->cur_exp.type == mp_known) { 30971 /* Transfer a greyscale from the current expression to object~|cp| */ 30972 mp_number qq; 30973 mp_stroked_node cp0 = (mp_stroked_node)cp; 30974 new_number (qq); 30975 number_clone (qq, cur_exp_value_number ()); 30976 clear_color (cp); 30977 mp_color_model (cp) = mp_grey_model; 30978 set_color_val (cp0->grey, qq); 30979 free_number (qq); 30980 } else if (cur_exp_value_boolean () == mp_false_code) { 30981 /* Transfer a noncolor from the current expression to object~|cp| */ 30982 clear_color (cp); 30983 mp_color_model (cp) = mp_no_model; 30984 } else if (cur_exp_value_boolean () == mp_true_code) { 30985 /* Transfer no color from the current expression to object~|cp| */ 30986 clear_color (cp); 30987 mp_color_model (cp) = mp_uninitialized_model; 30988 } 30989 } 30990 mp_flush_cur_exp (mp, new_expr); 30991 } else if (t == (mp_variable_type) mp_rgb_model) { 30992 mp_value new_expr; 30993 memset(&new_expr,0,sizeof(mp_value)); 30994 new_number(new_expr.data.n); 30995 if (cp == MP_VOID) 30996 make_cp_a_colored_object(); 30997 if (cp != NULL) { 30998 /* Transfer a rgbcolor from the current expression to object~|cp| */ 30999 mp_stroked_node cp0 = (mp_stroked_node)cp; 31000 q = value_node (cur_exp_node ()); 31001 clear_color(cp0); 31002 mp_color_model (cp) = mp_rgb_model; 31003 set_color_val (cp0->red, value_number (red_part (q))); 31004 set_color_val (cp0->green, value_number (green_part (q))); 31005 set_color_val (cp0->blue, value_number (blue_part (q))); 31006 } 31007 mp_flush_cur_exp (mp, new_expr); 31008 } else if (t == (mp_variable_type) mp_cmyk_model) { 31009 mp_value new_expr; 31010 memset(&new_expr,0,sizeof(mp_value)); 31011 new_number(new_expr.data.n); 31012 if (cp == MP_VOID) 31013 make_cp_a_colored_object(); 31014 if (cp != NULL) { 31015 /* Transfer a cmykcolor from the current expression to object~|cp| */ 31016 mp_stroked_node cp0 = (mp_stroked_node)cp; 31017 q = value_node (cur_exp_node ()); 31018 set_color_val (cp0->cyan, value_number (cyan_part (q))); 31019 set_color_val (cp0->magenta, value_number (magenta_part (q))); 31020 set_color_val (cp0->yellow, value_number (yellow_part (q))); 31021 set_color_val (cp0->black, value_number (black_part (q))); 31022 mp_color_model (cp) = mp_cmyk_model; 31023 } 31024 mp_flush_cur_exp (mp, new_expr); 31025 } else if (t == (mp_variable_type) mp_grey_model) { 31026 mp_value new_expr; 31027 memset(&new_expr,0,sizeof(mp_value)); 31028 new_number(new_expr.data.n); 31029 if (cp == MP_VOID) 31030 make_cp_a_colored_object(); 31031 if (cp != NULL) { 31032 /* Transfer a greyscale from the current expression to object~|cp| */ 31033 mp_number qq; 31034 mp_stroked_node cp0 = (mp_stroked_node)cp; 31035 new_number (qq); 31036 number_clone (qq, cur_exp_value_number ()); 31037 clear_color (cp); 31038 mp_color_model (cp) = mp_grey_model; 31039 set_color_val (cp0->grey, qq); 31040 free_number (qq); 31041 } 31042 mp_flush_cur_exp (mp, new_expr); 31043 } else if (t == (mp_variable_type) mp_no_model) { 31044 if (cp == MP_VOID) 31045 make_cp_a_colored_object(); 31046 if (cp != NULL) { 31047 /* Transfer a noncolor from the current expression to object~|cp| */ 31048 clear_color (cp); 31049 mp_color_model (cp) = mp_no_model; 31050 } 31051 } else if (t == mp_pen_type) { 31052 if (pp == MP_VOID) { 31053 /* Make |pp| an object in list~|p| that needs a pen */ 31054 pp = p; 31055 while (pp != NULL) { 31056 if (has_pen (pp)) 31057 break; 31058 pp = mp_link (pp); 31059 } 31060 } 31061 31062 if (pp != NULL) { 31063 switch (mp_type (pp)) { 31064 case mp_fill_node_type: 31065 if (mp_pen_p ((mp_fill_node) pp) != NULL) 31066 mp_toss_knot_list (mp, mp_pen_p ((mp_fill_node) pp)); 31067 mp_pen_p ((mp_fill_node) pp) = cur_exp_knot (); 31068 break; 31069 case mp_stroked_node_type: 31070 if (mp_pen_p ((mp_stroked_node) pp) != NULL) 31071 mp_toss_knot_list (mp, mp_pen_p ((mp_stroked_node) pp)); 31072 mp_pen_p ((mp_stroked_node) pp) = cur_exp_knot (); 31073 break; 31074 default: 31075 assert (0); 31076 break; 31077 } 31078 mp->cur_exp.type = mp_vacuous; 31079 } 31080 } else if (t == with_mp_pre_script) { 31081 if (cur_exp_str ()->len) { 31082 if (ap == MP_VOID) 31083 ap = p; 31084 while ((ap != NULL) && (!has_color (ap))) 31085 ap = mp_link (ap); 31086 if (ap != NULL) { 31087 if (mp_pre_script (ap) != NULL) { /* build a new,combined string */ 31088 unsigned old_setting; /* saved |selector| setting */ 31089 mp_string s; /* for string cleanup after combining */ 31090 s = mp_pre_script (ap); 31091 old_setting = mp->selector; 31092 mp->selector = new_string; 31093 str_room (mp_pre_script (ap)->len + cur_exp_str ()->len + 2); 31094 mp_print_str (mp, cur_exp_str ()); 31095 append_char (13); /* a forced \ps\ newline */ 31096 mp_print_str (mp, mp_pre_script (ap)); 31097 mp_pre_script (ap) = mp_make_string (mp); 31098 delete_str_ref (s); 31099 mp->selector = old_setting; 31100 } else { 31101 mp_pre_script (ap) = cur_exp_str (); 31102 } 31103 add_str_ref (mp_pre_script (ap)); 31104 mp->cur_exp.type = mp_vacuous; 31105 } 31106 } 31107 } else if (t == with_mp_post_script) { 31108 if (cur_exp_str ()->len) { 31109 mp_node k = NULL; /* for finding the near-last item in a list */ 31110 if (bp == MP_VOID) 31111 k = p; 31112 bp = k; 31113 while (k && mp_link (k) != NULL) { /* clang: dereference null pointer 'k' */ 31114 k = mp_link (k); 31115 if (has_color (k)) 31116 bp = k; 31117 } 31118 if (bp != NULL) { 31119 if (mp_post_script (bp) != NULL) { 31120 unsigned old_setting; /* saved |selector| setting */ 31121 mp_string s; /* for string cleanup after combining */ 31122 s = mp_post_script (bp); 31123 old_setting = mp->selector; 31124 mp->selector = new_string; 31125 str_room (mp_post_script (bp)->len + cur_exp_str ()->len + 2); 31126 mp_print_str (mp, mp_post_script (bp)); 31127 append_char (13); /* a forced \ps\ newline */ 31128 mp_print_str (mp, cur_exp_str ()); 31129 mp_post_script (bp) = mp_make_string (mp); 31130 delete_str_ref (s); 31131 mp->selector = old_setting; 31132 } else { 31133 mp_post_script (bp) = cur_exp_str (); 31134 } 31135 add_str_ref (mp_post_script (bp)); 31136 mp->cur_exp.type = mp_vacuous; 31137 } 31138 } 31139 } else { 31140 if (dp == MP_VOID) { 31141 /* Make |dp| a stroked node in list~|p| */ 31142 dp = p; 31143 while (dp != NULL) { 31144 if (mp_type (dp) == mp_stroked_node_type) 31145 break; 31146 dp = mp_link (dp); 31147 } 31148 } 31149 if (dp != NULL) { 31150 if (mp_dash_p (dp) != NULL) 31151 delete_edge_ref (mp_dash_p (dp)); 31152 mp_dash_p (dp) = (mp_node)mp_make_dashes (mp, (mp_edge_header_node)cur_exp_node ()); 31153 set_number_to_unity(((mp_stroked_node)dp)->dash_scale); 31154 mp->cur_exp.type = mp_vacuous; 31155 } 31156 } 31157 } 31158 /* Copy the information from objects |cp|, |pp|, and |dp| into the rest 31159 of the list */ 31160 if (cp > MP_VOID) { 31161 /* Copy |cp|'s color into the colored objects linked to~|cp| */ 31162 q = mp_link (cp); 31163 while (q != NULL) { 31164 if (has_color (q)) { 31165 mp_stroked_node q0 = (mp_stroked_node)q; 31166 mp_stroked_node cp0 = (mp_stroked_node)cp; 31167 number_clone(q0->red, cp0->red); 31168 number_clone(q0->green, cp0->green); 31169 number_clone(q0->blue, cp0->blue); 31170 number_clone(q0->black, cp0->black); 31171 mp_color_model (q) = mp_color_model (cp); 31172 } 31173 q = mp_link (q); 31174 } 31175 } 31176 if (pp > MP_VOID) { 31177 /* Copy |mp_pen_p(pp)| into stroked and filled nodes linked to |pp| */ 31178 q = mp_link (pp); 31179 while (q != NULL) { 31180 if (has_pen (q)) { 31181 switch (mp_type (q)) { 31182 case mp_fill_node_type: 31183 if (mp_pen_p ((mp_fill_node) q) != NULL) 31184 mp_toss_knot_list (mp, mp_pen_p ((mp_fill_node) q)); 31185 mp_pen_p ((mp_fill_node) q) = copy_pen (mp_pen_p ((mp_fill_node) pp)); 31186 break; 31187 case mp_stroked_node_type: 31188 if (mp_pen_p ((mp_stroked_node) q) != NULL) 31189 mp_toss_knot_list (mp, mp_pen_p ((mp_stroked_node) q)); 31190 mp_pen_p ((mp_stroked_node) q) = 31191 copy_pen (mp_pen_p ((mp_stroked_node) pp)); 31192 break; 31193 default: 31194 assert (0); 31195 break; 31196 } 31197 } 31198 q = mp_link (q); 31199 } 31200 } 31201 if (dp > MP_VOID) { 31202 /* Make stroked nodes linked to |dp| refer to |mp_dash_p(dp)| */ 31203 q = mp_link (dp); 31204 while (q != NULL) { 31205 if (mp_type (q) == mp_stroked_node_type) { 31206 if (mp_dash_p (q) != NULL) 31207 delete_edge_ref (mp_dash_p (q)); 31208 mp_dash_p (q) = mp_dash_p (dp); 31209 set_number_to_unity(((mp_stroked_node)q)->dash_scale); 31210 if (mp_dash_p (q) != NULL) 31211 add_edge_ref (mp_dash_p (q)); 31212 } 31213 q = mp_link (q); 31214 } 31215 } 31216} 31217 31218 31219@ One of the things we need to do when we've parsed an \&{addto} or 31220similar command is find the header of a supposed \&{picture} variable, given 31221a token list for that variable. Since the edge structure is about to be 31222updated, we use |private_edges| to make sure that this is possible. 31223 31224@<Declare action procedures for use by |do_statement|@>= 31225static mp_edge_header_node mp_find_edges_var (MP mp, mp_node t); 31226 31227@ @c 31228mp_edge_header_node mp_find_edges_var (MP mp, mp_node t) { 31229 mp_node p; 31230 mp_edge_header_node cur_edges; /* the return value */ 31231 p = mp_find_variable (mp, t); 31232 cur_edges = NULL; 31233 if (p == NULL) { 31234 const char *hlp[] = { 31235 "It seems you did a nasty thing---probably by accident,", 31236 "but nevertheless you nearly hornswoggled me...", 31237 "While I was evaluating the right-hand side of this", 31238 "command, something happened, and the left-hand side", 31239 "is no longer a variable! So I won't change anything.", 31240 NULL }; 31241 char *msg = mp_obliterated (mp, t); 31242 mp_back_error (mp, msg, hlp, true); 31243 free(msg); 31244 mp_get_x_next (mp); 31245 } else if (mp_type (p) != mp_picture_type) { 31246 char msg[256]; 31247 mp_string sname; 31248 int old_setting = mp->selector; 31249 const char *hlp[] = { 31250 "I was looking for a \"known\" picture variable.", 31251 "So I'll not change anything just now.", 31252 NULL }; 31253 mp->selector = new_string; 31254 mp_show_token_list (mp, t, NULL, 1000, 0); 31255 sname = mp_make_string(mp); 31256 mp->selector = old_setting; 31257 mp_snprintf (msg, 256, "Variable %s is the wrong type(%s)", 31258 mp_str(mp, sname), mp_type_string(mp_type (p))); 31259@.Variable x is the wrong type@>; 31260 delete_str_ref(sname); 31261 mp_back_error (mp, msg, hlp, true); 31262 mp_get_x_next (mp); 31263 } else { 31264 set_value_node (p, (mp_node)mp_private_edges (mp, (mp_edge_header_node)value_node (p))); 31265 cur_edges = (mp_edge_header_node)value_node (p); 31266 } 31267 mp_flush_node_list (mp, t); 31268 return cur_edges; 31269} 31270 31271 31272@ @<Put each...@>= 31273mp_primitive (mp, "clip", mp_bounds_command, mp_start_clip_node_type); 31274@:clip_}{\&{clip} primitive@>; 31275mp_primitive (mp, "setbounds", mp_bounds_command, mp_start_bounds_node_type); 31276@:set_bounds_}{\&{setbounds} primitive@> 31277 31278 31279@ @<Cases of |print_cmd...@>= 31280case mp_bounds_command: 31281if (m == mp_start_clip_node_type) 31282 mp_print (mp, "clip"); 31283else 31284 mp_print (mp, "setbounds"); 31285break; 31286 31287@ The following function parses the beginning of an \&{addto} or \&{clip} 31288command: it expects a variable name followed by a token with |cur_cmd=sep| 31289and then an expression. The function returns the token list for the variable 31290and stores the command modifier for the separator token in the global variable 31291|last_add_type|. We must be careful because this variable might get overwritten 31292any time we call |get_x_next|. 31293 31294@<Glob...@>= 31295quarterword last_add_type; 31296 /* command modifier that identifies the last \&{addto} command */ 31297 31298@ @<Declare action procedures for use by |do_statement|@>= 31299static mp_node mp_start_draw_cmd (MP mp, quarterword sep); 31300 31301@ @c 31302mp_node mp_start_draw_cmd (MP mp, quarterword sep) { 31303 mp_node lhv; /* variable to add to left */ 31304 quarterword add_type = 0; /* value to be returned in |last_add_type| */ 31305 lhv = NULL; 31306 mp_get_x_next (mp); 31307 mp->var_flag = sep; 31308 mp_scan_primary (mp); 31309 if (mp->cur_exp.type != mp_token_list) { 31310 /* Abandon edges command because there's no variable */ 31311 mp_value new_expr; 31312 const char *hlp[] = { 31313 "At this point I needed to see the name of a picture variable.", 31314 "(Or perhaps you have indeed presented me with one; I might", 31315 "have missed it, if it wasn't followed by the proper token.)", 31316 "So I'll not change anything just now.", 31317 NULL }; 31318 memset(&new_expr,0,sizeof(mp_value)); 31319 new_number(new_expr.data.n); 31320 mp_disp_err(mp, NULL); 31321 set_number_to_zero (new_expr.data.n); 31322 mp_back_error (mp, "Not a suitable variable", hlp, true); 31323 mp_get_x_next (mp); 31324 mp_flush_cur_exp (mp, new_expr); 31325 } else { 31326 lhv = cur_exp_node (); 31327 add_type = (quarterword) cur_mod(); 31328 mp->cur_exp.type = mp_vacuous; 31329 mp_get_x_next (mp); 31330 mp_scan_expression (mp); 31331 } 31332 mp->last_add_type = add_type; 31333 return lhv; 31334} 31335 31336@ Here is an example of how to use |start_draw_cmd|. 31337 31338@<Declare action procedures for use by |do_statement|@>= 31339static void mp_do_bounds (MP mp); 31340 31341@ @c 31342void mp_do_bounds (MP mp) { 31343 mp_node lhv; /* variable on left, the corresponding edge structure */ 31344 mp_edge_header_node lhe; 31345 mp_node p; /* for list manipulation */ 31346 integer m; /* initial value of |cur_mod| */ 31347 m = cur_mod(); 31348 lhv = mp_start_draw_cmd (mp, mp_to_token); 31349 if (lhv != NULL) { 31350 mp_value new_expr; 31351 memset(&new_expr,0,sizeof(mp_value)); 31352 lhe = mp_find_edges_var (mp, lhv); 31353 if (lhe == NULL) { 31354 new_number(new_expr.data.n); 31355 set_number_to_zero (new_expr.data.n); 31356 mp_flush_cur_exp (mp, new_expr); 31357 } else if (mp->cur_exp.type != mp_path_type) { 31358 const char *hlp[] ={ 31359 "This expression should have specified a known path.", 31360 "So I'll not change anything just now.", 31361 NULL }; 31362 mp_disp_err(mp, NULL); 31363 new_number(new_expr.data.n); 31364 set_number_to_zero (new_expr.data.n); 31365 mp_back_error (mp, "Improper `clip'", hlp, true); 31366 mp_get_x_next (mp); 31367 mp_flush_cur_exp (mp, new_expr); 31368 } else if (mp_left_type (cur_exp_knot ()) == mp_endpoint) { 31369 /* Complain about a non-cycle */ 31370 const char *hlp[] = { 31371 "That contour should have ended with `..cycle' or `&cycle'.", 31372 "So I'll not change anything just now.", 31373 NULL }; 31374 mp_back_error (mp, "Not a cycle" , hlp, true); 31375 mp_get_x_next (mp); 31376 } else { 31377 /* Make |cur_exp| into a \&{setbounds} or clipping path and add it to |lhe| */ 31378 p = mp_new_bounds_node (mp, cur_exp_knot (), (quarterword) m); 31379 mp_link (p) = mp_link (edge_list (lhe)); 31380 mp_link (edge_list (lhe)) = p; 31381 if (obj_tail (lhe) == edge_list (lhe)) 31382 obj_tail (lhe) = p; 31383 if (m == mp_start_clip_node_type) { 31384 p = mp_new_bounds_node (mp, NULL, mp_stop_clip_node_type); 31385 } else if (m == mp_start_bounds_node_type) { 31386 p = mp_new_bounds_node (mp, NULL, mp_stop_bounds_node_type); 31387 } 31388 mp_link (obj_tail (lhe)) = p; 31389 obj_tail (lhe) = p; 31390 mp_init_bbox (mp, lhe); 31391 } 31392 } 31393} 31394 31395 31396@ The |do_add_to| procedure is a little like |do_clip| but there are a lot more 31397cases to deal with. 31398 31399@<Declare action procedures for use by |do_statement|@>= 31400static void mp_do_add_to (MP mp); 31401 31402@ @c 31403void mp_do_add_to (MP mp) { 31404 mp_node lhv; 31405 mp_edge_header_node lhe; /* variable on left, the corresponding edge structure */ 31406 mp_node p; /* the graphical object or list for |scan_with_list| to update */ 31407 mp_edge_header_node e; /* an edge structure to be merged */ 31408 quarterword add_type; /* |also_code|, |contour_code|, or |double_path_code| */ 31409 lhv = mp_start_draw_cmd (mp, mp_thing_to_add); 31410 add_type = mp->last_add_type; 31411 if (lhv != NULL) { 31412 if (add_type == also_code) { 31413 /* Make sure the current expression is a suitable picture and set |e| and |p| 31414 appropriately */ 31415 /* Setting |p:=NULL| causes the $\langle$with list$\rangle$ to be ignored; 31416 setting |e:=NULL| prevents anything from being added to |lhe|. */ 31417 p = NULL; 31418 e = NULL; 31419 if (mp->cur_exp.type != mp_picture_type) { 31420 mp_value new_expr; 31421 const char *hlp[]= { 31422 "This expression should have specified a known picture.", 31423 "So I'll not change anything just now.", 31424 NULL }; 31425 memset(&new_expr,0,sizeof(mp_value)); 31426 new_number(new_expr.data.n); 31427 mp_disp_err(mp, NULL); 31428 set_number_to_zero (new_expr.data.n); 31429 mp_back_error (mp, "Improper `addto'", hlp, true); 31430 mp_get_x_next (mp); 31431 mp_flush_cur_exp (mp, new_expr); 31432 } else { 31433 e = mp_private_edges (mp, (mp_edge_header_node)cur_exp_node ()); 31434 mp->cur_exp.type = mp_vacuous; 31435 p = mp_link (edge_list (e)); 31436 } 31437 31438 } else { 31439 /* Create a graphical object |p| based on |add_type| and the current 31440 expression */ 31441 /* In this case |add_type<>also_code| so setting |p:=NULL| suppresses future 31442 attempts to add to the edge structure. */ 31443 e = NULL; 31444 p = NULL; 31445 if (mp->cur_exp.type == mp_pair_type) 31446 mp_pair_to_path (mp); 31447 if (mp->cur_exp.type != mp_path_type) { 31448 mp_value new_expr; 31449 const char *hlp[] = { 31450 "This expression should have specified a known path.", 31451 "So I'll not change anything just now.", 31452 NULL }; 31453 memset(&new_expr,0,sizeof(mp_value)); 31454 new_number(new_expr.data.n); 31455 mp_disp_err(mp, NULL); 31456 set_number_to_zero (new_expr.data.n); 31457 mp_back_error (mp, "Improper `addto'", hlp, true); 31458 mp_get_x_next (mp); 31459 mp_flush_cur_exp (mp, new_expr); 31460 } else if (add_type == contour_code) { 31461 if (mp_left_type (cur_exp_knot ()) == mp_endpoint) { 31462 /* Complain about a non-cycle */ 31463 const char *hlp[] = { 31464 "That contour should have ended with `..cycle' or `&cycle'.", 31465 "So I'll not change anything just now.", 31466 NULL }; 31467 mp_back_error (mp, "Not a cycle" , hlp, true); 31468 mp_get_x_next (mp); 31469 31470 } else { 31471 p = mp_new_fill_node (mp, cur_exp_knot ()); 31472 mp->cur_exp.type = mp_vacuous; 31473 } 31474 } else { 31475 p = mp_new_stroked_node (mp, cur_exp_knot ()); 31476 mp->cur_exp.type = mp_vacuous; 31477 } 31478 31479 } 31480 mp_scan_with_list (mp, p); 31481 /* Use |p|, |e|, and |add_type| to augment |lhv| as requested */ 31482 lhe = mp_find_edges_var (mp, lhv); 31483 if (lhe == NULL) { 31484 if ((e == NULL) && (p != NULL)) 31485 e = mp_toss_gr_object (mp, p); 31486 if (e != NULL) 31487 delete_edge_ref (e); 31488 } else if (add_type == also_code) { 31489 if (e != NULL) { 31490 /* Merge |e| into |lhe| and delete |e| */ 31491 if (mp_link (edge_list (e)) != NULL) { 31492 mp_link (obj_tail (lhe)) = mp_link (edge_list (e)); 31493 obj_tail (lhe) = obj_tail (e); 31494 obj_tail (e) = edge_list (e); 31495 mp_link (edge_list (e)) = NULL; 31496 mp_flush_dash_list (mp, lhe); 31497 } 31498 mp_toss_edges (mp, e); 31499 } 31500 } else if (p != NULL) { 31501 mp_link (obj_tail (lhe)) = p; 31502 obj_tail (lhe) = p; 31503 if (add_type == double_path_code) { 31504 if (mp_pen_p ((mp_stroked_node) p) == NULL) { 31505 mp_pen_p ((mp_stroked_node) p) = mp_get_pen_circle (mp, zero_t); 31506 } 31507 } 31508 } 31509 } 31510} 31511 31512@ @<Declare action procedures for use by |do_statement|@>= 31513@<Declare the \ps\ output procedures@>; 31514static void mp_do_ship_out (MP mp); 31515 31516@ @c 31517void mp_do_ship_out (MP mp) { 31518 integer c; /* the character code */ 31519 mp_value new_expr; 31520 memset(&new_expr,0,sizeof(mp_value)); 31521 new_number(new_expr.data.n); 31522 mp_get_x_next (mp); 31523 mp_scan_expression (mp); 31524 if (mp->cur_exp.type != mp_picture_type) { 31525 @<Complain that it's not a known picture@>; 31526 } else { 31527 c = round_unscaled (internal_value (mp_char_code)) % 256; 31528 if (c < 0) 31529 c = c + 256; 31530 @<Store the width information for character code~|c|@>; 31531 mp_ship_out (mp, cur_exp_node ()); 31532 set_number_to_zero (new_expr.data.n); 31533 mp_flush_cur_exp (mp, new_expr); 31534 } 31535} 31536 31537 31538@ @<Complain that it's not a known picture@>= 31539{ 31540 const char *hlp[] = { "I can only output known pictures.", NULL }; 31541 mp_disp_err(mp, NULL); 31542 set_number_to_zero (new_expr.data.n); 31543 mp_back_error (mp, "Not a known picture", hlp, true); 31544 mp_get_x_next (mp); 31545 mp_flush_cur_exp (mp, new_expr); 31546} 31547 31548 31549@ The \&{everyjob} command simply assigns a nonzero value to the global variable 31550|start_sym|. 31551 31552 31553@ @<Glob...@>= 31554mp_sym start_sym; /* a symbolic token to insert at beginning of job */ 31555 31556@ @<Set init...@>= 31557mp->start_sym = NULL; 31558 31559@ Finally, we have only the ``message'' commands remaining. 31560 31561@d message_code 0 31562@d err_message_code 1 31563@d err_help_code 2 31564@d filename_template_code 3 31565@d print_with_leading_zeroes(A,B) do { 31566 size_t g = mp->cur_length; 31567 size_t f = (size_t)(B); 31568 mp_print_int(mp, (A)); 31569 g = mp->cur_length - g; 31570 if ( f>g ) { 31571 mp->cur_length = mp->cur_length - g; 31572 while ( f>g ) { 31573 mp_print_char(mp, xord('0')); 31574 decr(f); 31575 }; 31576 mp_print_int(mp, (A)); 31577 }; 31578 f = 0; 31579 } while (0) 31580 31581@<Put each...@>= 31582mp_primitive (mp, "message", mp_message_command, message_code); 31583@:message_}{\&{message} primitive@>; 31584mp_primitive (mp, "errmessage", mp_message_command, err_message_code); 31585@:err_message_}{\&{errmessage} primitive@>; 31586mp_primitive (mp, "errhelp", mp_message_command, err_help_code); 31587@:err_help_}{\&{errhelp} primitive@>; 31588mp_primitive (mp, "filenametemplate", mp_message_command, filename_template_code); 31589@:filename_template_}{\&{filenametemplate} primitive@> 31590 31591 31592@ @<Cases of |print_cmd...@>= 31593case mp_message_command: 31594if (m < err_message_code) 31595 mp_print (mp, "message"); 31596else if (m == err_message_code) 31597 mp_print (mp, "errmessage"); 31598else if (m == filename_template_code) 31599 mp_print (mp, "filenametemplate"); 31600else 31601 mp_print (mp, "errhelp"); 31602break; 31603 31604@ @<Declare action procedures for use by |do_statement|@>= 31605@<Declare a procedure called |no_string_err|@>; 31606static void mp_do_message (MP mp); 31607 31608@ 31609@c 31610void mp_do_message (MP mp) { 31611 int m; /* the type of message */ 31612 mp_value new_expr; 31613 m = cur_mod(); 31614 memset(&new_expr,0,sizeof(mp_value)); 31615 new_number(new_expr.data.n); 31616 mp_get_x_next (mp); 31617 mp_scan_expression (mp); 31618 if (mp->cur_exp.type != mp_string_type) 31619 mp_no_string_err (mp, "A message should be a known string expression."); 31620 else { 31621 switch (m) { 31622 case message_code: 31623 mp_print_nl (mp, ""); 31624 mp_print_str (mp, cur_exp_str ()); 31625 break; 31626 case err_message_code: 31627 @<Print string |cur_exp| as an error message@>; 31628 break; 31629 case err_help_code: 31630 @<Save string |cur_exp| as the |err_help|@>; 31631 break; 31632 case filename_template_code: 31633 @<Save the filename template@>; 31634 break; 31635 } /* there are no other cases */ 31636 } 31637 set_number_to_zero (new_expr.data.n); 31638 mp_flush_cur_exp (mp, new_expr); 31639} 31640 31641 31642@ @<Save the filename template@>= 31643{ 31644 delete_str_ref (internal_string (mp_output_template)); 31645 if (cur_exp_str ()->len == 0) { 31646 set_internal_string (mp_output_template, mp_rts (mp, "%j.%c")); 31647 } else { 31648 set_internal_string (mp_output_template, cur_exp_str ()); 31649 add_str_ref (internal_string (mp_output_template)); 31650 } 31651} 31652 31653 31654@ @<Declare a procedure called |no_string_err|@>= 31655static void mp_no_string_err (MP mp, const char *s) { 31656 const char *hlp[] = {s, NULL}; 31657 mp_disp_err(mp, NULL); 31658 mp_back_error (mp, "Not a string", hlp, true); 31659@.Not a string@>; 31660 mp_get_x_next (mp); 31661} 31662 31663 31664@ The global variable |err_help| is zero when the user has most recently 31665given an empty help string, or if none has ever been given. 31666 31667@<Save string |cur_exp| as the |err_help|@>= 31668{ 31669 if (mp->err_help != NULL) 31670 delete_str_ref (mp->err_help); 31671 if (cur_exp_str ()->len == 0) 31672 mp->err_help = NULL; 31673 else { 31674 mp->err_help = cur_exp_str (); 31675 add_str_ref (mp->err_help); 31676 } 31677} 31678 31679 31680@ If \&{errmessage} occurs often in |mp_scroll_mode|, without user-defined 31681\&{errhelp}, we don't want to give a long help message each time. So we 31682give a verbose explanation only once. 31683 31684@<Glob...@>= 31685boolean long_help_seen; /* has the long \.{\\errmessage} help been used? */ 31686 31687@ @<Set init...@>= 31688mp->long_help_seen = false; 31689 31690@ @<Print string |cur_exp| as an error message@>= 31691{ 31692 char msg[256]; 31693 mp_snprintf(msg, 256, "%s", mp_str(mp, cur_exp_str ())); 31694 if (mp->err_help != NULL) { 31695 mp->use_err_help = true; 31696 mp_back_error (mp, msg, NULL, true); 31697 } else if (mp->long_help_seen) { 31698 const char *hlp[] = { "(That was another `errmessage'.)", NULL }; 31699 mp_back_error (mp, msg, hlp, true); 31700 } else { 31701 const char *hlp[] = { 31702 "This error message was generated by an `errmessage'", 31703 "command, so I can\'t give any explicit help.", 31704 "Pretend that you're Miss Marple: Examine all clues,", 31705 "and deduce the truth by inspired guesses.", 31706 NULL }; 31707@^Marple, Jane@> 31708 if (mp->interaction < mp_error_stop_mode) 31709 mp->long_help_seen = true; 31710 mp_back_error (mp, msg, hlp, true); 31711 } 31712 mp_get_x_next (mp); 31713 mp->use_err_help = false; 31714} 31715 31716 31717@ @<Declare action procedures for use by |do_statement|@>= 31718static void mp_do_write (MP mp); 31719 31720@ @c 31721void mp_do_write (MP mp) { 31722 mp_string t; /* the line of text to be written */ 31723 write_index n, n0; /* for searching |wr_fname| and |wr_file| arrays */ 31724 unsigned old_setting; /* for saving |selector| during output */ 31725 mp_value new_expr; 31726 memset(&new_expr,0,sizeof(mp_value)); 31727 new_number(new_expr.data.n); 31728 mp_get_x_next (mp); 31729 mp_scan_expression (mp); 31730 if (mp->cur_exp.type != mp_string_type) { 31731 mp_no_string_err (mp, 31732 "The text to be written should be a known string expression"); 31733 } else if (cur_cmd() != mp_to_token) { 31734 const char *hlp[] = { "A write command should end with `to <filename>'", NULL }; 31735 mp_back_error (mp, "Missing `to' clause", hlp, true); 31736 mp_get_x_next (mp); 31737 } else { 31738 t = cur_exp_str (); 31739 mp->cur_exp.type = mp_vacuous; 31740 mp_get_x_next (mp); 31741 mp_scan_expression (mp); 31742 if (mp->cur_exp.type != mp_string_type) 31743 mp_no_string_err (mp, 31744 "I can\'t write to that file name. It isn't a known string"); 31745 else { 31746 @<Write |t| to the file named by |cur_exp|@>; 31747 } 31748 /* |delete_str_ref(t);| *//* todo: is this right? */ 31749 } 31750 set_number_to_zero (new_expr.data.n); 31751 mp_flush_cur_exp (mp, new_expr); 31752} 31753 31754 31755@ @<Write |t| to the file named by |cur_exp|@>= 31756{ 31757 @<Find |n| where |wr_fname[n]=cur_exp| and call |open_write_file| if 31758 |cur_exp| must be inserted@>; 31759 if (mp_str_vs_str (mp, t, mp->eof_line) == 0) { 31760 @<Record the end of file on |wr_file[n]|@>; 31761 } else { 31762 old_setting = mp->selector; 31763 mp->selector = n + write_file; 31764 mp_print_str (mp, t); 31765 mp_print_ln (mp); 31766 mp->selector = old_setting; 31767 } 31768} 31769 31770 31771@ @<Find |n| where |wr_fname[n]=cur_exp| and call |open_write_file| if...@>= 31772{ 31773 char *fn = mp_str (mp, cur_exp_str ()); 31774 n = mp->write_files; 31775 n0 = mp->write_files; 31776 while (mp_xstrcmp (fn, mp->wr_fname[n]) != 0) { 31777 if (n == 0) { /* bottom reached */ 31778 if (n0 == mp->write_files) { 31779 if (mp->write_files < mp->max_write_files) { 31780 incr (mp->write_files); 31781 } else { 31782 void **wr_file; 31783 char **wr_fname; 31784 write_index l, k; 31785 l = mp->max_write_files + (mp->max_write_files / 4); 31786 wr_file = xmalloc ((l + 1), sizeof (void *)); 31787 wr_fname = xmalloc ((l + 1), sizeof (char *)); 31788 for (k = 0; k <= l; k++) { 31789 if (k <= mp->max_write_files) { 31790 wr_file[k] = mp->wr_file[k]; 31791 wr_fname[k] = mp->wr_fname[k]; 31792 } else { 31793 wr_file[k] = 0; 31794 wr_fname[k] = NULL; 31795 } 31796 } 31797 xfree (mp->wr_file); 31798 xfree (mp->wr_fname); 31799 mp->max_write_files = l; 31800 mp->wr_file = wr_file; 31801 mp->wr_fname = wr_fname; 31802 } 31803 } 31804 n = n0; 31805 mp_open_write_file (mp, fn, n); 31806 } else { 31807 decr (n); 31808 if (mp->wr_fname[n] == NULL) 31809 n0 = n; 31810 } 31811 } 31812} 31813 31814 31815@ @<Record the end of file on |wr_file[n]|@>= 31816{ 31817 (mp->close_file) (mp, mp->wr_file[n]); 31818 xfree (mp->wr_fname[n]); 31819 if (n == mp->write_files - 1) 31820 mp->write_files = n; 31821} 31822 31823 31824@* Writing font metric data. 31825\TeX\ gets its knowledge about fonts from font metric files, also called 31826\.{TFM} files; the `\.T' in `\.{TFM}' stands for \TeX, 31827but other programs know about them too. One of \MP's duties is to 31828write \.{TFM} files so that the user's fonts can readily be 31829applied to typesetting. 31830@:TFM files}{\.{TFM} files@> 31831@^font metric files@> 31832 31833The information in a \.{TFM} file appears in a sequence of 8-bit bytes. 31834Since the number of bytes is always a multiple of~4, we could 31835also regard the file as a sequence of 32-bit words, but \MP\ uses the 31836byte interpretation. The format of \.{TFM} files was designed by 31837Lyle Ramshaw in 1980. The intent is to convey a lot of different kinds 31838@^Ramshaw, Lyle Harold@> 31839of information in a compact but useful form. 31840 31841@<Glob...@>= 31842void *tfm_file; /* the font metric output goes here */ 31843char *metric_file_name; /* full name of the font metric file */ 31844 31845@ The first 24 bytes (6 words) of a \.{TFM} file contain twelve 16-bit 31846integers that give the lengths of the various subsequent portions 31847of the file. These twelve integers are, in order: 31848$$\vbox{\halign{\hfil#&$\null=\null$#\hfil\cr 31849|lf|&length of the entire file, in words;\cr 31850|lh|&length of the header data, in words;\cr 31851|bc|&smallest character code in the font;\cr 31852|ec|&largest character code in the font;\cr 31853|nw|&number of words in the width table;\cr 31854|nh|&number of words in the height table;\cr 31855|nd|&number of words in the depth table;\cr 31856|ni|&number of words in the italic correction table;\cr 31857|nl|&number of words in the lig/kern table;\cr 31858|nk|&number of words in the kern table;\cr 31859|ne|&number of words in the extensible character table;\cr 31860|np|&number of font parameter words.\cr}}$$ 31861They are all nonnegative and less than $2^{15}$. We must have |bc-1<=ec<=255|, 31862|ne<=256|, and 31863$$\hbox{|lf=6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np|.}$$ 31864Note that a font may contain as many as 256 characters (if |bc=0| and |ec=255|), 31865and as few as 0 characters (if |bc=ec+1|). 31866 31867Incidentally, when two or more 8-bit bytes are combined to form an integer of 3186816 or more bits, the most significant bytes appear first in the file. 31869This is called BigEndian order. 31870@^BigEndian order@> 31871 31872@ The rest of the \.{TFM} file may be regarded as a sequence of ten data 31873arrays. 31874 31875The most important data type used here is a |fix_word|, which is 31876a 32-bit representation of a binary fraction. A |fix_word| is a signed 31877quantity, with the two's complement of the entire word used to represent 31878negation. Of the 32 bits in a |fix_word|, exactly 12 are to the left of the 31879binary point; thus, the largest |fix_word| value is $2048-2^{-20}$, and 31880the smallest is $-2048$. We will see below, however, that all but two of 31881the |fix_word| values must lie between $-16$ and $+16$. 31882 31883@ The first data array is a block of header information, which contains 31884general facts about the font. The header must contain at least two words, 31885|header[0]| and |header[1]|, whose meaning is explained below. Additional 31886header information of use to other software routines might also be 31887included, and \MP\ will generate it if the \.{headerbyte} command occurs. 31888For example, 16 more words of header information are in use at the Xerox 31889Palo Alto Research Center; the first ten specify the character coding 31890scheme used (e.g., `\.{XEROX TEXT}' or `\.{TEX MATHSY}'), the next five 31891give the font family name (e.g., `\.{HELVETICA}' or `\.{CMSY}'), and the 31892last gives the ``face byte.'' 31893 31894\yskip\hang|header[0]| is a 32-bit check sum that \MP\ will copy into 31895the \.{GF} output file. This helps ensure consistency between files, 31896since \TeX\ records the check sums from the \.{TFM}'s it reads, and these 31897should match the check sums on actual fonts that are used. The actual 31898relation between this check sum and the rest of the \.{TFM} file is not 31899important; the check sum is simply an identification number with the 31900property that incompatible fonts almost always have distinct check sums. 31901@^check sum@> 31902 31903\yskip\hang|header[1]| is a |fix_word| containing the design size of the 31904font, in units of \TeX\ points. This number must be at least 1.0; it is 31905fairly arbitrary, but usually the design size is 10.0 for a ``10 point'' 31906font, i.e., a font that was designed to look best at a 10-point size, 31907whatever that really means. When a \TeX\ user asks for a font `\.{at} 31908$\delta$ \.{pt}', the effect is to override the design size and replace it 31909by $\delta$, and to multiply the $x$ and~$y$ coordinates of the points in 31910the font image by a factor of $\delta$ divided by the design size. {\sl 31911All other dimensions in the\/ \.{TFM} file are |fix_word|\kern-1pt\ 31912numbers in design-size units.} Thus, for example, the value of |param[6]|, 31913which defines the \.{em} unit, is often the |fix_word| value $2^{20}=1.0$, 31914since many fonts have a design size equal to one em. The other dimensions 31915must be less than 16 design-size units in absolute value; thus, 31916|header[1]| and |param[1]| are the only |fix_word| entries in the whole 31917\.{TFM} file whose first byte might be something besides 0 or 255. 31918@^design size@> 31919 31920@ Next comes the |char_info| array, which contains one |char_info_word| 31921per character. Each word in this part of the file contains six fields 31922packed into four bytes as follows. 31923 31924\yskip\hang first byte: |width_index| (8 bits)\par 31925\hang second byte: |height_index| (4 bits) times 16, plus |depth_index| 31926 (4~bits)\par 31927\hang third byte: |italic_index| (6 bits) times 4, plus |tag| 31928 (2~bits)\par 31929\hang fourth byte: |remainder| (8 bits)\par 31930\yskip\noindent 31931The actual width of a character is \\{width}|[width_index]|, in design-size 31932units; this is a device for compressing information, since many characters 31933have the same width. Since it is quite common for many characters 31934to have the same height, depth, or italic correction, the \.{TFM} format 31935imposes a limit of 16 different heights, 16 different depths, and 3193664 different italic corrections. 31937 31938Incidentally, the relation $\\{width}[0]=\\{height}[0]=\\{depth}[0]= 31939\\{italic}[0]=0$ should always hold, so that an index of zero implies a 31940value of zero. The |width_index| should never be zero unless the 31941character does not exist in the font, since a character is valid if and 31942only if it lies between |bc| and |ec| and has a nonzero |width_index|. 31943 31944@ The |tag| field in a |char_info_word| has four values that explain how to 31945interpret the |remainder| field. 31946 31947\yskip\hang|tag=0| (|no_tag|) means that |remainder| is unused.\par 31948\hang|tag=1| (|lig_tag|) means that this character has a ligature/kerning 31949program starting at location |remainder| in the |lig_kern| array.\par 31950\hang|tag=2| (|list_tag|) means that this character is part of a chain of 31951characters of ascending sizes, and not the largest in the chain. The 31952|remainder| field gives the character code of the next larger character.\par 31953\hang|tag=3| (|ext_tag|) means that this character code represents an 31954extensible character, i.e., a character that is built up of smaller pieces 31955so that it can be made arbitrarily large. The pieces are specified in 31956|exten[remainder]|.\par 31957\yskip\noindent 31958Characters with |tag=2| and |tag=3| are treated as characters with |tag=0| 31959unless they are used in special circumstances in math formulas. For example, 31960\TeX's \.{\\sum} operation looks for a |list_tag|, and the \.{\\left} 31961operation looks for both |list_tag| and |ext_tag|. 31962 31963@d no_tag 0 /* vanilla character */ 31964@d lig_tag 1 /* character has a ligature/kerning program */ 31965@d list_tag 2 /* character has a successor in a charlist */ 31966@d ext_tag 3 /* character is extensible */ 31967 31968@ The |lig_kern| array contains instructions in a simple programming language 31969that explains what to do for special letter pairs. Each word in this array is a 31970|lig_kern_command| of four bytes. 31971 31972\yskip\hang first byte: |skip_byte|, indicates that this is the final program 31973 step if the byte is 128 or more, otherwise the next step is obtained by 31974 skipping this number of intervening steps.\par 31975\hang second byte: |next_char|, ``if |next_char| follows the current character, 31976 then perform the operation and stop, otherwise continue.''\par 31977\hang third byte: |op_byte|, indicates a ligature step if less than~128, 31978 a kern step otherwise.\par 31979\hang fourth byte: |remainder|.\par 31980\yskip\noindent 31981In a kern step, an 31982additional space equal to |kern[256*(op_byte-128)+remainder]| is inserted 31983between the current character and |next_char|. This amount is 31984often negative, so that the characters are brought closer together 31985by kerning; but it might be positive. 31986 31987There are eight kinds of ligature steps, having |op_byte| codes $4a+2b+c$ where 31988$0\le a\le b+c$ and $0\le b,c\le1$. The character whose code is 31989|remainder| is inserted between the current character and |next_char|; 31990then the current character is deleted if $b=0$, and |next_char| is 31991deleted if $c=0$; then we pass over $a$~characters to reach the next 31992current character (which may have a ligature/kerning program of its own). 31993 31994If the very first instruction of the |lig_kern| array has |skip_byte=255|, 31995the |next_char| byte is the so-called right boundary character of this font; 31996the value of |next_char| need not lie between |bc| and~|ec|. 31997If the very last instruction of the |lig_kern| array has |skip_byte=255|, 31998there is a special ligature/kerning program for a left boundary character, 31999beginning at location |256*op_byte+remainder|. 32000The interpretation is that \TeX\ puts implicit boundary characters 32001before and after each consecutive string of characters from the same font. 32002These implicit characters do not appear in the output, but they can affect 32003ligatures and kerning. 32004 32005If the very first instruction of a character's |lig_kern| program has 32006|skip_byte>128|, the program actually begins in location 32007|256*op_byte+remainder|. This feature allows access to large |lig_kern| 32008arrays, because the first instruction must otherwise 32009appear in a location |<=255|. 32010 32011Any instruction with |skip_byte>128| in the |lig_kern| array must satisfy 32012the condition 32013$$\hbox{|256*op_byte+remainder<nl|.}$$ 32014If such an instruction is encountered during 32015normal program execution, it denotes an unconditional halt; no ligature 32016command is performed. 32017 32018@d stop_flag (128) 32019 /* value indicating `\.{STOP}' in a lig/kern program */ 32020@d kern_flag (128) /* op code for a kern step */ 32021@d skip_byte(A) mp->lig_kern[(A)].b0 32022@d next_char(A) mp->lig_kern[(A)].b1 32023@d op_byte(A) mp->lig_kern[(A)].b2 32024@d rem_byte(A) mp->lig_kern[(A)].b3 32025 32026@ Extensible characters are specified by an |extensible_recipe|, which 32027consists of four bytes called |top|, |mid|, |bot|, and |rep| (in this 32028order). These bytes are the character codes of individual pieces used to 32029build up a large symbol. If |top|, |mid|, or |bot| are zero, they are not 32030present in the built-up result. For example, an extensible vertical line is 32031like an extensible bracket, except that the top and bottom pieces are missing. 32032 32033Let $T$, $M$, $B$, and $R$ denote the respective pieces, or an empty box 32034if the piece isn't present. Then the extensible characters have the form 32035$TR^kMR^kB$ from top to bottom, for some |k>=0|, unless $M$ is absent; 32036in the latter case we can have $TR^kB$ for both even and odd values of~|k|. 32037The width of the extensible character is the width of $R$; and the 32038height-plus-depth is the sum of the individual height-plus-depths of the 32039components used, since the pieces are butted together in a vertical list. 32040 32041@d ext_top(A) mp->exten[(A)].b0 /* |top| piece in a recipe */ 32042@d ext_mid(A) mp->exten[(A)].b1 /* |mid| piece in a recipe */ 32043@d ext_bot(A) mp->exten[(A)].b2 /* |bot| piece in a recipe */ 32044@d ext_rep(A) mp->exten[(A)].b3 /* |rep| piece in a recipe */ 32045 32046@ The final portion of a \.{TFM} file is the |param| array, which is another 32047sequence of |fix_word| values. 32048 32049\yskip\hang|param[1]=slant| is the amount of italic slant, which is used 32050to help position accents. For example, |slant=.25| means that when you go 32051up one unit, you also go .25 units to the right. The |slant| is a pure 32052number; it is the only |fix_word| other than the design size itself that is 32053not scaled by the design size. 32054@^design size@> 32055 32056\hang|param[2]=space| is the normal spacing between words in text. 32057Note that character 040 in the font need not have anything to do with 32058blank spaces. 32059 32060\hang|param[3]=space_stretch| is the amount of glue stretching between words. 32061 32062\hang|param[4]=space_shrink| is the amount of glue shrinking between words. 32063 32064\hang|param[5]=x_height| is the size of one ex in the font; it is also 32065the height of letters for which accents don't have to be raised or lowered. 32066 32067\hang|param[6]=quad| is the size of one em in the font. 32068 32069\hang|param[7]=extra_space| is the amount added to |param[2]| at the 32070ends of sentences. 32071 32072\yskip\noindent 32073If fewer than seven parameters are present, \TeX\ sets the missing parameters 32074to zero. 32075 32076@d slant_code 1 32077@d space_code 2 32078@d space_stretch_code 3 32079@d space_shrink_code 4 32080@d x_height_code 5 32081@d quad_code 6 32082@d extra_space_code 7 32083 32084@ So that is what \.{TFM} files hold. One of \MP's duties is to output such 32085information, and it does this all at once at the end of a job. 32086In order to prepare for such frenetic activity, it squirrels away the 32087necessary facts in various arrays as information becomes available. 32088 32089Character dimensions (\&{charwd}, \&{charht}, \&{chardp}, and \&{charic}) 32090are stored respectively in |tfm_width|, |tfm_height|, |tfm_depth|, and 32091|tfm_ital_corr|. Other information about a character (e.g., about 32092its ligatures or successors) is accessible via the |char_tag| and 32093|char_remainder| arrays. Other information about the font as a whole 32094is kept in additional arrays called |header_byte|, |lig_kern|, 32095|kern|, |exten|, and |param|. 32096 32097@d max_tfm_int 32510 32098@d undefined_label max_tfm_int /* an undefined local label */ 32099 32100@<Glob...@>= 32101#define TFM_ITEMS 257 32102eight_bits bc; 32103eight_bits ec; /* smallest and largest character codes shipped out */ 32104mp_node tfm_width[TFM_ITEMS]; /* \&{charwd} values */ 32105mp_node tfm_height[TFM_ITEMS]; /* \&{charht} values */ 32106mp_node tfm_depth[TFM_ITEMS]; /* \&{chardp} values */ 32107mp_node tfm_ital_corr[TFM_ITEMS]; /* \&{charic} values */ 32108boolean char_exists[TFM_ITEMS]; /* has this code been shipped out? */ 32109int char_tag[TFM_ITEMS]; /* |remainder| category */ 32110int char_remainder[TFM_ITEMS]; /* the |remainder| byte */ 32111char *header_byte; /* bytes of the \.{TFM} header */ 32112int header_last; /* last initialized \.{TFM} header byte */ 32113int header_size; /* size of the \.{TFM} header */ 32114four_quarters *lig_kern; /* the ligature/kern table */ 32115short nl; /* the number of ligature/kern steps so far */ 32116mp_number *kern; /* distinct kerning amounts */ 32117short nk; /* the number of distinct kerns so far */ 32118four_quarters exten[TFM_ITEMS]; /* extensible character recipes */ 32119short ne; /* the number of extensible characters so far */ 32120mp_number *param; /* \&{fontinfo} parameters */ 32121short np; /* the largest \&{fontinfo} parameter specified so far */ 32122short nw; 32123short nh; 32124short nd; 32125short ni; /* sizes of \.{TFM} subtables */ 32126short skip_table[TFM_ITEMS]; /* local label status */ 32127boolean lk_started; /* has there been a lig/kern step in this command yet? */ 32128integer bchar; /* right boundary character */ 32129short bch_label; /* left boundary starting location */ 32130short ll; 32131short lll; /* registers used for lig/kern processing */ 32132short label_loc[257]; /* lig/kern starting addresses */ 32133eight_bits label_char[257]; /* characters for |label_loc| */ 32134short label_ptr; /* highest position occupied in |label_loc| */ 32135 32136@ @<Allocate or initialize ...@>= 32137mp->header_last = 7; 32138mp->header_size = 128; /* just for init */ 32139mp->header_byte = xmalloc (mp->header_size, sizeof (char)); 32140 32141@ @<Dealloc variables@>= 32142xfree (mp->header_byte); 32143xfree (mp->lig_kern); 32144if (mp->kern) { 32145 int i; 32146 for (i=0;i<(max_tfm_int + 1);i++) { 32147 free_number(mp->kern[i]); 32148 } 32149 xfree (mp->kern); 32150} 32151if (mp->param) { 32152 int i; 32153 for (i=0;i<(max_tfm_int + 1);i++) { 32154 free_number(mp->param[i]); 32155 } 32156 xfree (mp->param); 32157} 32158 32159@ @<Set init...@>= 32160for (k = 0; k <= 255; k++) { 32161 mp->tfm_width[k] = 0; 32162 mp->tfm_height[k] = 0; 32163 mp->tfm_depth[k] = 0; 32164 mp->tfm_ital_corr[k] = 0; 32165 mp->char_exists[k] = false; 32166 mp->char_tag[k] = no_tag; 32167 mp->char_remainder[k] = 0; 32168 mp->skip_table[k] = undefined_label; 32169} 32170memset (mp->header_byte, 0, (size_t) mp->header_size); 32171mp->bc = 255; 32172mp->ec = 0; 32173mp->nl = 0; 32174mp->nk = 0; 32175mp->ne = 0; 32176mp->np = 0; 32177set_internal_from_number (mp_boundary_char, unity_t); 32178number_negate (internal_value (mp_boundary_char)); 32179mp->bch_label = undefined_label; 32180mp->label_loc[0] = -1; 32181mp->label_ptr = 0; 32182 32183@ @<Declarations@>= 32184static mp_node mp_tfm_check (MP mp, quarterword m); 32185 32186@ @c 32187static mp_node mp_tfm_check (MP mp, quarterword m) { 32188 mp_number absm; 32189 mp_node p = mp_get_value_node (mp); 32190 new_number (absm); 32191 number_clone (absm, internal_value (m)); 32192 number_abs (absm); 32193 if (number_greaterequal (absm, fraction_half_t)) { 32194 char msg[256]; 32195 const char *hlp[] = { 32196 "Font metric dimensions must be less than 2048pt.", 32197 NULL } ; 32198 mp_snprintf (msg, 256, "Enormous %s has been reduced", internal_name (m)); 32199@.Enormous charwd...@> 32200@.Enormous chardp...@> 32201@.Enormous charht...@> 32202@.Enormous charic...@> 32203@.Enormous designsize...@>; 32204 mp_back_error (mp, msg, hlp, true); 32205 mp_get_x_next (mp); 32206 if (number_positive (internal_value (m))) { 32207 set_value_number (p, fraction_half_t); 32208 number_add_scaled (value_number (p), -1); 32209 } else { 32210 set_value_number (p, fraction_half_t); 32211 number_negate (value_number (p)); 32212 number_add_scaled (value_number (p), 1); 32213 } 32214 } else { 32215 set_value_number (p, internal_value (m)); 32216 } 32217 free_number (absm); 32218 return p; 32219} 32220 32221@ @<Store the width information for character code~|c|@>= 32222if (c < mp->bc) 32223 mp->bc = (eight_bits) c; 32224if (c > mp->ec) 32225 mp->ec = (eight_bits) c; 32226mp->char_exists[c] = true; 32227mp_free_value_node (mp, mp->tfm_width[c]); 32228mp->tfm_width[c] = mp_tfm_check (mp, mp_char_wd); 32229mp_free_value_node (mp, mp->tfm_height[c]); 32230mp->tfm_height[c] = mp_tfm_check (mp, mp_char_ht); 32231mp_free_value_node (mp, mp->tfm_depth[c]); 32232mp->tfm_depth[c] = mp_tfm_check (mp, mp_char_dp); 32233mp_free_value_node (mp, mp->tfm_ital_corr[c]); 32234mp->tfm_ital_corr[c] = mp_tfm_check (mp, mp_char_ic) 32235 32236 32237@ Now let's consider \MP's special \.{TFM}-oriented commands. 32238 32239 32240@ @d char_list_code 0 32241@d lig_table_code 1 32242@d extensible_code 2 32243@d header_byte_code 3 32244@d font_dimen_code 4 32245 32246@<Put each...@>= 32247mp_primitive (mp, "charlist", mp_tfm_command, char_list_code); 32248@:char_list_}{\&{charlist} primitive@>; 32249mp_primitive (mp, "ligtable", mp_tfm_command, lig_table_code); 32250@:lig_table_}{\&{ligtable} primitive@>; 32251mp_primitive (mp, "extensible", mp_tfm_command, extensible_code); 32252@:extensible_}{\&{extensible} primitive@>; 32253mp_primitive (mp, "headerbyte", mp_tfm_command, header_byte_code); 32254@:header_byte_}{\&{headerbyte} primitive@>; 32255mp_primitive (mp, "fontdimen", mp_tfm_command, font_dimen_code); 32256@:font_dimen_}{\&{fontdimen} primitive@> 32257 32258 32259@ @<Cases of |print_cmd...@>= 32260case mp_tfm_command: 32261switch (m) { 32262case char_list_code: 32263 mp_print (mp, "charlist"); 32264 break; 32265case lig_table_code: 32266 mp_print (mp, "ligtable"); 32267 break; 32268case extensible_code: 32269 mp_print (mp, "extensible"); 32270 break; 32271case header_byte_code: 32272 mp_print (mp, "headerbyte"); 32273 break; 32274default: 32275 mp_print (mp, "fontdimen"); 32276 break; 32277} 32278break; 32279 32280@ @<Declare action procedures for use by |do_statement|@>= 32281static eight_bits mp_get_code (MP mp); 32282 32283@ @c 32284eight_bits mp_get_code (MP mp) { /* scans a character code value */ 32285 integer c; /* the code value found */ 32286 mp_value new_expr; 32287 const char *hlp[] = { 32288 "I was looking for a number between 0 and 255, or for a", 32289 "string of length 1. Didn't find it; will use 0 instead.", 32290 NULL }; 32291 memset(&new_expr,0,sizeof(mp_value)); 32292 new_number(new_expr.data.n); 32293 mp_get_x_next (mp); 32294 mp_scan_expression (mp); 32295 if (mp->cur_exp.type == mp_known) { 32296 c = round_unscaled (cur_exp_value_number ()); 32297 if (c >= 0) 32298 if (c < 256) 32299 return (eight_bits) c; 32300 } else if (mp->cur_exp.type == mp_string_type) { 32301 if (cur_exp_str ()->len == 1) { 32302 c = (integer) (*(cur_exp_str ()->str)); 32303 return (eight_bits) c; 32304 } 32305 } 32306 mp_disp_err(mp, NULL); 32307 set_number_to_zero (new_expr.data.n); 32308 mp_back_error (mp, "Invalid code has been replaced by 0", hlp, true); 32309@.Invalid code...@>; 32310 mp_get_x_next (mp); 32311 mp_flush_cur_exp (mp, new_expr); 32312 c = 0; 32313 return (eight_bits) c; 32314} 32315 32316 32317@ @<Declare action procedures for use by |do_statement|@>= 32318static void mp_set_tag (MP mp, halfword c, quarterword t, halfword r); 32319 32320@ @c 32321void mp_set_tag (MP mp, halfword c, quarterword t, halfword r) { 32322 if (mp->char_tag[c] == no_tag) { 32323 mp->char_tag[c] = t; 32324 mp->char_remainder[c] = r; 32325 if (t == lig_tag) { 32326 mp->label_ptr++; 32327 mp->label_loc[mp->label_ptr] = (short) r; 32328 mp->label_char[mp->label_ptr] = (eight_bits) c; 32329 } 32330 } else { 32331 @<Complain about a character tag conflict@>; 32332 } 32333} 32334 32335 32336@ @<Complain about a character tag conflict@>= 32337{ 32338 const char *xtra = NULL; 32339 char msg[256]; 32340 const char *hlp[] = { 32341 "It's not legal to label a character more than once.", 32342 "So I'll not change anything just now.", 32343 NULL }; 32344 switch (mp->char_tag[c]) { 32345 case lig_tag: xtra = "in a ligtable"; break; 32346 case list_tag: xtra = "in a charlist"; break; 32347 case ext_tag: xtra = "extensible"; break; 32348 default: xtra = ""; break; 32349 } 32350 if ((c > ' ') && (c < 127)) { 32351 mp_snprintf(msg, 256, "Character %c is already %s", xord(c), xtra); 32352 } else if (c == 256) { 32353 mp_snprintf(msg, 256, "Character || is already %s", xtra); 32354 } else { 32355 mp_snprintf(msg, 256, "Character code %d is already %s", c, xtra); 32356 } 32357@.Character c is already...@>; 32358 mp_back_error (mp, msg, hlp, true); 32359 mp_get_x_next (mp); 32360} 32361 32362 32363@ @<Declare action procedures for use by |do_statement|@>= 32364static void mp_do_tfm_command (MP mp); 32365 32366@ @c 32367void mp_do_tfm_command (MP mp) { 32368 int c, cc; /* character codes */ 32369 int k; /* index into the |kern| array */ 32370 int j; /* index into |header_byte| or |param| */ 32371 mp_value new_expr; 32372 memset(&new_expr,0,sizeof(mp_value)); 32373 new_number(new_expr.data.n); 32374 switch (cur_mod()) { 32375 case char_list_code: 32376 c = mp_get_code (mp); 32377 /* we will store a list of character successors */ 32378 while (cur_cmd() == mp_colon) { 32379 cc = mp_get_code (mp); 32380 mp_set_tag (mp, c, list_tag, cc); 32381 c = cc; 32382 }; 32383 break; 32384 case lig_table_code: 32385 if (mp->lig_kern == NULL) 32386 mp->lig_kern = xmalloc ((max_tfm_int + 1), sizeof (four_quarters)); 32387 if (mp->kern == NULL) { 32388 int i; 32389 mp->kern = xmalloc ((max_tfm_int + 1), sizeof (mp_number)); 32390 for (i=0;i<(max_tfm_int + 1);i++) 32391 new_number (mp->kern[i]); 32392 } 32393 @<Store a list of ligature/kern steps@>; 32394 break; 32395 case extensible_code: 32396 @<Define an extensible recipe@>; 32397 break; 32398 case header_byte_code: 32399 case font_dimen_code: 32400 c = cur_mod(); 32401 mp_get_x_next (mp); 32402 mp_scan_expression (mp); 32403 if ((mp->cur_exp.type != mp_known) || number_less(cur_exp_value_number (), half_unit_t)) { 32404 const char *hlp[] = { 32405 "I was looking for a known, positive number.", 32406 "For safety's sake I'll ignore the present command.", 32407 NULL }; 32408 mp_disp_err(mp, NULL); 32409 mp_back_error (mp, "Improper location", hlp, true); 32410@.Improper location@>; 32411 mp_get_x_next (mp); 32412 } else { 32413 j = round_unscaled (cur_exp_value_number ()); 32414 if (cur_cmd() != mp_colon) { 32415 const char *hlp[] = { 32416 "A colon should follow a headerbyte or fontinfo location.", 32417 NULL }; 32418 mp_back_error (mp, "Missing `:' has been inserted", hlp, true); 32419@.Missing `:'@>; 32420 } 32421 if (c == header_byte_code) { 32422 @<Store a list of header bytes@>; 32423 } else { 32424 if (mp->param == NULL) { 32425 int i; 32426 mp->param = xmalloc ((max_tfm_int + 1), sizeof (mp_number)); 32427 for (i=0;i<(max_tfm_int + 1);i++) 32428 new_number (mp->param[i]); 32429 } 32430 @<Store a list of font dimensions@>; 32431 } 32432 } 32433 break; 32434 } /* there are no other cases */ 32435} 32436 32437 32438@ @<Store a list of ligature/kern steps@>= 32439{ 32440 mp->lk_started = false; 32441CONTINUE: 32442 mp_get_x_next (mp); 32443 if ((cur_cmd() == mp_skip_to) && mp->lk_started) 32444 @<Process a |skip_to| command and |goto done|@>; 32445 if (cur_cmd() == mp_bchar_label) { 32446 c = 256; 32447 set_cur_cmd((mp_variable_type)mp_colon); 32448 } else { 32449 mp_back_input (mp); 32450 c = mp_get_code (mp); 32451 }; 32452 if ((cur_cmd() == mp_colon) || (cur_cmd() == mp_double_colon)) { 32453 @<Record a label in a lig/kern subprogram and |goto continue|@>; 32454 } 32455 if (cur_cmd() == mp_lig_kern_token) { 32456 @<Compile a ligature/kern command@>; 32457 } else { 32458 const char *hlp[] = { "I was looking for `=:' or `kern' here.", NULL }; 32459 mp_back_error (mp, "Illegal ligtable step", hlp, true); 32460@.Illegal ligtable step@>; 32461 next_char (mp->nl) = qi (0); 32462 op_byte (mp->nl) = qi (0); 32463 rem_byte (mp->nl) = qi (0); 32464 skip_byte (mp->nl) = stop_flag + 1; /* this specifies an unconditional stop */ 32465 } 32466 if (mp->nl == max_tfm_int) 32467 mp_fatal_error (mp, "ligtable too large"); 32468 mp->nl++; 32469 if (cur_cmd() == mp_comma) 32470 goto CONTINUE; 32471 if (skip_byte (mp->nl - 1) < stop_flag) 32472 skip_byte (mp->nl - 1) = stop_flag; 32473} 32474DONE: 32475 32476@ @<Put each...@>= 32477mp_primitive (mp, "=:", mp_lig_kern_token, 0); 32478@:=:_}{\.{=:} primitive@>; 32479mp_primitive (mp, "=:|", mp_lig_kern_token, 1); 32480@:=:/_}{\.{=:\char'174} primitive@>; 32481mp_primitive (mp, "=:|>", mp_lig_kern_token, 5); 32482@:=:/>_}{\.{=:\char'174>} primitive@>; 32483mp_primitive (mp, "|=:", mp_lig_kern_token, 2); 32484@:=:/_}{\.{\char'174=:} primitive@>; 32485mp_primitive (mp, "|=:>", mp_lig_kern_token, 6); 32486@:=:/>_}{\.{\char'174=:>} primitive@>; 32487mp_primitive (mp, "|=:|", mp_lig_kern_token, 3); 32488@:=:/_}{\.{\char'174=:\char'174} primitive@>; 32489mp_primitive (mp, "|=:|>", mp_lig_kern_token, 7); 32490@:=:/>_}{\.{\char'174=:\char'174>} primitive@>; 32491mp_primitive (mp, "|=:|>>", mp_lig_kern_token, 11); 32492@:=:/>_}{\.{\char'174=:\char'174>>} primitive@>; 32493mp_primitive (mp, "kern", mp_lig_kern_token, mp_kern_flag); 32494@:kern_}{\&{kern} primitive@> 32495 32496 32497@ @<Cases of |print_cmd...@>= 32498case mp_lig_kern_token: 32499switch (m) { 32500case 0: 32501 mp_print (mp, "=:"); 32502 break; 32503case 1: 32504 mp_print (mp, "=:|"); 32505 break; 32506case 2: 32507 mp_print (mp, "|=:"); 32508 break; 32509case 3: 32510 mp_print (mp, "|=:|"); 32511 break; 32512case 5: 32513 mp_print (mp, "=:|>"); 32514 break; 32515case 6: 32516 mp_print (mp, "|=:>"); 32517 break; 32518case 7: 32519 mp_print (mp, "|=:|>"); 32520 break; 32521case 11: 32522 mp_print (mp, "|=:|>>"); 32523 break; 32524default: 32525 mp_print (mp, "kern"); 32526 break; 32527} 32528break; 32529 32530@ Local labels are implemented by maintaining the |skip_table| array, 32531where |skip_table[c]| is either |undefined_label| or the address of the 32532most recent lig/kern instruction that skips to local label~|c|. In the 32533latter case, the |skip_byte| in that instruction will (temporarily) 32534be zero if there were no prior skips to this label, or it will be the 32535distance to the prior skip. 32536 32537We may need to cancel skips that span more than 127 lig/kern steps. 32538 32539@d cancel_skips(A) mp->ll=(A); 32540 do { 32541 mp->lll=qo(skip_byte(mp->ll)); 32542 skip_byte(mp->ll)=stop_flag; mp->ll=(short)(mp->ll-mp->lll); 32543 } while (mp->lll!=0) 32544 32545@d skip_error(A) { 32546 const char *hlp[] = { "At most 127 lig/kern steps can separate skipto1 from 1::.", NULL}; 32547 mp_error(mp, "Too far to skip", hlp, true); 32548@.Too far to skip@> 32549 cancel_skips((A)); 32550} 32551 32552@<Process a |skip_to| command and |goto done|@>= 32553{ 32554 c = mp_get_code (mp); 32555 if (mp->nl - mp->skip_table[c] > 128) { 32556 skip_error (mp->skip_table[c]); 32557 mp->skip_table[c] = (short) undefined_label; 32558 } 32559 if (mp->skip_table[c] == undefined_label) 32560 skip_byte (mp->nl - 1) = qi (0); 32561 else 32562 skip_byte (mp->nl - 1) = qi (mp->nl - mp->skip_table[c] - 1); 32563 mp->skip_table[c] = (short) (mp->nl - 1); 32564 goto DONE; 32565} 32566 32567 32568@ @<Record a label in a lig/kern subprogram and |goto continue|@>= 32569{ 32570 if (cur_cmd() == mp_colon) { 32571 if (c == 256) 32572 mp->bch_label = mp->nl; 32573 else 32574 mp_set_tag (mp, c, lig_tag, mp->nl); 32575 } else if (mp->skip_table[c] < undefined_label) { 32576 mp->ll = mp->skip_table[c]; 32577 mp->skip_table[c] = undefined_label; 32578 do { 32579 mp->lll = qo (skip_byte (mp->ll)); 32580 if (mp->nl - mp->ll > 128) { 32581 skip_error (mp->ll); 32582 goto CONTINUE; 32583 } 32584 skip_byte (mp->ll) = qi (mp->nl - mp->ll - 1); 32585 mp->ll = (short) (mp->ll - mp->lll); 32586 } while (mp->lll != 0); 32587 } 32588 goto CONTINUE; 32589} 32590 32591 32592@ @<Compile a ligature/kern...@>= 32593{ 32594 next_char (mp->nl) = qi (c); 32595 skip_byte (mp->nl) = qi (0); 32596 if (cur_mod() < 128) { /* ligature op */ 32597 op_byte (mp->nl) = qi (cur_mod()); 32598 rem_byte (mp->nl) = qi (mp_get_code (mp)); 32599 } else { 32600 mp_get_x_next (mp); 32601 mp_scan_expression (mp); 32602 if (mp->cur_exp.type != mp_known) { 32603 const char *hlp[] = { 32604 "The amount of kern should be a known numeric value.", 32605 "I'm zeroing this one. Proceed, with fingers crossed.", 32606 NULL }; 32607 mp_disp_err(mp, NULL); 32608 set_number_to_zero (new_expr.data.n); 32609 mp_back_error (mp, "Improper kern", hlp, true); 32610@.Improper kern@>; 32611 mp_get_x_next (mp); 32612 mp_flush_cur_exp (mp, new_expr); 32613 } 32614 number_clone (mp->kern[mp->nk], cur_exp_value_number ()); 32615 k = 0; 32616 while (!number_equal (mp->kern[k], cur_exp_value_number ())) 32617 incr (k); 32618 if (k == mp->nk) { 32619 if (mp->nk == max_tfm_int) 32620 mp_fatal_error (mp, "too many TFM kerns"); 32621 mp->nk++; 32622 } 32623 op_byte (mp->nl) = qi (kern_flag + (k / 256)); 32624 rem_byte (mp->nl) = qi ((k % 256)); 32625 } 32626 mp->lk_started = true; 32627} 32628 32629 32630@ @d missing_extensible_punctuation(A) 32631 { 32632 char msg[256]; 32633 const char *hlp[] = { "I'm processing `extensible c: t,m,b,r'.", NULL }; 32634 mp_snprintf(msg, 256, "Missing %s has been inserted", (A)); 32635 mp_back_error(mp, msg, hlp, true); 32636@.Missing `\char`\#'@> 32637 } 32638 32639@<Define an extensible recipe@>= 32640{ 32641 if (mp->ne == 256) 32642 mp_fatal_error (mp, "too many extensible recipies"); 32643 c = mp_get_code (mp); 32644 mp_set_tag (mp, c, ext_tag, mp->ne); 32645 if (cur_cmd() != mp_colon) 32646 missing_extensible_punctuation (":"); 32647 ext_top (mp->ne) = qi (mp_get_code (mp)); 32648 if (cur_cmd() != mp_comma) 32649 missing_extensible_punctuation (","); 32650 ext_mid (mp->ne) = qi (mp_get_code (mp)); 32651 if (cur_cmd() != mp_comma) 32652 missing_extensible_punctuation (","); 32653 ext_bot (mp->ne) = qi (mp_get_code (mp)); 32654 if (cur_cmd() != mp_comma) 32655 missing_extensible_punctuation (","); 32656 ext_rep (mp->ne) = qi (mp_get_code (mp)); 32657 mp->ne++; 32658} 32659 32660 32661@ The header could contain ASCII zeroes, so can't use |strdup|. 32662 32663@<Store a list of header bytes@>= 32664j--; 32665do { 32666 if (j >= mp->header_size) { 32667 size_t l = (size_t) (mp->header_size + (mp->header_size / 4)); 32668 char *t = xmalloc (l, 1); 32669 memset (t, 0, l); 32670 (void) memcpy (t, mp->header_byte, (size_t) mp->header_size); 32671 xfree (mp->header_byte); 32672 mp->header_byte = t; 32673 mp->header_size = (int) l; 32674 } 32675 mp->header_byte[j] = (char) mp_get_code (mp); 32676 incr (j); 32677 incr (mp->header_last); 32678} while (cur_cmd() == mp_comma) 32679 32680@ @<Store a list of font dimensions@>= 32681do { 32682 if (j > max_tfm_int) 32683 mp_fatal_error (mp, "too many fontdimens"); 32684 while (j > mp->np) { 32685 mp->np++; 32686 set_number_to_zero(mp->param[mp->np]); 32687 }; 32688 mp_get_x_next (mp); 32689 mp_scan_expression (mp); 32690 if (mp->cur_exp.type != mp_known) { 32691 const char *hlp[] = { "I'm zeroing this one. Proceed, with fingers crossed.", NULL }; 32692 mp_disp_err(mp, NULL); 32693 set_number_to_zero (new_expr.data.n); 32694 mp_back_error (mp, "Improper font parameter", hlp, true); 32695@.Improper font parameter@>; 32696 mp_get_x_next (mp); 32697 mp_flush_cur_exp (mp, new_expr); 32698 } 32699 number_clone (mp->param[j], cur_exp_value_number ()); 32700 incr (j); 32701} while (cur_cmd() == mp_comma) 32702 32703@ OK: We've stored all the data that is needed for the \.{TFM} file. 32704All that remains is to output it in the correct format. 32705 32706An interesting problem needs to be solved in this connection, because 32707the \.{TFM} format allows at most 256~widths, 16~heights, 16~depths, 32708and 64~italic corrections. If the data has more distinct values than 32709this, we want to meet the necessary restrictions by perturbing the 32710given values as little as possible. 32711 32712\MP\ solves this problem in two steps. First the values of a given 32713kind (widths, heights, depths, or italic corrections) are sorted; 32714then the list of sorted values is perturbed, if necessary. 32715 32716The sorting operation is facilitated by having a special node of 32717essentially infinite |value| at the end of the current list. 32718 32719@<Initialize table entries@>= 32720mp->inf_val = mp_get_value_node (mp); 32721set_value_number (mp->inf_val, fraction_four_t); 32722 32723@ @<Free table entries@>= 32724mp_free_value_node (mp, mp->inf_val); 32725 32726@ Straight linear insertion is good enough for sorting, since the lists 32727are usually not terribly long. As we work on the data, the current list 32728will start at |mp_link(temp_head)| and end at |inf_val|; the nodes in this 32729list will be in increasing order of their |value| fields. 32730 32731Given such a list, the |sort_in| function takes a value and returns a pointer 32732to where that value can be found in the list. The value is inserted in 32733the proper place, if necessary. 32734 32735At the time we need to do these operations, most of \MP's work has been 32736completed, so we will have plenty of memory to play with. The value nodes 32737that are allocated for sorting will never be returned to free storage. 32738 32739@d clear_the_list mp_link(mp->temp_head)=mp->inf_val 32740 32741@c 32742static mp_node mp_sort_in (MP mp, mp_number v) { 32743 mp_node p, q, r; /* list manipulation registers */ 32744 p = mp->temp_head; 32745 while (1) { 32746 q = mp_link (p); 32747 if (number_lessequal(v, value_number (q))) 32748 break; 32749 p = q; 32750 } 32751 if (number_less (v, value_number (q))) { 32752 r = mp_get_value_node (mp); 32753 set_value_number (r, v); 32754 mp_link (r) = q; 32755 mp_link (p) = r; 32756 } 32757 return mp_link (p); 32758} 32759 32760 32761@ Now we come to the interesting part, where we reduce the list if necessary 32762until it has the required size. The |min_cover| routine is basic to this 32763process; it computes the minimum number~|m| such that the values of the 32764current sorted list can be covered by |m|~intervals of width~|d|. It 32765also sets the global value |perturbation| to the smallest value $d'>d$ 32766such that the covering found by this algorithm would be different. 32767 32768In particular, |min_cover(0)| returns the number of distinct values in the 32769current list and sets |perturbation| to the minimum distance between 32770adjacent values. 32771 32772@c 32773static integer mp_min_cover (MP mp, mp_number d) { 32774 mp_node p; /* runs through the current list */ 32775 mp_number l; /* the least element covered by the current interval */ 32776 mp_number test; 32777 integer m; /* lower bound on the size of the minimum cover */ 32778 m = 0; 32779 new_number (l); 32780 new_number (test); 32781 p = mp_link (mp->temp_head); 32782 set_number_to_inf(mp->perturbation); 32783 while (p != mp->inf_val) { 32784 incr (m); 32785 number_clone (l, value_number (p)); 32786 do { 32787 p = mp_link (p); 32788 set_number_from_addition(test, l, d); 32789 } while (number_lessequal(value_number (p), test)); 32790 32791 set_number_from_substraction(test, value_number (p), l); 32792 if (number_less (test, mp->perturbation)) { 32793 number_clone (mp->perturbation, value_number (p)); 32794 number_substract (mp->perturbation, l); 32795 } 32796 } 32797 free_number (test); 32798 free_number (l); 32799 return m; 32800} 32801 32802 32803@ @<Glob...@>= 32804mp_number perturbation; /* quantity related to \.{TFM} rounding */ 32805integer excess; /* the list is this much too long */ 32806 32807@ @<Initialize table...@>= 32808new_number (mp->perturbation); 32809 32810@ @<Dealloc...@>= 32811free_number (mp->perturbation); 32812 32813@ The smallest |d| such that a given list can be covered with |m| intervals 32814is determined by the |threshold| routine, which is sort of an inverse 32815to |min_cover|. The idea is to increase the interval size rapidly until 32816finding the range, then to go sequentially until the exact borderline has 32817been discovered. 32818 32819@c 32820static void mp_threshold (MP mp, mp_number ret, integer m) { 32821 mp_number d, arg1; /* lower bound on the smallest interval size */ 32822 new_number (d); 32823 new_number (arg1); 32824 mp->excess = mp_min_cover (mp, zero_t) - m; 32825 if (mp->excess <= 0) { 32826 number_clone (ret, zero_t); 32827 } else { 32828 do { 32829 number_clone (d, mp->perturbation); 32830 set_number_from_addition(arg1, d, d); 32831 } while (mp_min_cover (mp, arg1) > m); 32832 while (mp_min_cover (mp, d) > m) { 32833 number_clone (d, mp->perturbation); 32834 } 32835 number_clone (ret, d); 32836 } 32837 free_number (d); 32838 free_number (arg1); 32839} 32840 32841 32842@ The |skimp| procedure reduces the current list to at most |m| entries, 32843by changing values if necessary. It also sets |indep_value(p):=k| if |value(p)| 32844is the |k|th distinct value on the resulting list, and it sets 32845|perturbation| to the maximum amount by which a |value| field has 32846been changed. The size of the resulting list is returned as the 32847value of |skimp|. 32848 32849@c 32850static integer mp_skimp (MP mp, integer m) { 32851 mp_number d; /* the size of intervals being coalesced */ 32852 mp_node p, q, r; /* list manipulation registers */ 32853 mp_number l; /* the least value in the current interval */ 32854 mp_number v; /* a compromise value */ 32855 mp_number l_d; 32856 new_number (d); 32857 mp_threshold (mp, d, m); 32858 new_number (l); 32859 new_number (l_d); 32860 new_number (v); 32861 set_number_to_zero (mp->perturbation); 32862 q = mp->temp_head; 32863 m = 0; 32864 p = mp_link (mp->temp_head); 32865 while (p != mp->inf_val) { 32866 incr (m); 32867 number_clone (l, value_number (p)); 32868 set_indep_value (p,m); 32869 set_number_from_addition (l_d, l, d); 32870 if (number_lessequal (value_number (mp_link (p)), l_d)) { 32871 @<Replace an interval of values by its midpoint@>; 32872 } 32873 q = p; 32874 p = mp_link (p); 32875 } 32876 free_number (l_d); 32877 free_number (d); 32878 free_number (l); 32879 free_number (v); 32880 return m; 32881} 32882 32883 32884@ @<Replace an interval...@>= 32885{ 32886 mp_number test; 32887 new_number (test); 32888 do { 32889 p = mp_link (p); 32890 set_indep_value (p, m); 32891 decr (mp->excess); 32892 if (mp->excess == 0) { 32893 number_clone (l_d, l); 32894 } 32895 } while (number_lessequal(value_number (mp_link (p)), l_d)); 32896 set_number_from_substraction (test, value_number (p), l); 32897 number_halfp(test); 32898 set_number_from_addition (v, l, test); 32899 set_number_from_substraction (test, value_number (p), v); 32900 if (number_greater (test, mp->perturbation)) 32901 number_clone (mp->perturbation, test); 32902 r = q; 32903 do { 32904 r = mp_link (r); 32905 set_value_number (r, v); 32906 } while (r != p); 32907 mp_link (q) = p; /* remove duplicate values from the current list */ 32908 free_number (test); 32909} 32910 32911 32912@ A warning message is issued whenever something is perturbed by 32913more than 1/16\thinspace pt. 32914 32915@c 32916static void mp_tfm_warning (MP mp, quarterword m) { 32917 mp_print_nl (mp, "(some "); 32918 mp_print (mp, internal_name (m)); 32919@.some charwds...@> 32920@.some chardps...@> 32921@.some charhts...@> 32922@.some charics...@>; 32923 mp_print (mp, " values had to be adjusted by as much as "); 32924 print_number (mp->perturbation); 32925 mp_print (mp, "pt)"); 32926} 32927 32928 32929@ Here's an example of how we use these routines. 32930The width data needs to be perturbed only if there are 256 distinct 32931widths, but \MP\ must check for this case even though it is 32932highly unusual. 32933 32934An integer variable |k| will be defined when we use this code. 32935The |dimen_head| array will contain pointers to the sorted 32936lists of dimensions. 32937 32938@d tfm_warn_threshold_k ((math_data *)mp->math)->tfm_warn_threshold_t 32939 32940@<Massage the \.{TFM} widths@>= 32941clear_the_list; 32942for (k = mp->bc; k <= mp->ec; k++) { 32943 if (mp->char_exists[k]) 32944 mp->tfm_width[k] = mp_sort_in (mp, value_number (mp->tfm_width[k])); 32945} 32946mp->nw = (short) (mp_skimp (mp, 255) + 1); 32947mp->dimen_head[1] = mp_link (mp->temp_head); 32948if (number_greaterequal (mp->perturbation, tfm_warn_threshold_k)) 32949 mp_tfm_warning (mp, mp_char_wd) 32950 32951 32952@ @<Glob...@>= 32953mp_node dimen_head[5]; /* lists of \.{TFM} dimensions */ 32954 32955@ Heights, depths, and italic corrections are different from widths 32956not only because their list length is more severely restricted, but 32957also because zero values do not need to be put into the lists. 32958 32959@<Massage the \.{TFM} heights, depths, and italic corrections@>= 32960clear_the_list; 32961for (k = mp->bc; k <= mp->ec; k++) { 32962 if (mp->char_exists[k]) { 32963 if (mp->tfm_height[k] == 0) 32964 mp->tfm_height[k] = mp->zero_val; 32965 else 32966 mp->tfm_height[k] = mp_sort_in (mp, value_number (mp->tfm_height[k])); 32967 } 32968} 32969mp->nh = (short) (mp_skimp (mp, 15) + 1); 32970mp->dimen_head[2] = mp_link (mp->temp_head); 32971if (number_greaterequal (mp->perturbation, tfm_warn_threshold_k)) 32972 mp_tfm_warning (mp, mp_char_ht); 32973clear_the_list; 32974for (k = mp->bc; k <= mp->ec; k++) { 32975 if (mp->char_exists[k]) { 32976 if (mp->tfm_depth[k] == 0) 32977 mp->tfm_depth[k] = mp->zero_val; 32978 else 32979 mp->tfm_depth[k] = mp_sort_in (mp, value_number (mp->tfm_depth[k])); 32980 } 32981} 32982mp->nd = (short) (mp_skimp (mp, 15) + 1); 32983mp->dimen_head[3] = mp_link (mp->temp_head); 32984if (number_greaterequal (mp->perturbation, tfm_warn_threshold_k)) 32985 mp_tfm_warning (mp, mp_char_dp); 32986clear_the_list; 32987for (k = mp->bc; k <= mp->ec; k++) { 32988 if (mp->char_exists[k]) { 32989 if (mp->tfm_ital_corr[k] == 0) 32990 mp->tfm_ital_corr[k] = mp->zero_val; 32991 else 32992 mp->tfm_ital_corr[k] = mp_sort_in (mp, value_number (mp->tfm_ital_corr[k])); 32993 } 32994} 32995mp->ni = (short) (mp_skimp (mp, 63) + 1); 32996mp->dimen_head[4] = mp_link (mp->temp_head); 32997if (number_greaterequal (mp->perturbation, tfm_warn_threshold_k)) 32998 mp_tfm_warning (mp, mp_char_ic) 32999 33000 33001@ @<Initialize table entries@>= 33002mp->zero_val = mp_get_value_node (mp); 33003set_value_number (mp->zero_val, zero_t); 33004 33005@ @<Free table entries@>= 33006mp_free_value_node (mp, mp->zero_val); 33007 33008@ Bytes 5--8 of the header are set to the design size, unless the user has 33009some crazy reason for specifying them differently. 33010@^design size@> 33011 33012Error messages are not allowed at the time this procedure is called, 33013so a warning is printed instead. 33014 33015The value of |max_tfm_dimen| is calculated so that 33016$$\hbox{|make_scaled(16*max_tfm_dimen,internal_value(mp_design_size))|} 33017 < \\{three\_bytes}.$$ 33018 33019@d three_bytes 0100000000 /* $2^{24}$ */ 33020 33021@c 33022static void mp_fix_design_size (MP mp) { 33023 mp_number d; /* the design size */ 33024 new_number (d); 33025 number_clone (d, internal_value (mp_design_size)); 33026 if (number_less(d, unity_t) || number_greaterequal(d, fraction_half_t)) { 33027 if (!number_zero (d)) 33028 mp_print_nl (mp, "(illegal design size has been changed to 128pt)"); 33029@.illegal design size...@>; 33030 set_number_from_scaled (d, 040000000); 33031 number_clone (internal_value (mp_design_size), d); 33032 } 33033 if (mp->header_byte[4] == 0 && mp->header_byte[5] == 0 && 33034 mp->header_byte[6] == 0 && mp->header_byte[7] == 0) { 33035 integer dd = number_to_scaled (d); 33036 mp->header_byte[4] = (char) (dd / 04000000); 33037 mp->header_byte[5] = (char) ((dd / 4096) % 256); 33038 mp->header_byte[6] = (char) ((dd / 16) % 256); 33039 mp->header_byte[7] = (char) ((dd % 16) * 16); 33040 } 33041 /* |mp->max_tfm_dimen = 16 * internal_value (mp_design_size) - 1 - internal_value (mp_design_size) / 010000000| */ 33042 { 33043 mp_number secondpart; 33044 new_number (secondpart); 33045 number_clone (secondpart, internal_value (mp_design_size)); 33046 number_clone (mp->max_tfm_dimen, secondpart); 33047 number_divide_int (secondpart, 010000000); 33048 number_multiply_int (mp->max_tfm_dimen, 16); 33049 number_add_scaled (mp->max_tfm_dimen, -1); 33050 number_substract (mp->max_tfm_dimen, secondpart); 33051 free_number (secondpart); 33052 } 33053 if (number_greaterequal (mp->max_tfm_dimen, fraction_half_t)) { 33054 number_clone (mp->max_tfm_dimen, fraction_half_t); 33055 number_add_scaled (mp->max_tfm_dimen, -1); 33056 } 33057 free_number (d); 33058} 33059 33060 33061@ The |dimen_out| procedure computes a |fix_word| relative to the 33062design size. If the data was out of range, it is corrected and the 33063global variable |tfm_changed| is increased by~one. 33064 33065@c 33066static integer mp_dimen_out (MP mp, mp_number x_orig) { 33067 integer ret; 33068 mp_number abs_x; 33069 mp_number x; 33070 new_number (abs_x); 33071 new_number (x); 33072 number_clone (x, x_orig); 33073 number_clone (abs_x, x_orig); 33074 number_abs (abs_x); 33075 if (number_greater (abs_x, mp->max_tfm_dimen)) { 33076 incr (mp->tfm_changed); 33077 if (number_positive(x)) 33078 number_clone (x, mp->max_tfm_dimen); 33079 else { 33080 number_clone (x, mp->max_tfm_dimen); 33081 number_negate (x); 33082 } 33083 } 33084 { 33085 mp_number arg1; 33086 new_number (arg1); 33087 number_clone (arg1, x); 33088 number_multiply_int (arg1, 16); 33089 make_scaled (x, arg1, internal_value (mp_design_size)); 33090 free_number (arg1); 33091 } 33092 free_number (abs_x); 33093 ret = number_to_scaled (x); 33094 free_number (x); 33095 return ret; 33096} 33097 33098 33099@ @<Glob...@>= 33100mp_number max_tfm_dimen; /* bound on widths, heights, kerns, etc. */ 33101integer tfm_changed; /* the number of data entries that were out of bounds */ 33102 33103@ @<Initialize table...@>= 33104new_number (mp->max_tfm_dimen); 33105 33106@ @<Dealloc...@>= 33107free_number (mp->max_tfm_dimen); 33108 33109 33110@ If the user has not specified any of the first four header bytes, 33111the |fix_check_sum| procedure replaces them by a ``check sum'' computed 33112from the |tfm_width| data relative to the design size. 33113@^check sum@> 33114 33115@c 33116static void mp_fix_check_sum (MP mp) { 33117 eight_bits k; /* runs through character codes */ 33118 eight_bits B1, B2, B3, B4; /* bytes of the check sum */ 33119 integer x; /* hash value used in check sum computation */ 33120 if (mp->header_byte[0] == 0 && mp->header_byte[1] == 0 && 33121 mp->header_byte[2] == 0 && mp->header_byte[3] == 0) { 33122 @<Compute a check sum in |(b1,b2,b3,b4)|@>; 33123 mp->header_byte[0] = (char) B1; 33124 mp->header_byte[1] = (char) B2; 33125 mp->header_byte[2] = (char) B3; 33126 mp->header_byte[3] = (char) B4; 33127 return; 33128 } 33129} 33130 33131 33132@ @<Compute a check sum in |(b1,b2,b3,b4)|@>= 33133B1 = mp->bc; 33134B2 = mp->ec; 33135B3 = mp->bc; 33136B4 = mp->ec; 33137mp->tfm_changed = 0; 33138for (k = mp->bc; k <= mp->ec; k++) { 33139 if (mp->char_exists[k]) { 33140 x = mp_dimen_out (mp, value_number (mp->tfm_width[k])) + (k + 4) * 020000000; /* this is positive */ 33141 B1 = (eight_bits) ((B1 + B1 + x) % 255); 33142 B2 = (eight_bits) ((B2 + B2 + x) % 253); 33143 B3 = (eight_bits) ((B3 + B3 + x) % 251); 33144 B4 = (eight_bits) ((B4 + B4 + x) % 247); 33145 } 33146 if (k == mp->ec) 33147 break; 33148} 33149 33150 33151@ Finally we're ready to actually write the \.{TFM} information. 33152Here are some utility routines for this purpose. 33153 33154@d tfm_out(A) do { /* output one byte to |tfm_file| */ 33155 unsigned char s=(unsigned char)(A); 33156 (mp->write_binary_file)(mp,mp->tfm_file,(void *)&s,1); 33157 } while (0) 33158 33159@c 33160static void mp_tfm_two (MP mp, integer x) { /* output two bytes to |tfm_file| */ 33161 tfm_out (x / 256); 33162 tfm_out (x % 256); 33163} 33164static void mp_tfm_four (MP mp, integer x) { /* output four bytes to |tfm_file| */ 33165 if (x >= 0) 33166 tfm_out (x / three_bytes); 33167 else { 33168 x = x + 010000000000; /* use two's complement for negative values */ 33169 x = x + 010000000000; 33170 tfm_out ((x / three_bytes) + 128); 33171 }; 33172 x = x % three_bytes; 33173 tfm_out (x / number_to_scaled (unity_t)); 33174 x = x % number_to_scaled (unity_t); 33175 tfm_out (x / 0400); 33176 tfm_out (x % 0400); 33177} 33178static void mp_tfm_qqqq (MP mp, four_quarters x) { /* output four quarterwords to |tfm_file| */ 33179 tfm_out (qo (x.b0)); 33180 tfm_out (qo (x.b1)); 33181 tfm_out (qo (x.b2)); 33182 tfm_out (qo (x.b3)); 33183} 33184 33185 33186@ @<Finish the \.{TFM} file@>= 33187if (mp->job_name == NULL) 33188 mp_open_log_file (mp); 33189mp_pack_job_name (mp, ".tfm"); 33190while (!mp_open_out (mp, &mp->tfm_file, mp_filetype_metrics)) 33191 mp_prompt_file_name (mp, "file name for font metrics", ".tfm"); 33192mp->metric_file_name = xstrdup (mp->name_of_file); 33193@<Output the subfile sizes and header bytes@>; 33194@<Output the character information bytes, then 33195 output the dimensions themselves@>; 33196@<Output the ligature/kern program@>; 33197@<Output the extensible character recipes and the font metric parameters@>; 33198if (number_positive (internal_value (mp_tracing_stats))) 33199 @<Log the subfile sizes of the \.{TFM} file@>; 33200mp_print_nl (mp, "Font metrics written on "); 33201mp_print (mp, mp->metric_file_name); 33202mp_print_char (mp, xord ('.')); 33203@.Font metrics written...@>; 33204(mp->close_file) (mp, mp->tfm_file) 33205 33206 33207@ Integer variables |lh|, |k|, and |lk_offset| will be defined when we use 33208this code. 33209 33210@<Output the subfile sizes and header bytes@>= 33211k = mp->header_last; 33212LH = (k + 4) / 4; /* this is the number of header words */ 33213if (mp->bc > mp->ec) 33214 mp->bc = 1; /* if there are no characters, |ec=0| and |bc=1| */ 33215@<Compute the ligature/kern program offset and implant the 33216 left boundary label@>; 33217mp_tfm_two (mp, 33218 6 + LH + (mp->ec - mp->bc + 1) + mp->nw + mp->nh + mp->nd + mp->ni + 33219 mp->nl + lk_offset + mp->nk + mp->ne + mp->np); 33220 /* this is the total number of file words that will be output */ 33221mp_tfm_two (mp, LH); 33222mp_tfm_two (mp, mp->bc); 33223mp_tfm_two (mp, mp->ec); 33224mp_tfm_two (mp, mp->nw); 33225mp_tfm_two (mp, mp->nh); 33226mp_tfm_two (mp, mp->nd); 33227mp_tfm_two (mp, mp->ni); 33228mp_tfm_two (mp, mp->nl + lk_offset); 33229mp_tfm_two (mp, mp->nk); 33230mp_tfm_two (mp, mp->ne); 33231mp_tfm_two (mp, mp->np); 33232for (k = 0; k < 4 * LH; k++) { 33233 tfm_out (mp->header_byte[k]); 33234} 33235 33236 33237@ @<Output the character information bytes...@>= 33238for (k = mp->bc; k <= mp->ec; k++) { 33239 if (!mp->char_exists[k]) { 33240 mp_tfm_four (mp, 0); 33241 } else { 33242 tfm_out (indep_value (mp->tfm_width[k])); /* the width index */ 33243 tfm_out ((indep_value (mp->tfm_height[k])) * 16 + indep_value (mp->tfm_depth[k])); 33244 tfm_out ((indep_value (mp->tfm_ital_corr[k])) * 4 + mp->char_tag[k]); 33245 tfm_out (mp->char_remainder[k]); 33246 }; 33247} 33248mp->tfm_changed = 0; 33249for (k = 1; k <= 4; k++) { 33250 mp_tfm_four (mp, 0); 33251 p = mp->dimen_head[k]; 33252 while (p != mp->inf_val) { 33253 mp_tfm_four (mp, mp_dimen_out (mp, value_number (p))); 33254 p = mp_link (p); 33255 } 33256} 33257 33258 33259@ We need to output special instructions at the beginning of the 33260|lig_kern| array in order to specify the right boundary character 33261and/or to handle starting addresses that exceed 255. The |label_loc| 33262and |label_char| arrays have been set up to record all the 33263starting addresses; we have $-1=|label_loc|[0]<|label_loc|[1]\le\cdots 33264\le|label_loc|[|label_ptr]|$. 33265 33266@<Compute the ligature/kern program offset...@>= 33267mp->bchar = round_unscaled (internal_value (mp_boundary_char)); 33268if ((mp->bchar < 0) || (mp->bchar > 255)) { 33269 mp->bchar = -1; 33270 mp->lk_started = false; 33271 lk_offset = 0; 33272} else { 33273 mp->lk_started = true; 33274 lk_offset = 1; 33275} 33276@<Find the minimum |lk_offset| and adjust all remainders@>; 33277if (mp->bch_label < undefined_label) { 33278 skip_byte (mp->nl) = qi (255); 33279 next_char (mp->nl) = qi (0); 33280 op_byte (mp->nl) = qi (((mp->bch_label + lk_offset) / 256)); 33281 rem_byte (mp->nl) = qi (((mp->bch_label + lk_offset) % 256)); 33282 mp->nl++; /* possibly |nl=lig_table_size+1| */ 33283} 33284 33285@ @<Find the minimum |lk_offset|...@>= 33286k = mp->label_ptr; /* pointer to the largest unallocated label */ 33287if (mp->label_loc[k] + lk_offset > 255) { 33288 lk_offset = 0; 33289 mp->lk_started = false; /* location 0 can do double duty */ 33290 do { 33291 mp->char_remainder[mp->label_char[k]] = lk_offset; 33292 while (mp->label_loc[k - 1] == mp->label_loc[k]) { 33293 decr (k); 33294 mp->char_remainder[mp->label_char[k]] = lk_offset; 33295 } 33296 incr (lk_offset); 33297 decr (k); 33298 } while (!(lk_offset + mp->label_loc[k] < 256)); 33299 /* N.B.: |lk_offset=256| satisfies this when |k=0| */ 33300} 33301if (lk_offset > 0) { 33302 while (k > 0) { 33303 mp->char_remainder[mp->label_char[k]] 33304 = mp->char_remainder[mp->label_char[k]] + lk_offset; 33305 decr (k); 33306 } 33307} 33308 33309@ @<Output the ligature/kern program@>= 33310for (k = 0; k <= 255; k++) { 33311 if (mp->skip_table[k] < undefined_label) { 33312 mp_print_nl (mp, "(local label "); 33313 mp_print_int (mp, k); 33314 mp_print (mp, ":: was missing)"); 33315@.local label l:: was missing@>; 33316 cancel_skips (mp->skip_table[k]); 33317 } 33318} 33319if (mp->lk_started) { /* |lk_offset=1| for the special |bchar| */ 33320 tfm_out (255); 33321 tfm_out (mp->bchar); 33322 mp_tfm_two (mp, 0); 33323} else { 33324 for (k = 1; k <= lk_offset; k++) { /* output the redirection specs */ 33325 mp->ll = mp->label_loc[mp->label_ptr]; 33326 if (mp->bchar < 0) { 33327 tfm_out (254); 33328 tfm_out (0); 33329 } else { 33330 tfm_out (255); 33331 tfm_out (mp->bchar); 33332 }; 33333 mp_tfm_two (mp, mp->ll + lk_offset); 33334 do { 33335 mp->label_ptr--; 33336 } while (!(mp->label_loc[mp->label_ptr] < mp->ll)); 33337 } 33338} 33339for (k = 0; k < mp->nl; k++) 33340 mp_tfm_qqqq (mp, mp->lig_kern[k]); 33341{ 33342 mp_number arg; 33343 new_number (arg); 33344 for (k = 0; k < mp->nk; k++) { 33345 number_clone (arg, mp->kern[k]); 33346 mp_tfm_four (mp, mp_dimen_out (mp, arg)); 33347 } 33348 free_number (arg); 33349} 33350 33351@ @<Output the extensible character recipes...@>= 33352for (k = 0; k < mp->ne; k++) 33353 mp_tfm_qqqq (mp, mp->exten[k]); 33354{ 33355mp_number arg; 33356new_number (arg); 33357for (k = 1; k <= mp->np; k++) { 33358 if (k == 1) { 33359 number_clone (arg, mp->param[1]); 33360 number_abs (arg); 33361 if (number_less(arg, fraction_half_t)) { 33362 mp_tfm_four (mp, number_to_scaled (mp->param[1]) * 16); 33363 } else { 33364 incr (mp->tfm_changed); 33365 if (number_positive(mp->param[1])) 33366 mp_tfm_four (mp, max_integer); 33367 else 33368 mp_tfm_four (mp, -max_integer); 33369 } 33370 } else { 33371 number_clone (arg, mp->param[k]); 33372 mp_tfm_four (mp, mp_dimen_out (mp, arg)); 33373 } 33374} 33375free_number (arg); 33376} 33377if (mp->tfm_changed > 0) { 33378 if (mp->tfm_changed == 1) { 33379 mp_print_nl (mp, "(a font metric dimension"); 33380@.a font metric dimension...@> 33381 } else { 33382 mp_print_nl (mp, "("); 33383 mp_print_int (mp, mp->tfm_changed); 33384@.font metric dimensions...@>; 33385 mp_print (mp, " font metric dimensions"); 33386 } 33387 mp_print (mp, " had to be decreased)"); 33388} 33389 33390@ @<Log the subfile sizes of the \.{TFM} file@>= 33391{ 33392 char s[200]; 33393 wlog_ln (" "); 33394 if (mp->bch_label < undefined_label) 33395 mp->nl--; 33396 mp_snprintf (s, 128, 33397 "(You used %iw,%ih,%id,%ii,%il,%ik,%ie,%ip metric file positions)", 33398 mp->nw, mp->nh, mp->nd, mp->ni, mp->nl, mp->nk, mp->ne, mp->np); 33399 wlog_ln (s); 33400} 33401 33402 33403@* Reading font metric data. 33404 33405\MP\ isn't a typesetting program but it does need to find the bounding box 33406of a sequence of typeset characters. Thus it needs to read \.{TFM} files as 33407well as write them. 33408 33409@<Glob...@>= 33410void *tfm_infile; 33411 33412@ All the width, height, and depth information is stored in an array called 33413|font_info|. This array is allocated sequentially and each font is stored 33414as a series of |char_info| words followed by the width, height, and depth 33415tables. Since |font_name| entries are permanent, their |str_ref| values are 33416set to |MAX_STR_REF|. 33417 33418@<Types...@>= 33419typedef unsigned int font_number; /* |0..font_max| */ 33420 33421@ The |font_info| array is indexed via a group directory arrays. 33422For example, the |char_info| data for character~|c| in font~|f| will be 33423in |font_info[char_base[f]+c].qqqq|. 33424 33425@<Glob...@>= 33426font_number font_max; /* maximum font number for included text fonts */ 33427size_t font_mem_size; /* number of words for \.{TFM} information for text fonts */ 33428font_data *font_info; /* height, width, and depth data */ 33429char **font_enc_name; /* encoding names, if any */ 33430boolean *font_ps_name_fixed; /* are the postscript names fixed already? */ 33431size_t next_fmem; /* next unused entry in |font_info| */ 33432font_number last_fnum; /* last font number used so far */ 33433integer *font_dsize; /* 16 times the ``design'' size in \ps\ points */ 33434char **font_name; /* name as specified in the \&{infont} command */ 33435char **font_ps_name; /* PostScript name for use when |internal[mp_prologues]>0| */ 33436font_number last_ps_fnum; /* last valid |font_ps_name| index */ 33437eight_bits *font_bc; 33438eight_bits *font_ec; /* first and last character code */ 33439int *char_base; /* base address for |char_info| */ 33440int *width_base; /* index for zeroth character width */ 33441int *height_base; /* index for zeroth character height */ 33442int *depth_base; /* index for zeroth character depth */ 33443mp_node *font_sizes; 33444 33445@ @<Allocate or initialize ...@>= 33446mp->font_mem_size = 10000; 33447mp->font_info = xmalloc ((mp->font_mem_size + 1), sizeof (font_data)); 33448memset (mp->font_info, 0, sizeof (font_data) * (mp->font_mem_size + 1)); 33449mp->last_fnum = null_font; 33450 33451@ @<Dealloc variables@>= 33452for (k = 1; k <= (int) mp->last_fnum; k++) { 33453 xfree (mp->font_enc_name[k]); 33454 xfree (mp->font_name[k]); 33455 xfree (mp->font_ps_name[k]); 33456} 33457for (k = 0; k <= 255; k++) { 33458/* These are disabled for now following a bug-report about double free 33459 errors. TO BE FIXED, bug tracker id 831 */ 33460/*| 33461 mp_free_value_node (mp, mp->tfm_width[k]); 33462 mp_free_value_node (mp, mp->tfm_height[k]); 33463 mp_free_value_node (mp, mp->tfm_depth[k]); 33464 mp_free_value_node (mp, mp->tfm_ital_corr[k]); 33465|*/ 33466} 33467 33468xfree (mp->font_info); 33469xfree (mp->font_enc_name); 33470xfree (mp->font_ps_name_fixed); 33471xfree (mp->font_dsize); 33472xfree (mp->font_name); 33473xfree (mp->font_ps_name); 33474xfree (mp->font_bc); 33475xfree (mp->font_ec); 33476xfree (mp->char_base); 33477xfree (mp->width_base); 33478xfree (mp->height_base); 33479xfree (mp->depth_base); 33480xfree (mp->font_sizes); 33481 33482@ 33483@c 33484void mp_reallocate_fonts (MP mp, font_number l) { 33485 font_number f; 33486 XREALLOC (mp->font_enc_name, l, char *); 33487 XREALLOC (mp->font_ps_name_fixed, l, boolean); 33488 XREALLOC (mp->font_dsize, l, integer); 33489 XREALLOC (mp->font_name, l, char *); 33490 XREALLOC (mp->font_ps_name, l, char *); 33491 XREALLOC (mp->font_bc, l, eight_bits); 33492 XREALLOC (mp->font_ec, l, eight_bits); 33493 XREALLOC (mp->char_base, l, int); 33494 XREALLOC (mp->width_base, l, int); 33495 XREALLOC (mp->height_base, l, int); 33496 XREALLOC (mp->depth_base, l, int); 33497 XREALLOC (mp->font_sizes, l, mp_node); 33498 for (f = (mp->last_fnum + 1); f <= l; f++) { 33499 mp->font_enc_name[f] = NULL; 33500 mp->font_ps_name_fixed[f] = false; 33501 mp->font_name[f] = NULL; 33502 mp->font_ps_name[f] = NULL; 33503 mp->font_sizes[f] = NULL; 33504 } 33505 mp->font_max = l; 33506} 33507 33508 33509@ @<Internal library declarations@>= 33510void mp_reallocate_fonts (MP mp, font_number l); 33511 33512 33513@ A |null_font| containing no characters is useful for error recovery. Its 33514|font_name| entry starts out empty but is reset each time an erroneous font is 33515found. This helps to cut down on the number of duplicate error messages without 33516wasting a lot of space. 33517 33518@d null_font 0 /* the |font_number| for an empty font */ 33519 33520@<Set initial...@>= 33521mp->font_dsize[null_font] = 0; 33522mp->font_bc[null_font] = 1; 33523mp->font_ec[null_font] = 0; 33524mp->char_base[null_font] = 0; 33525mp->width_base[null_font] = 0; 33526mp->height_base[null_font] = 0; 33527mp->depth_base[null_font] = 0; 33528mp->next_fmem = 0; 33529mp->last_fnum = null_font; 33530mp->last_ps_fnum = null_font; 33531{ 33532 static char nullfont_name[] = "nullfont"; 33533 static char nullfont_psname[] = ""; 33534 mp->font_name[null_font] = nullfont_name; 33535 mp->font_ps_name[null_font] = nullfont_psname; 33536} 33537mp->font_ps_name_fixed[null_font] = false; 33538mp->font_enc_name[null_font] = NULL; 33539mp->font_sizes[null_font] = NULL; 33540 33541@ Each |char_info| word is of type |four_quarters|. The |b0| field contains 33542the |width index|; the |b1| field contains the height 33543index; the |b2| fields contains the depth index, and the |b3| field used only 33544for temporary storage. (It is used to keep track of which characters occur in 33545an edge structure that is being shipped out.) 33546The corresponding words in the width, height, and depth tables are stored as 33547|scaled| values in units of \ps\ points. 33548 33549With the macros below, the |char_info| word for character~|c| in font~|f| is 33550|char_mp_info(f,c)| and the width is 33551$$\hbox{|char_width(f,char_mp_info(f,c)).sc|.}$$ 33552 33553@d char_mp_info(A,B) mp->font_info[mp->char_base[(A)]+(B)].qqqq 33554@d char_width(A,B) mp->font_info[mp->width_base[(A)]+(B).b0].sc 33555@d char_height(A,B) mp->font_info[mp->height_base[(A)]+(B).b1].sc 33556@d char_depth(A,B) mp->font_info[mp->depth_base[(A)]+(B).b2].sc 33557@d ichar_exists(A) ((A).b0>0) 33558 33559@ When we have a font name and we don't know whether it has been loaded yet, 33560we scan the |font_name| array before calling |read_font_info|. 33561 33562@<Declarations@>= 33563static font_number mp_find_font (MP mp, char *f); 33564 33565@ @c 33566font_number mp_find_font (MP mp, char *f) { 33567 font_number n; 33568 for (n = 0; n <= mp->last_fnum; n++) { 33569 if (mp_xstrcmp (f, mp->font_name[n]) == 0) { 33570 return n; 33571 } 33572 } 33573 n = mp_read_font_info (mp, f); 33574 return n; 33575} 33576 33577 33578@ This is an interface function for getting the width of character, 33579as a double in ps units 33580 33581@c 33582double mp_get_char_dimension (MP mp, char *fname, int c, int t) { 33583 unsigned n; 33584 four_quarters cc; 33585 font_number f = 0; 33586 double w = -1.0; 33587 for (n = 0; n <= mp->last_fnum; n++) { 33588 if (mp_xstrcmp (fname, mp->font_name[n]) == 0) { 33589 f = n; 33590 break; 33591 } 33592 } 33593 if (f == 0) 33594 return 0.0; 33595 cc = char_mp_info (f, c); 33596 if (!ichar_exists (cc)) 33597 return 0.0; 33598 if (t == 'w') 33599 w = (double) char_width (f, cc); 33600 else if (t == 'h') 33601 w = (double) char_height (f, cc); 33602 else if (t == 'd') 33603 w = (double) char_depth (f, cc); 33604 return w / 655.35 * (72.27 / 72); 33605} 33606 33607 33608@ @<Exported function ...@>= 33609double mp_get_char_dimension (MP mp, char *fname, int n, int t); 33610 33611 33612@ If we discover that the font doesn't have a requested character, we omit it 33613from the bounding box computation and expect the \ps\ interpreter to drop it. 33614This routine issues a warning message if the user has asked for it. 33615 33616@<Declarations@>= 33617static void mp_lost_warning (MP mp, font_number f, int k); 33618 33619@ @c 33620void mp_lost_warning (MP mp, font_number f, int k) { 33621 if (number_positive (internal_value (mp_tracing_lost_chars))) { 33622 mp_begin_diagnostic (mp); 33623 if (mp->selector == log_only) 33624 incr (mp->selector); 33625 mp_print_nl (mp, "Missing character: There is no "); 33626@.Missing character@>; 33627 mp_print_int (mp, k); 33628 mp_print (mp, " in font "); 33629 mp_print (mp, mp->font_name[f]); 33630 mp_print_char (mp, xord ('!')); 33631 mp_end_diagnostic (mp, false); 33632 } 33633} 33634 33635 33636@ The whole purpose of saving the height, width, and depth information is to be 33637able to find the bounding box of an item of text in an edge structure. The 33638|set_text_box| procedure takes a text node and adds this information. 33639 33640@<Declarations@>= 33641static void mp_set_text_box (MP mp, mp_text_node p); 33642 33643@ @c 33644void mp_set_text_box (MP mp, mp_text_node p) { 33645 font_number f; /* |mp_font_n(p)| */ 33646 ASCII_code bc, ec; /* range of valid characters for font |f| */ 33647 size_t k, kk; /* current character and character to stop at */ 33648 four_quarters cc; /* the |char_info| for the current character */ 33649 mp_number h, d; /* dimensions of the current character */ 33650 new_number(h); 33651 new_number(d); 33652 set_number_to_zero(p->width); 33653 set_number_to_neg_inf(p->height); 33654 set_number_to_neg_inf(p->depth); 33655 f = (font_number) mp_font_n (p); 33656 bc = mp->font_bc[f]; 33657 ec = mp->font_ec[f]; 33658 kk = mp_text_p (p)->len; 33659 k = 0; 33660 while (k < kk) { 33661 @<Adjust |p|'s bounding box to contain |str_pool[k]|; advance |k|@>; 33662 } 33663 @<Set the height and depth to zero if the bounding box is empty@>; 33664 free_number (h); 33665 free_number (d); 33666} 33667 33668 33669@ @<Adjust |p|'s bounding box to contain |str_pool[k]|; advance |k|@>= 33670{ 33671 if ((*(mp_text_p (p)->str + k) < bc) || (*(mp_text_p (p)->str + k) > ec)) { 33672 mp_lost_warning (mp, f, *(mp_text_p (p)->str + k)); 33673 } else { 33674 cc = char_mp_info (f, *(mp_text_p (p)->str + k)); 33675 if (!ichar_exists (cc)) { 33676 mp_lost_warning (mp, f, *(mp_text_p (p)->str + k)); 33677 } else { 33678 set_number_from_scaled(p->width, number_to_scaled(p->width) + char_width (f, cc)); 33679 set_number_from_scaled(h, char_height (f, cc)); 33680 set_number_from_scaled(d, char_depth (f, cc)); 33681 if (number_greater(h, p->height)) 33682 number_clone(p->height, h); 33683 if (number_greater(d, p->depth)) 33684 number_clone(p->depth, d); 33685 } 33686 } 33687 incr (k); 33688} 33689 33690 33691@ Let's hope modern compilers do comparisons correctly when the difference would 33692overflow. 33693 33694@<Set the height and depth to zero if the bounding box is empty@>= 33695if (number_to_scaled(p->height) < -number_to_scaled(p->depth)) { 33696 set_number_to_zero(p->height); 33697 set_number_to_zero(p->depth); 33698} 33699 33700@ The new primitives fontmapfile and fontmapline. 33701 33702@<Declare action procedures for use by |do_statement|@>= 33703static void mp_do_mapfile (MP mp); 33704static void mp_do_mapline (MP mp); 33705 33706@ @c 33707static void mp_do_mapfile (MP mp) { 33708 mp_get_x_next (mp); 33709 mp_scan_expression (mp); 33710 if (mp->cur_exp.type != mp_string_type) { 33711 @<Complain about improper map operation@>; 33712 } else { 33713 mp_map_file (mp, cur_exp_str ()); 33714 } 33715} 33716static void mp_do_mapline (MP mp) { 33717 mp_get_x_next (mp); 33718 mp_scan_expression (mp); 33719 if (mp->cur_exp.type != mp_string_type) { 33720 @<Complain about improper map operation@>; 33721 } else { 33722 mp_map_line (mp, cur_exp_str ()); 33723 } 33724} 33725 33726 33727@ @<Complain about improper map operation@>= 33728{ 33729 const char *hlp[] = { "Only known strings can be map files or map lines.", NULL }; 33730 mp_disp_err(mp, NULL); 33731 mp_back_error (mp, "Unsuitable expression", hlp, true); 33732 mp_get_x_next (mp); 33733} 33734 33735 33736@ To print |scaled| value to PDF output we need some subroutines to ensure 33737accurary. 33738 33739@d max_integer 0x7FFFFFFF /* $2^{31}-1$ */ 33740 33741@<Glob...@>= 33742integer ten_pow[10]; /* $10^0..10^9$ */ 33743integer scaled_out; /* amount of |scaled| that was taken out in |divide_scaled| */ 33744 33745@ @<Set init...@>= 33746mp->ten_pow[0] = 1; 33747for (i = 1; i <= 9; i++) { 33748 mp->ten_pow[i] = 10 * mp->ten_pow[i - 1]; 33749} 33750 33751 33752@* Shipping pictures out. 33753The |ship_out| procedure, to be described below, is given a pointer to 33754an edge structure. Its mission is to output a file containing the \ps\ 33755description of an edge structure. 33756 33757@ Each time an edge structure is shipped out we write a new \ps\ output 33758file named according to the current \&{charcode}. 33759@:char_code_}{\&{charcode} primitive@> 33760 33761This is the only backend function that remains in the main |mpost.w| file. 33762There are just too many variable accesses needed for status reporting 33763etcetera to make it worthwile to move the code to |psout.w|. 33764 33765@<Internal library declarations@>= 33766void mp_open_output_file (MP mp); 33767char *mp_get_output_file_name (MP mp); 33768char *mp_set_output_file_name (MP mp, integer c); 33769 33770@ @c 33771static void mp_append_to_template (MP mp, integer ff, integer c, boolean rounding) { 33772 if (internal_type (c) == mp_string_type) { 33773 char *ss = mp_str (mp, internal_string (c)); 33774 mp_print (mp, ss); 33775 } else if (internal_type (c) == mp_known) { 33776 if (rounding) { 33777 int cc = round_unscaled (internal_value (c)); 33778 print_with_leading_zeroes (cc, ff); 33779 } else { 33780 print_number (internal_value (c)); 33781 } 33782 } 33783} 33784char *mp_set_output_file_name (MP mp, integer c) { 33785 char *ss = NULL; /* filename extension proposal */ 33786 char *nn = NULL; /* temp string for str() */ 33787 unsigned old_setting; /* previous |selector| setting */ 33788 size_t i; /* indexes into |filename_template| */ 33789 integer f; /* field width */ 33790 str_room (1024); 33791 if (mp->job_name == NULL) 33792 mp_open_log_file (mp); 33793 if (internal_string (mp_output_template) == NULL) { 33794 char *s; /* a file extension derived from |c| */ 33795 if (c < 0) 33796 s = xstrdup (".ps"); 33797 else 33798 @<Use |c| to compute the file extension |s|@>; 33799 mp_pack_job_name (mp, s); 33800 free (s); 33801 ss = xstrdup (mp->name_of_file); 33802 } else { /* initializations */ 33803 mp_string s, n, ftemplate; /* a file extension derived from |c| */ 33804 mp_number saved_char_code; 33805 new_number (saved_char_code); 33806 number_clone (saved_char_code, internal_value (mp_char_code)); 33807 set_internal_from_number (mp_char_code, unity_t); 33808 number_multiply_int (internal_value (mp_char_code), c); 33809 if (internal_string (mp_job_name) == NULL) { 33810 if (mp->job_name == NULL) { 33811 mp->job_name = xstrdup ("mpout"); 33812 } 33813 @<Fix up |mp->internal[mp_job_name]|@>; 33814 } 33815 old_setting = mp->selector; 33816 mp->selector = new_string; 33817 i = 0; 33818 n = mp_rts(mp,""); /* initialize */ 33819 ftemplate = internal_string (mp_output_template); 33820 while (i < ftemplate->len) { 33821 f = 0; 33822 if (*(ftemplate->str + i) == '%') { 33823 CONTINUE: 33824 incr (i); 33825 if (i < ftemplate->len) { 33826 switch (*(ftemplate->str + i)) { 33827 case 'j': 33828 mp_append_to_template (mp, f, mp_job_name, true); 33829 break; 33830 case 'c': 33831 if (number_negative (internal_value (mp_char_code))) { 33832 mp_print (mp, "ps"); 33833 } else { 33834 mp_append_to_template (mp, f, mp_char_code, true); 33835 } 33836 break; 33837 case 'o': 33838 mp_append_to_template (mp, f, mp_output_format, true); 33839 break; 33840 case 'd': 33841 mp_append_to_template (mp, f, mp_day, true); 33842 break; 33843 case 'm': 33844 mp_append_to_template (mp, f, mp_month, true); 33845 break; 33846 case 'y': 33847 mp_append_to_template (mp, f, mp_year, true); 33848 break; 33849 case 'H': 33850 mp_append_to_template (mp, f, mp_hour, true); 33851 break; 33852 case 'M': 33853 mp_append_to_template (mp, f, mp_minute, true); 33854 break; 33855 case '{': 33856 { 33857 /* look up a name */ 33858 size_t l = 0; 33859 size_t frst = i + 1; 33860 while (i < ftemplate->len) { 33861 i++; 33862 if (*(ftemplate->str + i) == '}') 33863 break; 33864 l++; 33865 } 33866 if (l > 0) { 33867 mp_sym p = 33868 mp_id_lookup (mp, (char *) (ftemplate->str + frst), l, false); 33869 char *id = xmalloc ((l + 1), 1); 33870 (void) memcpy (id, (char *) (ftemplate->str + frst), (size_t) l); 33871 *(id + l) = '\0'; 33872 if (p == NULL) { 33873 char err[256]; 33874 mp_snprintf (err, 256, 33875 "requested identifier (%s) in outputtemplate not found.", 33876 id); 33877 mp_warn (mp, err); 33878 } else { 33879 if (eq_type (p) == mp_internal_quantity) { 33880 if (equiv (p) == mp_output_template) { 33881 char err[256]; 33882 mp_snprintf (err, 256, 33883 "The appearance of outputtemplate inside outputtemplate is ignored."); 33884 mp_warn (mp, err); 33885 } else { 33886 mp_append_to_template (mp, f, equiv (p), false); 33887 } 33888 } else { 33889 char err[256]; 33890 mp_snprintf (err, 256, 33891 "requested identifier (%s) in outputtemplate is not an internal.", 33892 id); 33893 mp_warn (mp, err); 33894 } 33895 } 33896 free (id); 33897 } 33898 } 33899 break; 33900 case '0': 33901 case '1': 33902 case '2': 33903 case '3': 33904 case '4': 33905 case '5': 33906 case '6': 33907 case '7': 33908 case '8': 33909 case '9': 33910 if ((f < 10)) 33911 f = (f * 10) + ftemplate->str[i] - '0'; 33912 goto CONTINUE; 33913 break; 33914 case '%': 33915 mp_print_char (mp, '%'); 33916 break; 33917 default: 33918 { 33919 char err[256]; 33920 mp_snprintf (err, 256, 33921 "requested format (%c) in outputtemplate is unknown.", 33922 *(ftemplate->str + i)); 33923 mp_warn (mp, err); 33924 } 33925 mp_print_char (mp, *(ftemplate->str + i)); 33926 } 33927 } 33928 } else { 33929 if (*(ftemplate->str + i) == '.') 33930 if (n->len == 0) 33931 n = mp_make_string (mp); 33932 mp_print_char (mp, *(ftemplate->str + i)); 33933 }; 33934 incr (i); 33935 } 33936 s = mp_make_string (mp); 33937 number_clone (internal_value (mp_char_code), saved_char_code); 33938 free_number (saved_char_code); 33939 mp->selector = old_setting; 33940 if (n->len == 0) { 33941 n = s; 33942 s = mp_rts(mp,""); 33943 } 33944 ss = mp_str (mp, s); 33945 nn = mp_str (mp, n); 33946 mp_pack_file_name (mp, nn, "", ss); 33947 delete_str_ref (n); 33948 delete_str_ref (s); 33949 } 33950 return ss; 33951} 33952char *mp_get_output_file_name (MP mp) { 33953 char *f; 33954 char *saved_name; /* saved |name_of_file| */ 33955 saved_name = xstrdup (mp->name_of_file); 33956 (void) mp_set_output_file_name (mp, round_unscaled (internal_value(mp_char_code))); 33957 f = xstrdup (mp->name_of_file); 33958 mp_pack_file_name (mp, saved_name, NULL, NULL); 33959 free (saved_name); 33960 return f; 33961} 33962void mp_open_output_file (MP mp) { 33963 char *ss; /* filename extension proposal */ 33964 int c; /* \&{charcode} rounded to the nearest integer */ 33965 c = round_unscaled (internal_value (mp_char_code)); 33966 ss = mp_set_output_file_name (mp, c); 33967 while (!mp_open_out (mp, (void *) &mp->output_file, mp_filetype_postscript)) 33968 mp_prompt_file_name (mp, "file name for output", ss); 33969 mp_store_true_output_filename (mp, c); 33970} 33971 33972 33973@ The file extension created here could be up to five characters long in 33974extreme cases so it may have to be shortened on some systems. 33975@^system dependencies@> 33976 33977@<Use |c| to compute the file extension |s|@>= 33978{ 33979 s = xmalloc (7, 1); 33980 mp_snprintf (s, 7, ".%i", (int) c); 33981} 33982 33983 33984@ The user won't want to see all the output file names so we only save the 33985first and last ones and a count of how many there were. For this purpose 33986files are ordered primarily by \&{charcode} and secondarily by order of 33987creation. 33988@:char_code_}{\&{charcode} primitive@> 33989 33990@<Internal library ...@>= 33991void mp_store_true_output_filename (MP mp, int c); 33992 33993@ @c 33994void mp_store_true_output_filename (MP mp, int c) 33995{ 33996 if ((c < mp->first_output_code) && (mp->first_output_code >= 0)) { 33997 mp->first_output_code = c; 33998 xfree (mp->first_file_name); 33999 mp->first_file_name = xstrdup (mp->name_of_file); 34000 } 34001 if (c >= mp->last_output_code) { 34002 mp->last_output_code = c; 34003 xfree (mp->last_file_name); 34004 mp->last_file_name = xstrdup (mp->name_of_file); 34005 } 34006 set_internal_string (mp_output_filename, mp_rts (mp, mp->name_of_file)); 34007} 34008 34009@ @<Glob...@>= 34010char *first_file_name; 34011char *last_file_name; /* full file names */ 34012integer first_output_code; 34013integer last_output_code; /* rounded \&{charcode} values */ 34014@:char_code_}{\&{charcode} primitive@> 34015integer total_shipped; /* total number of |ship_out| operations completed */ 34016 34017@ @<Set init...@>= 34018mp->first_file_name = xstrdup (""); 34019mp->last_file_name = xstrdup (""); 34020mp->first_output_code = 32768; 34021mp->last_output_code = -32768; 34022mp->total_shipped = 0; 34023 34024@ @<Dealloc variables@>= 34025xfree (mp->first_file_name); 34026xfree (mp->last_file_name); 34027 34028@ @<Begin the progress report for the output of picture~|c|@>= 34029if ((int) mp->term_offset > mp->max_print_line - 6) 34030 mp_print_ln (mp); 34031else if ((mp->term_offset > 0) || (mp->file_offset > 0)) 34032 mp_print_char (mp, xord (' ')); 34033mp_print_char (mp, xord ('[')); 34034if (c >= 0) 34035 mp_print_int (mp, c) 34036 34037 34038@ @<End progress report@>= 34039mp_print_char (mp, xord (']')); 34040update_terminal(); 34041incr (mp->total_shipped) 34042 34043 34044@ @<Explain what output files were written@>= 34045if (mp->total_shipped > 0) { 34046 mp_print_nl (mp, ""); 34047 mp_print_int (mp, mp->total_shipped); 34048 if (mp->noninteractive) { 34049 mp_print (mp, " figure"); 34050 if (mp->total_shipped > 1) 34051 mp_print_char (mp, xord ('s')); 34052 mp_print (mp, " created."); 34053 } else { 34054 mp_print (mp, " output file"); 34055 if (mp->total_shipped > 1) 34056 mp_print_char (mp, xord ('s')); 34057 mp_print (mp, " written: "); 34058 mp_print (mp, mp->first_file_name); 34059 if (mp->total_shipped > 1) { 34060 if (31 + strlen (mp->first_file_name) + 34061 strlen (mp->last_file_name) > (unsigned) mp->max_print_line) 34062 mp_print_ln (mp); 34063 mp_print (mp, " .. "); 34064 mp_print (mp, mp->last_file_name); 34065 } 34066 mp_print_nl (mp, ""); 34067 } 34068} 34069 34070@ @<Internal library declarations@>= 34071boolean mp_has_font_size (MP mp, font_number f); 34072 34073@ @c 34074boolean mp_has_font_size (MP mp, font_number f) { 34075 return (mp->font_sizes[f] != NULL); 34076} 34077 34078 34079@ The \&{special} command saves up lines of text to be printed during the next 34080|ship_out| operation. The saved items are stored as a list of capsule tokens. 34081 34082@<Glob...@>= 34083mp_node last_pending; /* the last token in a list of pending specials */ 34084 34085 34086@ @<Declare action procedures for use by |do_statement|@>= 34087static void mp_do_special (MP mp); 34088 34089@ @c 34090void mp_do_special (MP mp) { 34091 mp_get_x_next (mp); 34092 mp_scan_expression (mp); 34093 if (mp->cur_exp.type != mp_string_type) { 34094 @<Complain about improper special operation@>; 34095 } else { 34096 mp_link (mp->last_pending) = mp_stash_cur_exp (mp); 34097 mp->last_pending = mp_link (mp->last_pending); 34098 mp_link (mp->last_pending) = NULL; 34099 } 34100} 34101 34102 34103@ @<Complain about improper special operation@>= 34104{ 34105 const char *hlp[] = { "Only known strings are allowed for output as specials.", NULL }; 34106 mp_disp_err(mp, NULL); 34107 mp_back_error (mp, "Unsuitable expression", hlp, true); 34108 mp_get_x_next (mp); 34109} 34110 34111 34112@ On the export side, we need an extra object type for special strings. 34113 34114@<Graphical object codes@>= 34115mp_special_code = 8, 34116 34117@ @<Export pending specials@>= 34118p = mp_link (mp->spec_head); 34119while (p != NULL) { 34120 mp_special_object *tp; 34121 tp = (mp_special_object *) mp_new_graphic_object (mp, mp_special_code); 34122 gr_pre_script (tp) = mp_xstrdup(mp,mp_str (mp, value_str (p))); 34123 if (hh->body == NULL) 34124 hh->body = (mp_graphic_object *) tp; 34125 else 34126 gr_link (hp) = (mp_graphic_object *) tp; 34127 hp = (mp_graphic_object *) tp; 34128 p = mp_link (p); 34129} 34130mp_flush_token_list (mp, mp_link (mp->spec_head)); 34131mp_link (mp->spec_head) = NULL; 34132mp->last_pending = mp->spec_head 34133 34134@ We are now ready for the main output procedure. Note that the |selector| 34135setting is saved in a global variable so that |begin_diagnostic| can access it. 34136 34137@<Declare the \ps\ output procedures@>= 34138static void mp_ship_out (MP mp, mp_node h); 34139 34140@ Once again, the |gr_XXXX| macros are defined in |mppsout.h| 34141 34142@d export_color(q,p) 34143 if ( mp_color_model(p)==mp_uninitialized_model ) { 34144 gr_color_model(q) = (unsigned char)(number_to_scaled (internal_value(mp_default_color_model))/65536); 34145 gr_cyan_val(q) = 0; 34146 gr_magenta_val(q) = 0; 34147 gr_yellow_val(q) = 0; 34148 gr_black_val(q) = ((gr_color_model(q)==mp_cmyk_model ? number_to_scaled (unity_t) : 0) / 65536.0); 34149 } else { 34150 gr_color_model(q) = (unsigned char)mp_color_model(p); 34151 gr_cyan_val(q) = number_to_double(p->cyan); 34152 gr_magenta_val(q) = number_to_double(p->magenta); 34153 gr_yellow_val(q) = number_to_double(p->yellow); 34154 gr_black_val(q) = number_to_double(p->black); 34155 } 34156 34157@d export_scripts(q,p) 34158 if (mp_pre_script(p)!=NULL) gr_pre_script(q) = mp_xstrdup(mp, mp_str(mp,mp_pre_script(p))); 34159 if (mp_post_script(p)!=NULL) gr_post_script(q) = mp_xstrdup(mp, mp_str(mp,mp_post_script(p))); 34160 34161@c 34162struct mp_edge_object *mp_gr_export (MP mp, mp_edge_header_node h) { 34163 mp_node p; /* the current graphical object */ 34164 integer t; /* a temporary value */ 34165 integer c; /* a rounded charcode */ 34166 mp_number d_width; /* the current pen width */ 34167 mp_edge_object *hh; /* the first graphical object */ 34168 mp_graphic_object *hq; /* something |hp| points to */ 34169 mp_text_object *tt; 34170 mp_fill_object *tf; 34171 mp_stroked_object *ts; 34172 mp_clip_object *tc; 34173 mp_bounds_object *tb; 34174 mp_graphic_object *hp = NULL; /* the current graphical object */ 34175 mp_set_bbox (mp, h, true); 34176 hh = xmalloc (1, sizeof (mp_edge_object)); 34177 hh->body = NULL; 34178 hh->next = NULL; 34179 hh->parent = mp; 34180 hh->minx = number_to_double(h->minx); 34181 hh->minx = (fabs(hh->minx)<0.00001 ? 0 : hh->minx); 34182 hh->miny = number_to_double(h->miny); 34183 hh->miny = (fabs(hh->miny)<0.00001 ? 0 : hh->miny); 34184 hh->maxx = number_to_double(h->maxx); 34185 hh->maxx = (fabs(hh->maxx)<0.00001 ? 0 : hh->maxx); 34186 hh->maxy = number_to_double(h->maxy); 34187 hh->maxy = (fabs(hh->maxy)<0.00001 ? 0 : hh->maxy); 34188 hh->filename = mp_get_output_file_name (mp); 34189 c = round_unscaled (internal_value (mp_char_code)); 34190 hh->charcode = c; 34191 hh->width = number_to_double (internal_value (mp_char_wd)); 34192 hh->height = number_to_double (internal_value (mp_char_ht)); 34193 hh->depth = number_to_double (internal_value (mp_char_dp)); 34194 hh->ital_corr = number_to_double (internal_value (mp_char_ic)); 34195 @<Export pending specials@>; 34196 p = mp_link (edge_list (h)); 34197 while (p != NULL) { 34198 hq = mp_new_graphic_object (mp, (int) ((mp_type (p) - mp_fill_node_type) + 1)); 34199 switch (mp_type (p)) { 34200 case mp_fill_node_type: 34201 { 34202 mp_fill_node p0 = (mp_fill_node)p; 34203 tf = (mp_fill_object *) hq; 34204 gr_pen_p (tf) = mp_export_knot_list (mp, mp_pen_p (p0)); 34205 new_number (d_width); 34206 mp_get_pen_scale (mp, &d_width, mp_pen_p (p0)); /* whats the point ? */ 34207 free_number (d_width); 34208 if ((mp_pen_p (p0) == NULL) || pen_is_elliptical (mp_pen_p (p0))) { 34209 gr_path_p (tf) = mp_export_knot_list (mp, mp_path_p (p0)); 34210 } else { 34211 mp_knot pc, pp; 34212 pc = mp_copy_path (mp, mp_path_p (p0)); 34213 pp = mp_make_envelope (mp, pc, mp_pen_p (p0), p0->ljoin, 0, p0->miterlim); 34214 gr_path_p (tf) = mp_export_knot_list (mp, pp); 34215 mp_toss_knot_list (mp, pp); 34216 pc = mp_htap_ypoc (mp, mp_path_p (p0)); 34217 pp = mp_make_envelope (mp, pc, mp_pen_p ((mp_fill_node) p), p0->ljoin, 0, p0->miterlim); 34218 gr_htap_p (tf) = mp_export_knot_list (mp, pp); 34219 mp_toss_knot_list (mp, pp); 34220 } 34221 export_color (tf, p0); 34222 export_scripts (tf, p); 34223 gr_ljoin_val (tf) = p0->ljoin; 34224 gr_miterlim_val (tf) = number_to_double(p0->miterlim); 34225 } 34226 break; 34227 case mp_stroked_node_type: 34228 { 34229 mp_stroked_node p0 = (mp_stroked_node)p; 34230 ts = (mp_stroked_object *) hq; 34231 gr_pen_p (ts) = mp_export_knot_list (mp, mp_pen_p (p0)); 34232 new_number (d_width); 34233 mp_get_pen_scale (mp, &d_width, mp_pen_p (p0)); 34234 if (pen_is_elliptical (mp_pen_p (p0))) { 34235 gr_path_p (ts) = mp_export_knot_list (mp, mp_path_p (p0)); 34236 } else { 34237 mp_knot pc; 34238 pc = mp_copy_path (mp, mp_path_p (p0)); 34239 t = p0->lcap; 34240 if (mp_left_type (pc) != mp_endpoint) { 34241 mp_left_type (mp_insert_knot (mp, pc, pc->x_coord, pc->y_coord)) = mp_endpoint; 34242 mp_right_type (pc) = mp_endpoint; 34243 pc = mp_next_knot (pc); 34244 t = 1; 34245 } 34246 pc = 34247 mp_make_envelope (mp, pc, mp_pen_p (p0), 34248 p0->ljoin, (quarterword) t, 34249 p0->miterlim); 34250 gr_path_p (ts) = mp_export_knot_list (mp, pc); 34251 mp_toss_knot_list (mp, pc); 34252 } 34253 export_color (ts, p0); 34254 export_scripts (ts, p); 34255 gr_ljoin_val (ts) = p0->ljoin; 34256 gr_miterlim_val (ts) = number_to_double(p0->miterlim); 34257 gr_lcap_val (ts) = p0->lcap; 34258 gr_dash_p (ts) = mp_export_dashes (mp, p0, d_width); 34259 free_number (d_width); 34260 } 34261 break; 34262 case mp_text_node_type: 34263 { 34264 mp_text_node p0 = (mp_text_node)p; 34265 tt = (mp_text_object *) hq; 34266 gr_text_p (tt) = mp_xstrldup (mp, mp_str (mp, mp_text_p (p)),mp_text_p (p)->len); 34267 gr_text_l (tt) = (size_t) mp_text_p (p)->len; 34268 gr_font_n (tt) = (unsigned int) mp_font_n (p); 34269 gr_font_name (tt) = mp_xstrdup (mp, mp->font_name[mp_font_n (p)]); 34270 gr_font_dsize (tt) = mp->font_dsize[mp_font_n (p)] / 65536.0; 34271 export_color (tt, p0); 34272 export_scripts (tt, p); 34273 gr_width_val (tt) = number_to_double(p0->width); 34274 gr_height_val (tt) = number_to_double(p0->height); 34275 gr_depth_val (tt) = number_to_double(p0->depth); 34276 gr_tx_val (tt) = number_to_double(p0->tx); 34277 gr_ty_val (tt) = number_to_double(p0->ty); 34278 gr_txx_val (tt) = number_to_double(p0->txx); 34279 gr_txy_val (tt) = number_to_double(p0->txy); 34280 gr_tyx_val (tt) = number_to_double(p0->tyx); 34281 gr_tyy_val (tt) = number_to_double(p0->tyy); 34282 } 34283 break; 34284 case mp_start_clip_node_type: 34285 tc = (mp_clip_object *) hq; 34286 gr_path_p (tc) = 34287 mp_export_knot_list (mp, mp_path_p ((mp_start_clip_node) p)); 34288 break; 34289 case mp_start_bounds_node_type: 34290 tb = (mp_bounds_object *) hq; 34291 gr_path_p (tb) = 34292 mp_export_knot_list (mp, mp_path_p ((mp_start_bounds_node) p)); 34293 break; 34294 case mp_stop_clip_node_type: 34295 case mp_stop_bounds_node_type: 34296 /* nothing to do here */ 34297 break; 34298 default: /* there are no other valid cases, but please the compiler */ 34299 break; 34300 } 34301 if (hh->body == NULL) 34302 hh->body = hq; 34303 else 34304 gr_link (hp) = hq; 34305 hp = hq; 34306 p = mp_link (p); 34307 } 34308 return hh; 34309} 34310 34311 34312@ This function is only used for the |glyph| operator, so 34313it takes quite a few shortcuts for cases that cannot appear 34314in the output of |mp_ps_font_charstring|. 34315 34316@c 34317mp_edge_header_node mp_gr_import (MP mp, struct mp_edge_object *hh) { 34318 mp_edge_header_node h; /* the edge object */ 34319 mp_node ph, pn, pt; /* for adding items */ 34320 mp_graphic_object *p; /* the current graphical object */ 34321 h = mp_get_edge_header_node (mp); 34322 mp_init_edges (mp, h); 34323 ph = edge_list (h); 34324 pt = ph; 34325 p = hh->body; 34326 set_number_from_double(h->minx, hh->minx); 34327 set_number_from_double(h->miny, hh->miny); 34328 set_number_from_double(h->maxx, hh->maxx); 34329 set_number_from_double(h->maxy, hh->maxy); 34330 while (p != NULL) { 34331 switch (gr_type (p)) { 34332 case mp_fill_code: 34333 if (gr_pen_p ((mp_fill_object *) p) == NULL) { 34334 mp_number turns; 34335 new_number (turns); 34336 pn = mp_new_fill_node (mp, NULL); 34337 mp_path_p ((mp_fill_node) pn) = 34338 mp_import_knot_list (mp, gr_path_p ((mp_fill_object *) p)); 34339 mp_color_model (pn) = mp_grey_model; 34340 mp_turn_cycles (mp, &turns, mp_path_p ((mp_fill_node) pn)); 34341 if (number_negative(turns)) { 34342 set_number_to_unity(((mp_fill_node) pn)->grey); 34343 mp_link (pt) = pn; 34344 pt = mp_link (pt); 34345 } else { 34346 set_number_to_zero(((mp_fill_node) pn)->grey); 34347 mp_link (pn) = mp_link (ph); 34348 mp_link (ph) = pn; 34349 if (ph == pt) 34350 pt = pn; 34351 } 34352 free_number (turns); 34353 } 34354 break; 34355 case mp_stroked_code: 34356 case mp_text_code: 34357 case mp_start_clip_code: 34358 case mp_stop_clip_code: 34359 case mp_start_bounds_code: 34360 case mp_stop_bounds_code: 34361 case mp_special_code: 34362 break; 34363 } /* all cases are enumerated */ 34364 p = p->next; 34365 } 34366 mp_gr_toss_objects (hh); 34367 return h; 34368} 34369 34370 34371@ @<Declarations@>= 34372struct mp_edge_object *mp_gr_export (MP mp, mp_edge_header_node h); 34373mp_edge_header_node mp_gr_import (MP mp, struct mp_edge_object *h); 34374 34375@ This function is now nearly trivial. 34376 34377@c 34378void mp_ship_out (MP mp, mp_node h) { /* output edge structure |h| */ 34379 int c; /* \&{charcode} rounded to the nearest integer */ 34380 c = round_unscaled (internal_value (mp_char_code)); 34381 @<Begin the progress report for the output of picture~|c|@>; 34382 (mp->shipout_backend) (mp, h); 34383 @<End progress report@>; 34384 if (number_positive (internal_value (mp_tracing_output))) 34385 mp_print_edges (mp, h, " (just shipped out)", true); 34386} 34387 34388 34389@ @<Declarations@>= 34390static void mp_shipout_backend (MP mp, void *h); 34391 34392@ 34393@c 34394void mp_shipout_backend (MP mp, void *voidh) { 34395 char *s; 34396 mp_edge_object *hh; /* the first graphical object */ 34397 mp_edge_header_node h = (mp_edge_header_node) voidh; 34398 hh = mp_gr_export (mp, h); 34399 s = NULL; 34400 if (internal_string (mp_output_format) != NULL) 34401 s = mp_str (mp, internal_string (mp_output_format)); 34402 if (s && strcmp (s, "svg") == 0) { 34403 (void) mp_svg_gr_ship_out (hh, 34404 (number_to_scaled (internal_value (mp_prologues)) / 65536), false); 34405 } else if (s && strcmp (s, "png") == 0) { 34406 (void) mp_png_gr_ship_out (hh, (const char *)((internal_string (mp_output_format_options))->str), false); 34407 } else { 34408 (void) mp_gr_ship_out (hh, 34409 (number_to_scaled (internal_value (mp_prologues)) / 65536), 34410 (number_to_scaled (internal_value (mp_procset)) / 65536), false); 34411 } 34412 mp_gr_toss_objects (hh); 34413} 34414 34415 34416@ @<Exported types@>= 34417typedef void (*mp_backend_writer) (MP, void *); 34418 34419@ @<Option variables@>= 34420mp_backend_writer shipout_backend; 34421 34422@ Now that we've finished |ship_out|, let's look at the other commands 34423by which a user can send things to the \.{GF} file. 34424 34425@ @<Glob...@>= 34426psout_data ps; 34427svgout_data svg; 34428pngout_data png; 34429 34430@ @<Allocate or initialize ...@>= 34431mp_ps_backend_initialize (mp); 34432mp_svg_backend_initialize (mp); 34433mp_png_backend_initialize (mp); 34434 34435@ @<Dealloc...@>= 34436mp_ps_backend_free (mp); 34437mp_svg_backend_free (mp); 34438mp_png_backend_free (mp); 34439 34440 34441@* Dumping and undumping the tables. 34442 34443When \.{MP} is started, it is possible to preload a macro file 34444containing definitions that will be usable in the main input 34445file. This action even takes place automatically, based on the 34446name of the executable (\.{mpost} will attempt to preload the 34447macros in the file \.{mpost.mp}). If such a preload is not 34448desired, the option variable |ini_version| has to be set |true|. 34449 34450The variable |mem_file| holds the open file pointer. 34451 34452@<Glob...@>= 34453void *mem_file; /* file for input or preloaded macros */ 34454 34455@ @<Declarations@>= 34456extern boolean mp_load_preload_file (MP mp); 34457 34458@ Preloading a file is a lot like |mp_run| itself, except that 34459\MP\ should not exit and that a bit of trickery is needed with 34460the input buffer to make sure that the preloading does not 34461interfere with the actual job. 34462 34463@c 34464boolean mp_load_preload_file (MP mp) { 34465 size_t k; 34466 in_state_record old_state; 34467 integer old_in_open = mp->in_open; 34468 void *old_cur_file = cur_file; 34469 char *fname = xstrdup (mp->name_of_file); 34470 size_t l = strlen (fname); 34471 old_state = mp->cur_input; 34472 str_room (l); 34473 for (k = 0; k < l; k++) { 34474 append_char (*(fname + k)); 34475 } 34476 name = mp_make_string (mp); 34477 if (!mp->log_opened) { 34478 mp_open_log_file (mp); 34479 } /* |open_log_file| doesn't |show_context|, so |limit| 34480 and |loc| needn't be set to meaningful values yet */ 34481 if (((int) mp->term_offset + (int) strlen (fname)) > (mp->max_print_line - 2)) 34482 mp_print_ln (mp); 34483 else if ((mp->term_offset > 0) || (mp->file_offset > 0)) 34484 mp_print_char (mp, xord (' ')); 34485 mp_print_char (mp, xord ('(')); 34486 incr (mp->open_parens); 34487 mp_print (mp, fname); 34488 update_terminal(); 34489 { 34490 line = 1; 34491 start = loc = limit + (mp->noninteractive ? 0 : 1); 34492 cur_file = mp->mem_file; 34493 (void) mp_input_ln (mp, cur_file); 34494 mp_firm_up_the_line (mp); 34495 mp->buffer[limit] = xord ('%'); 34496 mp->first = (size_t) (limit + 1); 34497 loc = start; 34498 } 34499 mp->reading_preload = true; 34500 do { 34501 mp_do_statement (mp); 34502 } while (!(cur_cmd() == mp_stop)); /* "dump" or EOF */ 34503 mp->reading_preload = false; 34504 mp_primitive (mp, "dump", mp_relax, 0); /* reset |dump| */ 34505 while (mp->input_ptr > 0) { 34506 if (token_state) 34507 mp_end_token_list (mp); 34508 else 34509 mp_end_file_reading (mp); 34510 } 34511 while (mp->loop_ptr != NULL) 34512 mp_stop_iteration (mp); 34513 while (mp->open_parens > 0) { 34514 mp_print (mp, " )"); 34515 decr (mp->open_parens); 34516 }; 34517 while (mp->cond_ptr != NULL) { 34518 mp_print_nl (mp, "(dump occurred when "); 34519@.dump occurred...@>; 34520 mp_print_cmd_mod (mp, mp_fi_or_else, mp->cur_if); 34521 /* `\.{if}' or `\.{elseif}' or `\.{else}' */ 34522 if (mp->if_line != 0) { 34523 mp_print (mp, " on line "); 34524 mp_print_int (mp, mp->if_line); 34525 } 34526 mp_print (mp, " was incomplete)"); 34527 mp->if_line = if_line_field (mp->cond_ptr); 34528 mp->cur_if = mp_name_type (mp->cond_ptr); 34529 mp->cond_ptr = mp_link (mp->cond_ptr); 34530 } 34531 34532/* |(mp->close_file) (mp, mp->mem_file);| */ 34533 cur_file = old_cur_file; 34534 mp->cur_input = old_state; 34535 mp->in_open = old_in_open; 34536 return true; 34537} 34538 34539 34540@* The main program. 34541This is it: the part of \MP\ that executes all those procedures we have 34542written. 34543 34544Well---almost. We haven't put the parsing subroutines into the 34545program yet; and we'd better leave space for a few more routines that may 34546have been forgotten. 34547 34548@c 34549@<Declare the basic parsing subroutines@>; 34550@<Declare miscellaneous procedures that were declared |forward|@> 34551 34552 34553@ Here we do whatever is needed to complete \MP's job gracefully on the 34554local operating system. The code here might come into play after a fatal 34555error; it must therefore consist entirely of ``safe'' operations that 34556cannot produce error messages. For example, it would be a mistake to call 34557|str_room| or |make_string| at this time, because a call on |overflow| 34558might lead to an infinite loop. 34559@^system dependencies@> 34560 34561@ @c 34562void mp_close_files_and_terminate (MP mp) { 34563 integer k; /* all-purpose index */ 34564 integer LH; /* the length of the \.{TFM} header, in words */ 34565 int lk_offset; /* extra words inserted at beginning of |lig_kern| array */ 34566 mp_node p; /* runs through a list of \.{TFM} dimensions */ 34567 if (mp->finished) 34568 return; 34569 @<Close all open files in the |rd_file| and |wr_file| arrays@>; 34570 if (number_positive (internal_value (mp_tracing_stats))) 34571 @<Output statistics about this job@>; 34572 wake_up_terminal(); 34573 @<Do all the finishing work on the \.{TFM} file@>; 34574 @<Explain what output files were written@>; 34575 if (mp->log_opened && !mp->noninteractive) { 34576 wlog_cr; 34577 (mp->close_file) (mp, mp->log_file); 34578 mp->selector = mp->selector - 2; 34579 if (mp->selector == term_only) { 34580 mp_print_nl (mp, "Transcript written on "); 34581@.Transcript written...@>; 34582 mp_print (mp, mp->log_name); 34583 mp_print_char (mp, xord ('.')); 34584 } 34585 } 34586 mp_print_ln (mp); 34587 mp->finished = true; 34588} 34589 34590 34591@ @<Declarations@>= 34592static void mp_close_files_and_terminate (MP mp); 34593 34594@ @<Close all open files in the |rd_file| and |wr_file| arrays@>= 34595if (mp->rd_fname != NULL) { 34596 for (k = 0; k < (int) mp->read_files; k++) { 34597 if (mp->rd_fname[k] != NULL) { 34598 (mp->close_file) (mp, mp->rd_file[k]); 34599 xfree (mp->rd_fname[k]); 34600 } 34601 } 34602} 34603if (mp->wr_fname != NULL) { 34604 for (k = 0; k < (int) mp->write_files; k++) { 34605 if (mp->wr_fname[k] != NULL) { 34606 (mp->close_file) (mp, mp->wr_file[k]); 34607 xfree (mp->wr_fname[k]); 34608 } 34609 } 34610} 34611 34612@ @<Dealloc ...@>= 34613for (k = 0; k < (int) mp->max_read_files; k++) { 34614 if (mp->rd_fname[k] != NULL) { 34615 (mp->close_file) (mp, mp->rd_file[k]); 34616 xfree (mp->rd_fname[k]); 34617 } 34618} 34619xfree (mp->rd_file); 34620xfree (mp->rd_fname); 34621for (k = 0; k < (int) mp->max_write_files; k++) { 34622 if (mp->wr_fname[k] != NULL) { 34623 (mp->close_file) (mp, mp->wr_file[k]); 34624 xfree (mp->wr_fname[k]); 34625 } 34626} 34627xfree (mp->wr_file); 34628xfree (mp->wr_fname); 34629 34630 34631@ We want to produce a \.{TFM} file if and only if |mp_fontmaking| is positive. 34632 34633We reclaim all of the variable-size memory at this point, so that 34634there is no chance of another memory overflow after the memory capacity 34635has already been exceeded. 34636 34637@<Do all the finishing work on the \.{TFM} file@>= 34638if (number_positive (internal_value (mp_fontmaking))) { 34639 @<Massage the \.{TFM} widths@>; 34640 mp_fix_design_size (mp); 34641 mp_fix_check_sum (mp); 34642 @<Massage the \.{TFM} heights, depths, and italic corrections@>; 34643 set_number_to_zero (internal_value (mp_fontmaking)); /* avoid loop in case of fatal error */ 34644 @<Finish the \.{TFM} file@>; 34645} 34646 34647@ The present section goes directly to the log file instead of using 34648|print| commands, because there's no need for these strings to take 34649up |str_pool| memory when a non-{\bf stat} version of \MP\ is being used. 34650 34651@<Output statistics...@>= 34652if (mp->log_opened) { 34653 char s[128]; 34654 wlog_ln (" "); 34655 wlog_ln ("Here is how much of MetaPost's memory you used:"); 34656@.Here is how much...@>; 34657 mp_snprintf (s, 128, " %i string%s using %i character%s", 34658 (int) mp->max_strs_used, (mp->max_strs_used != 1 ? "s" : ""), 34659 (int) mp->max_pl_used, (mp->max_pl_used != 1 ? "s" : "")); 34660 wlog_ln (s); 34661 mp_snprintf (s, 128, " %i bytes of node memory", (int) mp->var_used_max); 34662 wlog_ln (s); 34663 mp_snprintf (s, 128, " %i symbolic tokens", (int) mp->st_count); 34664 wlog_ln (s); 34665 mp_snprintf (s, 128, 34666 " %ii,%in,%ip,%ib,%if stack positions out of %ii,%in,%ip,%ib,%if", 34667 (int) mp->max_in_stack, (int) mp->int_ptr, 34668 (int) mp->max_param_stack, (int) mp->max_buf_stack + 1, 34669 (int) mp->in_open_max - file_bottom, (int) mp->stack_size, 34670 (int) mp->max_internal, (int) mp->param_size, (int) mp->buf_size, 34671 (int) mp->max_in_open - file_bottom); 34672 wlog_ln (s); 34673} 34674 34675@ It is nice to have have some of the stats available from the API. 34676 34677@<Exported function ...@>= 34678int mp_memory_usage (MP mp); 34679int mp_hash_usage (MP mp); 34680int mp_param_usage (MP mp); 34681int mp_open_usage (MP mp); 34682 34683@ @c 34684int mp_memory_usage (MP mp) { 34685 return (int) mp->var_used; 34686} 34687int mp_hash_usage (MP mp) { 34688 return (int) mp->st_count; 34689} 34690int mp_param_usage (MP mp) { 34691 return (int) mp->max_param_stack; 34692} 34693int mp_open_usage (MP mp) { 34694 return (int) mp->max_in_stack; 34695} 34696 34697 34698@ We get to the |final_cleanup| routine when \&{end} or \&{dump} has 34699been scanned. 34700 34701@c 34702void mp_final_cleanup (MP mp) { 34703 /* -Wunused: integer c; */ /* 0 for \&{end}, 1 for \&{dump} */ 34704 /* clang: never read: |c = cur_mod();| */ 34705 if (mp->job_name == NULL) 34706 mp_open_log_file (mp); 34707 while (mp->input_ptr > 0) { 34708 if (token_state) 34709 mp_end_token_list (mp); 34710 else 34711 mp_end_file_reading (mp); 34712 } 34713 while (mp->loop_ptr != NULL) 34714 mp_stop_iteration (mp); 34715 while (mp->open_parens > 0) { 34716 mp_print (mp, " )"); 34717 decr (mp->open_parens); 34718 }; 34719 while (mp->cond_ptr != NULL) { 34720 mp_print_nl (mp, "(end occurred when "); 34721@.end occurred...@>; 34722 mp_print_cmd_mod (mp, mp_fi_or_else, mp->cur_if); 34723 /* `\.{if}' or `\.{elseif}' or `\.{else}' */ 34724 if (mp->if_line != 0) { 34725 mp_print (mp, " on line "); 34726 mp_print_int (mp, mp->if_line); 34727 } 34728 mp_print (mp, " was incomplete)"); 34729 mp->if_line = if_line_field (mp->cond_ptr); 34730 mp->cur_if = mp_name_type (mp->cond_ptr); 34731 mp->cond_ptr = mp_link (mp->cond_ptr); 34732 } 34733 if (mp->history != mp_spotless) 34734 if (((mp->history == mp_warning_issued) 34735 || (mp->interaction < mp_error_stop_mode))) 34736 if (mp->selector == term_and_log) { 34737 mp->selector = term_only; 34738 mp_print_nl (mp, 34739 "(see the transcript file for additional information)"); 34740@.see the transcript file...@>; 34741 mp->selector = term_and_log; 34742 } 34743} 34744 34745 34746@ @<Declarations@>= 34747static void mp_final_cleanup (MP mp); 34748static void mp_init_prim (MP mp); 34749static void mp_init_tab (MP mp); 34750 34751@ @c 34752void mp_init_prim (MP mp) { /* initialize all the primitives */ 34753 @<Put each...@>; 34754} 34755@# 34756void mp_init_tab (MP mp) { /* initialize other tables */ 34757 @<Initialize table entries@>; 34758} 34759 34760 34761@ When we begin the following code, \MP's tables may still contain garbage; 34762thus we must proceed cautiously to get bootstrapped in. 34763 34764But when we finish this part of the program, \MP\ is ready to call on the 34765|main_control| routine to do its work. 34766 34767@<Get the first line...@>= 34768{ 34769 @<Initialize the input routines@>; 34770 if (!mp->ini_version) { 34771 if (!mp_load_preload_file (mp)) { 34772 mp->history = mp_fatal_error_stop; 34773 return mp; 34774 } 34775 } 34776 @<Initializations following first line@>; 34777} 34778 34779 34780@ @<Initializations following first line@>= 34781mp->buffer[limit] = (ASCII_code) '%'; 34782mp_fix_date_and_time (mp); 34783if (mp->random_seed == 0) 34784 mp->random_seed = (number_to_scaled (internal_value (mp_time)) / number_to_scaled (unity_t)) + number_to_scaled (internal_value (mp_day)); 34785init_randoms (mp->random_seed); 34786initialize_print_selector(); 34787mp_normalize_selector (mp); 34788if (loc < limit) 34789 if (mp->buffer[loc] != '\\') 34790 mp_start_input (mp); /* \&{input} assumed */ 34791 34792@* Debugging. 34793 34794 34795@* System-dependent changes. 34796This section should be replaced, if necessary, by any special 34797modification of the program 34798that are necessary to make \MP\ work at a particular installation. 34799It is usually best to design your change file so that all changes to 34800previous sections preserve the section numbering; then everybody's version 34801will be consistent with the published program. More extensive changes, 34802which introduce new sections, can be inserted here; then only the index 34803itself will get a new section number. 34804@^system dependencies@> 34805 34806@* Index. 34807Here is where you can find all uses of each identifier in the program, 34808with underlined entries pointing to where the identifier was defined. 34809If the identifier is only one letter long, however, you get to see only 34810the underlined entries. {\sl All references are to section numbers instead of 34811page numbers.} 34812 34813This index also lists error messages and other aspects of the program 34814that you might want to look up some day. For example, the entry 34815for ``system dependencies'' lists all sections that should receive 34816special attention from people who are installing \MP\ in a new 34817operating environment. A list of various things that can't happen appears 34818under ``this can't happen''. 34819Approximately 25 sections are listed under ``inner loop''; these account 34820for more than 60\pct! of \MP's running time, exclusive of input and output. 34821