diff options
author | Taco Hoekwater <taco@elvenkind.com> | 2011-04-05 14:13:11 +0000 |
---|---|---|
committer | Taco Hoekwater <taco@elvenkind.com> | 2011-04-05 14:13:11 +0000 |
commit | 3bd11c5b320d265b89c965f63f57018ef818b876 (patch) | |
tree | 67308b260935e36efaaf028ea64d7b8568530563 | |
parent | dce9945ab7f97cdf0849484839de601a8e959d39 (diff) |
Update to metapost 1.503
git-svn-id: svn://tug.org/texlive/trunk@21975 c570f23f-e606-0410-a88d-b1316a301751
40 files changed, 23496 insertions, 17866 deletions
diff --git a/Build/source/texk/web2c/Makefile.in b/Build/source/texk/web2c/Makefile.in index 7daa434a33b..969e9d3dfdf 100644 --- a/Build/source/texk/web2c/Makefile.in +++ b/Build/source/texk/web2c/Makefile.in @@ -438,11 +438,11 @@ libmf_a_OBJECTS = $(nodist_libmf_a_OBJECTS) libmplib_a_AR = $(AR) $(ARFLAGS) libmplib_a_LIBADD = am_libmplib_a_OBJECTS = libmplib_a-avl.$(OBJEXT) -am__objects_2 = libmplib_a-memio.$(OBJEXT) -am__objects_3 = libmplib_a-mp.$(OBJEXT) -am__objects_4 = libmplib_a-psout.$(OBJEXT) -am__objects_5 = libmplib_a-svgout.$(OBJEXT) -am__objects_6 = libmplib_a-tfmin.$(OBJEXT) +am__objects_2 = libmplib_a-mp.$(OBJEXT) +am__objects_3 = libmplib_a-psout.$(OBJEXT) +am__objects_4 = libmplib_a-svgout.$(OBJEXT) +am__objects_5 = libmplib_a-tfmin.$(OBJEXT) +am__objects_6 = libmplib_a-mpmath.$(OBJEXT) nodist_libmplib_a_OBJECTS = $(am__objects_2) $(am__objects_3) \ $(am__objects_4) $(am__objects_5) $(am__objects_6) libmplib_a_OBJECTS = $(am_libmplib_a_OBJECTS) \ @@ -1442,10 +1442,10 @@ DISTCLEANFILES = $(EXTRA_LIBRARIES) tangle.c tangle.h tangle.p \ mf-final.ch mf-web2c mf.p mf.pool mf-tangle \ $(nodist_mpost_SOURCES) mpxout-tangle mptrap.diffs ptmr.mpx \ tests/xptmr.mpx tests/xstory.mpx $(nodist_libmplib_a_SOURCES) \ - memio-tangle mp-tangle psout-tangle svgout-tangle tfmin-tangle \ - $(nodist_etex_SOURCES) etex.web etex.ch etex-web2c etex.p \ - etex.pool etex-tangle etrip.diffs $(pweb_programs:=.c) \ - $(pweb_programs:=.h) $(pweb_programs:=.p) \ + mpmath-tangle mp-tangle psout-tangle svgout-tangle \ + tfmin-tangle $(nodist_etex_SOURCES) etex.web etex.ch \ + etex-web2c etex.p etex.pool etex-tangle etrip.diffs \ + $(pweb_programs:=.c) $(pweb_programs:=.h) $(pweb_programs:=.p) \ $(pweb_programs:=-web2c) $(pweb_programs:=.web) \ $(nodist_ptex_SOURCES) ptex.web ptex.ch ptex-web2c ptex.p \ ptex.pool ptex-tangle tests/nissya_bib.* tests/xexampl.aux \ @@ -1632,14 +1632,14 @@ mpost_web = mplibdir/mpost.w mplibdir/mpxout.w mp_tests = mplibdir/dvitomp.test mplibdir/mptrap.test libmplib_a_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/mplibdir libmplib_a_SOURCES = mplibdir/avl.h mplibdir/avl.c -nodist_libmplib_a_SOURCES = $(memio_c_h) $(mp_c_h) $(psout_c_h) $(svgout_c_h) $(tfmin_c_h) -memio_c_h = memio.c mpmemio.h +nodist_libmplib_a_SOURCES = $(mp_c_h) $(psout_c_h) $(svgout_c_h) $(tfmin_c_h) $(mpmath_c_h) mp_c_h = mp.c mplib.h mpmp.h psout_c_h = mppsout.h psout.c mplibps.h svgout_c_h = mplibsvg.h mpsvgout.h svgout.c +mpmath_c_h = mpmath.h mpmath.c tfmin_c_h = mptfmin.h tfmin.c libmplib_web = mplibdir/mp.w mplibdir/psout.w mplibdir/svgout.w \ - mplibdir/memio.w mplibdir/tfmin.w + mplibdir/mpmath.w mplibdir/tfmin.w etex_CPPFLAGS = $(AM_CPPFLAGS) $(am__append_74) # With --enable-ipc, e-TeX may need to link with -lsocket. @@ -3096,8 +3096,8 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libluatex_a-writetype2.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmd5_a-md5.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmplib_a-avl.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmplib_a-memio.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmplib_a-mp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmplib_a-mpmath.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmplib_a-psout.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmplib_a-svgout.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmplib_a-tfmin.Po@am__quote@ @@ -6453,20 +6453,6 @@ libmplib_a-avl.obj: mplibdir/avl.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmplib_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmplib_a-avl.obj `if test -f 'mplibdir/avl.c'; then $(CYGPATH_W) 'mplibdir/avl.c'; else $(CYGPATH_W) '$(srcdir)/mplibdir/avl.c'; fi` -libmplib_a-memio.o: memio.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmplib_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmplib_a-memio.o -MD -MP -MF $(DEPDIR)/libmplib_a-memio.Tpo -c -o libmplib_a-memio.o `test -f 'memio.c' || echo '$(srcdir)/'`memio.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libmplib_a-memio.Tpo $(DEPDIR)/libmplib_a-memio.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='memio.c' object='libmplib_a-memio.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmplib_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmplib_a-memio.o `test -f 'memio.c' || echo '$(srcdir)/'`memio.c - -libmplib_a-memio.obj: memio.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmplib_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmplib_a-memio.obj -MD -MP -MF $(DEPDIR)/libmplib_a-memio.Tpo -c -o libmplib_a-memio.obj `if test -f 'memio.c'; then $(CYGPATH_W) 'memio.c'; else $(CYGPATH_W) '$(srcdir)/memio.c'; fi` -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libmplib_a-memio.Tpo $(DEPDIR)/libmplib_a-memio.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='memio.c' object='libmplib_a-memio.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmplib_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmplib_a-memio.obj `if test -f 'memio.c'; then $(CYGPATH_W) 'memio.c'; else $(CYGPATH_W) '$(srcdir)/memio.c'; fi` - libmplib_a-mp.o: mp.c @am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmplib_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmplib_a-mp.o -MD -MP -MF $(DEPDIR)/libmplib_a-mp.Tpo -c -o libmplib_a-mp.o `test -f 'mp.c' || echo '$(srcdir)/'`mp.c @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libmplib_a-mp.Tpo $(DEPDIR)/libmplib_a-mp.Po @@ -6523,6 +6509,20 @@ libmplib_a-tfmin.obj: tfmin.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmplib_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmplib_a-tfmin.obj `if test -f 'tfmin.c'; then $(CYGPATH_W) 'tfmin.c'; else $(CYGPATH_W) '$(srcdir)/tfmin.c'; fi` +libmplib_a-mpmath.o: mpmath.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmplib_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmplib_a-mpmath.o -MD -MP -MF $(DEPDIR)/libmplib_a-mpmath.Tpo -c -o libmplib_a-mpmath.o `test -f 'mpmath.c' || echo '$(srcdir)/'`mpmath.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libmplib_a-mpmath.Tpo $(DEPDIR)/libmplib_a-mpmath.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mpmath.c' object='libmplib_a-mpmath.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmplib_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmplib_a-mpmath.o `test -f 'mpmath.c' || echo '$(srcdir)/'`mpmath.c + +libmplib_a-mpmath.obj: mpmath.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmplib_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmplib_a-mpmath.obj -MD -MP -MF $(DEPDIR)/libmplib_a-mpmath.Tpo -c -o libmplib_a-mpmath.obj `if test -f 'mpmath.c'; then $(CYGPATH_W) 'mpmath.c'; else $(CYGPATH_W) '$(srcdir)/mpmath.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libmplib_a-mpmath.Tpo $(DEPDIR)/libmplib_a-mpmath.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mpmath.c' object='libmplib_a-mpmath.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmplib_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmplib_a-mpmath.obj `if test -f 'mpmath.c'; then $(CYGPATH_W) 'mpmath.c'; else $(CYGPATH_W) '$(srcdir)/mpmath.c'; fi` + libpdftex_a-avl.o: pdftexdir/avl.c @am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpdftex_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libpdftex_a-avl.o -MD -MP -MF $(DEPDIR)/libpdftex_a-avl.Tpo -c -o libpdftex_a-avl.o `test -f 'pdftexdir/avl.c' || echo '$(srcdir)/'`pdftexdir/avl.c @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libpdftex_a-avl.Tpo $(DEPDIR)/libpdftex_a-avl.Po @@ -9031,10 +9031,10 @@ mptrap-clean: rm -f trap.149 trap.150 trap.151 trap.197 trap.200 rm -f mptrap.pl -$(memio_c_h): memio-tangle - @$(mp_ctangle_sh) memio -memio-tangle: ctangle$(EXEEXT) mplibdir/memio.w tangle-sh - @$(mp_ctangle_sh) memio +$(mpmath_c_h): mpmath-tangle + @$(mp_ctangle_sh) mpmath +mpmath-tangle: ctangle$(EXEEXT) mplibdir/mpmath.w tangle-sh + @$(mp_ctangle_sh) mpmath $(mp_c_h): mp-tangle @$(mp_ctangle_sh) mp diff --git a/Build/source/texk/web2c/aclocal.m4 b/Build/source/texk/web2c/aclocal.m4 index 8d89b6d7a1a..09487015963 100644 --- a/Build/source/texk/web2c/aclocal.m4 +++ b/Build/source/texk/web2c/aclocal.m4 @@ -19,113 +19,6 @@ You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically `autoreconf'.])]) -# longlong.m4 serial 14 -dnl Copyright (C) 1999-2007, 2009-2010 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -dnl From Paul Eggert. - -# Define HAVE_LONG_LONG_INT if 'long long int' works. -# This fixes a bug in Autoconf 2.61, but can be removed once we -# assume 2.62 everywhere. - -# Note: If the type 'long long int' exists but is only 32 bits large -# (as on some very old compilers), HAVE_LONG_LONG_INT will not be -# defined. In this case you can treat 'long long int' like 'long int'. - -AC_DEFUN([AC_TYPE_LONG_LONG_INT], -[ - AC_CACHE_CHECK([for long long int], [ac_cv_type_long_long_int], - [AC_LINK_IFELSE( - [_AC_TYPE_LONG_LONG_SNIPPET], - [dnl This catches a bug in Tandem NonStop Kernel (OSS) cc -O circa 2004. - dnl If cross compiling, assume the bug isn't important, since - dnl nobody cross compiles for this platform as far as we know. - AC_RUN_IFELSE( - [AC_LANG_PROGRAM( - [[@%:@include <limits.h> - @%:@ifndef LLONG_MAX - @%:@ define HALF \ - (1LL << (sizeof (long long int) * CHAR_BIT - 2)) - @%:@ define LLONG_MAX (HALF - 1 + HALF) - @%:@endif]], - [[long long int n = 1; - int i; - for (i = 0; ; i++) - { - long long int m = n << i; - if (m >> i != n) - return 1; - if (LLONG_MAX / 2 < m) - break; - } - return 0;]])], - [ac_cv_type_long_long_int=yes], - [ac_cv_type_long_long_int=no], - [ac_cv_type_long_long_int=yes])], - [ac_cv_type_long_long_int=no])]) - if test $ac_cv_type_long_long_int = yes; then - AC_DEFINE([HAVE_LONG_LONG_INT], [1], - [Define to 1 if the system has the type `long long int'.]) - fi -]) - -# Define HAVE_UNSIGNED_LONG_LONG_INT if 'unsigned long long int' works. -# This fixes a bug in Autoconf 2.61, but can be removed once we -# assume 2.62 everywhere. - -# Note: If the type 'unsigned long long int' exists but is only 32 bits -# large (as on some very old compilers), AC_TYPE_UNSIGNED_LONG_LONG_INT -# will not be defined. In this case you can treat 'unsigned long long int' -# like 'unsigned long int'. - -AC_DEFUN([AC_TYPE_UNSIGNED_LONG_LONG_INT], -[ - AC_CACHE_CHECK([for unsigned long long int], - [ac_cv_type_unsigned_long_long_int], - [AC_LINK_IFELSE( - [_AC_TYPE_LONG_LONG_SNIPPET], - [ac_cv_type_unsigned_long_long_int=yes], - [ac_cv_type_unsigned_long_long_int=no])]) - if test $ac_cv_type_unsigned_long_long_int = yes; then - AC_DEFINE([HAVE_UNSIGNED_LONG_LONG_INT], [1], - [Define to 1 if the system has the type `unsigned long long int'.]) - fi -]) - -# Expands to a C program that can be used to test for simultaneous support -# of 'long long' and 'unsigned long long'. We don't want to say that -# 'long long' is available if 'unsigned long long' is not, or vice versa, -# because too many programs rely on the symmetry between signed and unsigned -# integer types (excluding 'bool'). -AC_DEFUN([_AC_TYPE_LONG_LONG_SNIPPET], -[ - AC_LANG_PROGRAM( - [[/* For now, do not test the preprocessor; as of 2007 there are too many - implementations with broken preprocessors. Perhaps this can - be revisited in 2012. In the meantime, code should not expect - #if to work with literals wider than 32 bits. */ - /* Test literals. */ - long long int ll = 9223372036854775807ll; - long long int nll = -9223372036854775807LL; - unsigned long long int ull = 18446744073709551615ULL; - /* Test constant expressions. */ - typedef int a[((-9223372036854775807LL < 0 && 0 < 9223372036854775807ll) - ? 1 : -1)]; - typedef int b[(18446744073709551615ULL <= (unsigned long long int) -1 - ? 1 : -1)]; - int i = 63;]], - [[/* Test availability of runtime routines for shift and division. */ - long long int llmax = 9223372036854775807ll; - unsigned long long int ullmax = 18446744073709551615ull; - return ((ll << 63) | (ll >> 63) | (ll < i) | (ll > i) - | (llmax / ll) | (llmax % ll) - | (ull << 63) | (ull >> 63) | (ull << i) | (ull >> i) - | (ullmax / ull) | (ullmax % ull));]]) -]) - # Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation diff --git a/Build/source/texk/web2c/configure b/Build/source/texk/web2c/configure index 2883b30e7d7..e8265523727 100755 --- a/Build/source/texk/web2c/configure +++ b/Build/source/texk/web2c/configure @@ -883,7 +883,6 @@ with_sysroot enable_libtool_lock enable_largefile enable_compiler_warnings -with_gnu_ld enable_cxx_runtime_hack enable_libtool_hack with_banner_add @@ -944,19 +943,12 @@ LDFLAGS LIBS CPPFLAGS CPP -CPPFLAGS CXX CXXFLAGS -LDFLAGS -LIBS -CPPFLAGS CCC CXXCPP OBJCXX OBJCXXFLAGS -LDFLAGS -LIBS -OBJCXX XMKMF YACC YFLAGS' @@ -19760,18 +19752,18 @@ else /* end confdefs.h. */ /* For now, do not test the preprocessor; as of 2007 there are too many - implementations with broken preprocessors. Perhaps this can - be revisited in 2012. In the meantime, code should not expect - #if to work with literals wider than 32 bits. */ + implementations with broken preprocessors. Perhaps this can + be revisited in 2012. In the meantime, code should not expect + #if to work with literals wider than 32 bits. */ /* Test literals. */ long long int ll = 9223372036854775807ll; long long int nll = -9223372036854775807LL; unsigned long long int ull = 18446744073709551615ULL; /* Test constant expressions. */ typedef int a[((-9223372036854775807LL < 0 && 0 < 9223372036854775807ll) - ? 1 : -1)]; + ? 1 : -1)]; typedef int b[(18446744073709551615ULL <= (unsigned long long int) -1 - ? 1 : -1)]; + ? 1 : -1)]; int i = 63; int main () @@ -19780,40 +19772,40 @@ main () long long int llmax = 9223372036854775807ll; unsigned long long int ullmax = 18446744073709551615ull; return ((ll << 63) | (ll >> 63) | (ll < i) | (ll > i) - | (llmax / ll) | (llmax % ll) - | (ull << 63) | (ull >> 63) | (ull << i) | (ull >> i) - | (ullmax / ull) | (ullmax % ull)); + | (llmax / ll) | (llmax % ll) + | (ull << 63) | (ull >> 63) | (ull << i) | (ull >> i) + | (ullmax / ull) | (ullmax % ull)); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : - if test "$cross_compiling" = yes; then : + if test "$cross_compiling" = yes; then : ac_cv_type_long_long_int=yes else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <limits.h> - #ifndef LLONG_MAX - # define HALF \ - (1LL << (sizeof (long long int) * CHAR_BIT - 2)) - # define LLONG_MAX (HALF - 1 + HALF) - #endif + #ifndef LLONG_MAX + # define HALF \ + (1LL << (sizeof (long long int) * CHAR_BIT - 2)) + # define LLONG_MAX (HALF - 1 + HALF) + #endif int main () { long long int n = 1; - int i; - for (i = 0; ; i++) - { - long long int m = n << i; - if (m >> i != n) - return 1; - if (LLONG_MAX / 2 < m) - break; - } - return 0; + int i; + for (i = 0; ; i++) + { + long long int m = n << i; + if (m >> i != n) + return 1; + if (LLONG_MAX / 2 < m) + break; + } + return 0; ; return 0; } @@ -23340,9 +23332,9 @@ esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. -config_files="$ac_config_files" -config_headers="$ac_config_headers" -config_commands="$ac_config_commands" +config_files="`echo $ac_config_files`" +config_headers="`echo $ac_config_headers`" +config_commands="`echo $ac_config_commands`" _ACEOF diff --git a/Build/source/texk/web2c/man/ChangeLog b/Build/source/texk/web2c/man/ChangeLog index de8ed048821..b7ec50f1b14 100644 --- a/Build/source/texk/web2c/man/ChangeLog +++ b/Build/source/texk/web2c/man/ChangeLog @@ -1,3 +1,7 @@ +2011-04-05 Taco Hoekwater <taco@metapost.org> + + * mpost.man: update to version 1.503 + 2011-03-01 Peter Breitenlohner <peb@mppmu.mpg.de> * aleph.man, etex.man, luatex.man, mf.man, pdftex.man, diff --git a/Build/source/texk/web2c/man/mpost.man b/Build/source/texk/web2c/man/mpost.man index 71b939e33ee..98fd294546f 100644 --- a/Build/source/texk/web2c/man/mpost.man +++ b/Build/source/texk/web2c/man/mpost.man @@ -1,4 +1,4 @@ -.TH MPOST 1 "14 May 2010" "MetaPost 1.201" +.TH MPOST 1 "5 April 2011" "MetaPost 1.503" .\"===================================================================== .de URL \\$2 \(laURL: \\$1 \(ra\\$3 @@ -44,19 +44,17 @@ or .BR troff (1) commands and accessing features of PostScript not found in \*(MF. .PP -Like \*(TX and \*(MF, \*(MP is normally used with a large body of -precompiled macros. When the -.B --ini -option is given, it can be used to precompile macros into a -.I .mem -file. When called without that option, \*(MP will use its executable name as -the name of the mem to use. For example, when called as +\*(MP is normally used with some preloaded macros, and it +will use its executable name as the name of the preload +file to use. For example, when called as .B mpost the -.I mpost -mem is used, which is identical to the -.I plain -mem. +.I mpost.mp +file is used, which is identical to +.I plain.mp. +When the +.B --ini +option is given, preloading does not happen. .PP The .I commands @@ -96,14 +94,11 @@ output commands or \*(MF's online display mechanism. .\"===================================================================== .SH FILES .TP -.I *.mem -Predigested \*(MP mem files. -.TP .I plain.mp -The standard mem file. +The standard preload file. .TP .I mfplain.mp -The \*(MF-compatible mem file. +The \*(MF-compatible preload file. .TP .I $TEXMFMAIN/metapost/base/*.mp The standard \*(MP macros included in the original distribution. diff --git a/Build/source/texk/web2c/mplibdir/ChangeLog b/Build/source/texk/web2c/mplibdir/ChangeLog index 953844b6b43..2b711ba0ee9 100644 --- a/Build/source/texk/web2c/mplibdir/ChangeLog +++ b/Build/source/texk/web2c/mplibdir/ChangeLog @@ -1,3 +1,11 @@ +2011-04-05 Taco Hoekwater <taco@luatex.org> + + * Import of MetaPost 1.503, updating the .w files as well + as lmplib.c and avl.c, and adding mpmath.w and removing memio.w + * am/libmplib.am: add support for mpmath.w, remove memio.w + * mptrap.test: remove the second part as metapost no longer + uses .mem files at all + 2011-03-28 Peter Breitenlohner <peb@mppmu.mpg.de> * am/libmplib.am: Use AM_CFLAGS instead of libmplib_a_CFLAGS. diff --git a/Build/source/texk/web2c/mplibdir/am/libmplib.am b/Build/source/texk/web2c/mplibdir/am/libmplib.am index f6a12646074..c4e32f2f602 100644 --- a/Build/source/texk/web2c/mplibdir/am/libmplib.am +++ b/Build/source/texk/web2c/mplibdir/am/libmplib.am @@ -11,17 +11,17 @@ libmplib_a_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/mplibdir ## libmplib C sources libmplib_a_SOURCES = mplibdir/avl.h mplibdir/avl.c -nodist_libmplib_a_SOURCES = $(memio_c_h) $(mp_c_h) $(psout_c_h) $(svgout_c_h) $(tfmin_c_h) -memio_c_h = memio.c mpmemio.h +nodist_libmplib_a_SOURCES = $(mp_c_h) $(psout_c_h) $(svgout_c_h) $(tfmin_c_h) $(mpmath_c_h) mp_c_h = mp.c mplib.h mpmp.h psout_c_h = mppsout.h psout.c mplibps.h svgout_c_h = mplibsvg.h mpsvgout.h svgout.c +mpmath_c_h = mpmath.h mpmath.c tfmin_c_h = mptfmin.h tfmin.c -$(memio_c_h): memio-tangle - @$(mp_ctangle_sh) memio -memio-tangle: ctangle$(EXEEXT) mplibdir/memio.w tangle-sh - @$(mp_ctangle_sh) memio +$(mpmath_c_h): mpmath-tangle + @$(mp_ctangle_sh) mpmath +mpmath-tangle: ctangle$(EXEEXT) mplibdir/mpmath.w tangle-sh + @$(mp_ctangle_sh) mpmath $(mp_c_h): mp-tangle @$(mp_ctangle_sh) mp @@ -45,14 +45,14 @@ tfmin-tangle: ctangle$(EXEEXT) mplibdir/tfmin.w tangle-sh ## libmplib CWeb sources libmplib_web = mplibdir/mp.w mplibdir/psout.w mplibdir/svgout.w -libmplib_web += mplibdir/memio.w mplibdir/tfmin.w +libmplib_web += mplibdir/mpmath.w mplibdir/tfmin.w $(libmplib_a_OBJECTS): $(nodist_libmplib_a_SOURCES) EXTRA_DIST += $(libmplib_web) DISTCLEANFILES += $(nodist_libmplib_a_SOURCES) \ - memio-tangle mp-tangle psout-tangle svgout-tangle tfmin-tangle + mpmath-tangle mp-tangle psout-tangle svgout-tangle tfmin-tangle ## only used by luaTeX EXTRA_DIST += mplibdir/lmplib.c diff --git a/Build/source/texk/web2c/mplibdir/avl.c b/Build/source/texk/web2c/mplibdir/avl.c index 1062de9d568..de3c63322ad 100644 --- a/Build/source/texk/web2c/mplibdir/avl.c +++ b/Build/source/texk/web2c/mplibdir/avl.c @@ -14,7 +14,7 @@ const void * avl_default_item_copy (const void *item) { - return item; + return (const void *) item; } void * @@ -574,13 +574,13 @@ node_find_index (avl_size_t idx, avl_tree t) if (idx == t->count) return node_last (a); - while ((c = idx - get_rank (a)) != 0) + while ((c = (int)(idx - get_rank (a))) != 0) { if (c < 0) a = sub_left (a); else { - idx = c; + idx = (avl_size_t)c; a = sub_right (a); } } @@ -601,7 +601,7 @@ rebalance_ins (avl_node * a, int dir, avl_tree t) while (1) { - incr_rank (a, !dir); + incr_rank (a, (rbal_t)(!dir)); if (get_bal (a)) break; set_skew (a, dir); @@ -748,7 +748,7 @@ rebalance_ins (avl_node * a, int dir, avl_tree t) while ((p = a->up) != NULL) { - incr_rank (p, (a == sub_left (p))); + incr_rank (p, (rbal_t)(a == sub_left (p))); a = p; } @@ -766,8 +766,9 @@ static avl_code_t rebalance_del (avl_node * p, avl_tree t, void **backup) { avl_node **r, *a, *c; - int dir, bal; - dir = 0; /* for -W */ + rbal_t bal; + int dir = 0; + a = p->up; if (a == NULL) r = &t->root; @@ -819,7 +820,7 @@ rebalance_del (avl_node * p, avl_tree t, void **backup) if (a == NULL) return 2; - decr_rank (a, !dir); + decr_rank (a, (rbal_t)(!dir)); bal = get_bal (a); if (0 == dir) @@ -990,7 +991,7 @@ rebalance_del (avl_node * p, avl_tree t, void **backup) /* Finish adjusting ranks */ while ((p = a->up) != NULL) { - decr_rank (p, (a == sub_left (p))); + decr_rank (p, (rbal_t)(a == sub_left (p))); a = p; } @@ -1255,8 +1256,8 @@ avl_ins (void *item, avl_tree t, avl_bool_t allow_duplicates) #endif avl_compare_func cmp = t->compare; avl_node **r, *a; - int dir; - dir = 0; /* for -W */ + int dir = 0; + for (r = &t->root, a = NULL; *r != NULL; r = &a->sub[dir = dir > 0]) { a = *r; @@ -1298,7 +1299,7 @@ static avl_code_t node_del_first (avl_tree t, struct ptr_handler *h) { avl_node *p, *a, *c; - int bal; + rbal_t bal; p = node_first (t->root); a = p->up; @@ -1412,7 +1413,7 @@ node_del_last (avl_tree t, struct ptr_handler *h) { avl_node *p, *a, *c; - int bal; + rbal_t bal; p = node_last (t->root); a = p->up; @@ -1528,7 +1529,7 @@ join_left (avl_node * p, avl_node ** r0, avl_node * r1, int delta, int n) while (*r != NULL) { a = *r; - n -= get_rank (a); + n -= (int)get_rank (a); r = &sub_right (a); } } @@ -1537,8 +1538,8 @@ join_left (avl_node * p, avl_node ** r0, avl_node * r1, int delta, int n) while (delta < -1) { a = *r; - delta += is_lskew (a) + 1; - n -= get_rank (a); + delta += (int)(is_lskew (a) + 1); + n -= (int)get_rank (a); r = &sub_right (a); } r1->up = p; @@ -1645,7 +1646,7 @@ join_right (avl_node * p, avl_node * r0, avl_node ** r1, int delta, int n) while (*r != NULL) { a = *r; - incr_rank (a, n); + incr_rank (a, (rbal_t)n); r = &sub_left (a); } n = 1; @@ -1655,8 +1656,8 @@ join_right (avl_node * p, avl_node * r0, avl_node ** r1, int delta, int n) while (delta > +1) { a = *r; - delta -= is_rskew (a) + 1; - incr_rank (a, n); + delta -= (int)(is_rskew (a) + 1); + incr_rank (a, (rbal_t)n); r = &sub_left (a); } r0->up = p; @@ -1819,7 +1820,7 @@ avl_ins_index (void *item, avl_size_t idx, avl_tree t) else if (idx == t->count) { return - join_left (p, &t->root, (avl_node *) NULL, /*delta= */ 0, t->count); + join_left (p, &t->root, (avl_node *) NULL, /*delta= */ 0, (int)t->count); } else { @@ -1897,14 +1898,14 @@ avl_cat (avl_tree t0, avl_tree t1) if (node_del_first (t1, &h) == 2) --delta; (void) join_left ((avl_node *) h.ptr, &t0->root, t1->root, delta, - t0->count + 1); + (int)(t0->count + 1)); } else { if (node_del_last (t0, &h) == 2) ++delta; (void) join_right ((avl_node *) h.ptr, t0->root, &t1->root, delta, - t0->count + 1); + (int)(t0->count + 1)); t0->root = t1->root; } @@ -1941,7 +1942,7 @@ avl_split (const void *item, avl_tree t, avl_tree t0, avl_tree t1) /* invariant: [na]= size of tree rooted at [a] plus one */ - for (a = t->root, na = t->count + 1, k = 0;;) + for (a = t->root, na = (int)(t->count + 1), k = 0;;) { d_ = Item_Compare (cmp, t, item, get_item (a)); CMPERR_CHECK__SPLIT (t->param); @@ -1952,9 +1953,9 @@ avl_split (const void *item, avl_tree t, avl_tree t0, avl_tree t1) return 0; an[k++] = na; if (d_) - na -= get_rank (a); + na -= (int)get_rank (a); else - na = get_rank (a); + na = (int)get_rank (a); a = p; } @@ -1988,7 +1989,7 @@ avl_split (const void *item, avl_tree t, avl_tree t0, avl_tree t1) h[0] = ha - (is_rskew (a) ? 2 : 1); h[1] = ha - (is_lskew (a) ? 2 : 1); n[0] = get_rank (a); /* size of r[0] plus one */ - n[1] = na - n[0]; /* size of r[1] plus one */ + n[1] = (avl_size_t)na - n[0]; /* size of r[1] plus one */ for (p = a->up, d_ = a != sub_left (p);;) { @@ -2002,7 +2003,7 @@ avl_split (const void *item, avl_tree t, avl_tree t0, avl_tree t1) ha += (is_rskew (a) ? 2 : 1); h[1] = ha - (is_lskew (a) ? 2 : 1); nn = n[1]; - n[1] += an[k - 1] - get_rank (a); + n[1] += (avl_size_t)(an[k - 1] - (int)get_rank (a)); if (p != NULL) d_ = a != sub_left (p); rbal (a) = 0; @@ -2013,14 +2014,14 @@ avl_split (const void *item, avl_tree t, avl_tree t0, avl_tree t1) r[1] = sub_right (a); if (r[1] != NULL) r[1]->up = NULL; - h[1] += (2 == join_right (a, rr, r + 1, h[1] - hh, nn)); + h[1] += (2 == join_right (a, rr, r + 1, h[1] - hh, (int)nn)); } else { h[1] = hh + (2 == join_left (a, r + 1, sub_right (a), h[1] - hh, - nn)); + (int)nn)); } } else @@ -2040,13 +2041,13 @@ avl_split (const void *item, avl_tree t, avl_tree t0, avl_tree t1) r[0] = sub_left (a); if (r[0] != NULL) r[0]->up = NULL; - h[0] += (2 == join_left (a, r, rr, hh - h[0], nn)); + h[0] += (2 == join_left (a, r, rr, hh - h[0], (int)nn)); } else { h[0] = hh + (2 == - join_right (a, sub_left (a), r, hh - h[0], nn)); + join_right (a, sub_left (a), r, hh - h[0], (int)nn)); } } diff --git a/Build/source/texk/web2c/mplibdir/lmplib.c b/Build/source/texk/web2c/mplibdir/lmplib.c index a155e705af0..80f7284fc8b 100644 --- a/Build/source/texk/web2c/mplibdir/lmplib.c +++ b/Build/source/texk/web2c/mplibdir/lmplib.c @@ -20,7 +20,9 @@ #include <w2c/config.h> #include <stdlib.h> #include <string.h> +#ifdef HAVE_UNISTD_H #include <unistd.h> +#endif #include <math.h> /* temporary */ #ifndef pdfTeX @@ -56,7 +58,7 @@ int luaopen_mplib(lua_State * L); /* forward */ #define mplib_init_S(a) do { \ lua_pushliteral(L,#a); \ - mplib_##a##_ptr = lua_tostring(L,-1); \ + mplib_##a##_ptr = lua_tostring(L,-1); \ mplib_##a##_index = luaL_ref (L,LUA_REGISTRYINDEX); \ } while (0) @@ -238,7 +240,7 @@ static char *mplib_find_file(MP mp, const char *fname, const char *fmode, int ft lua_getfield(L, LUA_REGISTRYINDEX, "mplib_file_finder"); if (lua_isfunction(L, -1)) { char *s = NULL; - const char *x = NULL; + const char *x = NULL; lua_pushstring(L, fname); lua_pushstring(L, fmode); if (ftype >= mp_filetype_text) { @@ -247,8 +249,7 @@ static char *mplib_find_file(MP mp, const char *fname, const char *fmode, int ft lua_pushstring(L, mplib_filetype_names[ftype]); } if (lua_pcall(L, 3, 1, 0) != 0) { - fprintf(stdout, "Error in mp.find_file: %s\n", - lua_tostring(L, -1)); + fprintf(stdout, "Error in mp.find_file: %s\n", lua_tostring(L, -1)); return NULL; } x = lua_tostring(L, -1); @@ -307,18 +308,6 @@ static int mplib_new(lua_State * L) options->max_print_line = (int)lua_tointeger(L, -1); if (options->max_print_line<60) options->max_print_line = 60; break; - case P_MAIN_MEMORY: - options->main_memory = (int)lua_tointeger(L, -1); - break; - case P_HASH_SIZE: - options->hash_size = (unsigned)lua_tointeger(L, -1); - break; - case P_PARAM_SIZE: - options->param_size = (int)lua_tointeger(L, -1); - break; - case P_IN_OPEN: - options->max_in_open = (int)lua_tointeger(L, -1); - break; case P_RANDOM_SEED: options->random_seed = (int)lua_tointeger(L, -1); break; @@ -386,16 +375,16 @@ static int mplib_wrapresults(lua_State * L, mp_run_data *res, int status) { lua_checkstack(L, 5); lua_newtable(L); - if (res->term_out.size != 0) { - lua_pushstring(L, res->term_out.data); + if (res->term_out.used != 0) { + lua_pushlstring(L, res->term_out.data, res->term_out.used); lua_setfield(L, -2, "term"); } - if (res->error_out.size != 0) { - lua_pushstring(L, res->error_out.data); + if (res->error_out.used != 0) { + lua_pushlstring(L, res->error_out.data, res->error_out.used); lua_setfield(L, -2, "error"); } - if (res->log_out.size != 0) { - lua_pushstring(L, res->log_out.data); + if (res->log_out.used != 0) { + lua_pushlstring(L, res->log_out.data, res->log_out.used); lua_setfield(L, -2, "log"); } if (res->edges != NULL) { @@ -433,7 +422,7 @@ static int mplib_execute(lua_State * L) char *s = xstrdup(lua_tolstring(L, 2, &l)); int h = mp_execute(*mp_ptr, s, l); mp_run_data *res = mp_rundata(*mp_ptr); - xfree(s); + free(s); return mplib_wrapresults(L, res, h); } else { lua_pushnil(L); @@ -469,7 +458,7 @@ static int mplib_char_dimension(lua_State * L, int t) } else { lua_pushnumber(L,(lua_Number)mp_get_char_dimension(*mp_ptr,fname,charnum,t)); } - xfree(fname); + free(fname); } else { lua_pushnumber(L, (lua_Number)0); } @@ -721,10 +710,10 @@ static int mplib_gr_tostring(lua_State * L) static double eps = 0.0001; -static double coord_range_x (mp_knot *h, double dz) { +static double coord_range_x (mp_knot h, double dz) { double z; double zlo = 0.0, zhi = 0.0; - mp_knot *f = h; + mp_knot f = h; while (h != NULL) { z = (double)h->x_coord; if (z < zlo) zlo = z; else if (z > zhi) zhi = z; @@ -739,10 +728,10 @@ static double coord_range_x (mp_knot *h, double dz) { return (zhi - zlo <= dz ? aspect_bound : aspect_default); } -static double coord_range_y (mp_knot *h, double dz) { +static double coord_range_y (mp_knot h, double dz) { double z; double zlo = 0.0, zhi = 0.0; - mp_knot *f = h; + mp_knot f = h; while (h != NULL) { z = (double)h->y_coord; if (z < zlo) zlo = z; else if (z > zhi) zhi = z; @@ -764,7 +753,7 @@ static int mplib_gr_peninfo(lua_State * L) { double rx = 1.0, sx = 0.0, sy = 0.0, ry = 1.0, tx = 0.0, ty = 0.0; double divider = 1.0; double width = 1.0; - mp_knot *p = NULL, *path = NULL; + mp_knot p = NULL, path = NULL; struct mp_graphic_object **hh = is_gr_object(L, -1); if (!*hh) { lua_pushnil(L); @@ -877,9 +866,9 @@ static int mplib_gr_fields(lua_State * L) #define MPLIB_PATH 0 #define MPLIB_PEN 1 -static void mplib_push_path(lua_State * L, struct mp_knot *h, int is_pen) +static void mplib_push_path(lua_State * L, struct mp_knot_data *h, int is_pen) { - struct mp_knot *p; /* for scanning the path */ + struct mp_knot_data *p; /* for scanning the path */ int i = 1; p = h; if (p != NULL) { @@ -887,14 +876,14 @@ static void mplib_push_path(lua_State * L, struct mp_knot *h, int is_pen) do { lua_createtable(L, 0, 6); if (!is_pen) { - if (p->left_type != mp_explicit) { + if (p->data.types.left_type != mp_explicit) { mplib_push_S(left_type); - lua_pushstring(L, knot_type_enum[p->left_type]); + lua_pushstring(L, knot_type_enum[p->data.types.left_type]); lua_rawset(L, -3); } - if (p->right_type != mp_explicit) { + if (p->data.types.right_type != mp_explicit) { mplib_push_S(right_type); - lua_pushstring(L, knot_type_enum[p->right_type]); + lua_pushstring(L, knot_type_enum[p->data.types.right_type]); lua_rawset(L, -3); } } @@ -918,7 +907,7 @@ static void mplib_push_path(lua_State * L, struct mp_knot *h, int is_pen) lua_rawset(L, -3); lua_rawseti(L, -2, i); i++; - if (p->right_type == mp_endpoint) { + if (p->data.types.right_type == mp_endpoint) { return; } p = p->next; @@ -931,9 +920,9 @@ static void mplib_push_path(lua_State * L, struct mp_knot *h, int is_pen) /* this assumes that the top of the stack is a table or nil already in the case */ -static void mplib_push_pentype(lua_State * L, struct mp_knot *h) +static void mplib_push_pentype(lua_State * L, mp_knot h) { - struct mp_knot *p; /* for scanning the path */ + mp_knot p; /* for scanning the path */ p = h; if (p == NULL) { /* do nothing */ diff --git a/Build/source/texk/web2c/mplibdir/memio.w b/Build/source/texk/web2c/mplibdir/memio.w deleted file mode 100644 index 2c85ae10282..00000000000 --- a/Build/source/texk/web2c/mplibdir/memio.w +++ /dev/null @@ -1,514 +0,0 @@ -% $Id: psout.w 616 2008-07-09 14:05:55Z taco $ -% -% Copyright 2008-2009 Taco Hoekwater. -% -% This program is free software: you can redistribute it and/or modify -% it under the terms of the GNU Lesser General Public License as published by -% the Free Software Foundation, either version 3 of the License, or -% (at your option) any later version. -% -% This program is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU Lesser General Public License for more details. -% -% You should have received a copy of the GNU Lesser General Public License -% along with this program. If not, see <http://www.gnu.org/licenses/>. -% -% TeX is a trademark of the American Mathematical Society. -% METAFONT is a trademark of Addison-Wesley Publishing Company. -% PostScript is a trademark of Adobe Systems Incorporated. - -% Here is TeX material that gets inserted after \input webmac - -\font\tenlogo=logo10 % font used for the METAFONT logo -\def\MP{{\tenlogo META}\-{\tenlogo POST}} -\def\title{MetaPost MEM reading and writing} -\def\topofcontents{\hsize 5.5in - \vglue -30pt plus 1fil minus 1.5in - \def\?##1]{\hbox to 1in{\hfil##1.\ }} - } -\def\botofcontents{\vskip 0pt plus 1fil minus 1.5in} -\pdfoutput=1 -\pageno=3 - -@ As usual, need true and false. - -@d true 1 -@d false 0 -@d null 0 /* the null pointer */ -@d incr(A) (A)=(A)+1 /* increase a variable by unity */ -@d decr(A) (A)=(A)-1 /* decrease a variable by unity */ -@d qo(A) (A) /* to read eight bits from a quarterword */ -@d qi(A) (quarterword)(A) /* to store eight bits in a quarterword */ -@d max_quarterword 0x3FFF /* largest allowable value in a |quarterword| */ -@d max_halfword 0xFFFFFFF /* largest allowable value in a |halfword| */ -@d max_str_ref 127 /* ``infinite'' number of references */ - - -@c -#include <w2c/config.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include "mplib.h" -#include "mpmp.h" -static void swap_items (char *p, int nitems, size_t size); -/* If we have these macros, use them, as they provide a better guide to - the endianess when cross-compiling. */ -#if defined (BYTE_ORDER) && defined (BIG_ENDIAN) && defined (LITTLE_ENDIAN) -#ifdef WORDS_BIGENDIAN -#undef WORDS_BIGENDIAN -#endif -#if BYTE_ORDER == BIG_ENDIAN -#define WORDS_BIGENDIAN -#endif -#endif -/* More of the same, but now NeXT-specific. */ -#ifdef NeXT -#ifdef WORDS_BIGENDIAN -#undef WORDS_BIGENDIAN -#endif -#ifdef __BIG_ENDIAN__ -#define WORDS_BIGENDIAN -#endif -#endif -#include "mpmemio.h" /* internal header */ - -@ @(mpmemio.h@>= -extern void mp_store_mem_file (MP mp); - -@ @c void mp_store_mem_file (MP mp) { - integer k; /* all-purpose index */ - pointer p,q; /* all-purpose pointers */ - integer x; /* something to dump */ - four_quarters w; /* four ASCII codes */ - memory_word WW; - @<Create the |mem_ident|, open the mem file, - and inform the user that dumping has begun@>; - @<Dump constants for consistency check@>; - @<Dump the string pool@>; - @<Dump the dynamic memory@>; - @<Dump the table of equivalents and the hash table@>; - @<Dump a few more things and the closing check word@>; - @<Close the mem file@>; -} - -@ Corresponding to the procedure that dumps a mem file, we also have a function -that reads~one~in. The function returns |false| if the dumped mem is -incompatible with the present \MP\ table sizes, etc. - -@d too_small(A) { wake_up_terminal; - wterm_ln("---! Must increase the "); wterm((A)); -@.Must increase the x@> - goto OFF_BASE; - } - -@(mpmemio.h@>= -extern boolean mp_load_mem_file (MP mp); - -@ @c -boolean mp_load_mem_file (MP mp) { - integer k; /* all-purpose index */ - pointer p,q; /* all-purpose pointers */ - integer x; /* something undumped */ - str_number s; /* some temporary string */ - four_quarters w; /* four ASCII codes */ - memory_word WW; - @<Undump the string pool@>; - @<Undump the dynamic memory@>; - @<Undump the table of equivalents and the hash table@>; - @<Undump a few more things and the closing check word@>; - return true; /* it worked! */ -OFF_BASE: - wake_up_terminal; - wterm_ln("(Fatal mem file error; I'm stymied)\n"); -@.Fatal mem file error@> - return false; -} - -@ If we want mem files to be shareable between different endianness architectures, -we have to swap some of the items so that everything turns out right. -Code borrowed from |texmfmp.c|. - -Make the NITEMS items pointed at by P, each of size SIZE, be the - opposite-endianness of whatever they are now. */ - -@d SWAP(x, y) temp = (x); (x) = (y); (y) = temp - -@c -static void -swap_items (char *p, int nitems, size_t size) -{ - char temp; -#if !defined (WORDS_BIGENDIAN) - /* Since `size' does not change, we can write a while loop for each - case, and avoid testing `size' for each time. */ - switch (size) - { - /* 16-byte items happen on the DEC Alpha machine when we are not - doing sharable memory dumps. */ - case 16: - while (nitems--) - { - SWAP (p[0], p[15]); - SWAP (p[1], p[14]); - SWAP (p[2], p[13]); - SWAP (p[3], p[12]); - SWAP (p[4], p[11]); - SWAP (p[5], p[10]); - SWAP (p[6], p[9]); - SWAP (p[7], p[8]); - p += size; - } - break; - - case 8: - while (nitems--) - { - SWAP (p[0], p[7]); - SWAP (p[1], p[6]); - SWAP (p[2], p[5]); - SWAP (p[3], p[4]); - p += size; - } - break; - - case 4: - while (nitems--) - { - SWAP (p[0], p[3]); - SWAP (p[1], p[2]); - p += size; - } - break; - - case 2: - while (nitems--) - { - SWAP (p[0], p[1]); - p += size; - } - break; - - case 1: - /* Nothing to do. */ - break; - - default: - fprintf (stderr, "Can't swap a %d-byte item for (un)dumping", (int)size); - exit(1); - } -#endif -} - -@ Mem files consist of |memory_word| items, and we use the following -macros to dump words of different types: - -@d dump_wd(A) { WW=(A); swap_items((char *)&WW,1,sizeof(WW)); (mp->write_binary_file)(mp,mp->mem_file,&WW,sizeof(WW)); } -@d dump_int(A) { int cint=(A); swap_items((char *)&cint,1,sizeof(cint)); (mp->write_binary_file)(mp,mp->mem_file,&cint,sizeof(cint)); } -@d dump_hh(A) { WW.hh=(A); swap_items((char *)&WW,1,sizeof(WW)); (mp->write_binary_file)(mp,mp->mem_file,&WW,sizeof(WW)); } -@d dump_qqqq(A) { WW.qqqq=(A); swap_items((char *)&WW,1,sizeof(WW)); (mp->write_binary_file)(mp,mp->mem_file,&WW,sizeof(WW)); } -@d dump_string(A) { dump_int((int)(strlen(A)+1)); - (mp->write_binary_file)(mp,mp->mem_file,A,strlen(A)+1); } - -@ The inverse macros are slightly more complicated, since we need to check -the range of the values we are reading in. We say `|undump(a)(b)(x)|' to -read an integer value |x| that is supposed to be in the range |a<=x<=b|. - -@d mgeti(A) do { - size_t wanted = sizeof(A); - void *A_ptr = &A; - (mp->read_binary_file)(mp, mp->mem_file,&A_ptr,&wanted); - if (wanted!=sizeof(A)) goto OFF_BASE; - swap_items(A_ptr,1,wanted); -} while (0) - -@d mgetw(A) do { - size_t wanted = sizeof(A); - void *A_ptr = &A; - (mp->read_binary_file)(mp, mp->mem_file,&A_ptr,&wanted); - if (wanted!=sizeof(A)) goto OFF_BASE; - swap_items(A_ptr,1,wanted); -} while (0) - -@d undump_wd(A) { mgetw(WW); A=WW; } -@d undump_int(A) { int cint; mgeti(cint); A=cint; } -@d undump_hh(A) { mgetw(WW); A=WW.hh; } -@d undump_qqqq(A) { mgetw(WW); A=WW.qqqq; } -@d undump_strings(A,B,C) { - undump_int(x); if ( (x<(A)) || (x>(B)) ) goto OFF_BASE; else C=str(x); } -@d undump(A,B,C) { undump_int(x); - if ( (x<(A)) || (x>(int)(B)) ) goto OFF_BASE; else C=x; } -@d undump_size(A,B,C,D) { undump_int(x); - if (x<(A)) goto OFF_BASE; - if (x>(B)) too_small((C)); else D=x; } -@d undump_string(A) { - size_t the_wanted; - void *the_string; - integer XX=0; - undump_int(XX); - the_wanted = (size_t)XX; - the_string = mp_xmalloc(mp,(size_t)XX,1); - (mp->read_binary_file)(mp,mp->mem_file,&the_string,&the_wanted); - A = (char *)the_string; - if (the_wanted!=(size_t)XX) goto OFF_BASE; -} - -@ The next few sections of the program should make it clear how we use the -dump/undump macros. - -@<Dump constants for consistency check@>= -x = metapost_magic; dump_int(x); -dump_int(mp->mem_top); -dump_int((integer)mp->hash_size); -dump_int(mp->hash_prime) -dump_int(mp->param_size); -dump_int(mp->max_in_open); - -@ Sections of a \.{WEB} program that are ``commented out'' still contribute -strings to the string pool; therefore \.{INIMP} and \MP\ will have -the same strings. (And it is, of course, a good thing that they do.) -@.WEB@> -@^string pool@> - -@(mpmemio.h@>= -extern int mp_undump_constants (MP mp); - -@ @c -int mp_undump_constants (MP mp) { - integer x; - undump_int(x); - if (x!=metapost_magic) return x; - undump_int(x); mp->mem_top = x; - undump_int(x); mp->hash_size = (unsigned)x; - undump_int(x); mp->hash_prime = x; - undump_int(x); mp->param_size = x; - undump_int(x); mp->max_in_open = x; - return metapost_magic; - OFF_BASE: - return -1; -} - -@ We do string pool compaction to avoid dumping unused strings. - -@d dump_four_ASCII - w.b0=qi(mp->str_pool[k]); w.b1=qi(mp->str_pool[k+1]); - w.b2=qi(mp->str_pool[k+2]); w.b3=qi(mp->str_pool[k+3]); - dump_qqqq(w) - -@<Dump the string pool@>= -mp_do_compaction(mp, mp->pool_size); -dump_int(mp->pool_ptr); -dump_int(mp->max_str_ptr); -dump_int(mp->str_ptr); -k=0; -while ( (mp->next_str[k]==k+1) && (k<=mp->max_str_ptr) ) - k++; -dump_int(k); -while ( k<=mp->max_str_ptr ) { - dump_int(mp->next_str[k]); incr(k); -} -k=0; -while (1) { - dump_int(mp->str_start[k]); /* TODO: valgrind warning here */ - if ( k==mp->str_ptr ) { - break; - } else { - k=mp->next_str[k]; - } -} -k=0; -while (k+4<mp->pool_ptr ) { - dump_four_ASCII; k=k+4; -} -k=mp->pool_ptr-4; dump_four_ASCII; -mp_print_ln(mp); mp_print(mp, "at most "); mp_print_int(mp, mp->max_str_ptr); -mp_print(mp, " strings of total length "); -mp_print_int(mp, mp->pool_ptr) - -@ @d undump_four_ASCII - undump_qqqq(w); - mp->str_pool[k]=(ASCII_code)qo(w.b0); mp->str_pool[k+1]=(ASCII_code)qo(w.b1); - mp->str_pool[k+2]=(ASCII_code)qo(w.b2); mp->str_pool[k+3]=(ASCII_code)qo(w.b3) - -@<Undump the string pool@>= -undump_int(mp->pool_ptr); -mp_reallocate_pool(mp, mp->pool_ptr) ; -undump_int(mp->max_str_ptr); -mp_reallocate_strings (mp,mp->max_str_ptr) ; -undump(0,mp->max_str_ptr,mp->str_ptr); -undump(0,mp->max_str_ptr+1,s); -for (k=0;k<=s-1;k++) - mp->next_str[k]=k+1; -for (k=s;k<=mp->max_str_ptr;k++) - undump(s+1,mp->max_str_ptr+1,mp->next_str[k]); -mp->fixed_str_use=0; -k=0; -while (1) { - undump(0,mp->pool_ptr,mp->str_start[k]); - if ( k==mp->str_ptr ) break; - mp->str_ref[k]=max_str_ref; - incr(mp->fixed_str_use); - mp->last_fixed_str=k; k=mp->next_str[k]; -} -k=0; -while ( k+4<mp->pool_ptr ) { - undump_four_ASCII; k=k+4; -} -k=mp->pool_ptr-4; undump_four_ASCII; -mp->init_str_use=mp->fixed_str_use; mp->init_pool_ptr=mp->pool_ptr; -mp->max_pool_ptr=mp->pool_ptr; -mp->strs_used_up=mp->fixed_str_use; -mp->pool_in_use=mp->str_start[mp->str_ptr]; mp->strs_in_use=mp->fixed_str_use; -mp->max_pl_used=mp->pool_in_use; mp->max_strs_used=mp->strs_in_use; -mp->pact_count=0; mp->pact_chars=0; mp->pact_strs=0; - -@ By sorting the list of available spaces in the variable-size portion of -|mem|, we are usually able to get by without having to dump very much -of the dynamic memory. - -We recompute |var_used| and |dyn_used|, so that \.{INIMP} dumps valid -information even when it has not been gathering statistics. - -@<Dump the dynamic memory@>= -mp_sort_avail(mp); mp->var_used=0; -dump_int(mp->lo_mem_max); dump_int(mp->rover); -p=0; q=mp->rover; x=0; -do { - for (k=p;k<= q+1;k++) - dump_wd(mp->mem[k]); - x=x+q+2-p; mp->var_used=mp->var_used+q-p; - p=q+node_size(q); q=rmp_link(q); -} while (q!=mp->rover); -mp->var_used=mp->var_used+mp->lo_mem_max-p; -mp->dyn_used=mp->mem_end+1-mp->hi_mem_min; -for (k=p;k<= mp->lo_mem_max;k++ ) - dump_wd(mp->mem[k]); -x=x+mp->lo_mem_max+1-p; -dump_int(mp->hi_mem_min); dump_int(mp->avail); -for (k=mp->hi_mem_min;k<=mp->mem_end;k++ ) - dump_wd(mp->mem[k]); -x=x+mp->mem_end+1-mp->hi_mem_min; -p=mp->avail; -while ( p!=null ) { - decr(mp->dyn_used); p=mp_link(p); -} -dump_int(mp->var_used); dump_int(mp->dyn_used); -mp_print_ln(mp); mp_print_int(mp, x); -mp_print(mp, " memory locations dumped; current usage is "); -mp_print_int(mp, mp->var_used); mp_print_char(mp, xord('&')); mp_print_int(mp, mp->dyn_used) - -@ @<Undump the dynamic memory@>= -undump(lo_mem_stat_max+1000,hi_mem_stat_min-1,mp->lo_mem_max); -undump(lo_mem_stat_max+1,mp->lo_mem_max,mp->rover); -p=0; q=mp->rover; -do { - for (k=p;k<= q+1; k++) - undump_wd(mp->mem[k]); - p=q+node_size(q); - if ( (p>mp->lo_mem_max)||((q>=rmp_link(q))&&(rmp_link(q)!=mp->rover)) ) - goto OFF_BASE; - q=rmp_link(q); -} while (q!=mp->rover); -for (k=p;k<=mp->lo_mem_max;k++ ) - undump_wd(mp->mem[k]); -undump(mp->lo_mem_max+1,hi_mem_stat_min,mp->hi_mem_min); -undump(null,mp->mem_top,mp->avail); mp->mem_end=mp->mem_top; -mp->last_pending=spec_head; -for (k=mp->hi_mem_min;k<= mp->mem_end;k++) - undump_wd(mp->mem[k]); -undump_int(mp->var_used); undump_int(mp->dyn_used) - -@ A different scheme is used to compress the hash table, since its lower region -is usually sparse. When |text(p)<>0| for |p<=hash_used|, we output three -words: |p|, |hash[p]|, and |eqtb[p]|. The hash table is, of course, densely -packed for |p>=hash_used|, so the remaining entries are output in~a~block. - -@<Dump the table of equivalents and the hash table@>= -dump_int(mp->hash_used); -mp->st_count=frozen_inaccessible-1-mp->hash_used; -for (p=1;p<=mp->hash_used;p++) { - if ( text(p)!=0 ) { - dump_int(p); dump_hh(mp->hash[p]); dump_hh(mp->eqtb[p]); incr(mp->st_count); - } -} -for (p=mp->hash_used+1;p<=(int)hash_end;p++) { - dump_hh(mp->hash[p]); dump_hh(mp->eqtb[p]); -} -dump_int(mp->st_count); -mp_print_ln(mp); mp_print_int(mp, mp->st_count); mp_print(mp, " symbolic tokens") - -@ @<Undump the table of equivalents and the hash table@>= -undump(1,frozen_inaccessible,mp->hash_used); -p=0; -do { - undump(p+1,mp->hash_used,p); - undump_hh(mp->hash[p]); undump_hh(mp->eqtb[p]); -} while (p!=mp->hash_used); -for (p=mp->hash_used+1;p<=(int)hash_end;p++ ) { - undump_hh(mp->hash[p]); undump_hh(mp->eqtb[p]); -} -undump_int(mp->st_count) - -@ We have already printed a lot of statistics, so we set |mp_tracing_stats:=0| -to prevent them appearing again. - -@<Dump a few more things and the closing check word@>= -dump_int(mp->max_internal); -dump_int(mp->int_ptr); -for (k=1;k<= mp->int_ptr;k++ ) { - dump_int(mp->internal[k]); - dump_int(mp->int_type[k]); - dump_string(mp->int_name[k]); -} -dump_int(mp->start_sym); -dump_int(mp->interaction); -dump_string(mp->mem_ident); -dump_int(mp->bg_loc); dump_int(mp->eg_loc); dump_int(mp->serial_no); dump_int(69073); -mp->internal[mp_tracing_stats]=0 - -@ @<Undump a few more things and the closing check word@>= -undump_int(x); -if (x>mp->max_internal) mp_grow_internals(mp,x); -undump_int(mp->int_ptr); -for (k=1;k<= mp->int_ptr;k++) { - undump_int(mp->internal[k]); - undump_int(mp->int_type[k]); - undump_string(mp->int_name[k]); -} -undump(0,frozen_inaccessible,mp->start_sym); -if (mp->interaction==mp_unspecified_mode) { - undump(mp_unspecified_mode,mp_error_stop_mode,mp->interaction); -} else { - undump(mp_unspecified_mode,mp_error_stop_mode,x); -} -undump_string(mp->mem_ident); -undump(1,hash_end,mp->bg_loc); -undump(1,hash_end,mp->eg_loc); -undump_int(mp->serial_no); -undump_int(x); -if (x!=69073) goto OFF_BASE - -@ @<Create the |mem_ident|...@>= -{ - char *tmp = mp_xmalloc(mp,11,1); - mp_xfree(mp->mem_ident); - mp->mem_ident = mp_xmalloc(mp,256,1); - mp_snprintf(tmp,11,"%04d.%02d.%02d", - (int)mp_round_unscaled(mp, mp->internal[mp_year]), - (int)mp_round_unscaled(mp, mp->internal[mp_month]), - (int)mp_round_unscaled(mp, mp->internal[mp_day])); - mp_snprintf(mp->mem_ident,256," (mem=%s %s)",mp->job_name, tmp); - mp_xfree(tmp); - mp_pack_job_name(mp, ".mem"); - while (! mp_w_open_out(mp, &mp->mem_file) ) - mp_prompt_file_name(mp, "mem file name", ".mem"); - mp_print_nl(mp, "Beginning to dump on file "); -@.Beginning to dump...@> - mp_print(mp, mp->name_of_file); - mp_print_nl(mp, mp->mem_ident); -} - -@ @<Close the mem file@>= -(mp->close_file)(mp,mp->mem_file) diff --git a/Build/source/texk/web2c/mplibdir/mp.w b/Build/source/texk/web2c/mplibdir/mp.w index a5a58af697e..ae88e33eb47 100644 --- a/Build/source/texk/web2c/mplibdir/mp.w +++ b/Build/source/texk/web2c/mplibdir/mp.w @@ -1,4 +1,4 @@ -% $Id: mp.w 1242 2010-05-31 08:18:21Z taco $ +% $Id: mp.w 1429 2010-10-21 13:59:22Z taco $ % % Copyright 2008-2009 Taco Hoekwater. % @@ -30,7 +30,6 @@ \font\logos=logosl10 \def\MF{{\tenlogo META}\-{\tenlogo FONT}} \def\MP{{\tenlogo META}\-{\tenlogo POST}} -\def\[#1]{\ignorespaces} % left over from pascal web \def\<#1>{$\langle#1\rangle$} \def\section{\mathhexbox278} \let\swap=\leftrightarrow @@ -44,7 +43,7 @@ \pdfoutput=1 \pageno=3 -@* \[1] Introduction. +@* Introduction. This is \MP\ by John Hobby, a graphics-language processor based on D. E. Knuth's \MF. @@ -89,14 +88,12 @@ undergoes any modifications, so that it will be clear which version of @^extensions to \MP@> @^system dependencies@> -@d default_banner "This is MetaPost, Version 1.211" /* printed when \MP\ starts */ +@d default_banner "This is MetaPost, Version 1.503" /* printed when \MP\ starts */ @d true 1 @d false 0 @(mpmp.h@>= -#define metapost_version "1.211" -#define metapost_magic (('M'*256) + 'P')*65536 + 1211 -#define metapost_old_magic (('M'*256) + 'P')*65536 + 1080 +#define metapost_version "1.503" @ The external library header for \MP\ is |mplib.h|. It contains a few typedefs and the header defintions for the externally used @@ -107,12 +104,13 @@ The most important of the typedefs is the definition of the structure large |MP_instance| structure. @(mplib.h@>= -typedef struct MP_instance * MP; -@<Exported types@> +typedef struct MP_instance *MP; +@<Exported types@>; typedef struct MP_options { @<Option variables@> } MP_options; @<Exported function headers@> + @ The internal header file is much longer: it not only lists the complete |MP_instance|, but also a lot of functions that have to be available to @@ -122,25 +120,28 @@ The variables from |MP_options| are included inside the |MP_instance| wholesale. @(mpmp.h@>= +#include "avl.h" #include <setjmp.h> -typedef struct psout_data_struct * psout_data; -typedef struct svgout_data_struct * svgout_data; +typedef struct psout_data_struct *psout_data; +typedef struct svgout_data_struct *svgout_data; #ifndef HAVE_BOOLEAN typedef int boolean; #endif #ifndef INTEGER_TYPE typedef int integer; #endif -@<Declare helpers@> -@<Types in the outer block@> -@<Constants in the outer block@> +@<Declare helpers@>; +@<Enumeration types@>; +@<Types in the outer block@>; +@<Constants in the outer block@>; typedef struct MP_instance { @<Option variables@> @<Global variables@> } MP_instance; @<Internal library declarations@> + -@ @c +@ @c #include <w2c/config.h> #include <stdio.h> #include <stdlib.h> @@ -148,42 +149,90 @@ typedef struct MP_instance { #include <stdarg.h> #include <assert.h> #ifdef HAVE_UNISTD_H -#include <unistd.h> /* for access() */ +# include <unistd.h> /* for access */ #endif -#include <time.h> /* for struct tm \& co */ +#include <time.h> /* for struct tm \& co */ #include "mplib.h" -#include "mplibps.h" /* external header */ -#include "mplibsvg.h" /* external header */ -#include "mpmp.h" /* internal header */ -#include "mppsout.h" /* internal header */ -#include "mpsvgout.h" /* internal header */ -#include "mptfmin.h" /* internal header */ -#include "mpmemio.h" /* internal header */ -@h -@<Declarations@> -@<Basic printing procedures@> +#include "mplibps.h" /* external header */ +#include "mplibsvg.h" /* external header */ +#include "mpmp.h" /* internal header */ +#include "mppsout.h" /* internal header */ +#include "mpsvgout.h" /* internal header */ +#include "mpmath.h" /* internal header */ +extern font_number mp_read_font_info (MP mp, char *fname); /* tfmin.w */ +@h @<Declarations@>; +@<Basic printing procedures@>; @<Error handling procedures@> + +@ Some debugging support for development. The trick with the variadic macros +probably only works in gcc, as this preprocessor feature was not formalized +until the c99 standard (and that is too new for us). Lets' hope that at least +most compilers understand the non-debug version. +@^system dependencies@> + +@(mpmp.h@>= +#define DEBUG 0 +#if DEBUG +void do_debug_printf(MP mp, const char *prefix, const char *fmt, ...); +# define debug_printf(a1,a2,a3) do_debug_printf(mp, "", a1,a2,a3) +# define FUNCTION_TRACE1(a1) do_debug_printf(mp, "FTRACE: ", a1) +# define FUNCTION_TRACE2(a1,a2) do_debug_printf(mp, "FTRACE: ", a1,a2) +# define FUNCTION_TRACE3(a1,a2,a3) do_debug_printf(mp, "FTRACE: ", a1,a2,a3) +# define FUNCTION_TRACE4(a1,a2,a3,a4) do_debug_printf(mp, "FTRACE: ", a1,a2,a3,a4) +#else +# define debug_printf(a1,a2,a3) +# define FUNCTION_TRACE1(a1) +# define FUNCTION_TRACE2(a1,a2) +# define FUNCTION_TRACE3(a1,a2,a3) +# define FUNCTION_TRACE4(a1,a2,a3,a4) +#endif + +@ This function occasionally crashes (if something is written after the +log file is already closed), but that is not so important while debugging. + +@c +#if DEBUG +void do_debug_printf(MP mp, const char *prefix, const char *fmt, ...) { + va_list ap; + va_start (ap, fmt); + if (mp->log_file && !ferror((FILE *)mp->log_file)) { + fputs(prefix, mp->log_file); + vfprintf(mp->log_file, fmt, ap); + } + va_end(ap); + va_start (ap, fmt); + if (mp->term_out && !ferror((FILE *)mp->term_out)) { + fputs(prefix, mp->term_out); + vfprintf(mp->term_out, fmt, ap); + } else { + fputs(prefix, stdout); + vfprintf(stdout, fmt, ap); + } + va_end(ap); +} +#endif @ Here are the functions that set up the \MP\ instance. -@<Declarations@> = +@<Declarations@>= MP_options *mp_options (void); -MP mp_initialize (MP_options *opt); +MP mp_initialize (MP_options * opt); @ @c MP_options *mp_options (void) { MP_options *opt; - size_t l = sizeof(MP_options); - opt = malloc(l); - if (opt!=NULL) { - memset (opt,0,l); - opt->ini_version = true; + size_t l = sizeof (MP_options); + opt = malloc (l); + if (opt != NULL) { + memset (opt, 0, l); } return opt; -} +} + @ @<Internal library declarations@>= @<Declare subroutines for parsing file names@> + @ The whole instance structure is initialized with zeroes, this greatly reduces the number of statements needed in @@ -194,123 +243,139 @@ the |Allocate or initialize variables| block. } while (0) @c -static MP mp_do_new (jmp_buf *buf) { - MP mp = malloc(sizeof(MP_instance)); - if (mp==NULL) { - xfree(buf); - return NULL; +static MP mp_do_new (jmp_buf * buf) { + MP mp = malloc (sizeof (MP_instance)); + if (mp == NULL) { + xfree (buf); + return NULL; } - memset(mp,0,sizeof(MP_instance)); + memset (mp, 0, sizeof (MP_instance)); mp->jump_buf = buf; return mp; } + @ @c static void mp_free (MP mp) { - int k; /* loop variable */ - @<Dealloc variables@> + int k; /* loop variable */ + @<Dealloc variables@>; if (mp->noninteractive) { @<Finish non-interactive use@>; } - xfree(mp->jump_buf); - xfree(mp); + xfree (mp->jump_buf); + @<Free table entries@>; + mp_free_math(mp); + xfree (mp); } + @ @c -static void mp_do_initialize ( MP mp) { - @<Local variables for initialization@> - @<Set initial values of key variables@> +static void mp_do_initialize (MP mp) { + @<Local variables for initialization@>; + @<Set initial values of key variables@>; } +@ For the retargetable math library, we need to have a pointer, at least. + +@<Global variables@>= +void *math; + @ This procedure gets things started properly. @c -MP mp_initialize (MP_options *opt) { +MP mp_initialize (MP_options * opt) { MP mp; - jmp_buf *buf = malloc(sizeof(jmp_buf)); - if (buf == NULL || setjmp(*buf) != 0) + jmp_buf *buf = malloc (sizeof (jmp_buf)); + if (buf == NULL || setjmp (*buf) != 0) return NULL; - mp = mp_do_new(buf); + mp = mp_do_new (buf); if (mp == NULL) return NULL; - mp->userdata=opt->userdata; - @<Set |ini_version|@>; - mp->noninteractive=opt->noninteractive; - set_callback_option(find_file); - set_callback_option(open_file); - set_callback_option(read_ascii_file); - set_callback_option(read_binary_file); - set_callback_option(close_file); - set_callback_option(eof_file); - set_callback_option(flush_file); - set_callback_option(write_ascii_file); - set_callback_option(write_binary_file); - set_callback_option(shipout_backend); + mp->userdata = opt->userdata; + mp->noninteractive = opt->noninteractive; + set_callback_option (find_file); + set_callback_option (open_file); + set_callback_option (read_ascii_file); + set_callback_option (read_binary_file); + set_callback_option (close_file); + set_callback_option (eof_file); + set_callback_option (flush_file); + set_callback_option (write_ascii_file); + set_callback_option (write_binary_file); + set_callback_option (shipout_backend); if (opt->banner && *(opt->banner)) { - mp->banner = xstrdup(opt->banner); + mp->banner = xstrdup (opt->banner); } else { - mp->banner = xstrdup(default_banner); + mp->banner = xstrdup (default_banner); } if (opt->command_line && *(opt->command_line)) - mp->command_line = xstrdup(opt->command_line); + mp->command_line = xstrdup (opt->command_line); if (mp->noninteractive) { @<Prepare function pointers for non-interactive use@>; - } + } /* open the terminal for output */ - t_open_out; - @<Find constant sizes@>; - @<Allocate or initialize variables@> - mp_reallocate_memory(mp,mp->mem_max); - mp_reallocate_paths(mp,1000); - mp_reallocate_fonts(mp,8); - mp->history=mp_fatal_error_stop; /* in case we quit during initialization */ + t_open_out; + mp->math = mp_initialize_math(mp); + @<Find and load preload file, if required@>; + @<Allocate or initialize variables@>; + mp_reallocate_paths (mp, 1000); + mp_reallocate_fonts (mp, 8); + mp->history = mp_fatal_error_stop; /* in case we quit during initialization */ @<Check the ``constant'' values...@>; - if ( mp->bad>0 ) { - char ss[256]; - mp_snprintf(ss,256,"Ouch---my internal constants have been clobbered!\n" - "---case %i",(int)mp->bad); - do_fprintf(mp->err_out,(char *)ss); -@.Ouch...clobbered@> + if (mp->bad > 0) { + char ss[256]; + mp_snprintf (ss, 256, "Ouch---my internal constants have been clobbered!\n" + "---case %i", (int) mp->bad); + do_putsf (mp->err_out, (char *) ss); +@.Ouch...clobbered@>; return mp; } - mp_do_initialize(mp); /* erase preloaded mem */ - if (mp->ini_version) { - @<Run inimpost commands@>; - } + mp_do_initialize (mp); /* erase preloaded mem */ + mp_init_tab (mp); /* initialize the tables */ + mp_init_prim (mp); /* call |primitive| for each primitive */ + mp_fix_date_and_time (mp); if (!mp->noninteractive) { @<Initialize the output routines@>; @<Get the first line of input and prepare to start@>; @<Initializations after first line is read@>; @<Fix up |mp->internal[mp_job_name]|@>; } else { - mp->history=mp_spotless; + mp->history = mp_spotless; } return mp; } + @ @<Initializations after first line is read@>= -mp_open_log_file(mp); -mp_set_job_id(mp); -mp_init_map_file(mp, mp->troff_mode); -mp->history=mp_spotless; /* ready to go! */ +mp_open_log_file (mp); +mp_set_job_id (mp); +mp_init_map_file (mp, mp->troff_mode); +mp->history = mp_spotless; /* ready to go! */ if (mp->troff_mode) { - mp->internal[mp_gtroffmode]=unity; - mp->internal[mp_prologues]=unity; + internal_value (mp_gtroffmode) = unity; + internal_value (mp_prologues) = unity; } -if ( mp->start_sym>0 ) { /* insert the `\&{everyjob}' symbol */ - mp->cur_sym=mp->start_sym; mp_back_input(mp); +if (mp->start_sym != NULL) { /* insert the `\&{everyjob}' symbol */ + mp->cur_sym = mp->start_sym; + mp_back_input (mp); } @ @<Exported function headers@>= extern MP_options *mp_options (void); -extern MP mp_initialize (MP_options *opt) ; -extern int mp_status(MP mp); -extern void *mp_userdata(MP mp); +extern MP mp_initialize (MP_options * opt); +extern int mp_status (MP mp); +extern void *mp_userdata (MP mp); @ @c -int mp_status(MP mp) { return mp->history; } +int mp_status (MP mp) { + return mp->history; +} + @ @c -void *mp_userdata(MP mp) { return mp->userdata; } +void *mp_userdata (MP mp) { + return mp->userdata; +} + @ The overall \MP\ program begins with the heading just shown, after which comes a bunch of procedure declarations and function declarations. @@ -329,78 +394,52 @@ when system wizards are fooling around with \MP\ without quite knowing what they are doing. Such code will not normally be compiled; it is delimited by the preprocessor test `|#ifdef DEBUG .. #endif|'. -@ This program has two important variations: (1) There is a long and slow -version called \.{INIMP}, which does the extra calculations needed to -@.INIMP@> -initialize \MP's internal tables; and (2)~there is a shorter and faster -production version, which cuts the initialization to a bare minimum. - -Which is which is decided at runtime. - @ The following parameters can be changed at compile time to extend or -reduce \MP's capacity. They may have different values in \.{INIMP} and -in production versions of \MP. -@.INIMP@> +reduce \MP's capacity. @^system dependencies@> @<Constants...@>= -#define file_name_size 255 /* file names shouldn't be longer than this */ -#define bistack_size 1500 /* size of stack for bisection algorithms; - should probably be left at this value */ +#define bistack_size 1500 /* size of stack for bisection algorithms; + should probably be left at this value */ @ Like the preceding parameters, the following quantities can be changed -to extend or reduce \MP's capacity. But if they are changed, -it is necessary to rerun the initialization program \.{INIMP} -@.INIMP@> -to generate new tables for the production \MP\ program. -One can't simply make helter-skelter changes to the following constants, -since certain rather complex initialization -numbers are computed from them. +to extend or reduce \MP's capacity. @ @<Glob...@>= -int max_strings; /* maximum number of strings; must not exceed |max_halfword| */ -int pool_size; /* maximum number of characters in strings, including all - error messages and help texts, and the names of all identifiers */ -int old_pool_size; /* a helper used by |mp_cat| */ -int mem_max; /* greatest index in \MP's internal |mem| array; - must be strictly less than |max_halfword|; - must be equal to |mem_top| in \.{INIMP}, otherwise |>=mem_top| */ -int mem_top; /* largest index in the |mem| array dumped by \.{INIMP}; - must not be greater than |mem_max| */ -int hash_prime; /* a prime number equal to about 85\pct! of |hash_size| */ +int pool_size; /* maximum number of characters in strings, including all + error messages and help texts, and the names of all identifiers */ +int max_in_open; /* maximum number of input files and error insertions that + can be going on simultaneously */ +int param_size; /* maximum number of simultaneous macro parameters */ @ @<Option variables@>= int error_line; /* width of context lines on terminal error messages */ -int half_error_line; /* width of first lines of contexts in terminal - error messages; should be between 30 and |error_line-15| */ -int halt_on_error; /* do we quit at the first error? */ -int max_print_line; /* width of longest text lines output; should be at least 60 */ -unsigned hash_size; /* maximum number of symbolic tokens, - must be less than |max_halfword-3*param_size| */ -int param_size; /* maximum number of simultaneous macro parameters */ -int max_in_open; /* maximum number of input files and error insertions that - can be going on simultaneously */ -int main_memory; /* only for options, to set up |mem_max| and |mem_top| */ +int half_error_line; /* width of first lines of contexts in terminal + error messages; should be between 30 and |error_line-15| */ +int halt_on_error; /* do we quit at the first error? */ +int max_print_line; /* width of longest text lines output; should be at least 60 */ void *userdata; /* this allows the calling application to setup local */ -char *banner; /* the banner that is printed to the screen and log */ +char *banner; /* the banner that is printed to the screen and log */ +int ini_version; @ @<Dealloc variables@>= -xfree(mp->banner); +xfree (mp->banner); @ -@d set_value(a,b,c) do { a=c; if (b>c) a=b; } while (0) +@d set_lower_limited_value(a,b,c) do { a=c; if (b>c) a=b; } while (0) @<Allocate or ...@>= -mp->max_strings=500; -mp->pool_size=10000; -mp->old_pool_size=10000; -set_value(mp->error_line,opt->error_line,79); -set_value(mp->half_error_line,opt->half_error_line,50); -if (mp->half_error_line>mp->error_line-15 ) - mp->half_error_line = mp->error_line-15; -mp->max_print_line=100; -set_value(mp->max_print_line,opt->max_print_line,79); +mp->param_size = 4; +mp->max_in_open = 0; +mp->pool_size = 10000; +set_lower_limited_value (mp->error_line, opt->error_line, 79); +set_lower_limited_value (mp->half_error_line, opt->half_error_line, 50); +if (mp->half_error_line > mp->error_line - 15) + mp->half_error_line = mp->error_line - 15; +mp->max_print_line = 100; +set_lower_limited_value (mp->max_print_line, opt->max_print_line, 79); mp->halt_on_error = (opt->halt_on_error ? true : false); +mp->ini_version = (opt->ini_version ? true : false); @ In case somebody has inadvertently made bad settings of the ``constants,'' \MP\ checks them using a global variable called |bad|. @@ -409,18 +448,17 @@ This is the second of many sections of \MP\ where global variables are defined. @<Glob...@>= -integer bad; /* is some ``constant'' wrong? */ +integer bad; /* is some ``constant'' wrong? */ -@ Later on we will say `\ignorespaces|if (mem_max>=max_halfword) bad=10;|', -or something similar. (We can't do that until |max_halfword| has been defined.) +@ Later on we will say `|if ( int_packets+17*int_increment>bistack_size )mp->bad=19;|', +or something similar. -In case you are wondering about the non-consequtive values of |bad|: some +In case you are wondering about the non-consequtive values of |bad|: most of the things that used to be WEB constants are now runtime variables with checking at assignment time. @<Check the ``constant'' values for consistency@>= -mp->bad=0; -if ( mp->mem_top<=1100 ) mp->bad=4; +mp->bad = 0; @ Some |goto| labels are used by the following definitions. The label `|restart|' is occasionally used at the very beginning of a procedure; and @@ -440,9 +478,8 @@ end up the same, the shared code may be gathered together at @d negate(A) (A)=-(A) /* change the sign of a variable */ @d double(A) (A)=(A)+(A) @d odd(A) ((A)%2==1) -@d do_nothing /* empty statement */ -@* \[2] The character set. +@* The character set. In order to make \MP\ readily portable to a wide variety of computers, all of its input text is converted to an internal eight-bit code that includes standard ASCII, the ``American Standard Code for @@ -459,7 +496,7 @@ the \&{char} and \&{ASCII} operations, and the comparison of strings. are said to be of type |ASCII_code|, which is a subrange of the integers. @<Types...@>= -typedef unsigned char ASCII_code; /* eight-bit numbers */ +typedef unsigned char ASCII_code; /* eight-bit numbers */ @ The present specification of \MP\ has been written under the assumption that the character set contains at least the letters and symbols associated @@ -467,7 +504,7 @@ with ASCII codes 040 through 0176; all of these characters are now available on most computer terminals. @<Types...@>= -typedef unsigned char text_char; /* the data type of characters in text files */ +typedef unsigned char text_char; /* the data type of characters in text files */ @ @<Local variables for init...@>= integer i; @@ -481,8 +518,8 @@ that are analogous to Pascal's |ord| and |chr| functions. #define xord(A) mp->xord[(A)] @ @<Glob...@>= -ASCII_code xord[256]; /* specifies conversion of input characters */ -text_char xchr[256]; /* specifies conversion of output characters */ +ASCII_code xord[256]; /* specifies conversion of input characters */ +text_char xchr[256]; /* specifies conversion of output characters */ @ The core system assumes all 8-bit is acceptable. If it is not, a change file has to alter the below section. @@ -499,7 +536,10 @@ more difficult, so they should be introduced cautiously if at all. @^system dependencies@> @<Set initial ...@>= -for (i=0;i<=0377;i++) { xchr(i)=(text_char)i; } +for (i = 0; i <= 0377; i++) { + xchr (i) = (text_char) i; +} + @ The following system-independent code makes the |xord| array contain a suitable inverse to the information in |xchr|. Note that if |xchr[i]=xchr[j]| @@ -508,13 +548,18 @@ where |i<j<0177|, the value of |xord[xchr[i]]| will turn out to be codes below 040 in case there is a coincidence. @<Set initial ...@>= -for (i=0;i<=255;i++) { - xord(xchr(i))=0177; +for (i = 0; i <= 255; i++) { + xord (xchr (i)) = 0177; +} +for (i = 0200; i <= 0377; i++) { + xord (xchr (i)) = (ASCII_code) i; +} +for (i = 0; i <= 0176; i++) { + xord (xchr (i)) = (ASCII_code) i; } -for (i=0200;i<=0377;i++) { xord(xchr(i))=(ASCII_code)i;} -for (i=0;i<=0176;i++) { xord(xchr(i))=(ASCII_code)i;} -@* \[3] Input and output. + +@* Input and output. The bane of portability is the fact that different operating systems treat input and output quite differently, perhaps because computer scientists have not given sufficient attention to this problem. People have felt somehow @@ -547,31 +592,31 @@ differentiate file searches if a library like kpathsea is used, the fopen mode is passed along for the same reason. @<Types...@>= -typedef unsigned char eight_bits ; /* unsigned one-byte quantity */ +typedef unsigned char eight_bits; /* unsigned one-byte quantity */ @ @<Exported types@>= enum mp_filetype { - mp_filetype_terminal = 0, /* the terminal */ - mp_filetype_error, /* the terminal */ - mp_filetype_program , /* \MP\ language input */ - mp_filetype_log, /* the log file */ - mp_filetype_postscript, /* the postscript output */ - mp_filetype_memfile, /* memory dumps */ - mp_filetype_metrics, /* TeX font metric files */ - mp_filetype_fontmap, /* PostScript font mapping files */ - mp_filetype_font, /* PostScript type1 font programs */ - mp_filetype_encoding, /* PostScript font encoding files */ - mp_filetype_text /* first text file for readfrom and writeto primitives */ + mp_filetype_terminal = 0, /* the terminal */ + mp_filetype_error, /* the terminal */ + mp_filetype_program, /* \MP\ language input */ + mp_filetype_log, /* the log file */ + mp_filetype_postscript, /* the postscript output */ + mp_filetype_memfile, /* memory dumps, obsolete */ + mp_filetype_metrics, /* TeX font metric files */ + mp_filetype_fontmap, /* PostScript font mapping files */ + mp_filetype_font, /* PostScript type1 font programs */ + mp_filetype_encoding, /* PostScript font encoding files */ + mp_filetype_text /* first text file for readfrom and writeto primitives */ }; -typedef char *(*mp_file_finder)(MP, const char *, const char *, int); -typedef void *(*mp_file_opener)(MP, const char *, const char *, int); -typedef char *(*mp_file_reader)(MP, void *, size_t *); -typedef void (*mp_binfile_reader)(MP, void *, void **, size_t *); -typedef void (*mp_file_closer)(MP, void *); -typedef int (*mp_file_eoftest)(MP, void *); -typedef void (*mp_file_flush)(MP, void *); -typedef void (*mp_file_writer)(MP, void *, const char *); -typedef void (*mp_binfile_writer)(MP, void *, void *, size_t); +typedef char *(*mp_file_finder) (MP, const char *, const char *, int); +typedef void *(*mp_file_opener) (MP, const char *, const char *, int); +typedef char *(*mp_file_reader) (MP, void *, size_t *); +typedef void (*mp_binfile_reader) (MP, void *, void **, size_t *); +typedef void (*mp_file_closer) (MP, void *); +typedef int (*mp_file_eoftest) (MP, void *); +typedef void (*mp_file_flush) (MP, void *); +typedef void (*mp_file_writer) (MP, void *, const char *); +typedef void (*mp_binfile_writer) (MP, void *, void *, size_t); @ @<Option variables@>= mp_file_finder find_file; @@ -591,70 +636,73 @@ This function may disappear altogether, it is currently only used for the default font map file. @c -static char *mp_find_file (MP mp, const char *fname, const char *fmode, int ftype) { +static char *mp_find_file (MP mp, const char *fname, const char *fmode, + int ftype) { (void) mp; - if (fmode[0] != 'r' || (! access (fname,R_OK)) || ftype) { - return mp_strdup(fname); + if (fmode[0] != 'r' || (!access (fname, R_OK)) || ftype) { + return mp_strdup (fname); } return NULL; } + @ Because |mp_find_file| is used so early, it has to be in the helpers section. @<Declarations@>= -static char *mp_find_file (MP mp, const char *fname, const char *fmode, int ftype) ; -static void *mp_open_file (MP mp , const char *fname, const char *fmode, int ftype) ; -static char *mp_read_ascii_file (MP mp, void *f, size_t *size) ; -static void mp_read_binary_file (MP mp, void *f, void **d, size_t *size) ; -static void mp_close_file (MP mp, void *f) ; -static int mp_eof_file (MP mp, void *f) ; -static void mp_flush_file (MP mp, void *f) ; -static void mp_write_ascii_file (MP mp, void *f, const char *s) ; -static void mp_write_binary_file (MP mp, void *f, void *s, size_t t) ; +static char *mp_find_file (MP mp, const char *fname, const char *fmode, + int ftype); +static void *mp_open_file (MP mp, const char *fname, const char *fmode, + int ftype); +static char *mp_read_ascii_file (MP mp, void *f, size_t * size); +static void mp_read_binary_file (MP mp, void *f, void **d, size_t * size); +static void mp_close_file (MP mp, void *f); +static int mp_eof_file (MP mp, void *f); +static void mp_flush_file (MP mp, void *f); +static void mp_write_ascii_file (MP mp, void *f, const char *s); +static void mp_write_binary_file (MP mp, void *f, void *s, size_t t); @ The function to open files can now be very short. @c -void *mp_open_file(MP mp, const char *fname, const char *fmode, int ftype) { +void *mp_open_file (MP mp, const char *fname, const char *fmode, int ftype) { char realmode[3]; (void) mp; realmode[0] = *fmode; realmode[1] = 'b'; realmode[2] = 0; - if (ftype==mp_filetype_terminal) { + if (ftype == mp_filetype_terminal) { return (fmode[0] == 'r' ? stdin : stdout); - } else if (ftype==mp_filetype_error) { + } else if (ftype == mp_filetype_error) { return stderr; - } else if (fname != NULL && (fmode[0] != 'r' || (! access (fname,R_OK)))) { - return (void *)fopen(fname, realmode); + } else if (fname != NULL && (fmode[0] != 'r' || (!access (fname, R_OK)))) { + return (void *) fopen (fname, realmode); } return NULL; } -@ This is a legacy interface: (almost) all file names pass through |name_of_file|. + +@ (Almost) all file names pass through |name_of_file|. @<Glob...@>= -char name_of_file[file_name_size+1]; /* the name of a system file */ -int name_length;/* this many characters are actually - relevant in |name_of_file| (the rest are blank) */ +char *name_of_file; /* the name of a system file */ @ If this parameter is true, the terminal and log will report the found file names for input files instead of the requested ones. It is off by default because it creates an extra filename lookup. @<Option variables@>= -int print_found_names; /* configuration parameter */ +int print_found_names; /* configuration parameter */ @ @<Allocate or initialize ...@>= -mp->print_found_names = (opt->print_found_names>0 ? true : false); +mp->print_found_names = (opt->print_found_names > 0 ? true : false); @ The |file_line_error_style| parameter makes \MP\ use a more standard compiler error message format instead of the Knuthian exclamation mark. It needs the actual version of the current input file name, that will be saved by |a_open_in| in the |long_name|. -TODO: currently these strings cause memory leaks, because they cannot +TODO: currently these long strings cause memory leaks, because they cannot be safely freed as they may appear in the |input_stack| multiple times. In fact, the current implementation is just a quick hack in response to a bug report for metapost 1.205. @@ -662,17 +710,15 @@ to a bug report for metapost 1.205. @d long_name mp->cur_input.long_name_field /* long name of the current file */ @<Option variables@>= -int file_line_error_style; /* configuration parameter */ +int file_line_error_style; /* configuration parameter */ @ @<Allocate or initialize ...@>= -mp->file_line_error_style = (opt->file_line_error_style>0 ? true : false); +mp->file_line_error_style = (opt->file_line_error_style > 0 ? true : false); @ \MP's file-opening procedures return |false| if no file identified by |name_of_file| could be opened. The |OPEN_FILE| macro takes care of the |print_found_names| parameter. -It is not used for opening a mem file for read, because that file name -is never printed. @d OPEN_FILE(A) do { if (mp->print_found_names || mp->file_line_error_style) { @@ -680,7 +726,8 @@ is never printed. if (s!=NULL) { *f = (mp->open_file)(mp,mp->name_of_file,A, ftype); if (mp->print_found_names) { - strncpy(mp->name_of_file,s,file_name_size); + xfree(mp->name_of_file); + mp->name_of_file = xstrdup(s); } if ((*(A) == 'r') && (ftype == mp_filetype_program)) { long_name = xstrdup(s); @@ -695,121 +742,113 @@ is never printed. } while (0); return (*f ? true : false) -@c +@c static boolean mp_a_open_in (MP mp, void **f, int ftype) { /* open a text file for input */ - OPEN_FILE("r"); -} -@# -#if 0 -boolean mp_w_open_in (MP mp, void **f) { - /* open a word file for input */ - *f = (mp->open_file)(mp,mp->name_of_file,"r",mp_filetype_memfile); - return (*f ? true : false); + OPEN_FILE ("r"); } -#endif @# static boolean mp_a_open_out (MP mp, void **f, int ftype) { /* open a text file for output */ - OPEN_FILE("w"); + OPEN_FILE ("w"); } @# static boolean mp_b_open_out (MP mp, void **f, int ftype) { /* open a binary file for output */ - OPEN_FILE("w"); -} -@# -boolean mp_w_open_out (MP mp, void **f) { - /* open a word file for output */ - int ftype = mp_filetype_memfile; - OPEN_FILE("w"); + OPEN_FILE ("w"); } -@ @<Internal library ...@>= -boolean mp_w_open_out (MP mp, void **f); @ @c -static char *mp_read_ascii_file (MP mp, void *ff, size_t *size) { +static char *mp_read_ascii_file (MP mp, void *ff, size_t * size) { int c; size_t len = 0, lim = 128; char *s = NULL; - FILE *f = (FILE *)ff; + FILE *f = (FILE *) ff; *size = 0; - (void) mp; /* for -Wunused */ - if (f==NULL) + (void) mp; /* for -Wunused */ + if (f == NULL) return NULL; - c = fgetc(f); - if (c==EOF) + c = fgetc (f); + if (c == EOF) return NULL; - s = malloc(lim); - if (s==NULL) return NULL; - while (c!=EOF && c!='\n' && c!='\r') { - if ((len+1)==lim) { - s =realloc(s, (lim+(lim>>2))); - if (s==NULL) return NULL; - lim+=(lim>>2); + s = malloc (lim); + if (s == NULL) + return NULL; + while (c != EOF && c != '\n' && c != '\r') { + if ((len + 1) == lim) { + s = realloc (s, (lim + (lim >> 2))); + if (s == NULL) + return NULL; + lim += (lim >> 2); } - s[len++] = (char)c; - c =fgetc(f); + s[len++] = (char) c; + c = fgetc (f); } - if (c=='\r') { - c = fgetc(f); - if (c!=EOF && c!='\n') - ungetc(c,f); + if (c == '\r') { + c = fgetc (f); + if (c != EOF && c != '\n') + ungetc (c, f); } s[len] = 0; *size = len; return s; } + @ @c void mp_write_ascii_file (MP mp, void *f, const char *s) { (void) mp; - if (f!=NULL) { - fputs(s,(FILE *)f); + if (f != NULL) { + fputs (s, (FILE *) f); } } + @ @c -void mp_read_binary_file (MP mp, void *f, void **data, size_t *size) { +void mp_read_binary_file (MP mp, void *f, void **data, size_t * size) { size_t len = 0; (void) mp; - if (f!=NULL) - len = fread(*data,1,*size,(FILE *)f); + if (f != NULL) + len = fread (*data, 1, *size, (FILE *) f); *size = len; } + @ @c void mp_write_binary_file (MP mp, void *f, void *s, size_t size) { (void) mp; - if (f!=NULL) - (void)fwrite(s,size,1,(FILE *)f); + if (f != NULL) + (void) fwrite (s, size, 1, (FILE *) f); } @ @c void mp_close_file (MP mp, void *f) { (void) mp; - if (f!=NULL) - fclose((FILE *)f); + if (f != NULL) + fclose ((FILE *) f); } + @ @c int mp_eof_file (MP mp, void *f) { (void) mp; - if (f!=NULL) - return feof((FILE *)f); - else + if (f != NULL) + return feof ((FILE *) f); + else return 1; } + @ @c void mp_flush_file (MP mp, void *f) { (void) mp; - if (f!=NULL) - fflush((FILE *)f); + if (f != NULL) + fflush ((FILE *) f); } + @ Input from text files is read one line at a time, using a routine called |input_ln|. This function is defined in terms of global variables called |buffer|, |first|, and |last| that will be described in detail later; for @@ -818,33 +857,34 @@ values, and that |first| and |last| are indices into this array representing the beginning and ending of a line of text. @<Glob...@>= -size_t buf_size; /* maximum number of characters simultaneously present in - current lines of open files */ -ASCII_code *buffer; /* lines of characters being read */ -size_t first; /* the first unused position in |buffer| */ -size_t last; /* end of the line just input to |buffer| */ -size_t max_buf_stack; /* largest index used in |buffer| */ +size_t buf_size; /* maximum number of characters simultaneously present in + current lines of open files */ +ASCII_code *buffer; /* lines of characters being read */ +size_t first; /* the first unused position in |buffer| */ +size_t last; /* end of the line just input to |buffer| */ +size_t max_buf_stack; /* largest index used in |buffer| */ @ @<Allocate or initialize ...@>= mp->buf_size = 200; -mp->buffer = xmalloc((mp->buf_size+1),sizeof(ASCII_code)); +mp->buffer = xmalloc ((mp->buf_size + 1), sizeof (ASCII_code)); @ @<Dealloc variables@>= -xfree(mp->buffer); +xfree (mp->buffer); @ @c -static void mp_reallocate_buffer(MP mp, size_t l) { +static void mp_reallocate_buffer (MP mp, size_t l) { ASCII_code *buffer; - if (l>max_halfword) { - mp_confusion(mp,"buffer size"); /* can't happen (I hope) */ + if (l > max_halfword) { + mp_confusion (mp, "buffer size"); /* can't happen (I hope) */ } - buffer = xmalloc((l+1),sizeof(ASCII_code)); - (void)memcpy(buffer,mp->buffer,(mp->buf_size+1)); - xfree(mp->buffer); - mp->buffer = buffer ; + buffer = xmalloc ((l + 1), sizeof (ASCII_code)); + (void) memcpy (buffer, mp->buffer, (mp->buf_size + 1)); + xfree (mp->buffer); + mp->buffer = buffer; mp->buf_size = l; } + @ The |input_ln| function brings the next line of input from the specified field into available positions of the buffer array and returns the value |true|, unless the file has already been entirely read, in which case it @@ -860,29 +900,30 @@ The variable |max_buf_stack|, which is used to keep track of how large the |buf_size| parameter must be to accommodate the present job, is also kept up to date by |input_ln|. -@c -static boolean mp_input_ln (MP mp, void *f ) { +@c +static boolean mp_input_ln (MP mp, void *f) { /* inputs the next line or returns |false| */ char *s; - size_t size = 0; - mp->last=mp->first; /* cf.\ Matthew 19\thinspace:\thinspace30 */ - s = (mp->read_ascii_file)(mp,f, &size); - if (s==NULL) - return false; - if (size>0) { - mp->last = mp->first+size; - if ( mp->last>=mp->max_buf_stack ) { - mp->max_buf_stack=mp->last+1; - while ( mp->max_buf_stack>mp->buf_size ) { - mp_reallocate_buffer(mp,(mp->buf_size+(mp->buf_size>>2))); + size_t size = 0; + mp->last = mp->first; /* cf.\ Matthew 19\thinspace:\thinspace30 */ + s = (mp->read_ascii_file) (mp, f, &size); + if (s == NULL) + return false; + if (size > 0) { + mp->last = mp->first + size; + if (mp->last >= mp->max_buf_stack) { + mp->max_buf_stack = mp->last + 1; + while (mp->max_buf_stack > mp->buf_size) { + mp_reallocate_buffer (mp, (mp->buf_size + (mp->buf_size >> 2))); } } - (void)memcpy((mp->buffer+mp->first),s,size); - } - free(s); + (void) memcpy ((mp->buffer + mp->first), s, size); + } + free (s); return true; } + @ The user's terminal acts essentially like other files of text, except that it is used both for input and for output. When the terminal is considered an input file, the file variable is called |term_in|, and when it @@ -890,16 +931,14 @@ is considered an output file the file variable is |term_out|. @^system dependencies@> @<Glob...@>= -void * term_in; /* the terminal as an input file */ -void * term_out; /* the terminal as an output file */ -void * err_out; /* the terminal as an output file */ +void *term_in; /* the terminal as an input file */ +void *term_out; /* the terminal as an output file */ +void *err_out; /* the terminal as an output file */ @ Here is how to open the terminal files. In the default configuration, nothing happens except that the command line (if there is one) is copied to the input buffer. The variable |command_line| will be filled by the -|main| procedure. The copying can not be done earlier in the program -logic because in the |INI| version, the |buffer| is also used for primitive -initialization. +|main| procedure. @d t_open_out do {/* open the terminal for text output */ mp->term_out = (mp->open_file)(mp,"terminal", "w", mp_filetype_terminal); @@ -934,9 +973,9 @@ these operations can be specified: @^system dependencies@> @(mpmp.h@>= -#define update_terminal (mp->flush_file)(mp,mp->term_out) /* empty the terminal output buffer */ -#define clear_terminal do_nothing /* clear the terminal input buffer */ -#define wake_up_terminal (mp->flush_file)(mp,mp->term_out) +#define update_terminal (mp->flush_file)(mp,mp->term_out) /* empty the terminal output buffer */ +#define clear_terminal /* clear the terminal input buffer */ +#define wake_up_terminal (mp->flush_file)(mp,mp->term_out) /* cancel the user's cancellation of output */ @ We need a special routine to read the first line of \MP\ input from @@ -984,40 +1023,44 @@ not be typed immediately after~`\.{**}'.) @d loc mp->cur_input.loc_field /* location of first unread character in |buffer| */ -@c -boolean mp_init_terminal (MP mp) { /* gets the terminal input started */ - t_open_in; - if (mp->last!=0) { - loc = 0; mp->first = 0; - return true; +@c +boolean mp_init_terminal (MP mp) { /* gets the terminal input started */ + t_open_in; + if (mp->last != 0) { + loc = 0; + mp->first = 0; + return true; } - while (1) { + while (1) { if (!mp->noninteractive) { - wake_up_terminal; do_fprintf(mp->term_out,"**"); update_terminal; -@.**@> + wake_up_terminal; + do_putsf (mp->term_out, "**"); +@.**@>; + update_terminal; } - if ( ! mp_input_ln(mp, mp->term_in ) ) { /* this shouldn't happen */ - do_fprintf(mp->term_out,"\n! End of file on the terminal... why?"); -@.End of file on the terminal@> + if (!mp_input_ln (mp, mp->term_in)) { /* this shouldn't happen */ + do_putsf (mp->term_out, "\n! End of file on the terminal... why?"); +@.End of file on the terminal@>; return false; } - loc=(halfword)mp->first; - while ( (loc<(int)mp->last)&&(mp->buffer[loc]==' ') ) - incr(loc); - if ( loc<(int)mp->last ) { - return true; /* return unless the line was all blank */ + loc = (halfword) mp->first; + while ((loc < (int) mp->last) && (mp->buffer[loc] == ' ')) + incr (loc); + if (loc < (int) mp->last) { + return true; /* return unless the line was all blank */ } if (!mp->noninteractive) { - do_fprintf(mp->term_out,"Please type the name of your input file.\n"); + do_putsf (mp->term_out, "Please type the name of your input file.\n"); } } } + @ @<Declarations@>= -static boolean mp_init_terminal (MP mp) ; +static boolean mp_init_terminal (MP mp); -@* \[4] String handling. +@* String handling. Symbolic token names and diagnostic messages are variable-length strings of eight-bit characters. Many strings \MP\ uses are simply literals in the compiled source, like the error messages and the names of the @@ -1026,139 +1069,177 @@ language, and these have to be interned. \MP\ uses strings more extensively than \MF\ does, but the necessary operations can still be handled with a fairly simple data structure. -The array |str_pool| contains all of the (eight-bit) ASCII codes in all -of the strings, and the array |str_start| contains indices of the starting -points of each string. Strings are referred to by integer numbers, so that -string number |s| comprises the characters |str_pool[j]| for -|str_start[s]<=j<str_start[ss]| where |ss=next_str[s]|. The string pool -is allocated sequentially and |str_pool[pool_ptr]| is the next unused -location. The first string number not currently in use is |str_ptr| -and |next_str[str_ptr]| begins a list of free string numbers. String -pool entries |str_start[str_ptr]| up to |pool_ptr| are reserved for a -string currently being constructed. - -String numbers 0 to 255 are reserved for strings that correspond to single -ASCII characters. This is in accordance with the conventions of \.{WEB}, -@.WEB@> -which converts single-character strings into the ASCII code number of the -single character involved, while it converts other strings into integers -and builds a string pool file. Thus, when the string constant \.{"."} appears -in the program below, \.{WEB} converts it into the integer 46, which is the -ASCII code for a period, while \.{WEB} will convert a string like \.{"hello"} -into some integer greater than~255. String number 46 will presumably be the -single character `\..'\thinspace; but some ASCII codes have no standard visible -representation, and \MP\ may need to be able to print an arbitrary -ASCII character, so the first 256 strings are used to specify exactly what -should be printed for each of the 256 possibilities. +The avl tree |strings| contains all of the known string structures. -@<Types...@>= -typedef int pool_pointer; /* for variables that point into |str_pool| */ -typedef int str_number; /* for variables that point into |str_start| */ +Each structure contains an |unsigned char| pointer containing the eight-bit +data, a |size_t| that holds the length of that data, and an |int| that +indicates how often this string is referenced (this will be explained below). +Such strings are referred to by structure pointers called |str_number|. + +Besides the avl tree, there is a set of three variables called |cur_string|, +|cur_length| and |cur_string_size| that are used for strings while they are +being built. + +@<Exported types...@>= +typedef struct { + unsigned char *str; /* the string value */ + size_t len; /* its length */ + int refs; /* number of references */ +} mp_lstring; +typedef mp_lstring *str_number; /* for pointers to string values */ @ @<Glob...@>= -ASCII_code *str_pool; /* the characters */ -pool_pointer *str_start; /* the starting pointers */ -str_number *next_str; /* for linking strings in order */ -pool_pointer pool_ptr; /* first unused position in |str_pool| */ -str_number str_ptr; /* number of the current string being created */ -pool_pointer init_pool_ptr; /* the starting value of |pool_ptr| */ -str_number init_str_use; /* the initial number of strings in use */ -pool_pointer max_pool_ptr; /* the maximum so far of |pool_ptr| */ -str_number max_str_ptr; /* the maximum so far of |str_ptr| */ +avl_tree strings; /* string avl tree */ +unsigned char *cur_string; /* current string buffer */ +size_t cur_length; /* current index in that buffer */ +size_t cur_string_size; /* malloced size of |cur_string| */ + +@ Here are the functions needed for the avl construction. + +@<Declarations@>= +static int comp_strings_entry (void *p, const void *pa, const void *pb); +static void *copy_strings_entry (const void *p); +static void *delete_strings_entry (void *p); + +@ An earlier version of this function used |strncmp|, but that produces +wrong results in some cases. +@c +#define STRCMP_RESULT(a) ((a)<0 ? -1 : ((a)>0 ? 1 : 0)) +static int comp_strings_entry (void *p, const void *pa, const void *pb) { + const mp_lstring *a = (const mp_lstring *) pa; + const mp_lstring *b = (const mp_lstring *) pb; + size_t l; + unsigned char *s,*t; + (void) p; + s = a->str; + t = b->str; + l = (a->len<=b->len ? a->len : b->len); + while ( l-->0 ) { + if ( *s!=*t) + return STRCMP_RESULT(*s-*t); + s++; t++; + } + return STRCMP_RESULT(a->len-b->len); +} +static void *copy_strings_entry (const void *p) { + str_number ff; + const mp_lstring *fp; + fp = (const mp_lstring *) p; + ff = malloc (sizeof (mp_lstring)); + if (ff == NULL) + return NULL; + ff->str = malloc (fp->len + 1); + if (ff->str == NULL) { + return NULL; + } + memcpy ((char *) ff->str, (char *) fp->str, fp->len + 1); + ff->len = fp->len; + ff->refs = 0; + return ff; +} +static void *delete_strings_entry (void *p) { + str_number ff = (str_number) p; + mp_xfree (ff->str); + mp_xfree (ff); + return NULL; +} + @ @<Allocate or initialize ...@>= -mp->str_pool = xmalloc ((mp->pool_size +1),sizeof(ASCII_code)); -mp->str_start = xmalloc ((mp->max_strings+1),sizeof(pool_pointer)); -mp->next_str = xmalloc ((mp->max_strings+1),sizeof(str_number)); +mp->strings = avl_create (comp_strings_entry, + copy_strings_entry, + delete_strings_entry, malloc, free, NULL); +mp->cur_string = NULL; +mp->cur_length = 0; +mp->cur_string_size = 0; @ @<Dealloc variables@>= -xfree(mp->str_pool); -xfree(mp->str_start); -xfree(mp->next_str); +if (mp->strings != NULL) + avl_destroy (mp->strings); +xfree (mp->cur_string); + +@ Actually creating strings is done by |make_string|, but in order to +do so it needs a way to create a new, empty string structure. + +@<Declarations@>= +static str_number new_strings_entry (MP mp); + +@ @c +static str_number new_strings_entry (MP mp) { + str_number ff; + ff = mp_xmalloc (mp, 1, sizeof (mp_lstring)); + ff->str = NULL; + ff->len = 0; + ff->refs = 0; + return ff; +} + @ Most printing is done from |char *|s, but sometimes not. Here are functions that convert an internal string into a |char *| for use by the printing routines, and vice versa. -@d str(A) mp_str(mp,A) -@d rts(A) mp_rts(mp,A) -@d null_str rts("") +@d null_str mp_rts(mp,"") @<Internal ...@>= int mp_xstrcmp (const char *a, const char *b); -char * mp_str (MP mp, str_number s); +char *mp_str (MP mp, str_number s); @ @<Declarations@>= +static str_number mp_rtsl (MP mp, const char *s, size_t l); static str_number mp_rts (MP mp, const char *s); static str_number mp_make_string (MP mp); -@ @c +@ @c int mp_xstrcmp (const char *a, const char *b) { - if (a==NULL && b==NULL) - return 0; - if (a==NULL) - return -1; - if (b==NULL) - return 1; - return strcmp(a,b); + if (a == NULL && b == NULL) + return 0; + if (a == NULL) + return -1; + if (b == NULL) + return 1; + return strcmp (a, b); } -@ The attempt to catch interrupted strings that is in |mp_rts|, is not -very good: it does not handle nesting over more than one level. -@c -char * mp_str (MP mp, str_number ss) { - char *s; - size_t len; - if (ss==mp->str_ptr) { - return NULL; - } else { - len = (size_t)length(ss); - s = xmalloc(len+1,sizeof(char)); - (void)memcpy(s,(char *)(mp->str_pool+(mp->str_start[ss])),len); - s[len] = 0; - return (char *)s; - } +@ @c +char *mp_str (MP mp, str_number ss) { + (void) mp; + return (char *) ss->str; +} +str_number mp_rtsl (MP mp, const char *s, size_t l) { + str_number str; + mp_lstring tmp; + tmp.str = xmalloc (l + 1, 1); + memcpy (tmp.str, s, (l + 1)); + tmp.len = l; + str = (str_number) avl_find (&tmp, mp->strings); + if (str == NULL) { /* not yet known */ + str = new_strings_entry (mp); + str->str = xmalloc (l + 1, 1); + memcpy (str->str, s, (l + 1)); + str->len = tmp.len; + assert (avl_ins (str, mp->strings, avl_false) > 0); + xfree (str->str); + xfree (str); + str = (str_number) avl_find (&tmp, mp->strings); + } + str->refs++; + free (tmp.str); + return str; } str_number mp_rts (MP mp, const char *s) { - int r; /* the new string */ - int old; /* a possible string in progress */ - int i=0; - if (strlen(s)==0) { - return 256; - } else if (strlen(s)==1) { - return s[0]; - } else { - old=0; - str_room((integer)strlen(s)); - if (mp->str_start[mp->str_ptr]<mp->pool_ptr) - old = mp_make_string(mp); - while (*s) { - append_char(*s); - s++; - } - r = mp_make_string(mp); - if (old!=0) { - str_room(length(old)); - while (i<length(old)) { - append_char((mp->str_start[old]+i)); - } - mp_flush_string(mp,old); - } - return r; - } + return mp_rtsl (mp, s, strlen (s)); } -@ Except for |strs_used_up|, the following string statistics are only -maintained when code between |stat| $\ldots$ |tats| delimiters is not -commented out: + +@ The next four variables for keeping track of string pool usage. @<Glob...@>= -integer strs_used_up; /* strings in use or unused but not reclaimed */ -integer pool_in_use; /* total number of cells of |str_pool| actually in use */ -integer strs_in_use; /* total number of strings actually in use */ -integer max_pl_used; /* maximum |pool_in_use| so far */ -integer max_strs_used; /* maximum |strs_in_use| so far */ +integer pool_in_use; /* total number of string bytes actually in use */ +integer max_pl_used; /* maximum |pool_in_use| so far */ +integer strs_in_use; /* total number of strings actually in use */ +integer max_strs_used; /* maximum |strs_in_use| so far */ @ Several of the elementary string operations are performed using \.{WEB} macros instead of functions, because many of the @@ -1167,401 +1248,156 @@ overhead of procedure calls. For example, here is a simple macro that computes the length of a string. @.WEB@> -@d str_stop(A) mp->str_start[mp->next_str[(A)]] /* one cell past the end of string \# */ -@d length(A) (str_stop((A))-mp->str_start[(A)]) /* the number of characters in string \# */ - -@ The length of the current string is called |cur_length|. If we decide that -the current string is not needed, |flush_cur_string| resets |pool_ptr| so that -|cur_length| becomes zero. - -@d cur_length (mp->pool_ptr - mp->str_start[mp->str_ptr]) -@d flush_cur_string mp->pool_ptr=mp->str_start[mp->str_ptr] +@d length(A) ((A)->len) /* the number of characters in string \# */ -@ Strings are created by appending character codes to |str_pool|. +@ Strings are created by appending character codes to |cur_string|. The |append_char| macro, defined here, does not check to see if the -value of |pool_ptr| has gotten too high; this test is supposed to be +buffer overflows; this test is supposed to be made before |append_char| is used. -To test if there is room to append |l| more characters to |str_pool|, +To test if there is room to append |l| more characters to |cur_string|, we shall write |str_room(l)|, which tries to make sure there is enough room -by compacting the string pool if necessary. If this does not work, -|do_compaction| aborts \MP\ and gives an apologetic error message. +in the |cur_string|. + +@d EXTRA_STRING 500 + +@d append_char(A) do { + if (mp->cur_string==NULL) reset_cur_string(mp); + else str_room(1); + *(mp->cur_string+mp->cur_length)=(unsigned char)(A); + mp->cur_length++; +} while (0) + +@d str_room(wsize) do { + size_t nsize; + if ((mp->cur_length+(size_t)wsize) > mp->cur_string_size) { + nsize = mp->cur_string_size + mp->cur_string_size / 5 + EXTRA_STRING; + if (nsize < (size_t)(wsize)) { + nsize = (size_t)wsize + EXTRA_STRING; + } + mp->cur_string = (unsigned char *) xrealloc(mp->cur_string, (unsigned)nsize, sizeof(unsigned char)); + memset (mp->cur_string+mp->cur_length,0,(nsize-mp->cur_length)); + mp->cur_string_size = nsize; + } +} while (0) -@d append_char(A) /* put |ASCII_code| \# at the end of |str_pool| */ -{ mp->str_pool[mp->pool_ptr]=(ASCII_code)(A); incr(mp->pool_ptr); -} -@d str_room(A) /* make sure that the pool hasn't overflowed */ - { if ( mp->pool_ptr+(A) > mp->max_pool_ptr ) { - if ( mp->pool_ptr+(A) > mp->pool_size ) mp_do_compaction(mp, (A)); - else mp->max_pool_ptr=mp->pool_ptr+(A); } - } -@ The following routine is similar to |str_room(1)| but it uses the -argument |mp->pool_size| to prevent |do_compaction| from aborting when -string space is exhausted. +@ At the very start of the metapost run and each time after +|make_string| has stored a new string in the avl tree, the +|cur_string| variable has to be prepared so that it will be ready to +start creating a new string. The initial size is fairly arbitrary, but +setting it a little higher than expected helps prevent |reallocs| @<Declarations@>= -static void mp_unit_str_room (MP mp); +static void reset_cur_string (MP mp); @ @c -void mp_unit_str_room (MP mp) { - if ( mp->pool_ptr>=mp->pool_size ) mp_do_compaction(mp, mp->pool_size); - if ( mp->pool_ptr>=mp->max_pool_ptr ) mp->max_pool_ptr=mp->pool_ptr+1; +static void reset_cur_string (MP mp) { + xfree (mp->cur_string); + mp->cur_length = 0; + mp->cur_string_size = 63; + mp->cur_string = (unsigned char *) xmalloc (64, sizeof (unsigned char)); + memset (mp->cur_string, 0, 64); } + @ \MP's string expressions are implemented in a brute-force way: Every -new string or substring that is needed is simply copied into the string pool. -Space is eventually reclaimed by a procedure called |do_compaction| with -the aid of a simple system system of reference counts. +new string or substring that is needed is simply stored into the string pool. +Space is eventually reclaimed using the aid of a simple system system +of reference counts. @^reference counts@> -The number of references to string number |s| will be |str_ref[s]|. The -special value |str_ref[s]=max_str_ref=127| is used to denote an unknown +The number of references to string number |s| will be |s->refs|. The +special value |s->refs=MAX_STR_REF=127| is used to denote an unknown positive number of references; such strings will never be recycled. If a string is ever referred to more than 126 times, simultaneously, we -put it in this category. Hence a single byte suffices to store each |str_ref|. - -@d max_str_ref 127 /* ``infinite'' number of references */ -@d add_str_ref(A) { if ( mp->str_ref[(A)]<max_str_ref ) incr(mp->str_ref[(A)]); } - -@<Glob...@>= -int *str_ref; +put it in this category. -@ @<Allocate or initialize ...@>= -mp->str_ref = xmalloc ((mp->max_strings+1),sizeof(int)); - -@ @<Dealloc variables@>= -xfree(mp->str_ref); +@d MAX_STR_REF 127 /* ``infinite'' number of references */ +@d add_str_ref(A) { if ( (A)->refs < MAX_STR_REF ) incr((A)->refs); } @ Here's what we do when a string reference disappears: @d delete_str_ref(A) { - if ( mp->str_ref[(A)]<max_str_ref ) { - if ( mp->str_ref[(A)]>1 ) decr(mp->str_ref[(A)]); + if ( (A)->refs < MAX_STR_REF ) { + if ( (A)->refs > 1 ) decr((A)->refs); else mp_flush_string(mp, (A)); } } @<Declarations@>= -static void mp_flush_string (MP mp,str_number s) ; - -@ We can't flush the first set of static strings at all, so there -is no point in trying +static void mp_flush_string (MP mp, str_number s); -@c -void mp_flush_string (MP mp,str_number s) { - if (length(s)>1) { - mp->pool_in_use=mp->pool_in_use-length(s); - decr(mp->strs_in_use); - if ( mp->next_str[s]!=mp->str_ptr ) { - mp->str_ref[s]=0; - } else { - mp->str_ptr=s; - decr(mp->strs_used_up); - } - mp->pool_ptr=mp->str_start[mp->str_ptr]; +@ @c +void mp_flush_string (MP mp, str_number s) { + if (s->refs == 0) { + decr (mp->strs_in_use); + mp->pool_in_use = mp->pool_in_use - (integer) length (s); + (void) avl_del (s, mp->strings, NULL); } } -@ C literals cannot be simply added, they need to be set so they can't -be flushed. -@d intern(A) mp_intern(mp,(A)) +@ Some C literals that are used as values cannot be simply added, +their reference count has to be set such that they can not be flushed. @c str_number mp_intern (MP mp, const char *s) { - str_number r ; - r = rts(s); - mp->str_ref[r] = max_str_ref; + str_number r; + r = mp_rts (mp, s); + r->refs = MAX_STR_REF; return r; } + @ @<Declarations@>= static str_number mp_intern (MP mp, const char *s); -@ Once a sequence of characters has been appended to |str_pool|, it +@ Once a sequence of characters has been appended to |cur_string|, it officially becomes a string when the function |make_string| is called. -This function returns the identification number of the new string as its -value. - -When getting the next unused string number from the linked list, we pretend -that -$$ \hbox{|max_str_ptr+1|, |max_str_ptr+2|, $\ldots$, |mp->max_strings|} $$ -are linked sequentially even though the |next_str| entries have not been -initialized yet. We never allow |str_ptr| to reach |mp->max_strings|; -|do_compaction| is responsible for making sure of this. +This function returns a pointer to the new string as its value. @<Declarations@>= static str_number mp_make_string (MP mp); -@ @c -str_number mp_make_string (MP mp) { /* current string enters the pool */ - str_number s; /* the new string */ -RESTART: - s=mp->str_ptr; - mp->str_ptr=mp->next_str[s]; - if ( mp->str_ptr>mp->max_str_ptr ) { - if ( mp->str_ptr==mp->max_strings ) { - mp->str_ptr=s; - mp_do_compaction(mp, 0); - goto RESTART; - } else { - mp->max_str_ptr=mp->str_ptr; - mp->next_str[mp->str_ptr]=mp->max_str_ptr+1; - } - } - mp->str_ref[s]=1; - mp->str_start[mp->str_ptr]=mp->pool_ptr; - incr(mp->strs_used_up); - incr(mp->strs_in_use); - mp->pool_in_use=mp->pool_in_use+length(s); - if ( mp->pool_in_use>mp->max_pl_used ) - mp->max_pl_used=mp->pool_in_use; - if ( mp->strs_in_use>mp->max_strs_used ) - mp->max_strs_used=mp->strs_in_use; - return s; -} - -@ The most interesting string operation is string pool compaction. The idea -is to recover unused space in the |str_pool| array by recopying the strings -to close the gaps created when some strings become unused. All string -numbers~$k$ where |str_ref[k]=0| are to be linked into the list of free string -numbers after |str_ptr|. If this fails to free enough pool space we issue an -|overflow| error unless |needed=mp->pool_size|. Calling |do_compaction| -with |needed=mp->pool_size| supresses all overflow tests. - -The compaction process starts with |last_fixed_str| because all lower numbered -strings are permanently allocated with |max_str_ref| in their |str_ref| entries. - -@<Glob...@>= -str_number last_fixed_str; /* last permanently allocated string */ -str_number fixed_str_use; /* number of permanently allocated strings */ - -@ @<Internal library ...@>= -void mp_do_compaction (MP mp, pool_pointer needed) ; - @ @c -void mp_do_compaction (MP mp, pool_pointer needed) { - str_number str_use; /* a count of strings in use */ - str_number r,s,t; /* strings being manipulated */ - pool_pointer p,q; /* destination and source for copying string characters */ - @<Advance |last_fixed_str| as far as possible and set |str_use|@>; - r=mp->last_fixed_str; - s=mp->next_str[r]; - p=mp->str_start[s]; - while ( s!=mp->str_ptr ) { - while ( mp->str_ref[s]==0 ) { - @<Advance |s| and add the old |s| to the list of free string numbers; - then |break| if |s=str_ptr|@>; - } - r=s; s=mp->next_str[s]; - incr(str_use); - @<Move string |r| back so that |str_start[r]=p|; make |p| the location - after the end of the string@>; - } -DONE: - @<Move the current string back so that it starts at |p|@>; - if ( needed<mp->pool_size ) { - @<Make sure that there is room for another string with |needed| characters@>; - } - @<Account for the compaction and make sure the statistics agree with the - global versions@>; - mp->strs_used_up=str_use; -} - -@ @<Advance |last_fixed_str| as far as possible and set |str_use|@>= -t=mp->next_str[mp->last_fixed_str]; -while (t!=mp->str_ptr && mp->str_ref[t]==max_str_ref) { - incr(mp->fixed_str_use); - mp->last_fixed_str=t; - t=mp->next_str[t]; -} -str_use=mp->fixed_str_use - -@ Because of the way |flush_string| has been written, it should never be -necessary to |break| here. The extra line of code seems worthwhile to -preserve the generality of |do_compaction|. - -@<Advance |s| and add the old |s| to the list of free string numbers;...@>= -{ -t=s; -s=mp->next_str[s]; -mp->next_str[r]=s; -mp->next_str[t]=mp->next_str[mp->str_ptr]; -mp->next_str[mp->str_ptr]=t; -if ( s==mp->str_ptr ) goto DONE; -} - -@ The string currently starts at |str_start[r]| and ends just before -|str_start[s]|. We don't change |str_start[s]| because it might be needed -to locate the next string. - -@<Move string |r| back so that |str_start[r]=p|; make |p| the location...@>= -q=mp->str_start[r]; -mp->str_start[r]=p; -while ( q<mp->str_start[s] ) { - mp->str_pool[p]=mp->str_pool[q]; - incr(p); incr(q); -} - -@ Pointers |str_start[str_ptr]| and |pool_ptr| have not been updated. When -we do this, anything between them should be moved. - -@ @<Move the current string back so that it starts at |p|@>= -q=mp->str_start[mp->str_ptr]; -mp->str_start[mp->str_ptr]=p; -while ( q<mp->pool_ptr ) { - mp->str_pool[p]=mp->str_pool[q]; - incr(p); incr(q); -} -mp->pool_ptr=p - -@ We must remember that |str_ptr| is not allowed to reach |mp->max_strings|. - -@<Make sure that there is room for another string with |needed| char...@>= -if ( str_use>=mp->max_strings-1 ) - mp_reallocate_strings (mp,str_use); -if ( mp->pool_ptr+needed>mp->max_pool_ptr ) { - mp_reallocate_pool(mp, mp->pool_ptr+needed); - mp->max_pool_ptr=mp->pool_ptr+needed; -} - -@ @<Internal library ...@>= -void mp_reallocate_strings (MP mp, str_number str_use) ; -void mp_reallocate_pool(MP mp, pool_pointer needed) ; - -@ @c -void mp_reallocate_strings (MP mp, str_number str_use) { - while ( str_use>=mp->max_strings-1 ) { - int l = mp->max_strings + (mp->max_strings/4); - XREALLOC (mp->str_ref, l, int); - XREALLOC (mp->str_start, l, pool_pointer); - XREALLOC (mp->next_str, l, str_number); - mp->max_strings = l; - } -} -void mp_reallocate_pool(MP mp, pool_pointer needed) { - while ( needed>mp->pool_size ) { - int l = mp->pool_size + (mp->pool_size/4); - XREALLOC (mp->str_pool, l, ASCII_code); - mp->pool_size = l; - } -} - -@ @<Account for the compaction and make sure the statistics agree with...@>= -if ( (mp->str_start[mp->str_ptr]!=mp->pool_in_use)||(str_use!=mp->strs_in_use) ) - mp_confusion(mp, "string"); -@:this can't happen string}{\quad string@> -incr(mp->pact_count); -mp->pact_chars=mp->pact_chars+mp->pool_ptr-str_stop(mp->last_fixed_str); -mp->pact_strs=mp->pact_strs+str_use-mp->fixed_str_use; - -@ A few more global variables are needed to keep track of statistics when -|stat| $\ldots$ |tats| blocks are not commented out. - -@<Glob...@>= -integer pact_count; /* number of string pool compactions so far */ -integer pact_chars; /* total number of characters moved during compactions */ -integer pact_strs; /* total number of strings moved during compactions */ - -@ @<Initialize compaction statistics@>= -mp->pact_count=0; -mp->pact_chars=0; -mp->pact_strs=0; - -@ The following subroutine compares string |s| with another string of the -same length that appears in |buffer| starting at position |k|; -the result is |true| if and only if the strings are equal. - -@c -static boolean mp_str_eq_buf (MP mp,str_number s, integer k) { - /* test equality of strings */ - pool_pointer j; /* running index */ - j=mp->str_start[s]; - while ( j<str_stop(s) ) { - if ( mp->str_pool[j++]!=mp->buffer[k++] ) - return false; - } - return true; -} - -@ This routine compares a pool string with a sequence of characters -of equal length. - -@c -static boolean mp_str_eq_cstr (MP mp,str_number s, char *k) { - /* test equality of strings */ - pool_pointer j; /* running index */ - j=mp->str_start[s]; - while ( j<str_stop(s) ) { - if ( mp->str_pool[j++]!=*k++ ) - return false; - } - return true; -} - -@ Here is a similar routine, but it compares two strings in the string pool, +str_number mp_make_string (MP mp) { /* current string enters the pool */ + str_number str; + mp_lstring tmp; + tmp.str = mp->cur_string; + tmp.len = mp->cur_length; + str = (str_number) avl_find (&tmp, mp->strings); + if (str == NULL) { /* not yet known */ + str = xmalloc (1, sizeof (mp_lstring)); + str->str = mp->cur_string; + str->len = tmp.len; + assert (avl_ins (str, mp->strings, avl_false) > 0); + str = (str_number) avl_find (&tmp, mp->strings); + mp->pool_in_use = mp->pool_in_use + (integer) length (str); + if (mp->pool_in_use > mp->max_pl_used) + mp->max_pl_used = mp->pool_in_use; + incr (mp->strs_in_use); + if (mp->strs_in_use > mp->max_strs_used) + mp->max_strs_used = mp->strs_in_use; + str->refs = 1; + } + reset_cur_string (mp); + return str; +} + + +@ Here is a routine that compares two strings in the string pool, and it does not assume that they have the same length. If the first string is lexicographically greater than, less than, or equal to the second, the result is respectively positive, negative, or zero. -@c +@c static integer mp_str_vs_str (MP mp, str_number s, str_number t) { - /* test equality of strings */ - pool_pointer j,k; /* running indices */ - integer ls,lt; /* lengths */ - integer l; /* length remaining to test */ - ls=length(s); lt=length(t); - if ( ls<=lt ) l=ls; else l=lt; - j=mp->str_start[s]; k=mp->str_start[t]; - while ( l-->0 ) { - if ( mp->str_pool[j]!=mp->str_pool[k] ) { - return (mp->str_pool[j]-mp->str_pool[k]); - } - j++; k++; - } - return (ls-lt); -} - -@ The initial values of |str_pool|, |str_start|, |pool_ptr|, -and |str_ptr| are computed by the \.{INIMP} program, based in part -on the information that \.{WEB} has output while processing \MP. -@.INIMP@> -@^string pool@> - -@c -void mp_get_strings_started (MP mp) { - /* initializes the string pool, - but returns |false| if something goes wrong */ - int k; /* small indices or counters */ - str_number g; /* a new string */ - mp->pool_ptr=0; mp->str_ptr=0; mp->max_pool_ptr=0; mp->max_str_ptr=0; - mp->str_start[0]=0; - mp->next_str[0]=1; - mp->pool_in_use=0; mp->strs_in_use=0; - mp->max_pl_used=0; mp->max_strs_used=0; - @<Initialize compaction statistics@>; - mp->strs_used_up=0; - @<Make the first 256 strings@>; - g=mp_make_string(mp); /* string 256 == "" */ - mp->str_ref[g]=max_str_ref; - mp->last_fixed_str=mp->str_ptr-1; - mp->fixed_str_use=mp->str_ptr; - return; + (void) mp; + return comp_strings_entry (NULL, (const void *) s, (const void *) t); } -@ @<Declarations@>= -static void mp_get_strings_started (MP mp); - -@ The first 256 strings will consist of a single character only. - -@<Make the first 256...@>= -for (k=0;k<=255;k++) { - append_char(k); - g=mp_make_string(mp); - mp->str_ref[g]=max_str_ref; -} @ The first 128 strings will contain 95 standard ASCII characters, and the other 33 characters will be printed in three-symbol form like `\.{\^\^A}' @@ -1584,9 +1420,10 @@ must be printable. @^system dependencies@> @<Character |k| cannot be printed@>= - (k<' ')||(k==127) +(k < ' ') || (k == 127) + -@* \[5] On-line and off-line printing. +@* On-line and off-line printing. Messages that are sent to a user's terminal and to the transcript-log file are produced by several `|print|' procedures. These procedures will direct their output to a variety of places, based on the setting of @@ -1639,27 +1476,29 @@ to the terminal, the transcript file, or the \ps\ output file, respectively. @d write_file 7 /* first write file selector */ @<Glob...@>= -void * log_file; /* transcript of \MP\ session */ -void * output_file; /* the generic font output goes here */ -unsigned int selector; /* where to print a message */ -unsigned char dig[23]; /* digits in a number, for rounding */ -integer tally; /* the number of characters recently printed */ +void *log_file; /* transcript of \MP\ session */ +void *output_file; /* the generic font output goes here */ +unsigned int selector; /* where to print a message */ +integer tally; /* the number of characters recently printed */ unsigned int term_offset; /* the number of characters on the current terminal line */ unsigned int file_offset; /* the number of characters on the current file line */ -ASCII_code *trick_buf; /* circular buffer for pseudoprinting */ -integer trick_count; /* threshold for pseudoprinting, explained later */ -integer first_count; /* another variable for pseudoprinting */ +ASCII_code *trick_buf; /* circular buffer for pseudoprinting */ +integer trick_count; /* threshold for pseudoprinting, explained later */ +integer first_count; /* another variable for pseudoprinting */ @ @<Allocate or initialize ...@>= -mp->trick_buf = xmalloc((mp->error_line+1),sizeof(ASCII_code)); +mp->trick_buf = xmalloc ((mp->error_line + 1), sizeof (ASCII_code)); @ @<Dealloc variables@>= -xfree(mp->trick_buf); +xfree (mp->trick_buf); @ @<Initialize the output routines@>= -mp->selector=term_only; mp->tally=0; mp->term_offset=0; mp->file_offset=0; +mp->selector = term_only; +mp->tally = 0; +mp->term_offset = 0; +mp->file_offset = 0; @ Macro abbreviations for output to the terminal and to the log file are defined here for convenience. Some systems need special conventions @@ -1668,15 +1507,15 @@ by changing |wterm|, |wterm_ln|, and |wterm_cr| here. @^system dependencies@> @(mpmp.h@>= -#define do_fprintf(f,b) (mp->write_ascii_file)(mp,f,b) -#define wterm(A) do_fprintf(mp->term_out,(A)) +#define do_putsf(f,b) (mp->write_ascii_file)(mp,f,b) +#define wterm(A) do_putsf(mp->term_out,(A)) #define wterm_chr(A) { unsigned char ss[2]; ss[0]=(A); ss[1]='\0'; wterm((char *)ss);} -#define wterm_cr do_fprintf(mp->term_out,"\n") -#define wterm_ln(A) { wterm_cr; do_fprintf(mp->term_out,(A)); } -#define wlog(A) do_fprintf(mp->log_file,(A)) +#define wterm_cr do_putsf(mp->term_out,"\n") +#define wterm_ln(A) { wterm_cr; do_putsf(mp->term_out,(A)); } +#define wlog(A) do_putsf(mp->log_file,(A)) #define wlog_chr(A) { unsigned char ss[2]; ss[0]=(A); ss[1]='\0'; wlog((char *)ss);} -#define wlog_cr do_fprintf(mp->log_file, "\n") -#define wlog_ln(A) { wlog_cr; do_fprintf(mp->log_file,(A)); } +#define wlog_cr do_putsf(mp->log_file, "\n") +#define wlog_ln(A) { wlog_cr; do_putsf(mp->log_file,(A)); } @ To end a line of text output, we call |print_ln|. Cases |0..max_write_files| @@ -1687,34 +1526,40 @@ use an array |wr_file| that will be declared later. @<Internal library ...@>= void mp_print (MP mp, const char *s); void mp_print_ln (MP mp); -void mp_print_visible_char (MP mp, ASCII_code s); void mp_print_char (MP mp, ASCII_code k); void mp_print_str (MP mp, str_number s); void mp_print_nl (MP mp, const char *s); -void mp_print_two (MP mp,scaled x, scaled y) ; -void mp_print_scaled (MP mp,scaled s); +void mp_print_two (MP mp, scaled x, scaled y); + +@ @<Declarations@>= +static void mp_print_visible_char (MP mp, ASCII_code s); @ @<Basic print...@>= -void mp_print_ln (MP mp) { /* prints an end-of-line */ - switch (mp->selector) { - case term_and_log: - wterm_cr; wlog_cr; - mp->term_offset=0; mp->file_offset=0; +void mp_print_ln (MP mp) { /* prints an end-of-line */ + switch (mp->selector) { + case term_and_log: + wterm_cr; + wlog_cr; + mp->term_offset = 0; + mp->file_offset = 0; break; - case log_only: - wlog_cr; mp->file_offset=0; + case log_only: + wlog_cr; + mp->file_offset = 0; break; - case term_only: - wterm_cr; mp->term_offset=0; + case term_only: + wterm_cr; + mp->term_offset = 0; break; case no_print: - case pseudo: - case new_string: + case pseudo: + case new_string: break; - default: - do_fprintf(mp->wr_file[(mp->selector-write_file)],"\n"); + default: + do_putsf (mp->wr_file[(mp->selector - write_file)], "\n"); } -} /* note that |tally| is not affected */ +} /* note that |tally| is not affected */ + @ The |print_visible_char| procedure sends one character to the desired destination, using the |xchr| array to map it into an external character @@ -1725,54 +1570,56 @@ routines are the ones that limit lines to at most |max_print_line| characters. But we must make an exception for the \ps\ output file since it is not safe to cut up lines arbitrarily in \ps. -Procedure |unit_str_room| needs to be declared |forward| here because it calls -|do_compaction| and |do_compaction| can call the error routines. Actually, -|unit_str_room| avoids |overflow| errors but it can call |confusion|. - @<Basic printing...@>= -void mp_print_visible_char (MP mp, ASCII_code s) { /* prints a single character */ +static void mp_print_visible_char (MP mp, ASCII_code s) { /* prints a single character */ switch (mp->selector) { - case term_and_log: - wterm_chr(xchr(s)); wlog_chr(xchr(s)); - incr(mp->term_offset); incr(mp->file_offset); - if ( mp->term_offset==(unsigned)mp->max_print_line ) { - wterm_cr; mp->term_offset=0; + case term_and_log: + wterm_chr (xchr (s)); + wlog_chr (xchr (s)); + incr (mp->term_offset); + incr (mp->file_offset); + if (mp->term_offset == (unsigned) mp->max_print_line) { + wterm_cr; + mp->term_offset = 0; }; - if ( mp->file_offset==(unsigned)mp->max_print_line ) { - wlog_cr; mp->file_offset=0; + if (mp->file_offset == (unsigned) mp->max_print_line) { + wlog_cr; + mp->file_offset = 0; }; break; - case log_only: - wlog_chr(xchr(s)); incr(mp->file_offset); - if ( mp->file_offset==(unsigned)mp->max_print_line ) mp_print_ln(mp); + case log_only: + wlog_chr (xchr (s)); + incr (mp->file_offset); + if (mp->file_offset == (unsigned) mp->max_print_line) + mp_print_ln (mp); break; - case term_only: - wterm_chr(xchr(s)); incr(mp->term_offset); - if ( mp->term_offset==(unsigned)mp->max_print_line ) mp_print_ln(mp); + case term_only: + wterm_chr (xchr (s)); + incr (mp->term_offset); + if (mp->term_offset == (unsigned) mp->max_print_line) + mp_print_ln (mp); break; - case no_print: + case no_print: break; - case pseudo: - if ( mp->tally<mp->trick_count ) - mp->trick_buf[mp->tally % mp->error_line]=s; + case pseudo: + if (mp->tally < mp->trick_count) + mp->trick_buf[mp->tally % mp->error_line] = s; break; - case new_string: - if (mp->pool_ptr>=mp->pool_size || mp->pool_ptr>=mp->max_pool_ptr ) { - mp_unit_str_room(mp); - if ( mp->pool_ptr>=mp->pool_size ) - goto DONE; /* drop characters if string space is full */ - }; - append_char(s); + case new_string: + append_char (s); break; default: - { text_char ss[2]; ss[0] = xchr(s); ss[1]=0; - do_fprintf(mp->wr_file[(mp->selector-write_file)],(char *)ss); + { + text_char ss[2]; + ss[0] = xchr (s); + ss[1] = 0; + do_putsf (mp->wr_file[(mp->selector - write_file)], (char *) ss); } } -DONE: - incr(mp->tally); + incr (mp->tally); } + @ The |print_char| procedure sends one character to the desired destination. File names and string expressions might contain |ASCII_code| values that can't be printed using |print_visible_char|. These characters will be @@ -1782,27 +1629,28 @@ characters when |selector| is in the range |0..max_write_files-1|. The user might want to write unprintable characters. @<Basic printing...@>= -void mp_print_char (MP mp, ASCII_code k) { /* prints a single character */ - if ( mp->selector<pseudo || mp->selector>=write_file) { - mp_print_visible_char(mp, k); - } else if ( @<Character |k| cannot be printed@> ) { - mp_print(mp, "^^"); - if ( k<0100 ) { - mp_print_visible_char(mp, (ASCII_code)(k+0100)); - } else if ( k<0200 ) { - mp_print_visible_char(mp, (ASCII_code)(k-0100)); +void mp_print_char (MP mp, ASCII_code k) { /* prints a single character */ + if (mp->selector < pseudo || mp->selector >= write_file) { + mp_print_visible_char (mp, k); + } else if (@<Character |k| cannot be printed@>) { + mp_print (mp, "^^"); + if (k < 0100) { + mp_print_visible_char (mp, (ASCII_code) (k + 0100)); + } else if (k < 0200) { + mp_print_visible_char (mp, (ASCII_code) (k - 0100)); } else { - int l; /* small index or counter */ + int l; /* small index or counter */ l = (k / 16); - mp_print_visible_char(mp, xord(l<10 ? l+'0' : l-10+'a')); + mp_print_visible_char (mp, xord (l < 10 ? l + '0' : l - 10 + 'a')); l = (k % 16); - mp_print_visible_char(mp, xord(l<10 ? l+'0' : l-10+'a')); + mp_print_visible_char (mp, xord (l < 10 ? l + '0' : l - 10 + 'a')); } } else { - mp_print_visible_char(mp, k); + mp_print_visible_char (mp, k); } } + @ An entire string is output by calling |print|. Note that if we are outputting the single standard ASCII character \.c, we could call |print("c")|, since |"c"=99| is the number of a single-character string, as explained above. But @@ -1812,48 +1660,29 @@ assumes that it is always safe to print a visible ASCII character.) @^system dependencies@> @<Basic print...@>= -static void mp_do_print (MP mp, const char *ss, size_t len) { /* prints string |s| */ +static void mp_do_print (MP mp, const char *ss, size_t len) { /* prints string |s| */ size_t j = 0; if (mp->selector == new_string) { - str_room((integer)(len*4)); + str_room ((len * 4)); } - while ( j<len ){ + while (j < len) { /* this was |xord((int)ss[j])| but that doesnt work */ - mp_print_char(mp, (ASCII_code)ss[j]); j++; + mp_print_char (mp, (ASCII_code) ss[j]); + j++; } } + @ @<Basic print...@>= void mp_print (MP mp, const char *ss) { - if (ss==NULL) return; - mp_do_print(mp, ss,strlen(ss)); + if (ss == NULL) + return; + mp_do_print (mp, ss, strlen (ss)); } - -@ This function is somewhat less trivial than expected -because it is not safe to directly print data in the -string pool since |mp_do_print()| can potentially reallocate -the whole lot. - -@<Basic print...@>= void mp_print_str (MP mp, str_number s) { - pool_pointer j; /* current character code position */ - char *ss; /* a temporary C string */ - size_t len; /* its length */ - if ( (s<0)||(s>mp->max_str_ptr) ) { - mp_do_print(mp,"???",3); /* this can't happen */ -@.???@> - } - j=mp->str_start[s]; - len = (size_t)(str_stop(s)-j); - ss = xmalloc(len+1, sizeof(char)); - if (len > 0) { - /* the man page doesnt say whether 0 is allowed */ - memcpy(ss,(char *)(mp->str_pool+j),len); - } - ss[len] = '\0'; - mp_do_print(mp, ss, len); - mp_xfree(ss); + assert (s != NULL); + mp_do_print (mp, (const char *) s->str, s->len); } @@ -1865,61 +1694,66 @@ character positions. @<Initialize the output...@>= wterm (mp->banner); -if (mp->mem_ident!=NULL) - mp_print(mp,mp->mem_ident); -mp_print_ln(mp); +if (mp->mem_ident != NULL) + mp_print (mp, mp->mem_ident); +mp_print_ln (mp); update_terminal; @ The procedure |print_nl| is like |print|, but it makes sure that the string appears at the beginning of a new line. @<Basic print...@>= -void mp_print_nl (MP mp, const char *s) { /* prints string |s| at beginning of line */ - switch(mp->selector) { - case term_and_log: - if ( (mp->term_offset>0)||(mp->file_offset>0) ) mp_print_ln(mp); +void mp_print_nl (MP mp, const char *s) { /* prints string |s| at beginning of line */ + switch (mp->selector) { + case term_and_log: + if ((mp->term_offset > 0) || (mp->file_offset > 0)) + mp_print_ln (mp); break; - case log_only: - if ( mp->file_offset>0 ) mp_print_ln(mp); + case log_only: + if (mp->file_offset > 0) + mp_print_ln (mp); break; - case term_only: - if ( mp->term_offset>0 ) mp_print_ln(mp); + case term_only: + if (mp->term_offset > 0) + mp_print_ln (mp); break; case no_print: case pseudo: - case new_string: - break; - } /* there are no other cases */ - mp_print(mp, s); + case new_string: + break; + } /* there are no other cases */ + mp_print (mp, s); } + @ The following procedure, which prints out the decimal representation of a given integer |n|, assumes that all integers fit nicely into a |int|. @^system dependencies@> @<Basic print...@>= -void mp_print_int (MP mp,integer n) { /* prints an integer in decimal form */ +void mp_print_int (MP mp, integer n) { /* prints an integer in decimal form */ char s[12]; - mp_snprintf(s,12,"%d", (int)n); - mp_print(mp,s); + mp_snprintf (s, 12, "%d", (int) n); + mp_print (mp, s); } + @ @<Internal library ...@>= -void mp_print_int (MP mp,integer n); +void mp_print_int (MP mp, integer n); @ \MP\ also makes use of a trivial procedure to print two digits. The following subroutine is usually called with a parameter in the range |0<=n<=99|. -@c -static void mp_print_dd (MP mp,integer n) { /* prints two least significant digits */ - n=abs(n) % 100; - mp_print_char(mp, xord('0'+(n / 10))); - mp_print_char(mp, xord('0'+(n % 10))); +@c +static void mp_print_dd (MP mp, integer n) { /* prints two least significant digits */ + n = abs (n) % 100; + mp_print_char (mp, xord ('0' + (n / 10))); + mp_print_char (mp, xord ('0' + (n % 10))); } @ @<Declarations@>= -static void mp_print_dd (MP mp,integer n); +static void mp_print_dd (MP mp, integer n); @ Here is a procedure that asks the user to type a line of input, assuming that the |selector| setting is either |term_only| or |term_and_log|. @@ -1935,33 +1769,34 @@ This procedure is never called when |interaction<mp_scroll_mode|. mp_term_input(mp); } while (0) /* prints a string and gets a line of input */ -@c -void mp_term_input (MP mp) { /* gets a line from the terminal */ - size_t k; /* index into |buffer| */ +@c +void mp_term_input (MP mp) { /* gets a line from the terminal */ + size_t k; /* index into |buffer| */ if (mp->noninteractive) { - if (!mp_input_ln(mp, mp->term_in )) - longjmp(*(mp->jump_buf),1); /* chunk finished */ - mp->buffer[mp->last]=xord('%'); + if (!mp_input_ln (mp, mp->term_in)) + longjmp (*(mp->jump_buf), 1); /* chunk finished */ + mp->buffer[mp->last] = xord ('%'); } else { - update_terminal; /* Now the user sees the prompt for sure */ - if (!mp_input_ln(mp, mp->term_in )) { - mp_fatal_error(mp, "End of file on the terminal!"); + update_terminal; /* Now the user sees the prompt for sure */ + if (!mp_input_ln (mp, mp->term_in)) { + mp_fatal_error (mp, "End of file on the terminal!"); @.End of file on the terminal@> } - mp->term_offset=0; /* the user's line ended with \<\rm return> */ - decr(mp->selector); /* prepare to echo the input */ - if ( mp->last!=mp->first ) { - for (k=mp->first;k<mp->last;k++) { - mp_print_char(mp, mp->buffer[k]); + mp->term_offset = 0; /* the user's line ended with \<\rm return> */ + decr (mp->selector); /* prepare to echo the input */ + if (mp->last != mp->first) { + for (k = mp->first; k < mp->last; k++) { + mp_print_char (mp, mp->buffer[k]); } } - mp_print_ln(mp); - mp->buffer[mp->last]=xord('%'); - incr(mp->selector); /* restore previous status */ + mp_print_ln (mp); + mp->buffer[mp->last] = xord ('%'); + incr (mp->selector); /* restore previous status */ } } -@* \[6] Reporting errors. + +@* Reporting errors. When something anomalous is detected, \MP\ typically does something like this: $$\vbox{\halign{#\hfil\cr |print_err("Something anomalous has been detected");|\cr @@ -1988,52 +1823,53 @@ message may be printed. amounts of user interaction: @<Exported types@>= -enum mp_interaction_mode { - mp_unspecified_mode=0, /* extra value for command-line switch */ - mp_batch_mode, /* omits all stops and omits terminal output */ - mp_nonstop_mode, /* omits all stops */ - mp_scroll_mode, /* omits error stops */ - mp_error_stop_mode /* stops at every opportunity to interact */ +enum mp_interaction_mode { + mp_unspecified_mode = 0, /* extra value for command-line switch */ + mp_batch_mode, /* omits all stops and omits terminal output */ + mp_nonstop_mode, /* omits all stops */ + mp_scroll_mode, /* omits error stops */ + mp_error_stop_mode /* stops at every opportunity to interact */ }; @ @<Option variables@>= -int interaction; /* current level of interaction */ -int noninteractive; /* do we have a terminal? */ +int interaction; /* current level of interaction */ +int noninteractive; /* do we have a terminal? */ @ Set it here so it can be overwritten by the commandline @<Allocate or initialize ...@>= -mp->interaction=opt->interaction; -if (mp->interaction==mp_unspecified_mode || mp->interaction>mp_error_stop_mode) - mp->interaction=mp_error_stop_mode; -if (mp->interaction<mp_unspecified_mode) - mp->interaction=mp_batch_mode; +mp->interaction = opt->interaction; +if (mp->interaction == mp_unspecified_mode + || mp->interaction > mp_error_stop_mode) + mp->interaction = mp_error_stop_mode; +if (mp->interaction < mp_unspecified_mode) + mp->interaction = mp_batch_mode; @ @d print_err(A) mp_print_err(mp,(A)) @<Internal ...@>= -void mp_print_err(MP mp, const char * A); +void mp_print_err (MP mp, const char *A); @ @c -void mp_print_err(MP mp, const char * A) { - if ( mp->interaction==mp_error_stop_mode ) +void mp_print_err (MP mp, const char *A) { + if (mp->interaction == mp_error_stop_mode) wake_up_terminal; if (mp->file_line_error_style && file_state && !terminal_input) { - mp_print_nl(mp, ""); + mp_print_nl (mp, ""); if (long_name != NULL) { - mp_print(mp, long_name); + mp_print (mp, long_name); } else { - mp_print(mp, mp_str(mp,name)); + mp_print (mp, mp_str (mp, name)); } - mp_print(mp, ":"); - mp_print_int(mp, line); - mp_print(mp, ": "); - } else{ - mp_print_nl(mp, "! "); + mp_print (mp, ":"); + mp_print_int (mp, line); + mp_print (mp, ": "); + } else { + mp_print_nl (mp, "! "); } - mp_print(mp, A); + mp_print (mp, A); @.!\relax@> } @@ -2052,7 +1888,10 @@ error messages are \hang|term_and_log| (when |interaction>mp_batch_mode| and |log_file| is open). @<Initialize the print |selector| based on |interaction|@>= -if ( mp->interaction==mp_batch_mode ) mp->selector=no_print; else mp->selector=term_only +if (mp->interaction == mp_batch_mode) + mp->selector = no_print; +else + mp->selector = term_only @ A global variable |deletions_allowed| is set |false| if the |get_next| routine is active when |error| is called; this ensures that |get_next| @@ -2070,23 +1909,23 @@ that there is no point in continuing further. @<Exported types@>= enum mp_history_state { - mp_spotless=0, /* |history| value when nothing has been amiss yet */ - mp_warning_issued, /* |history| value when |begin_diagnostic| has been called */ - mp_error_message_issued, /* |history| value when |error| has been called */ - mp_fatal_error_stop, /* |history| value when termination was premature */ - mp_system_error_stop /* |history| value when termination was due to disaster */ + mp_spotless = 0, /* |history| value when nothing has been amiss yet */ + mp_warning_issued, /* |history| value when |begin_diagnostic| has been called */ + mp_error_message_issued, /* |history| value when |error| has been called */ + mp_fatal_error_stop, /* |history| value when termination was premature */ + mp_system_error_stop /* |history| value when termination was due to disaster */ }; @ @<Glob...@>= -boolean deletions_allowed; /* is it safe for |error| to call |get_next|? */ -int history; /* has the source input been clean so far? */ -int error_count; /* the number of scrolled errors since the last statement ended */ +boolean deletions_allowed; /* is it safe for |error| to call |get_next|? */ +int history; /* has the source input been clean so far? */ +int error_count; /* the number of scrolled errors since the last statement ended */ @ The value of |history| is initially |fatal_error_stop|, but it will be changed to |spotless| if \MP\ survives the initialization process. @<Allocate or ...@>= -mp->deletions_allowed=true; /* |history| is initialized elsewhere */ +mp->deletions_allowed = true; /* |history| is initialized elsewhere */ @ Since errors can be detected almost anywhere in \MP, we want to declare the error procedures near the beginning of the program. But the error procedures @@ -2129,13 +1968,13 @@ in reverse order, i.e., with |help_line[0]| appearing last. @d help6 { mp->help_ptr=6; hlp6 /* use this with six help lines */ @<Glob...@>= -const char * help_line[6]; /* helps for the next |error| */ -unsigned int help_ptr; /* the number of help lines present */ -boolean use_err_help; /* should the |err_help| string be shown? */ -str_number err_help; /* a string set up by \&{errhelp} */ +const char *help_line[6]; /* helps for the next |error| */ +unsigned int help_ptr; /* the number of help lines present */ +boolean use_err_help; /* should the |err_help| string be shown? */ +str_number err_help; /* a string set up by \&{errhelp} */ @ @<Allocate or ...@>= -mp->use_err_help=false; +mp->use_err_help = false; @ The |jump_out| procedure just cuts across all active procedure levels and goes to |end_of_MP|. This is the only nonlocal |goto| statement in the @@ -2144,7 +1983,6 @@ whole program. It is used when there is no recovery from a particular error. The program uses a |jump_buf| to handle this, this is initialized at three spots: the start of |mp_new|, the start of |mp_initialize|, and the start of |mp_run|. Those are the only library enty points. - @^system dependencies@> @<Glob...@>= @@ -2155,61 +1993,70 @@ crash occured during initialization, and it is not safe to run the normal cleanup routine. @<Error hand...@>= -static void mp_jump_out (MP mp) { - if (mp->internal!=NULL && mp->history < mp_system_error_stop) - mp_close_files_and_terminate(mp); - longjmp(*(mp->jump_buf),1); +static void mp_jump_out (MP mp) { + if (mp->internal != NULL && mp->history < mp_system_error_stop) + mp_close_files_and_terminate (mp); + longjmp (*(mp->jump_buf), 1); } + @ Here now is the general |error| routine. @<Error hand...@>= -void mp_error (MP mp) { /* completes the job of error reporting */ +void mp_error (MP mp) { /* completes the job of error reporting */ ASCII_code c; /* what the user types */ - integer s1,s2,s3; /* used to save global variables when deleting tokens */ - pool_pointer j; /* character position being printed */ - if ( mp->history<mp_error_message_issued ) - mp->history=mp_error_message_issued; - mp_print_char(mp, xord('.')); mp_show_context(mp); + integer s1, s2; /* used to save global variables when deleting tokens */ + mp_sym s3; /* likewise */ + if (mp->history < mp_error_message_issued) + mp->history = mp_error_message_issued; + mp_print_char (mp, xord ('.')); + mp_show_context (mp); if (mp->halt_on_error) { - mp->history=mp_fatal_error_stop; mp_jump_out(mp); + mp->history = mp_fatal_error_stop; + mp_jump_out (mp); } - if ((!mp->noninteractive) && (mp->interaction==mp_error_stop_mode )) { + if ((!mp->noninteractive) && (mp->interaction == mp_error_stop_mode)) { @<Get user's advice and |return|@>; } - incr(mp->error_count); - if ( mp->error_count==100 ) { - mp_print_nl(mp,"(That makes 100 errors; please try again.)"); -@.That makes 100 errors...@> - mp->history=mp_fatal_error_stop; mp_jump_out(mp); + incr (mp->error_count); + if (mp->error_count == 100) { + mp_print_nl (mp, "(That makes 100 errors; please try again.)"); +@.That makes 100 errors...@>; + mp->history = mp_fatal_error_stop; + mp_jump_out (mp); } @<Put help message on the transcript file@>; } void mp_warn (MP mp, const char *msg) { unsigned saved_selector = mp->selector; - mp_normalize_selector(mp); - mp_print_nl(mp,"Warning: "); - mp_print(mp,msg); - mp_print_ln(mp); + mp_normalize_selector (mp); + mp_print_nl (mp, "Warning: "); + mp_print (mp, msg); + mp_print_ln (mp); mp->selector = saved_selector; } + @ @<Exported function ...@>= extern void mp_error (MP mp); extern void mp_warn (MP mp, const char *msg); @ @<Get user's advice...@>= -while (true) { +while (true) { CONTINUE: - mp_clear_for_error_prompt(mp); prompt_input("? "); -@.?\relax@> - if ( mp->last==mp->first ) return; - c=mp->buffer[mp->first]; - if ( c>='a' ) c=(ASCII_code)(c+'A'-'a'); /* convert to uppercase */ + mp_clear_for_error_prompt (mp); + prompt_input ("? "); +@.?\relax@>; + if (mp->last == mp->first) + return; + c = mp->buffer[mp->first]; + if (c >= 'a') + c = (ASCII_code) (c + 'A' - 'a'); /* convert to uppercase */ @<Interpret code |c| and |return| if done@>; } + @ It is desirable to provide an `\.E' option here that gives the user an easy way to return from \MP\ to the system editor, with the offending line ready to be edited. But such an extension requires some system @@ -2219,25 +2066,26 @@ edited and the relevant line number. @^system dependencies@> @<Exported types@>= -typedef void (*mp_editor_cmd)(MP, char *, int); +typedef void (*mp_editor_cmd) (MP, char *, int); @ @<Option variables@>= mp_editor_cmd run_editor; @ @<Allocate or initialize ...@>= -set_callback_option(run_editor); +set_callback_option (run_editor); @ @<Declarations@>= static void mp_run_editor (MP mp, char *fname, int fline); -@ @c +@ @c void mp_run_editor (MP mp, char *fname, int fline) { - char *s = xmalloc(256,1); - mp_snprintf(s, 256,"You want to edit file %s at line %d\n", fname, fline); - wterm_ln(s); + char *s = xmalloc (256, 1); + mp_snprintf (s, 256, "You want to edit file %s at line %d\n", fname, fline); + wterm_ln (s); @.You want to edit file x@> } + @ There is a secret `\.D' option available when the debugging routines haven't been commented~out. @@ -2245,164 +2093,224 @@ been commented~out. @<Interpret code |c| and |return| if done@>= switch (c) { -case '0': case '1': case '2': case '3': case '4': -case '5': case '6': case '7': case '8': case '9': - if ( mp->deletions_allowed ) { - @<Delete |c-"0"| tokens and |continue|@>; +case '0': +case '1': +case '2': +case '3': +case '4': +case '5': +case '6': +case '7': +case '8': +case '9': + if (mp->deletions_allowed) { + @<Delete tokens and |continue|@>; } break; -case 'E': - if ( mp->file_ptr>0 ){ - mp->interaction=mp_scroll_mode; - mp_close_files_and_terminate(mp); - (mp->run_editor)(mp, - str(mp->input_stack[mp->file_ptr].name_field), - mp_true_line(mp)); - mp_jump_out(mp); +case 'E': + if (mp->file_ptr > 0) { + mp->interaction = mp_scroll_mode; + mp_close_files_and_terminate (mp); + (mp->run_editor) (mp, + mp_str (mp, mp->input_stack[mp->file_ptr].name_field), + mp_true_line (mp)); + mp_jump_out (mp); } break; -case 'H': +case 'H': @<Print the help information and |continue|@>; /* |break;| */ case 'I': @<Introduce new material from the terminal and |return|@>; /* |break;| */ -case 'Q': case 'R': case 'S': +case 'Q': +case 'R': +case 'S': @<Change the interaction level and |return|@>; /* |break;| */ case 'X': - mp->interaction=mp_scroll_mode; mp_jump_out(mp); + mp->interaction = mp_scroll_mode; + mp_jump_out (mp); break; default: break; } @<Print the menu of available options@> + @ @<Print the menu...@>= -{ - mp_print(mp, "Type <return> to proceed, S to scroll future error messages,"); -@.Type <return> to proceed...@> - mp_print_nl(mp, "R to run without stopping, Q to run quietly,"); - mp_print_nl(mp, "I to insert something, "); - if ( mp->file_ptr>0 ) - mp_print(mp, "E to edit your file,"); - if ( mp->deletions_allowed ) - mp_print_nl(mp, "1 or ... or 9 to ignore the next 1 to 9 tokens of input,"); - mp_print_nl(mp, "H for help, X to quit."); +{ + mp_print (mp, "Type <return> to proceed, S to scroll future error messages,"); +@.Type <return> to proceed...@>; + mp_print_nl (mp, "R to run without stopping, Q to run quietly,"); + mp_print_nl (mp, "I to insert something, "); + if (mp->file_ptr > 0) + mp_print (mp, "E to edit your file,"); + if (mp->deletions_allowed) + mp_print_nl (mp, + "1 or ... or 9 to ignore the next 1 to 9 tokens of input,"); + mp_print_nl (mp, "H for help, X to quit."); } + @ Here the author of \MP\ apologizes for making use of the numerical relation between |"Q"|, |"R"|, |"S"|, and the desired interaction settings |mp_batch_mode|, |mp_nonstop_mode|, |mp_scroll_mode|. @^Knuth, Donald Ervin@> @<Change the interaction...@>= -{ - mp->error_count=0; mp->interaction=mp_batch_mode+c-'Q'; - mp_print(mp, "OK, entering "); +{ + mp->error_count = 0; + mp->interaction = mp_batch_mode + c - 'Q'; + mp_print (mp, "OK, entering "); switch (c) { - case 'Q': mp_print(mp, "batchmode"); decr(mp->selector); break; - case 'R': mp_print(mp, "nonstopmode"); break; - case 'S': mp_print(mp, "scrollmode"); break; - } /* there are no other cases */ - mp_print(mp, "..."); mp_print_ln(mp); update_terminal; return; + case 'Q': + mp_print (mp, "batchmode"); + decr (mp->selector); + break; + case 'R': + mp_print (mp, "nonstopmode"); + break; + case 'S': + mp_print (mp, "scrollmode"); + break; + } /* there are no other cases */ + mp_print (mp, "..."); + mp_print_ln (mp); + update_terminal; + return; } + @ When the following code is executed, |buffer[(first+1)..(last-1)]| may contain the material inserted by the user; otherwise another prompt will be given. In order to understand this part of the program fully, you need to be familiar with \MP's input stacks. @<Introduce new material...@>= -{ - mp_begin_file_reading(mp); /* enter a new syntactic level for terminal input */ - if ( mp->last>mp->first+1 ) { - loc=(halfword)(mp->first+1); mp->buffer[mp->first]=xord(' '); - } else { - prompt_input("insert>"); loc=(halfword)mp->first; +{ + mp_begin_file_reading (mp); /* enter a new syntactic level for terminal input */ + if (mp->last > mp->first + 1) { + loc = (halfword) (mp->first + 1); + mp->buffer[mp->first] = xord (' '); + } else { + prompt_input ("insert>"); + loc = (halfword) mp->first; @.insert>@> - }; - mp->first=mp->last+1; mp->cur_input.limit_field=(halfword)mp->last; return; + } + mp->first = mp->last + 1; + mp->cur_input.limit_field = (halfword) mp->last; + return; } + @ We allow deletion of up to 99 tokens at a time. -@<Delete |c-"0"| tokens...@>= -{ - s1=mp->cur_cmd; s2=mp->cur_mod; s3=mp->cur_sym; mp->OK_to_interrupt=false; - if ( (mp->last>mp->first+1) && (mp->buffer[mp->first+1]>='0')&&(mp->buffer[mp->first+1]<='9') ) - c=xord(c*10+mp->buffer[mp->first+1]-'0'*11); - else +@<Delete tokens...@>= +{ + s1 = mp->cur_cmd; + s2 = mp->cur_mod; + s3 = mp->cur_sym; + mp->OK_to_interrupt = false; + if ((mp->last > mp->first + 1) && (mp->buffer[mp->first + 1] >= '0') + && (mp->buffer[mp->first + 1] <= '9')) + c = xord (c * 10 + mp->buffer[mp->first + 1] - '0' * 11); + else c = (ASCII_code) (c - '0'); - while ( c>0 ) { - mp_get_next(mp); /* one-level recursive call of |error| is possible */ + while (c > 0) { + mp_get_next (mp); /* one-level recursive call of |error| is possible */ @<Decrease the string reference count, if the current token is a string@>; c--; }; - mp->cur_cmd=s1; mp->cur_mod=s2; mp->cur_sym=s3; mp->OK_to_interrupt=true; - help2("I have just deleted some text, as you asked.", - "You can now delete more, or insert, or whatever."); - mp_show_context(mp); + mp->cur_cmd = s1; + mp->cur_mod = s2; + mp->cur_sym = s3; + mp->OK_to_interrupt = true; + help2 ("I have just deleted some text, as you asked.", + "You can now delete more, or insert, or whatever."); + mp_show_context (mp); goto CONTINUE; } + @ @<Print the help info...@>= -{ - if ( mp->use_err_help ) { +{ + if (mp->use_err_help) { @<Print the string |err_help|, possibly on several lines@>; - mp->use_err_help=false; - } else { - if ( mp->help_ptr==0 ) { - help2("Sorry, I don't know how to help in this situation.", - "Maybe you should try asking a human?"); - } - do { - decr(mp->help_ptr); mp_print(mp, mp->help_line[mp->help_ptr]); mp_print_ln(mp); - } while (mp->help_ptr!=0); + mp->use_err_help = false; + } else { + if (mp->help_ptr == 0) { + help2 ("Sorry, I don't know how to help in this situation.", + "Maybe you should try asking a human?"); + } + do { + decr (mp->help_ptr); + mp_print (mp, mp->help_line[mp->help_ptr]); + mp_print_ln (mp); + } while (mp->help_ptr != 0); }; - help4("Sorry, I already gave what help I could...", - "Maybe you should try asking a human?", - "An error might have occurred before I noticed any problems.", - "``If all else fails, read the instructions.''"); + help4 ("Sorry, I already gave what help I could...", + "Maybe you should try asking a human?", + "An error might have occurred before I noticed any problems.", + "``If all else fails, read the instructions.''"); goto CONTINUE; } + @ @<Print the string |err_help|, possibly on several lines@>= -j=mp->str_start[mp->err_help]; -while ( j<str_stop(mp->err_help) ) { - if ( mp->str_pool[j]!='%' ) mp_print_str(mp, mp->str_pool[j]); - else if ( j+1==str_stop(mp->err_help) ) mp_print_ln(mp); - else if ( mp->str_pool[j+1]!='%' ) mp_print_ln(mp); - else { j++; mp_print_char(mp, xord('%')); }; - j++; +{ + size_t j = 0; + while (j < length (mp->err_help)) { + if (*(mp->err_help->str + j) != '%') + mp_print (mp, (const char *) (mp->err_help->str + j)); + else if (j + 1 == length (mp->err_help)) + mp_print_ln (mp); + else if (*(mp->err_help->str + j) != '%') + mp_print_ln (mp); + else { + j++; + mp_print_char (mp, xord ('%')); + }; + j++; + } } + @ @<Put help message on the transcript file@>= -if ( mp->interaction>mp_batch_mode ) decr(mp->selector); /* avoid terminal output */ -if ( mp->use_err_help ) { - mp_print_nl(mp, ""); +if (mp->interaction > mp_batch_mode) + decr (mp->selector); /* avoid terminal output */ +if (mp->use_err_help) { + mp_print_nl (mp, ""); @<Print the string |err_help|, possibly on several lines@>; -} else { - while ( mp->help_ptr>0 ){ - decr(mp->help_ptr); mp_print_nl(mp, mp->help_line[mp->help_ptr]); +} else { + while (mp->help_ptr > 0) { + decr (mp->help_ptr); + mp_print_nl (mp, mp->help_line[mp->help_ptr]); }; + mp_print_ln (mp); + if (mp->interaction > mp_batch_mode) + incr (mp->selector); /* re-enable terminal output */ + mp_print_ln (mp); } -mp_print_ln(mp); -if ( mp->interaction>mp_batch_mode ) incr(mp->selector); /* re-enable terminal output */ -mp_print_ln(mp) + @ In anomalous cases, the print selector might be in an unknown state; the following subroutine is called to fix things just enough to keep running a bit longer. -@c -void mp_normalize_selector (MP mp) { - if ( mp->log_opened ) mp->selector=term_and_log; - else mp->selector=term_only; - if ( mp->job_name==NULL) mp_open_log_file(mp); - if ( mp->interaction==mp_batch_mode ) decr(mp->selector); +@c +void mp_normalize_selector (MP mp) { + if (mp->log_opened) + mp->selector = term_and_log; + else + mp->selector = term_only; + if (mp->job_name == NULL) + mp_open_log_file (mp); + if (mp->interaction == mp_batch_mode) + decr (mp->selector); } + @ The following procedure prints \MP's last words before dying. @d succumb { if ( mp->interaction==mp_error_stop_mode ) @@ -2412,12 +2320,15 @@ void mp_normalize_selector (MP mp) { } @<Error hand...@>= -void mp_fatal_error (MP mp, const char *s) { /* prints |s|, and that's it */ - mp_normalize_selector(mp); - print_err("Emergency stop"); help1(s); succumb; +void mp_fatal_error (MP mp, const char *s) { /* prints |s|, and that's it */ + mp_normalize_selector (mp); + print_err ("Emergency stop"); + help1 (s); + succumb; @.Emergency stop@> } + @ @<Exported function ...@>= extern void mp_fatal_error (MP mp, const char *s); @@ -2425,17 +2336,19 @@ extern void mp_fatal_error (MP mp, const char *s); @ Here is the most dreaded error message. @<Error hand...@>= -void mp_overflow (MP mp, const char *s, integer n) { /* stop due to finiteness */ +void mp_overflow (MP mp, const char *s, integer n) { /* stop due to finiteness */ char msg[256]; - mp_normalize_selector(mp); - mp_snprintf(msg, 256, "MetaPost capacity exceeded, sorry [%s=%d]",s,(int)n); -@.MetaPost capacity exceeded ...@> - print_err(msg); - help2("If you really absolutely need more capacity,", - "you can ask a wizard to enlarge me."); + mp_normalize_selector (mp); + mp_snprintf (msg, 256, "MetaPost capacity exceeded, sorry [%s=%d]", s, + (int) n); +@.MetaPost capacity exceeded ...@>; + print_err (msg); + help2 ("If you really absolutely need more capacity,", + "you can ask a wizard to enlarge me."); succumb; } + @ @<Internal library declarations@>= void mp_overflow (MP mp, const char *s, integer n); @@ -2454,21 +2367,22 @@ void mp_confusion (MP mp, const char *s); @<Error hand...@>= void mp_confusion (MP mp, const char *s) { char msg[256]; - mp_normalize_selector(mp); - if ( mp->history<mp_error_message_issued ) { - mp_snprintf(msg, 256, "This can't happen (%s)",s); -@.This can't happen@> - print_err(msg); - help1("I'm broken. Please show this to someone who can fix can fix"); - } else { - print_err("I can\'t go on meeting you like this"); -@.I can't go on...@> - help2("One of your faux pas seems to have wounded me deeply...", - "in fact, I'm barely conscious. Please fix it and try again."); + mp_normalize_selector (mp); + if (mp->history < mp_error_message_issued) { + mp_snprintf (msg, 256, "This can't happen (%s)", s); +@.This can't happen@>; + print_err (msg); + help1 ("I'm broken. Please show this to someone who can fix can fix"); + } else { + print_err ("I can\'t go on meeting you like this"); +@.I can't go on...@>; + help2 ("One of your faux pas seems to have wounded me deeply...", + "in fact, I'm barely conscious. Please fix it and try again."); } succumb; } + @ Users occasionally want to interrupt \MP\ while it's running. If the runtime system allows this, one can implement a routine that sets the global variable |interrupt| to some nonzero value @@ -2481,49 +2395,53 @@ a way to make |interrupt| nonzero using the C debugger. mp_pause_for_instructions(mp); } @<Global...@>= -integer interrupt; /* should \MP\ pause for instructions? */ -boolean OK_to_interrupt; /* should interrupts be observed? */ -integer run_state; /* are we processing input ?*/ -boolean finished; /* set true by |close_files_and_terminate| */ +integer interrupt; /* should \MP\ pause for instructions? */ +boolean OK_to_interrupt; /* should interrupts be observed? */ +integer run_state; /* are we processing input ? */ +boolean finished; /* set true by |close_files_and_terminate| */ @ @<Allocate or ...@>= -mp->OK_to_interrupt=true; -mp->finished=false; +mp->OK_to_interrupt = true; +mp->finished = false; @ When an interrupt has been detected, the program goes into its highest interaction level and lets the user have the full flexibility of the |error| routine. \MP\ checks for interrupts only at times when it is safe to do this. -@c -static void mp_pause_for_instructions (MP mp) { - if ( mp->OK_to_interrupt ) { - mp->interaction=mp_error_stop_mode; - if ( (mp->selector==log_only)||(mp->selector==no_print) ) - incr(mp->selector); - print_err("Interruption"); -@.Interruption@> - help3("You rang?", - "Try to insert some instructions for me (e.g.,`I show x'),", - "unless you just want to quit by typing `X'."); - mp->deletions_allowed=false; mp_error(mp); mp->deletions_allowed=true; - mp->interrupt=0; +@c +static void mp_pause_for_instructions (MP mp) { + if (mp->OK_to_interrupt) { + mp->interaction = mp_error_stop_mode; + if ((mp->selector == log_only) || (mp->selector == no_print)) + incr (mp->selector); + print_err ("Interruption"); +@.Interruption@>; + help3 ("You rang?", + "Try to insert some instructions for me (e.g.,`I show x'),", + "unless you just want to quit by typing `X'."); + mp->deletions_allowed = false; + mp_error (mp); + mp->deletions_allowed = true; + mp->interrupt = 0; } } + @ Many of \MP's error messages state that a missing token has been inserted behind the scenes. We can save string space and program space by putting this common code into a subroutine. -@c -static void mp_missing_err (MP mp, const char *s) { +@c +static void mp_missing_err (MP mp, const char *s) { char msg[256]; - mp_snprintf(msg, 256, "Missing `%s' has been inserted", s); -@.Missing...inserted@> - print_err(msg); + mp_snprintf (msg, 256, "Missing `%s' has been inserted", s); +@.Missing...inserted@>; + print_err (msg); } -@* \[7] Arithmetic with scaled numbers. + +@* Arithmetic with scaled numbers. The principal computations performed by \MP\ are done entirely in terms of integers less than $2^{31}$ in magnitude; thus, the arithmetic specified in this program can be carried out in exactly the same way on a wide variety of @@ -2541,25 +2459,7 @@ language is being used properly. The \TeX\ processor has been defined carefully so that both varieties of arithmetic will produce identical output, but it would be too inefficient to constrain \MP\ in a similar way. -@d el_gordo 0x7fffffff /* $2^{31}-1$, the largest value that \MP\ likes */ - - -@ One of \MP's most common operations is the calculation of -$\lfloor{a+b\over2}\rfloor$, -the midpoint of two given integers |a| and~|b|. The most decent way to do -this is to write `|(a+b)/2|'; but on many machines it is more efficient -to calculate `|(a+b)>>1|'. - -Therefore the midpoint operation will always be denoted by `|half(a+b)|' -in this program. If \MP\ is being implemented with languages that permit -binary shifting, the |half| macro should be changed to make this operation -as efficient as possible. Since some systems have shift operators that can -only be trusted to work on positive numbers, there is also a macro |halfp| -that is used only when the quantity being halved is known to be positive -or zero. - -@d half(A) ((A) / 2) -@d halfp(A) (integer)((unsigned)(A) >> 1) +@d EL_GORDO ((math_data *)mp->math)->max_scaled_ @ A single computation might use several subroutine calls, and it is desirable to avoid producing multiple error messages in case of arithmetic @@ -2568,1084 +2468,80 @@ instead of reporting errors directly to the user. @^overflow in arithmetic@> @<Glob...@>= -boolean arith_error; /* has arithmetic overflow occurred recently? */ +boolean arith_error; /* has arithmetic overflow occurred recently? */ @ @<Allocate or ...@>= -mp->arith_error=false; +mp->arith_error = false; @ At crucial points the program will say |check_arith|, to test if an arithmetic error has been detected. -@d check_arith { if ( mp->arith_error ) mp_clear_arith(mp); } - -@c -static void mp_clear_arith (MP mp) { - print_err("Arithmetic overflow"); -@.Arithmetic overflow@> - help4("Uh, oh. A little while ago one of the quantities that I was", - "computing got too large, so I'm afraid your answers will be", - "somewhat askew. You'll probably have to adopt different", - "tactics next time. But I shall try to carry on anyway."); - mp_error(mp); - mp->arith_error=false; -} - -@ Addition is not always checked to make sure that it doesn't overflow, -but in places where overflow isn't too unlikely the |slow_add| routine -is used. - -@c static integer mp_slow_add (MP mp,integer x, integer y) { - if ( x>=0 ) { - if ( y<=el_gordo-x ) { - return x+y; - } else { - mp->arith_error=true; - return el_gordo; - } - } else if ( -y<=el_gordo+x ) { - return x+y; - } else { - mp->arith_error=true; - return -el_gordo; - } -} - -@ Fixed-point arithmetic is done on {\sl scaled integers\/} that are multiples -of $2^{-16}$. In other words, a binary point is assumed to be sixteen bit -positions from the right end of a binary computer word. - -@d quarter_unit 040000 /* $2^{14}$, represents 0.250000 */ -@d half_unit 0100000 /* $2^{15}$, represents 0.50000 */ -@d three_quarter_unit 0140000 /* $3\cdot2^{14}$, represents 0.75000 */ -@d unity 0200000 /* $2^{16}$, represents 1.00000 */ -@d two 0400000 /* $2^{17}$, represents 2.00000 */ -@d three 0600000 /* $2^{17}+2^{16}$, represents 3.00000 */ +@d check_arith do { + if ( mp->arith_error ) + mp_clear_arith(mp); +} while (0) -@<Types...@>= -typedef integer scaled; /* this type is used for scaled integers */ +@c +static void mp_clear_arith (MP mp) { + print_err ("Arithmetic overflow"); +@.Arithmetic overflow@>; + help4 ("Uh, oh. A little while ago one of the quantities that I was", + "computing got too large, so I'm afraid your answers will be", + "somewhat askew. You'll probably have to adopt different", + "tactics next time. But I shall try to carry on anyway."); + mp_error (mp); + mp->arith_error = false; +} -@ The following function is used to create a scaled integer from a given decimal -fraction $(.d_0d_1\ldots d_{k-1})$, where |0<=k<=17|. The digit $d_i$ is -given in |dig[i]|, and the calculation produces a correctly rounded result. -@c -static scaled mp_round_decimals (MP mp,quarterword k) { - /* converts a decimal fraction */ - unsigned a = 0; /* the accumulator */ - while ( k-->0 ) { - a=(a+(unsigned)mp->dig[k]*two) / 10; - } - return (scaled)halfp(a+1); -} +@ The definitions of these are set up by the math initialization. -@ Conversely, here is a procedure analogous to |print_int|. If the output -of this procedure is subsequently read by \MP\ and converted by the -|round_decimals| routine above, it turns out that the original value will -be reproduced exactly. A decimal point is printed only if the value is -not an integer. If there is more than one way to print the result with -the optimum number of digits following the decimal point, the closest -possible value is given. +@d unity ((math_data *)mp->math)->unity_ +@d two ((math_data *)mp->math)->two_ +@d three ((math_data *)mp->math)->three_ +@d half_unit ((math_data *)mp->math)->half_unit_ +@d three_quarter_unit ((math_data *)mp->math)->three_quarter_unit_ -The invariant relation in the \&{repeat} loop is that a sequence of -decimal digits yet to be printed will yield the original number if and only if -they form a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$. -We can stop if and only if $f=0$ satisfies this condition; the loop will -terminate before $s$ can possibly become zero. +@ In fact, the two sorts of scaling discussed above aren't quite +sufficient; \MP\ has yet another, used internally to keep track of angles. -@<Basic printing...@>= -void mp_print_scaled (MP mp,scaled s) { /* prints scaled real, rounded to five digits */ - scaled delta; /* amount of allowable inaccuracy */ - if ( s<0 ) { - mp_print_char(mp, xord('-')); - negate(s); /* print the sign, if negative */ - } - mp_print_int(mp, s / unity); /* print the integer part */ - s=10*(s % unity)+5; - if ( s!=5 ) { - delta=10; - mp_print_char(mp, xord('.')); - do { - if ( delta>unity ) - s=s+0100000-(delta / 2); /* round the final digit */ - mp_print_char(mp, xord('0'+(s / unity))); - s=10*(s % unity); - delta=delta*10; - } while (s>delta); - } -} +@<Exported types...@>= +#if 1 +typedef int scaled; /* this type is used for scaled integers */ +#else +typedef struct scaled { + int val; +} scaled; +#endif +typedef int fraction; /* this type is used for scaled fractions */ +typedef int angle; /* this type is used for scaled angles */ @ We often want to print two scaled quantities in parentheses, separated by a comma. @<Basic printing...@>= -void mp_print_two (MP mp,scaled x, scaled y) { /* prints `|(x,y)|' */ - mp_print_char(mp, xord('(')); - mp_print_scaled(mp, x); - mp_print_char(mp, xord(',')); - mp_print_scaled(mp, y); - mp_print_char(mp, xord(')')); -} - -@ The |scaled| quantities in \MP\ programs are generally supposed to be -less than $2^{12}$ in absolute value, so \MP\ does much of its internal -arithmetic with 28~significant bits of precision. A |fraction| denotes -a scaled integer whose binary point is assumed to be 28 bit positions -from the right. - -@d fraction_half 01000000000 /* $2^{27}$, represents 0.50000000 */ -@d fraction_one 02000000000 /* $2^{28}$, represents 1.00000000 */ -@d fraction_two 04000000000 /* $2^{29}$, represents 2.00000000 */ -@d fraction_three 06000000000 /* $3\cdot2^{28}$, represents 3.00000000 */ -@d fraction_four 010000000000 /* $2^{30}$, represents 4.00000000 */ - -@<Types...@>= -typedef integer fraction; /* this type is used for scaled fractions */ - -@ In fact, the two sorts of scaling discussed above aren't quite -sufficient; \MP\ has yet another, used internally to keep track of angles -in units of $2^{-20}$ degrees. - -@d forty_five_deg 0264000000 /* $45\cdot2^{20}$, represents $45^\circ$ */ -@d ninety_deg 0550000000 /* $90\cdot2^{20}$, represents $90^\circ$ */ -@d one_eighty_deg 01320000000 /* $180\cdot2^{20}$, represents $180^\circ$ */ -@d three_sixty_deg 02640000000 /* $360\cdot2^{20}$, represents $360^\circ$ */ - -@<Types...@>= -typedef integer angle; /* this type is used for scaled angles */ - -@ The |make_fraction| routine produces the |fraction| equivalent of -|p/q|, given integers |p| and~|q|; it computes the integer -$f=\lfloor2^{28}p/q+{1\over2}\rfloor$, when $p$ and $q$ are -positive. If |p| and |q| are both of the same scaled type |t|, -the ``type relation'' |make_fraction(t,t)=fraction| is valid; -and it's also possible to use the subroutine ``backwards,'' using -the relation |make_fraction(t,fraction)=t| between scaled types. - -If the result would have magnitude $2^{31}$ or more, |make_fraction| -sets |arith_error:=true|. Most of \MP's internal computations have -been designed to avoid this sort of error. - -If this subroutine were programmed in assembly language on a typical -machine, we could simply compute |(@t$2^{28}$@>*p)div q|, since a -double-precision product can often be input to a fixed-point division -instruction. But when we are restricted to int-eger arithmetic it -is necessary either to resort to multiple-precision maneuvering -or to use a simple but slow iteration. The multiple-precision technique -would be about three times faster than the code adopted here, but it -would be comparatively long and tricky, involving about sixteen -additional multiplications and divisions. - -This operation is part of \MP's ``inner loop''; indeed, it will -consume nearly 10\pct! of the running time (exclusive of input and output) -if the code below is left unchanged. A machine-dependent recoding -will therefore make \MP\ run faster. The present implementation -is highly portable, but slow; it avoids multiplication and division -except in the initial stage. System wizards should be careful to -replace it with a routine that is guaranteed to produce identical -results in all cases. -@^system dependencies@> - -As noted below, a few more routines should also be replaced by machine-dependent -code, for efficiency. But when a procedure is not part of the ``inner loop,'' -such changes aren't advisable; simplicity and robustness are -preferable to trickery, unless the cost is too high. -@^inner loop@> - -@<Internal library declarations@>= -integer mp_take_scaled (MP mp,integer q, scaled f) ; - -@ @<Declarations@>= -static fraction mp_make_fraction (MP mp,integer p, integer q); - -@ If FIXPT is not defined, we need these preprocessor values - -@d TWEXP31 2147483648.0 -@d TWEXP28 268435456.0 -@d TWEXP16 65536.0 -@d TWEXP_16 (1.0/65536.0) -@d TWEXP_28 (1.0/268435456.0) - - -@c -fraction mp_make_fraction (MP mp,integer p, integer q) { - fraction i; - if ( q==0 ) mp_confusion(mp, "/"); -@:this can't happen /}{\quad \./@> -#ifdef FIXPT -{ - integer f; /* the fraction bits, with a leading 1 bit */ - integer n; /* the integer part of $\vert p/q\vert$ */ - boolean negative = false; /* should the result be negated? */ - if ( p<0 ) { - negate(p); negative=true; - } - if ( q<0 ) { - negate(q); negative = ! negative; - } - n=p / q; p=p % q; - if ( n>=8 ){ - mp->arith_error=true; - i= ( negative ? -el_gordo : el_gordo); - } else { - n=(n-1)*fraction_one; - @<Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$@>; - i = (negative ? (-(f+n)) : (f+n)); - } -} -#else /* FIXPT */ - { - register double d; - d = TWEXP28 * (double)p /(double)q; - if ((p^q) >= 0) { - d += 0.5; - if (d>=TWEXP31) {mp->arith_error=true; return el_gordo;} - i = (integer) d; - if (d==(double)i && ( ((q>0 ? -q : q)&077777) - * (((i&037777)<<1)-1) & 04000)!=0) --i; - } else { - d -= 0.5; - if (d<= -TWEXP31) {mp->arith_error=true; return -el_gordo;} - i = (integer) d; - if (d==(double)i && ( ((q>0 ? q : -q)&077777) - * (((i&037777)<<1)+1) & 04000)!=0) ++i; - } - } -#endif /* FIXPT */ - return i; -} - -@ The |repeat| loop here preserves the following invariant relations -between |f|, |p|, and~|q|: -(i)~|0<=p<q|; (ii)~$fq+p=2^k(q+p_0)$, where $k$ is an integer and -$p_0$ is the original value of~$p$. - -Notice that the computation specifies -|(p-q)+p| instead of |(p+p)-q|, because the latter could overflow. -Let us hope that optimizing compilers do not miss this point; a -special variable |be_careful| is used to emphasize the necessary -order of computation. Optimizing compilers should keep |be_careful| -in a register, not store it in memory. -@^inner loop@> - -@<Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$@>= -{ - integer be_careful; /* disables certain compiler optimizations */ - f=1; - do { - be_careful=p-q; p=be_careful+p; - if ( p>=0 ) { - f=f+f+1; - } else { - f+=f; p=p+q; - } - } while (f<fraction_one); - be_careful=p-q; - if ( be_careful+p>=0 ) incr(f); -} - -@ The dual of |make_fraction| is |take_fraction|, which multiplies a -given integer~|q| by a fraction~|f|. When the operands are positive, it -computes $p=\lfloor qf/2^{28}+{1\over2}\rfloor$, a symmetric function -of |q| and~|f|. - -This routine is even more ``inner loopy'' than |make_fraction|; -the present implementation consumes almost 20\pct! of \MP's computation -time during typical jobs, so a machine-language substitute is advisable. -@^inner loop@> @^system dependencies@> - -@<Internal library declarations@>= -integer mp_take_fraction (MP mp,integer q, fraction f) ; - -@ @c -#ifdef FIXPT -integer mp_take_fraction (MP mp,integer q, fraction f) { - integer p; /* the fraction so far */ - boolean negative; /* should the result be negated? */ - integer n; /* additional multiple of $q$ */ - integer be_careful; /* disables certain compiler optimizations */ - @<Reduce to the case that |f>=0| and |q>=0|@>; - if ( f<fraction_one ) { - n=0; - } else { - n=f / fraction_one; f=f % fraction_one; - if ( q<=el_gordo / n ) { - n=n*q ; - } else { - mp->arith_error=true; n=el_gordo; - } - } - f=f+fraction_one; - @<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>; - be_careful=n-el_gordo; - if ( be_careful+p>0 ){ - mp->arith_error=true; n=el_gordo-p; - } - if ( negative ) - return (-(n+p)); - else - return (n+p); -#else /* FIXPT */ -integer mp_take_fraction (MP mp,integer p, fraction q) { - register double d; - register integer i; - d = (double)p * (double)q * TWEXP_28; - if ((p^q) >= 0) { - d += 0.5; - if (d>=TWEXP31) { - if (d!=TWEXP31 || (((p&077777)*(q&077777))&040000)==0) - mp->arith_error = true; - return el_gordo; - } - i = (integer) d; - if (d==(double)i && (((p&077777)*(q&077777))&040000)!=0) --i; - } else { - d -= 0.5; - if (d<= -TWEXP31) { - if (d!= -TWEXP31 || ((-(p&077777)*(q&077777))&040000)==0) - mp->arith_error = true; - return -el_gordo; - } - i = (integer) d; - if (d==(double)i && ((-(p&077777)*(q&077777))&040000)!=0) ++i; - } - return i; -#endif /* FIXPT */ -} - -@ @<Reduce to the case that |f>=0| and |q>=0|@>= -if ( f>=0 ) { - negative=false; -} else { - negate( f); negative=true; -} -if ( q<0 ) { - negate(q); negative=! negative; -} - -@ The invariant relations in this case are (i)~$\lfloor(qf+p)/2^k\rfloor -=\lfloor qf_0/2^{28}+{1\over2}\rfloor$, where $k$ is an integer and -$f_0$ is the original value of~$f$; (ii)~$2^k\L f<2^{k+1}$. -@^inner loop@> - -@<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>= -p=fraction_half; /* that's $2^{27}$; the invariants hold now with $k=28$ */ -if ( q<fraction_four ) { - do { - if ( odd(f) ) p=halfp(p+q); else p=halfp(p); - f=halfp(f); - } while (f!=1); -} else { - do { - if ( odd(f) ) p=p+halfp(q-p); else p=halfp(p); - f=halfp(f); - } while (f!=1); +void mp_print_two (MP mp, scaled x, scaled y) { /* prints `|(x,y)|' */ + mp_print_char (mp, xord ('(')); + mp_print_scaled (mp, x); + mp_print_char (mp, xord (',')); + mp_print_scaled (mp, y); + mp_print_char (mp, xord (')')); } -@ When we want to multiply something by a |scaled| quantity, we use a scheme -analogous to |take_fraction| but with a different scaling. -Given positive operands, |take_scaled| -computes the quantity $p=\lfloor qf/2^{16}+{1\over2}\rfloor$. - -Once again it is a good idea to use a machine-language replacement if -possible; otherwise |take_scaled| will use more than 2\pct! of the running time -when the Computer Modern fonts are being generated. -@^inner loop@> - -@c -#ifdef FIXPT -integer mp_take_scaled (MP mp,integer q, scaled f) { - integer p; /* the fraction so far */ - boolean negative; /* should the result be negated? */ - integer n; /* additional multiple of $q$ */ - integer be_careful; /* disables certain compiler optimizations */ - @<Reduce to the case that |f>=0| and |q>=0|@>; - if ( f<unity ) { - n=0; - } else { - n=f / unity; f=f % unity; - if ( q<=el_gordo / n ) { - n=n*q; - } else { - mp->arith_error=true; n=el_gordo; - } - } - f=f+unity; - @<Compute $p=\lfloor qf/2^{16}+{1\over2}\rfloor-q$@>; - be_careful=n-el_gordo; - if ( be_careful+p>0 ) { - mp->arith_error=true; n=el_gordo-p; - } - return ( negative ?(-(n+p)) :(n+p)); -#else /* FIXPT */ -integer mp_take_scaled (MP mp,integer p, scaled q) { - register double d; - register integer i; - d = (double)p * (double)q * TWEXP_16; - if ((p^q) >= 0) { - d += 0.5; - if (d>=TWEXP31) { - if (d!=TWEXP31 || (((p&077777)*(q&077777))&040000)==0) - mp->arith_error = true; - return el_gordo; - } - i = (integer) d; - if (d==(double)i && (((p&077777)*(q&077777))&040000)!=0) --i; - } else { - d -= 0.5; - if (d<= -TWEXP31) { - if (d!= -TWEXP31 || ((-(p&077777)*(q&077777))&040000)==0) - mp->arith_error = true; - return -el_gordo; - } - i = (integer) d; - if (d==(double)i && ((-(p&077777)*(q&077777))&040000)!=0) ++i; - } - return i; -#endif /* FIXPT */ -} - -@ @<Compute $p=\lfloor qf/2^{16}+{1\over2}\rfloor-q$@>= -p=half_unit; /* that's $2^{15}$; the invariants hold now with $k=16$ */ -@^inner loop@> -if ( q<fraction_four ) { - do { - p = (odd(f) ? halfp(p+q) : halfp(p)); - f=halfp(f); - } while (f!=1); -} else { - do { - p = (odd(f) ? p+halfp(q-p) : halfp(p)); - f=halfp(f); - } while (f!=1); -} - -@ For completeness, there's also |make_scaled|, which computes a -quotient as a |scaled| number instead of as a |fraction|. -In other words, the result is $\lfloor2^{16}p/q+{1\over2}\rfloor$, if the -operands are positive. \ (This procedure is not used especially often, -so it is not part of \MP's inner loop.) - -@<Internal library ...@>= -scaled mp_make_scaled (MP mp,integer p, integer q) ; - -@ @c -scaled mp_make_scaled (MP mp,integer p, integer q) { - register integer i; - if ( q==0 ) mp_confusion(mp, "/"); -@:this can't happen /}{\quad \./@> - { -#ifdef FIXPT - integer f; /* the fraction bits, with a leading 1 bit */ - integer n; /* the integer part of $\vert p/q\vert$ */ - boolean negative; /* should the result be negated? */ - integer be_careful; /* disables certain compiler optimizations */ - if ( p>=0 ) negative=false; - else { negate(p); negative=true; }; - if ( q<0 ) { - negate(q); negative=! negative; - } - n=p / q; p=p % q; - if ( n>=0100000 ) { - mp->arith_error=true; - return (negative ? (-el_gordo) : el_gordo); - } else { - n=(n-1)*unity; - @<Compute $f=\lfloor 2^{16}(1+p/q)+{1\over2}\rfloor$@>; - i = (negative ? (-(f+n)) :(f+n)); - } -#else /* FIXPT */ - register double d; - d = TWEXP16 * (double)p /(double)q; - if ((p^q) >= 0) { - d += 0.5; - if (d>=TWEXP31) {mp->arith_error=true; return el_gordo;} - i = (integer) d; - if (d==(double)i && ( ((q>0 ? -q : q)&077777) - * (((i&037777)<<1)-1) & 04000)!=0) --i; - } else { - d -= 0.5; - if (d<= -TWEXP31) {mp->arith_error=true; return -el_gordo;} - i = (integer) d; - if (d==(double)i && ( ((q>0 ? q : -q)&077777) - * (((i&037777)<<1)+1) & 04000)!=0) ++i; - } -#endif /* FIXPT */ - } - return i; -} - -@ @<Compute $f=\lfloor 2^{16}(1+p/q)+{1\over2}\rfloor$@>= -f=1; -do { - be_careful=p-q; p=be_careful+p; - if ( p>=0 ) f=f+f+1; - else { f+=f; p=p+q; }; -} while (f<unity); -be_careful=p-q; -if ( be_careful+p>=0 ) incr(f) - -@ Here is a typical example of how the routines above can be used. -It computes the function -$${1\over3\tau}f(\theta,\phi)= -{\tau^{-1}\bigl(2+\sqrt2\,(\sin\theta-{1\over16}\sin\phi) - (\sin\phi-{1\over16}\sin\theta)(\cos\theta-\cos\phi)\bigr)\over -3\,\bigl(1+{1\over2}(\sqrt5-1)\cos\theta+{1\over2}(3-\sqrt5\,)\cos\phi\bigr)},$$ -where $\tau$ is a |scaled| ``tension'' parameter. This is \MP's magic -fudge factor for placing the first control point of a curve that starts -at an angle $\theta$ and ends at an angle $\phi$ from the straight path. -(Actually, if the stated quantity exceeds 4, \MP\ reduces it to~4.) - -The trigonometric quantity to be multiplied by $\sqrt2$ is less than $\sqrt2$. -(It's a sum of eight terms whose absolute values can be bounded using -relations such as $\sin\theta\cos\theta\L{1\over2}$.) Thus the numerator -is positive; and since the tension $\tau$ is constrained to be at least -$3\over4$, the numerator is less than $16\over3$. The denominator is -nonnegative and at most~6. Hence the fixed-point calculations below -are guaranteed to stay within the bounds of a 32-bit computer word. - -The angles $\theta$ and $\phi$ are given implicitly in terms of |fraction| -arguments |st|, |ct|, |sf|, and |cf|, representing $\sin\theta$, $\cos\theta$, -$\sin\phi$, and $\cos\phi$, respectively. - -@c -static fraction mp_velocity (MP mp,fraction st, fraction ct, fraction sf, - fraction cf, scaled t) { - integer acc,num,denom; /* registers for intermediate calculations */ - acc=mp_take_fraction(mp, st-(sf / 16), sf-(st / 16)); - acc=mp_take_fraction(mp, acc,ct-cf); - num=fraction_two+mp_take_fraction(mp, acc,379625062); - /* $2^{28}\sqrt2\approx379625062.497$ */ - denom=fraction_three+mp_take_fraction(mp, ct,497706707)+mp_take_fraction(mp, cf,307599661); - /* $3\cdot2^{27}\cdot(\sqrt5-1)\approx497706706.78$ and - $3\cdot2^{27}\cdot(3-\sqrt5\,)\approx307599661.22$ */ - if ( t!=unity ) num=mp_make_scaled(mp, num,t); - /* |make_scaled(fraction,scaled)=fraction| */ - if ( num / 4>=denom ) - return fraction_four; - else - return mp_make_fraction(mp, num, denom); -} - -@ The following somewhat different subroutine tests rigorously if $ab$ is -greater than, equal to, or less than~$cd$, -given integers $(a,b,c,d)$. In most cases a quick decision is reached. -The result is $+1$, 0, or~$-1$ in the three respective cases. - -@d mp_ab_vs_cd(M,A,B,C,D) mp_do_ab_vs_cd(A,B,C,D) - -@c -static integer mp_do_ab_vs_cd (integer a,integer b, integer c, integer d) { - integer q,r; /* temporary registers */ - @<Reduce to the case that |a,c>=0|, |b,d>0|@>; - while (1) { - q = a / d; r = c / b; - if ( q!=r ) - return ( q>r ? 1 : -1); - q = a % d; r = c % b; - if ( r==0 ) - return (q ? 1 : 0); - if ( q==0 ) return -1; - a=b; b=q; c=d; d=r; - } /* now |a>d>0| and |c>b>0| */ -} - -@ @<Reduce to the case that |a...@>= -if ( a<0 ) { negate(a); negate(b); }; -if ( c<0 ) { negate(c); negate(d); }; -if ( d<=0 ) { - if ( b>=0 ) { - if ( (a==0||b==0)&&(c==0||d==0) ) return 0; - else return 1; - } - if ( d==0 ) - return ( a==0 ? 0 : -1); - q=a; a=c; c=q; q=-b; b=-d; d=q; -} else if ( b<=0 ) { - if ( b<0 ) if ( a>0 ) return -1; - return (c==0 ? 0 : -1); -} - -@ We conclude this set of elementary routines with some simple rounding -and truncation operations. - -@<Internal library declarations@>= -#define mp_floor_scaled(M,i) ((i)&(-65536)) - -#define mp_round_unscaled(M,x) (x>=0100000 ? 1+((x-0100000) / 0200000) \ - : ( x>=-0100000 ? 0 : -(1+((-(x+1)-0100000) / 0200000)))) - -#define mp_round_fraction(M,x) (x>=2048 ? 1+((x-2048) / 4096) \ - : ( x>=-2048 ? 0 : -(1+((-(x+1)-2048) / 4096)))) - - -@* \[8] Algebraic and transcendental functions. -\MP\ computes all of the necessary special functions from scratch, without -relying on |real| arithmetic or system subroutines for sines, cosines, etc. - -@ To get the square root of a |scaled| number |x|, we want to calculate -$s=\lfloor 2^8\!\sqrt x +{1\over2}\rfloor$. If $x>0$, this is the unique -integer such that $2^{16}x-s\L s^2<2^{16}x+s$. The following subroutine -determines $s$ by an iterative method that maintains the invariant -relations $x=2^{46-2k}x_0\bmod 2^{30}$, $0<y=\lfloor 2^{16-2k}x_0\rfloor --s^2+s\L q=2s$, where $x_0$ is the initial value of $x$. The value of~$y$ -might, however, be zero at the start of the first iteration. - -@<Declarations@>= -static scaled mp_square_rt (MP mp,scaled x) ; - -@ @c -scaled mp_square_rt (MP mp,scaled x) { - quarterword k; /* iteration control counter */ - integer y; /* register for intermediate calculations */ - integer q; /* register for intermediate calculations */ - if ( x<=0 ) { - @<Handle square root of zero or negative argument@>; - } else { - k=23; q=2; - while ( x<fraction_two ) { /* i.e., |while x<@t$2^{29}$@>|\unskip */ - k--; x=x+x+x+x; - } - if ( x<fraction_four ) y=0; - else { x=x-fraction_four; y=1; }; - do { - @<Decrease |k| by 1, maintaining the invariant - relations between |x|, |y|, and~|q|@>; - } while (k!=0); - return (scaled)(halfp(q)); - } -} - -@ @<Handle square root of zero...@>= -{ - if ( x<0 ) { - print_err("Square root of "); -@.Square root...replaced by 0@> - mp_print_scaled(mp, x); mp_print(mp, " has been replaced by 0"); - help2("Since I don't take square roots of negative numbers,", - "I'm zeroing this one. Proceed, with fingers crossed."); - mp_error(mp); - }; - return 0; -} - -@ @<Decrease |k| by 1, maintaining...@>= -x+=x; y+=y; -if ( x>=fraction_four ) { /* note that |fraction_four=@t$2^{30}$@>| */ - x=x-fraction_four; y++; -}; -x+=x; y=y+y-q; q+=q; -if ( x>=fraction_four ) { x=x-fraction_four; y++; }; -if ( y>(int)q ){ y-=q; q+=2; } -else if ( y<=0 ) { q-=2; y+=q; }; -k-- - -@ Pythagorean addition $\psqrt{a^2+b^2}$ is implemented by an elegant -iterative scheme due to Cleve Moler and Donald Morrison [{\sl IBM Journal -@^Moler, Cleve Barry@> -@^Morrison, Donald Ross@> -of Research and Development\/ \bf27} (1983), 577--581]. It modifies |a| and~|b| -in such a way that their Pythagorean sum remains invariant, while the -smaller argument decreases. - -@<Internal library ...@>= -integer mp_pyth_add (MP mp,integer a, integer b); - - -@ @c -integer mp_pyth_add (MP mp,integer a, integer b) { - fraction r; /* register used to transform |a| and |b| */ - boolean big; /* is the result dangerously near $2^{31}$? */ - a=abs(a); b=abs(b); - if ( a<b ) { r=b; b=a; a=r; }; /* now |0<=b<=a| */ - if ( b>0 ) { - if ( a<fraction_two ) { - big=false; - } else { - a=a / 4; b=b / 4; big=true; - }; /* we reduced the precision to avoid arithmetic overflow */ - @<Replace |a| by an approximation to $\psqrt{a^2+b^2}$@>; - if ( big ) { - if ( a<fraction_two ) { - a=a+a+a+a; - } else { - mp->arith_error=true; a=el_gordo; - }; - } - } - return a; -} - -@ The key idea here is to reflect the vector $(a,b)$ about the -line through $(a,b/2)$. - -@<Replace |a| by an approximation to $\psqrt{a^2+b^2}$@>= -while (1) { - r=mp_make_fraction(mp, b,a); - r=mp_take_fraction(mp, r,r); /* now $r\approx b^2/a^2$ */ - if ( r==0 ) break; - r=mp_make_fraction(mp, r,fraction_four+r); - a=a+mp_take_fraction(mp, a+a,r); b=mp_take_fraction(mp, b,r); -} - - -@ Here is a similar algorithm for $\psqrt{a^2-b^2}$. -It converges slowly when $b$ is near $a$, but otherwise it works fine. - -@c -static integer mp_pyth_sub (MP mp,integer a, integer b) { - fraction r; /* register used to transform |a| and |b| */ - boolean big; /* is the input dangerously near $2^{31}$? */ - a=abs(a); b=abs(b); - if ( a<=b ) { - @<Handle erroneous |pyth_sub| and set |a:=0|@>; - } else { - if ( a<fraction_four ) { - big=false; - } else { - a=(integer)halfp(a); b=(integer)halfp(b); big=true; - } - @<Replace |a| by an approximation to $\psqrt{a^2-b^2}$@>; - if ( big ) double(a); - } - return a; -} - -@ @<Replace |a| by an approximation to $\psqrt{a^2-b^2}$@>= -while (1) { - r=mp_make_fraction(mp, b,a); - r=mp_take_fraction(mp, r,r); /* now $r\approx b^2/a^2$ */ - if ( r==0 ) break; - r=mp_make_fraction(mp, r,fraction_four-r); - a=a-mp_take_fraction(mp, a+a,r); b=mp_take_fraction(mp, b,r); -} - -@ @<Handle erroneous |pyth_sub| and set |a:=0|@>= -{ - if ( a<b ){ - print_err("Pythagorean subtraction "); mp_print_scaled(mp, a); - mp_print(mp, "+-+"); mp_print_scaled(mp, b); - mp_print(mp, " has been replaced by 0"); -@.Pythagorean...@> - help2("Since I don't take square roots of negative numbers,", - "I'm zeroing this one. Proceed, with fingers crossed."); - mp_error(mp); - } - a=0; -} - -@ The subroutines for logarithm and exponential involve two tables. -The first is simple: |two_to_the[k]| equals $2^k$. The second involves -a bit more calculation, which the author claims to have done correctly: -|spec_log[k]| is $2^{27}$ times $\ln\bigl(1/(1-2^{-k})\bigr)= -2^{-k}+{1\over2}2^{-2k}+{1\over3}2^{-3k}+\cdots\,$, rounded to the -nearest integer. - -@d two_to_the(A) (1<<(unsigned)(A)) +@ +@d fraction_one ((math_data *)mp->math)->fraction_one_ +@d fraction_half ((math_data *)mp->math)->fraction_half_ +@d fraction_two ((math_data *)mp->math)->fraction_two_ +@d fraction_three ((math_data *)mp->math)->fraction_three_ +@d fraction_four ((math_data *)mp->math)->fraction_four_ -@<Declarations@>= -static const integer spec_log[29] = { 0, /* special logarithms */ -93032640, 38612034, 17922280, 8662214, 4261238, 2113709, -1052693, 525315, 262400, 131136, 65552, 32772, 16385, -8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1, 1 }; +@d ninety_deg ((math_data *)mp->math)->ninety_deg_ +@d one_eighty_deg ((math_data *)mp->math)->one_eighty_deg_ +@d three_sixty_deg ((math_data *)mp->math)->three_sixty_deg_ @ @<Local variables for initialization@>= -integer k; /* all-purpose loop index */ - - -@ Here is the routine that calculates $2^8$ times the natural logarithm -of a |scaled| quantity; it is an integer approximation to $2^{24}\ln(x/2^{16})$, -when |x| is a given positive integer. - -The method is based on exercise 1.2.2--25 in {\sl The Art of Computer -Programming\/}: During the main iteration we have $1\L 2^{-30}x<1/(1-2^{1-k})$, -and the logarithm of $2^{30}x$ remains to be added to an accumulator -register called~$y$. Three auxiliary bits of accuracy are retained in~$y$ -during the calculation, and sixteen auxiliary bits to extend |y| are -kept in~|z| during the initial argument reduction. (We add -$100\cdot2^{16}=6553600$ to~|z| and subtract 100 from~|y| so that |z| will -not become negative; also, the actual amount subtracted from~|y| is~96, -not~100, because we want to add~4 for rounding before the final division by~8.) - -@c -static scaled mp_m_log (MP mp,scaled x) { - integer y,z; /* auxiliary registers */ - integer k; /* iteration counter */ - if ( x<=0 ) { - @<Handle non-positive logarithm@>; - } else { - y=1302456956+4-100; /* $14\times2^{27}\ln2\approx1302456956.421063$ */ - z=27595+6553600; /* and $2^{16}\times .421063\approx 27595$ */ - while ( x<fraction_four ) { - double(x); y-=93032639; z-=48782; - } /* $2^{27}\ln2\approx 93032639.74436163$ and $2^{16}\times.74436163\approx 48782$ */ - y=y+(z / unity); k=2; - while ( x>fraction_four+4 ) { - @<Increase |k| until |x| can be multiplied by a - factor of $2^{-k}$, and adjust $y$ accordingly@>; - } - return (y / 8); - } -} - -@ @<Increase |k| until |x| can...@>= -{ - z=((x-1) / two_to_the(k))+1; /* $z=\lceil x/2^k\rceil$ */ - while ( x<fraction_four+z ) { z=halfp(z+1); k++; }; - y+=spec_log[k]; x-=z; -} - -@ @<Handle non-positive logarithm@>= -{ - print_err("Logarithm of "); -@.Logarithm...replaced by 0@> - mp_print_scaled(mp, x); mp_print(mp, " has been replaced by 0"); - help2("Since I don't take logs of non-positive numbers,", - "I'm zeroing this one. Proceed, with fingers crossed."); - mp_error(mp); - return 0; -} - -@ Conversely, the exponential routine calculates $\exp(x/2^8)$, -when |x| is |scaled|. The result is an integer approximation to -$2^{16}\exp(x/2^{24})$, when |x| is regarded as an integer. - -@c -static scaled mp_m_exp (MP mp,scaled x) { - quarterword k; /* loop control index */ - integer y,z; /* auxiliary registers */ - if ( x>174436200 ) { - /* $2^{24}\ln((2^{31}-1)/2^{16})\approx 174436199.51$ */ - mp->arith_error=true; - return el_gordo; - } else if ( x<-197694359 ) { - /* $2^{24}\ln(2^{-1}/2^{16})\approx-197694359.45$ */ - return 0; - } else { - if ( x<=0 ) { - z=-8*x; y=04000000; /* $y=2^{20}$ */ - } else { - if ( x<=127919879 ) { - z=1023359037-8*x; - /* $2^{27}\ln((2^{31}-1)/2^{20})\approx 1023359037.125$ */ - } else { - z=8*(174436200-x); /* |z| is always nonnegative */ - } - y=el_gordo; - }; - @<Multiply |y| by $\exp(-z/2^{27})$@>; - if ( x<=127919879 ) - return ((y+8) / 16); - else - return y; - } -} - -@ The idea here is that subtracting |spec_log[k]| from |z| corresponds -to multiplying |y| by $1-2^{-k}$. - -A subtle point (which had to be checked) was that if $x=127919879$, the -value of~|y| will decrease so that |y+8| doesn't overflow. In fact, -$z$ will be 5 in this case, and |y| will decrease by~64 when |k=25| -and by~16 when |k=27|. - -@<Multiply |y| by...@>= -k=1; -while ( z>0 ) { - while ( z>=spec_log[k] ) { - z-=spec_log[k]; - y=y-1-((y-two_to_the(k-1)) / two_to_the(k)); - } - k++; -} - -@ The trigonometric subroutines use an auxiliary table such that -|spec_atan[k]| contains an approximation to the |angle| whose tangent -is~$1/2^k$. $\arctan2^{-k}$ times $2^{20}\cdot180/\pi$ - -@<Declarations@>= -static const angle spec_atan[27] = { 0, 27855475, 14718068, 7471121, 3750058, -1876857, 938658, 469357, 234682, 117342, 58671, 29335, 14668, 7334, 3667, -1833, 917, 458, 229, 115, 57, 29, 14, 7, 4, 2, 1 }; - -@ Given integers |x| and |y|, not both zero, the |n_arg| function -returns the |angle| whose tangent points in the direction $(x,y)$. -This subroutine first determines the correct octant, then solves the -problem for |0<=y<=x|, then converts the result appropriately to -return an answer in the range |-one_eighty_deg<=@t$\theta$@><=one_eighty_deg|. -(The answer is |+one_eighty_deg| if |y=0| and |x<0|, but an answer of -|-one_eighty_deg| is possible if, for example, |y=-1| and $x=-2^{30}$.) - -The octants are represented in a ``Gray code,'' since that turns out -to be computationally simplest. - -@d negate_x 1 -@d negate_y 2 -@d switch_x_and_y 4 -@d first_octant 1 -@d second_octant (first_octant+switch_x_and_y) -@d third_octant (first_octant+switch_x_and_y+negate_x) -@d fourth_octant (first_octant+negate_x) -@d fifth_octant (first_octant+negate_x+negate_y) -@d sixth_octant (first_octant+switch_x_and_y+negate_x+negate_y) -@d seventh_octant (first_octant+switch_x_and_y+negate_y) -@d eighth_octant (first_octant+negate_y) - -@c -static angle mp_n_arg (MP mp,integer x, integer y) { - angle z; /* auxiliary register */ - integer t; /* temporary storage */ - quarterword k; /* loop counter */ - int octant; /* octant code */ - if ( x>=0 ) { - octant=first_octant; - } else { - negate(x); octant=first_octant+negate_x; - } - if ( y<0 ) { - negate(y); octant=octant+negate_y; - } - if ( x<y ) { - t=y; y=x; x=t; octant=octant+switch_x_and_y; - } - if ( x==0 ) { - @<Handle undefined arg@>; - } else { - @<Set variable |z| to the arg of $(x,y)$@>; - @<Return an appropriate answer based on |z| and |octant|@>; - } -} - -@ @<Handle undefined arg@>= -{ - print_err("angle(0,0) is taken as zero"); -@.angle(0,0)...zero@> - help2("The `angle' between two identical points is undefined.", - "I'm zeroing this one. Proceed, with fingers crossed."); - mp_error(mp); - return 0; -} - -@ @<Return an appropriate answer...@>= -switch (octant) { -case first_octant: return z; -case second_octant: return (ninety_deg-z); -case third_octant: return (ninety_deg+z); -case fourth_octant: return (one_eighty_deg-z); -case fifth_octant: return (z-one_eighty_deg); -case sixth_octant: return (-z-ninety_deg); -case seventh_octant: return (z-ninety_deg); -case eighth_octant: return (-z); -}; /* there are no other cases */ -return 0 - -@ At this point we have |x>=y>=0|, and |x>0|. The numbers are scaled up -or down until $2^{28}\L x<2^{29}$, so that accurate fixed-point calculations -will be made. - -@<Set variable |z| to the arg...@>= -while ( x>=fraction_two ) { - x=halfp(x); y=halfp(y); -} -z=0; -if ( y>0 ) { - while ( x<fraction_one ) { - x+=x; y+=y; - }; - @<Increase |z| to the arg of $(x,y)$@>; -} - -@ During the calculations of this section, variables |x| and~|y| -represent actual coordinates $(x,2^{-k}y)$. We will maintain the -condition |x>=y|, so that the tangent will be at most $2^{-k}$. -If $x<2y$, the tangent is greater than $2^{-k-1}$. The transformation -$(a,b)\mapsto(a+b\tan\phi,b-a\tan\phi)$ replaces $(a,b)$ by -coordinates whose angle has decreased by~$\phi$; in the special case -$a=x$, $b=2^{-k}y$, and $\tan\phi=2^{-k-1}$, this operation reduces -to the particularly simple iteration shown here. [Cf.~John E. Meggitt, -@^Meggitt, John E.@> -{\sl IBM Journal of Research and Development\/ \bf6} (1962), 210--226.] - -The initial value of |x| will be multiplied by at most -$(1+{1\over2})(1+{1\over8})(1+{1\over32})\cdots\approx 1.7584$; hence -there is no chance of integer overflow. - -@<Increase |z|...@>= -k=0; -do { - y+=y; k++; - if ( y>x ){ - z=z+spec_atan[k]; t=x; x=x+(y / two_to_the(k+k)); y=y-t; - }; -} while (k!=15); -do { - y+=y; k++; - if ( y>x ) { z=z+spec_atan[k]; y=y-x; }; -} while (k!=26) - -@ Conversely, the |n_sin_cos| routine takes an |angle| and produces the sine -and cosine of that angle. The results of this routine are -stored in global integer variables |n_sin| and |n_cos|. - -@<Glob...@>= -fraction n_sin;fraction n_cos; /* results computed by |n_sin_cos| */ - -@ Given an integer |z| that is $2^{20}$ times an angle $\theta$ in degrees, -the purpose of |n_sin_cos(z)| is to set -|x=@t$r\cos\theta$@>| and |y=@t$r\sin\theta$@>| (approximately), -for some rather large number~|r|. The maximum of |x| and |y| -will be between $2^{28}$ and $2^{30}$, so that there will be hardly -any loss of accuracy. Then |x| and~|y| are divided by~|r|. - -@c -static void mp_n_sin_cos (MP mp,angle z) { /* computes a multiple of the sine - and cosine */ - quarterword k; /* loop control variable */ - int q; /* specifies the quadrant */ - fraction r; /* magnitude of |(x,y)| */ - integer x,y,t; /* temporary registers */ - while ( z<0 ) z=z+three_sixty_deg; - z=z % three_sixty_deg; /* now |0<=z<three_sixty_deg| */ - q=z / forty_five_deg; z=z % forty_five_deg; - x=fraction_one; y=x; - if ( ! odd(q) ) z=forty_five_deg-z; - @<Subtract angle |z| from |(x,y)|@>; - @<Convert |(x,y)| to the octant determined by~|q|@>; - r=mp_pyth_add(mp, x,y); - mp->n_cos=mp_make_fraction(mp, x,r); - mp->n_sin=mp_make_fraction(mp, y,r); -} - -@ In this case the octants are numbered sequentially. - -@<Convert |(x,...@>= -switch (q) { -case 0: break; -case 1: t=x; x=y; y=t; break; -case 2: t=x; x=-y; y=t; break; -case 3: negate(x); break; -case 4: negate(x); negate(y); break; -case 5: t=x; x=-y; y=-t; break; -case 6: t=x; x=y; y=-t; break; -case 7: negate(y); break; -} /* there are no other cases */ - -@ The main iteration of |n_sin_cos| is similar to that of |n_arg| but -applied in reverse. The values of |spec_atan[k]| decrease slowly enough -that this loop is guaranteed to terminate before the (nonexistent) value -|spec_atan[27]| would be required. - -@<Subtract angle |z|...@>= -k=1; -while ( z>0 ){ - if ( z>=spec_atan[k] ) { - z=z-spec_atan[k]; t=x; - x=t+y / two_to_the(k); - y=y-t / two_to_the(k); - } - k++; -} -if ( y<0 ) y=0 /* this precaution may never be needed */ +integer k; /* all-purpose loop index */ @ And now let's complete our collection of numeric utility routines by considering random number generation. @@ -3667,14 +2563,14 @@ multiple MetaPost processes within the same second. @^system dependencies@> @<Glob...@>= -fraction randoms[55]; /* the last 55 random values generated */ -int j_random; /* the number of unused |randoms| */ +fraction randoms[55]; /* the last 55 random values generated */ +int j_random; /* the number of unused |randoms| */ @ @<Option variables@>= -int random_seed; /* the default random seed */ +int random_seed; /* the default random seed */ @ @<Allocate or initialize ...@>= -mp->random_seed = (scaled)opt->random_seed; +mp->random_seed = opt->random_seed; @ To consume a random fraction, the program below will say `|next_random|' and then it will fetch |randoms[j_random]|. @@ -3682,45 +2578,53 @@ and then it will fetch |randoms[j_random]|. @d next_random { if ( mp->j_random==0 ) mp_new_randoms(mp); else decr(mp->j_random); } -@c +@c static void mp_new_randoms (MP mp) { - int k; /* index into |randoms| */ - fraction x; /* accumulator */ - for (k=0;k<=23;k++) { - x=mp->randoms[k]-mp->randoms[k+31]; - if ( x<0 ) x=x+fraction_one; - mp->randoms[k]=x; + int k; /* index into |randoms| */ + fraction x; /* accumulator */ + for (k = 0; k <= 23; k++) { + x = mp->randoms[k] - mp->randoms[k + 31]; + if (x < 0) + x = x + fraction_one; + mp->randoms[k] = x; } - for (k=24;k<= 54;k++){ - x=mp->randoms[k]-mp->randoms[k-24]; - if ( x<0 ) x=x+fraction_one; - mp->randoms[k]=x; + for (k = 24; k <= 54; k++) { + x = mp->randoms[k] - mp->randoms[k - 24]; + if (x < 0) + x = x + fraction_one; + mp->randoms[k] = x; } - mp->j_random=54; + mp->j_random = 54; } + @ @<Declarations@>= -static void mp_init_randoms (MP mp,scaled seed); +static void mp_init_randoms (MP mp, int seed); @ To initialize the |randoms| table, we call the following routine. -@c -void mp_init_randoms (MP mp,scaled seed) { - fraction j,jj,k; /* more or less random integers */ - int i; /* index into |randoms| */ - j=abs(seed); - while ( j>=fraction_one ) j=halfp(j); - k=1; - for (i=0;i<=54;i++ ){ - jj=k; k=j-k; j=jj; - if ( k<0 ) k=k+fraction_one; - mp->randoms[(i*21)% 55]=j; - } - mp_new_randoms(mp); - mp_new_randoms(mp); - mp_new_randoms(mp); /* ``warm up'' the array */ +@c +void mp_init_randoms (MP mp, int seed) { + fraction j, jj, k; /* more or less random integers */ + int i; /* index into |randoms| */ + j = abs (seed); + while (j >= fraction_one) + j = halfp (j); + k = 1; + for (i = 0; i <= 54; i++) { + jj = k; + k = j - k; + j = jj; + if (k < 0) + k = k + fraction_one; + mp->randoms[(i * 21) % 55] = j; + } + mp_new_randoms (mp); + mp_new_randoms (mp); + mp_new_randoms (mp); /* ``warm up'' the array */ } + @ To produce a uniform random number in the range |0<=u<x| or |0>=u>x| or |0=u=x|, given a |scaled| value~|x|, we proceed as shown here. @@ -3728,72 +2632,49 @@ Note that the call of |take_fraction| will produce the values 0 and~|x| with about half the probability that it will produce any other particular values between 0 and~|x|, because it rounds its answers. -@c -static scaled mp_unif_rand (MP mp,scaled x) { - scaled y; /* trial value */ - next_random; y=mp_take_fraction(mp, abs(x),mp->randoms[mp->j_random]); - if ( y==abs(x) ) return 0; - else if ( x>0 ) return y; - else return (-y); +@c +static scaled mp_unif_rand (MP mp, scaled x) { + scaled y; /* trial value */ + next_random; + y = mp_take_fraction (mp, abs (x), mp->randoms[mp->j_random]); + if (y == abs (x)) + return 0; + else if (x > 0) + return y; + else + return (-y); } + @ Finally, a normal deviate with mean zero and unit standard deviation can readily be obtained with the ratio method (Algorithm 3.4.1R in {\sl The Art of Computer Programming\/}). -@c +@c static scaled mp_norm_rand (MP mp) { - integer x,u,l; /* what the book would call $2^{16}X$, $2^{28}U$, and $-2^{24}\ln U$ */ - do { - do { + integer x, u, l; /* what the book would call $2^{16}X$, $2^{28}U$, and $-2^{24}\ln U$ */ + do { + do { next_random; - x=mp_take_fraction(mp, 112429,mp->randoms[mp->j_random]-fraction_half); + x = + mp_take_fraction (mp, 112429, + mp->randoms[mp->j_random] - fraction_half); /* $2^{16}\sqrt{8/e}\approx 112428.82793$ */ - next_random; u=mp->randoms[mp->j_random]; - } while (abs(x)>=u); - x=mp_make_fraction(mp, x,u); - l=139548960-mp_m_log(mp, u); /* $2^{24}\cdot12\ln2\approx139548959.6165$ */ - } while (mp_ab_vs_cd(mp, 1024,l,x,x)<0); + next_random; + u = mp->randoms[mp->j_random]; + } while (abs (x) >= u); + x = mp_make_fraction (mp, x, u); + l = 139548960 - mp_m_log (mp, u); /* $2^{24}\cdot12\ln2\approx139548959.6165$ */ + } while (mp_ab_vs_cd (mp, 1024, l, x, x) < 0); return x; } -@* \[9] Packed data. -In order to make efficient use of storage space, \MP\ bases its major data -structures on a |memory_word|, which contains either a (signed) integer, -possibly scaled, or a small number of fields that are one half or one -quarter of the size used for storing integers. - -If |x| is a variable of type |memory_word|, it contains up to four -fields that can be referred to as follows: -$$\vbox{\halign{\hfil#&#\hfil&#\hfil\cr -|x|&.|int|&(an |integer|)\cr -|x|&.|sc|\qquad&(a |scaled| integer)\cr -|x.hh.lh|, |x.hh|&.|rh|&(two halfword fields)\cr -|x.hh.b0|, |x.hh.b1|, |x.hh|&.|rh|&(two quarterword fields, one halfword - field)\cr -|x.qqqq.b0|, |x.qqqq.b1|, |x.qqqq|&.|b2|, |x.qqqq.b3|\hskip-100pt - &\qquad\qquad\qquad(four quarterword fields)\cr}}$$ -This is somewhat cumbersome to write, and not very readable either, but -macros will be used to make the notation shorter and more transparent. -The code below gives a formal definition of |memory_word| and -its subsidiary types, using packed variant records. \MP\ makes no -assumptions about the relative positions of the fields within a word. + +@* Packed data. @d max_quarterword 0x3FFF /* largest allowable value in a |quarterword| */ @d max_halfword 0xFFFFFFF /* largest allowable value in a |halfword| */ -@ Here are the inequalities that the quarterword and halfword values -must satisfy (or rather, the inequalities that they mustn't satisfy): - -@<Check the ``constant''...@>= -if (mp->ini_version) { - if ( mp->mem_max!=mp->mem_top ) mp->bad=8; -} else { - if ( mp->mem_max<mp->mem_top ) mp->bad=8; -} -if ( mp->mem_max>=max_halfword ) mp->bad=12; -if ( mp->max_strings>max_halfword ) mp->bad=13; - @ The macros |qi| and |qo| are used for input to and output from quarterwords. These are legacy macros. @^system dependencies@> @@ -3804,108 +2685,31 @@ from quarterwords. These are legacy macros. @ The reader should study the following definitions closely: @^system dependencies@> -@d sc cint /* |scaled| data is equivalent to |integer| */ - @<Types...@>= -typedef short quarterword; /* 1/4 of a word */ -typedef int halfword; /* 1/2 of a word */ -typedef union { - struct { - halfword RH, LH; - } v; - struct { /* Make B0,B1 overlap the most significant bytes of LH. */ - halfword junk; - quarterword B0, B1; - } u; -} two_halves; +typedef struct mp_value_node_data *mp_value_node; +typedef struct mp_node_data *mp_node; +typedef struct mp_symbol_entry *mp_sym; +typedef short quarterword; /* 1/4 of a word */ +typedef int halfword; /* 1/2 of a word */ +typedef struct { + halfword val; + integer scale; + str_number str; + mp_sym sym; + mp_node node; + mp_knot p; +} mp_value_data; +typedef struct { + mp_variable_type type; + mp_value_data data; +} mp_value; typedef struct { - struct { - quarterword B2, B3, B0, B1; - } u; + quarterword b0, b1, b2, b3; } four_quarters; typedef union { - two_halves hh; - integer cint; + integer sc; four_quarters qqqq; -} memory_word; -#define b0 u.B0 -#define b1 u.B1 -#define b2 u.B2 -#define b3 u.B3 -#define rh v.RH -#define lh v.LH - -@ When debugging, we may want to print a |memory_word| without knowing -what type it is; so we print it in all modes. -@^debugging@> - -@c -#if 0 -void mp_print_word (MP mp,memory_word w) { - /* prints |w| in all ways */ - mp_print_int(mp, w.cint); mp_print_char(mp, xord(' ')); - mp_print_scaled(mp, w.sc); mp_print_char(mp, xord(' ')); - mp_print_scaled(mp, w.sc / 010000); mp_print_ln(mp); - mp_print_int(mp, w.hh.lh); mp_print_char(mp, xord('=')); - mp_print_int(mp, w.hh.b0); mp_print_char(mp, xord(':')); - mp_print_int(mp, w.hh.b1); mp_print_char(mp, xord(';')); - mp_print_int(mp, w.hh.rh); mp_print_char(mp, xord(' ')); - mp_print_int(mp, w.qqqq.b0); mp_print_char(mp, xord(':')); - mp_print_int(mp, w.qqqq.b1); mp_print_char(mp, xord(':')); - mp_print_int(mp, w.qqqq.b2); mp_print_char(mp, xord(':')); - mp_print_int(mp, w.qqqq.b3); -} -#endif - -@* \[10] Dynamic memory allocation. - -The \MP\ system does nearly all of its own memory allocation, so that it -can readily be transported into environments that do not have automatic -facilities for strings, garbage collection, etc., and so that it can be in -control of what error messages the user receives. The dynamic storage -requirements of \MP\ are handled by providing a large array |mem| in -which consecutive blocks of words are used as nodes by the \MP\ routines. - -Pointer variables are indices into this array, or into another array -called |eqtb| that will be explained later. A pointer variable might -also be a special flag that lies outside the bounds of |mem|, so we -allow pointers to assume any |halfword| value. The minimum memory -index represents a null pointer. - -@d null 0 /* the null pointer */ -@d mp_void (null+1) /* a null pointer different from |null| */ - - -@<Types...@>= -typedef halfword pointer; /* a flag or a location in |mem| or |eqtb| */ - -@ The |mem| array is divided into two regions that are allocated separately, -but the dividing line between these two regions is not fixed; they grow -together until finding their ``natural'' size in a particular job. -Locations less than or equal to |lo_mem_max| are used for storing -variable-length records consisting of two or more words each. This region -is maintained using an algorithm similar to the one described in exercise -2.5--19 of {\sl The Art of Computer Programming}. However, no size field -appears in the allocated nodes; the program is responsible for knowing the -relevant size when a node is freed. Locations greater than or equal to -|hi_mem_min| are used for storing one-word records; a conventional -\.{AVAIL} stack is used for allocation in this region. - -Locations of |mem| between |0| and |mem_top| may be dumped as part -of preloaded mem files, by the \.{INIMP} preprocessor. -@.INIMP@> -Production versions of \MP\ may extend the memory at the top end in order to -provide more space; these locations, between |mem_top| and |mem_max|, -are always used for single-word nodes. - -The key pointers that govern |mem| allocation have a prescribed order: -$$\hbox{|null=0<lo_mem_max<hi_mem_min<mem_top<=mem_end<=mem_max|.}$$ - -@<Glob...@>= -memory_word *mem; /* the big dynamic storage area */ -pointer lo_mem_max; /* the largest location of variable-size memory in use */ -pointer hi_mem_min; /* the smallest location of one-word memory in use */ - +} font_data; @ @d xfree(A) do { mp_xfree(A); A=NULL; } while (0) @@ -3915,16 +2719,35 @@ pointer hi_mem_min; /* the smallest location of one-word memory in use */ @d XREALLOC(a,b,c) a = xrealloc(a,(b+1),sizeof(c)); @<Declare helpers@>= -extern char *mp_strdup(const char *p) ; -extern char *mp_strldup(const char *p, size_t l) ; -extern void mp_xfree ( @= /*@@only@@*/ /*@@out@@*/ /*@@null@@*/ @> void *x); -extern @= /*@@only@@*/ @> void *mp_xrealloc (MP mp, void *p, size_t nmem, size_t size) ; -extern @= /*@@only@@*/ @> void *mp_xmalloc (MP mp, size_t nmem, size_t size) ; -extern @= /*@@only@@*/ @> char *mp_xstrdup(MP mp, const char *s); -extern @= /*@@only@@*/ @> char *mp_xstrldup(MP mp, const char *s, size_t l); -#ifndef HAVE_SNPRINTF -extern void mp_do_snprintf(char *str, int size, const char *fmt, ...); -#endif +extern char *mp_strdup (const char *p); +extern char *mp_strldup (const char *p, size_t l); +extern void mp_xfree (void *x); +extern void *mp_xrealloc (MP mp, void *p, size_t nmem, size_t size); +extern void *mp_xmalloc (MP mp, size_t nmem, size_t size); +extern char *mp_xstrdup (MP mp, const char *s); +extern char *mp_xstrldup (MP mp, const char *s, size_t l); +extern void mp_do_snprintf (char *str, int size, const char *fmt, ...); + +@ Some care has to be taken while copying strings + +@c +char *mp_strldup (const char *p, size_t l) { + char *r, *s; + if (p == NULL) + return NULL; + r = malloc ((size_t) (l * sizeof (char) + 1)); + if (r == NULL) + return NULL; + s = memcpy (r, p, (size_t) (l)); + *(s + l) = '\0'; + return s; +} +char *mp_strdup (const char *p) { + if (p == NULL) + return NULL; + return mp_strldup (p, strlen (p)); +} + @ The |max_size_test| guards against overflow, on the assumption that |size_t| is at least 31bits wide. @@ -3932,774 +2755,422 @@ extern void mp_do_snprintf(char *str, int size, const char *fmt, ...); @d max_size_test 0x7FFFFFFF @c -char *mp_strldup(const char *p, size_t l) { - char *r; - if (p==NULL) return NULL; - r = malloc ((size_t)(l*sizeof(char)+1)); - if (r==NULL) - return NULL; - return memcpy (r,p,(size_t)(l+1)); -} -char *mp_strdup(const char *p) { - if (p==NULL) return NULL; - return mp_strldup(p, strlen(p)); -} void mp_xfree (void *x) { - if (x!=NULL) free(x); + if (x != NULL) + free (x); } -void *mp_xrealloc (MP mp, void *p, size_t nmem, size_t size) { - void *w ; - if ((max_size_test/size)<nmem) { - do_fprintf(mp->err_out,"Memory size overflow!\n"); - mp->history =mp_fatal_error_stop; mp_jump_out(mp); +void *mp_xrealloc (MP mp, void *p, size_t nmem, size_t size) { + void *w; + if ((max_size_test / size) < nmem) { + do_putsf (mp->err_out, "Memory size overflow!\n"); + mp->history = mp_fatal_error_stop; + mp_jump_out (mp); } - w = realloc (p,(nmem*size)); - if (w==NULL) { - do_fprintf(mp->err_out,"Out of memory!\n"); - mp->history =mp_system_error_stop; mp_jump_out(mp); + w = realloc (p, (nmem * size)); + if (w == NULL) { + do_putsf (mp->err_out, "Out of memory!\n"); + mp->history = mp_system_error_stop; + mp_jump_out (mp); } return w; } -void *mp_xmalloc (MP mp, size_t nmem, size_t size) { +void *mp_xmalloc (MP mp, size_t nmem, size_t size) { void *w; - if ((max_size_test/size)<nmem) { - do_fprintf(mp->err_out,"Memory size overflow!\n"); - mp->history =mp_fatal_error_stop; mp_jump_out(mp); + if ((max_size_test / size) < nmem) { + do_putsf (mp->err_out, "Memory size overflow!\n"); + mp->history = mp_fatal_error_stop; + mp_jump_out (mp); } - w = malloc (nmem*size); - if (w==NULL) { - do_fprintf(mp->err_out,"Out of memory!\n"); - mp->history =mp_system_error_stop; mp_jump_out(mp); + w = malloc (nmem * size); + if (w == NULL) { + do_putsf (mp->err_out, "Out of memory!\n"); + mp->history = mp_system_error_stop; + mp_jump_out (mp); } return w; } -char *mp_xstrldup(MP mp, const char *s, size_t l) { - char *w; - if (s==NULL) +char *mp_xstrldup (MP mp, const char *s, size_t l) { + char *w; + if (s == NULL) return NULL; - w = mp_strldup(s, l); - if (w==NULL) { - do_fprintf(mp->err_out,"Out of memory!\n"); - mp->history =mp_system_error_stop; mp_jump_out(mp); + w = mp_strldup (s, l); + if (w == NULL) { + do_putsf (mp->err_out, "Out of memory!\n"); + mp->history = mp_system_error_stop; + mp_jump_out (mp); } return w; } -char *mp_xstrdup(MP mp, const char *s) { - if (s==NULL) return NULL; - return mp_xstrldup(mp,s,strlen(s)); +char *mp_xstrdup (MP mp, const char *s) { + if (s == NULL) + return NULL; + return mp_xstrldup (mp, s, strlen (s)); } @ @<Internal library declarations@>= #ifdef HAVE_SNPRINTF -#define mp_snprintf (void)snprintf +# define mp_snprintf (void)snprintf #else -#define mp_snprintf mp_do_snprintf +# define mp_snprintf mp_do_snprintf #endif @ This internal version is rather stupid, but good enough for its purpose. @c -#ifndef HAVE_SNPRINTF static char *mp_itoa (int i) { - char res[32] ; + char res[32]; unsigned idx = 30; - unsigned v = (unsigned)abs(i); - memset(res,0,32*sizeof(char)); - while (v>=10) { - char d = (char)(v % 10); + unsigned v = (unsigned) abs (i); + memset (res, 0, 32 * sizeof (char)); + while (v >= 10) { + char d = (char) (v % 10); v = v / 10; - res[idx--] = (char)(d + '0'); + res[idx--] = (char) (d + '0'); } - res[idx--] = (char)(v + '0'); - if (i<0) { - res[idx--] = '-'; + res[idx--] = (char) (v + '0'); + if (i < 0) { + res[idx--] = '-'; } - return mp_strdup((res+idx+1)); + return mp_strdup ((res + idx + 1)); } static char *mp_utoa (unsigned v) { - char res[32] ; + char res[32]; unsigned idx = 30; - memset(res,0,32*sizeof(char)); - while (v>=10) { - char d = (char)(v % 10); + memset (res, 0, 32 * sizeof (char)); + while (v >= 10) { + char d = (char) (v % 10); v = v / 10; - res[idx--] = (char)(d + '0'); + res[idx--] = (char) (d + '0'); } - res[idx--] = (char)(v + '0'); - return mp_strdup((res+idx+1)); + res[idx--] = (char) (v + '0'); + return mp_strdup ((res + idx + 1)); } void mp_do_snprintf (char *str, int size, const char *format, ...) { const char *fmt; char *res; int fw, pad; va_list ap; - va_start(ap, format); + va_start (ap, format); res = str; - for (fmt=format;*fmt!='\0';fmt++) { - if (*fmt=='%') { - fw=0; - pad=0; - RESTART: - fmt++; - switch(*fmt) { - case '0': - pad=1; - goto RESTART; - break; - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - assert(fw==0); - fw = *fmt-'0'; - goto RESTART; - break; - case 's': - { - char *s = va_arg(ap, char *); - while (*s) { - *res = *s++; - if (size-->0) res++; - } - } - break; - case 'c': - { - int s = va_arg(ap, int); - *res = (char)s; - if (size-->0) res++; - } - break; - case 'i': - case 'd': - { - char *sstart, *s = mp_itoa(va_arg(ap, int)); - sstart = s; - if (fw) { - int ffw = fw-(int)strlen(s); - while (ffw-->0) { - *res = (char)(pad ? '0' : ' '); - if (size-->0) res++; - } - } - if (s != NULL) { - while (*s) { - *res = *s++; - if (size-->0) res++; - } - mp_xfree(sstart); - } - } - break; - case 'u': - { - char *sstart, *s = mp_utoa(va_arg(ap, unsigned)); - sstart = s; - if (fw) { - int ffw = fw-(int)strlen(s); - while (ffw-->0) { - *res = (char)(pad ? '0' : ' '); - if (size-->0) res++; - } - } - if (s != NULL) { - while (*s) { - *res = *s++; - if (size-->0) res++; - } - mp_xfree(sstart); - } - } - break; - case '%': - *res = '%'; - if (size-->0) res++; - break; - default: - *res = '%'; - if (size-->0) res++; - *res = *fmt; - if (size-->0) res++; - break; - } - } else { - *res = *fmt; - if (size-->0) res++; - } - } - *res = '\0'; - va_end(ap); -} -#endif - -@ -@<Allocate or initialize ...@>= -mp->mem = xmalloc ((mp->mem_max+1),sizeof (memory_word)); -memset(mp->mem,0,(size_t)(mp->mem_max+1)*sizeof (memory_word)); - -@ @<Dealloc variables@>= -xfree(mp->mem); - -@ Users who wish to study the memory requirements of particular applications can -can use optional special features that keep track of current and -maximum memory usage. When code between the delimiters |stat| $\ldots$ -|tats| is not ``commented out,'' \MP\ will run a bit slower but it will -report these statistics when |mp_tracing_stats| is positive. - -@<Glob...@>= -integer var_used; integer dyn_used; /* how much memory is in use */ - -@ Let's consider the one-word memory region first, since it's the -simplest. The pointer variable |mem_end| holds the highest-numbered location -of |mem| that has ever been used. The free locations of |mem| that -occur between |hi_mem_min| and |mem_end|, inclusive, are of type -|two_halves|, and we write |info(p)| and |mp_link(p)| for the |lh| -and |rh| fields of |mem[p]| when it is of this type. The single-word -free locations form a linked list -$$|avail|,\;\hbox{|mp_link(avail)|},\;\hbox{|mp_link(mp_link(avail))|},\;\ldots$$ -terminated by |null|. - -@(mpmp.h@>= -#define mp_link(A) mp->mem[(A)].hh.rh /* the |link| field of a memory word */ -#define mp_info(A) mp->mem[(A)].hh.lh /* the |info| field of a memory word */ - -@ @<Glob...@>= -pointer avail; /* head of the list of available one-word nodes */ -pointer mem_end; /* the last one-word node used in |mem| */ - -@ If one-word memory is exhausted, it might mean that the user has forgotten -a token like `\&{enddef}' or `\&{endfor}'. We will define some procedures -later that try to help pinpoint the trouble. - -@ The function |get_avail| returns a pointer to a new one-word node whose -|link| field is null. However, \MP\ will halt if there is no more room left. -@^inner loop@> - -@c -static pointer mp_get_avail (MP mp) { /* single-word node allocation */ - pointer p; /* the new node being got */ - p=mp->avail; /* get top location in the |avail| stack */ - if ( p!=null ) { - mp->avail=mp_link(mp->avail); /* and pop it off */ - } else if ( mp->mem_end<mp->mem_max ) { /* or go into virgin territory */ - incr(mp->mem_end); p=mp->mem_end; - } else { - decr(mp->hi_mem_min); p=mp->hi_mem_min; - if ( mp->hi_mem_min<=mp->lo_mem_max ) { - mp_runaway(mp); /* if memory is exhausted, display possible runaway text */ - mp_overflow(mp, "main memory size",mp->mem_max); - /* quit; all one-word nodes are busy */ -@:MetaPost capacity exceeded main memory size}{\quad main memory size@> + for (fmt = format; *fmt != '\0'; fmt++) { + if (*fmt == '%') { + fw = 0; + pad = 0; + RESTART: + fmt++; + switch (*fmt) { + case '0': + pad = 1; + goto RESTART; + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + assert (fw == 0); + fw = *fmt - '0'; + goto RESTART; + break; + case 's': + { + char *s = va_arg (ap, char *); + while (*s) { + *res = *s++; + if (size-- > 0) + res++; + } + } + break; + case 'c': + { + int s = va_arg (ap, int); + *res = (char) s; + if (size-- > 0) + res++; + } + break; + case 'i': + case 'd': + { + char *sstart, *s = mp_itoa (va_arg (ap, int)); + sstart = s; + if (fw) { + int ffw = fw - (int) strlen (s); + while (ffw-- > 0) { + *res = (char) (pad ? '0' : ' '); + if (size-- > 0) + res++; + } + } + if (s != NULL) { + while (*s) { + *res = *s++; + if (size-- > 0) + res++; + } + mp_xfree (sstart); + } + } + break; + case 'u': + { + char *sstart, *s = mp_utoa (va_arg (ap, unsigned)); + sstart = s; + if (fw) { + int ffw = fw - (int) strlen (s); + while (ffw-- > 0) { + *res = (char) (pad ? '0' : ' '); + if (size-- > 0) + res++; + } + } + if (s != NULL) { + while (*s) { + *res = *s++; + if (size-- > 0) + res++; + } + mp_xfree (sstart); + } + } + break; + case '%': + *res = '%'; + if (size-- > 0) + res++; + break; + default: + *res = '%'; + if (size-- > 0) + res++; + *res = *fmt; + if (size-- > 0) + res++; + break; + } + } else { + *res = *fmt; + if (size-- > 0) + res++; } } - mp_link(p)=null; /* provide an oft-desired initialization of the new node */ - incr(mp->dyn_used);/* maintain statistics */ - return p; + *res = '\0'; + va_end (ap); } -@ Conversely, a one-word node is recycled by calling |free_avail|. - -@d free_avail(A) /* single-word node liberation */ - { mp_link((A))=mp->avail; mp->avail=(A); decr(mp->dyn_used); } - -@ There's also a |fast_get_avail| routine, which saves the procedure-call -overhead at the expense of extra programming. This macro is used in -the places that would otherwise account for the most calls of |get_avail|. -@^inner loop@> - -@d fast_get_avail(A) { - (A)=mp->avail; /* avoid |get_avail| if possible, to save time */ - if ( (A)==null ) { (A)=mp_get_avail(mp); } - else { mp->avail=mp_link((A)); mp_link((A))=null; incr(mp->dyn_used); } - } - -@ The available-space list that keeps track of the variable-size portion -of |mem| is a nonempty, doubly-linked circular list of empty nodes, -pointed to by the roving pointer |rover|. -Each empty node has size 2 or more; the first word contains the special -value |max_halfword| in its |link| field and the size in its |info| field; -the second word contains the two pointers for double linking. +@* Dynamic memory allocation. -Each nonempty node also has size 2 or more. Its first word is of type -|two_halves|\kern-1pt, and its |link| field is never equal to |max_halfword|. -Otherwise there is complete flexibility with respect to the contents -of its other fields and its other words. - -(We require |mem_max<max_halfword| because terrible things can happen -when |max_halfword| appears in the |link| field of a nonempty node.) - -@d empty_flag max_halfword /* the |link| of an empty variable-size node */ -@d is_empty(A) (mp_link((A))==empty_flag) /* tests for empty node */ - -@(mpmp.h@>= -#define node_size mp_info /* the size field in empty variable-size nodes */ -#define lmp_link(A) mp_info((A)+1) /* left link in doubly-linked list of empty nodes */ -#define rmp_link(A) mp_link((A)+1) /* right link in doubly-linked list of empty nodes */ - -@ @<Glob...@>= -pointer rover; /* points to some node in the list of empties */ +The \MP\ system does nearly all of its own memory allocation, so that it +can readily be transported into environments that do not have automatic +facilities for strings, garbage collection, etc., and so that it can be in +control of what error messages the user receives. + +@d MP_VOID (mp_node)(1) /* |NULL+1|, a |NULL| pointer different from |NULL| */ + +@d mp_link(A) (A)->link /* the |link| field of a node */ +@d set_mp_link(A,B) do { + mp_node d = (B); + /* |printf("set link of %p to %p on line %d\n", (A), d, __LINE__);| */ + mp_link((A)) = d; + } while (0) +@d mp_type(A) (A)->type /* identifies what kind of value this is */ +@d mp_name_type(A) (A)->name_type /* a clue to the name of this value */ + +@ @(mpmp.h@>= +#define NODE_BODY \ + mp_variable_type type; \ + quarterword name_type; \ + halfword info; \ + struct mp_node_data *link +typedef struct mp_node_data { + NODE_BODY; +} mp_node_data; +typedef struct mp_symbolic_node_data { + NODE_BODY; + mp_sym sym; +} mp_symbolic_node_data; +typedef struct mp_symbolic_node_data *mp_symbolic_node; -@ A call to |get_node| with argument |s| returns a pointer to a new node -of size~|s|, which must be 2~or more. The |link| field of the first word -of this new node is set to null. An overflow stop occurs if no suitable -space exists. +@ Users who wish to study the memory requirements of particular applications can +can use the special features that keep track of current and maximum memory usage. +\MP\ will report these statistics when |mp_tracing_stats| is positive. -If |get_node| is called with $s=2^{30}$, it simply merges adjacent free -areas and returns the value |max_halfword|. +@d add_var_used(a) do { + mp->var_used+=(a); + if (mp->var_used>mp->var_used_max) mp->var_used_max=mp->var_used; +} while (0) -@<Internal library declarations@>= -pointer mp_get_node (MP mp,integer s) ; - -@ @c -pointer mp_get_node (MP mp,integer s) { /* variable-size node allocation */ - pointer p; /* the node currently under inspection */ - pointer q; /* the node physically after node |p| */ - integer r; /* the newly allocated node, or a candidate for this honor */ - integer t,tt; /* temporary registers */ -@^inner loop@> - RESTART: - p=mp->rover; /* start at some free node in the ring */ - do { - @<Try to allocate within node |p| and its physical successors, - and |goto found| if allocation was possible@>; - if (rmp_link(p)==null || (rmp_link(p)==p && p!=mp->rover)) { - print_err("Free list garbled"); - help3("I found an entry in the list of free nodes that links", - "badly. I will try to ignore the broken link, but something", - "is seriously amiss. It is wise to warn the maintainers.") - mp_error(mp); - rmp_link(p)=mp->rover; - } - p=rmp_link(p); /* move to the next node in the ring */ - } while (p!=mp->rover); /* repeat until the whole list has been traversed */ - if ( s==010000000000 ) { - return max_halfword; - }; - if ( mp->lo_mem_max+2<mp->hi_mem_min ) { - if ( mp->lo_mem_max+2<=max_halfword ) { - @<Grow more variable-size memory and |goto restart|@>; - } - } - mp_overflow(mp, "main memory size",mp->mem_max); - /* sorry, nothing satisfactory is left */ -@:MetaPost capacity exceeded main memory size}{\quad main memory size@> -FOUND: - mp_link(r)=null; /* this node is now nonempty */ - mp->var_used+=s; /* maintain usage statistics */ - return r; -} +@<Glob...@>= +size_t var_used; /* how much memory is in use */ +size_t var_used_max; /* how much memory was in use max */ -@ The lower part of |mem| grows by 1000 words at a time, unless -we are very close to going under. When it grows, we simply link -a new node into the available-space list. This method of controlled -growth helps to keep the |mem| usage consecutive when \MP\ is -implemented on ``virtual memory'' systems. -@^virtual memory@> +@ These redirect to function to aid in debugging. -@<Grow more variable-size memory and |goto restart|@>= -{ - if ( mp->hi_mem_min-mp->lo_mem_max>=1998 ) { - t=mp->lo_mem_max+1000; - } else { - t=mp->lo_mem_max+1+(mp->hi_mem_min-mp->lo_mem_max) / 2; - /* |lo_mem_max+2<=t<hi_mem_min| */ - } - if ( t>max_halfword ) t=max_halfword; - p=lmp_link(mp->rover); q=mp->lo_mem_max; rmp_link(p)=q; lmp_link(mp->rover)=q; - rmp_link(q)=mp->rover; lmp_link(q)=p; mp_link(q)=empty_flag; - node_size(q)=t-mp->lo_mem_max; - mp->lo_mem_max=t; mp_link(mp->lo_mem_max)=null; mp_info(mp->lo_mem_max)=null; - mp->rover=q; - goto RESTART; -} +@d mp_sym_info(A) get_mp_sym_info(mp,(A)) +@d set_mp_sym_info(A,B) do_set_mp_sym_info(mp,(A),(B)) -@ @<Try to allocate...@>= -q=p+node_size(p); /* find the physical successor */ -while ( is_empty(q) ) { /* merge node |p| with node |q| */ - t=rmp_link(q); tt=lmp_link(q); -@^inner loop@> - if ( q==mp->rover ) mp->rover=t; - lmp_link(t)=tt; rmp_link(tt)=t; - q=q+node_size(q); -} -r=q-s; -if ( r>p+1 ) { - @<Allocate from the top of node |p| and |goto found|@>; +@c +static void do_set_mp_sym_info (MP mp, mp_node p, halfword v) { + (void) mp; + assert (p->type == mp_symbol_node); + p->info = v; } -if ( r==p ) { - if ( rmp_link(p)!=p ) { - @<Allocate entire node |p| and |goto found|@>; - } +static halfword get_mp_sym_info (MP mp, mp_node p) { + (void) mp; + assert (p->type == mp_symbol_node); + return p->info; } -node_size(p)=q-p /* reset the size in case it grew */ -@ @<Allocate from the top...@>= -{ - node_size(p)=r-p; /* store the remaining size */ - mp->rover=p; /* start searching here next time */ - goto FOUND; -} +@ Similarly, so do these redirect to functions. -@ Here we delete node |p| from the ring, and let |rover| rove around. +@d mp_sym_sym(A) get_mp_sym_sym(mp,(A)) +@d set_mp_sym_sym(A,B) do_set_mp_sym_sym(mp,(A),(mp_sym)(B)) -@<Allocate entire...@>= -{ - mp->rover=rmp_link(p); t=lmp_link(p); - lmp_link(mp->rover)=t; rmp_link(t)=mp->rover; - goto FOUND; +@c +static void do_set_mp_sym_sym (MP mp, mp_node p, mp_sym v) { + mp_symbolic_node pp = (mp_symbolic_node) p; + (void) mp; + assert (pp->type == mp_symbol_node); + pp->sym = v; } - -@ Conversely, when some variable-size node |p| of size |s| is no longer needed, -the operation |free_node(p,s)| will make its words available, by inserting -|p| as a new empty node just before where |rover| now points. - -@<Internal library declarations@>= -void mp_free_node (MP mp, pointer p, halfword s) ; - -@ @c -void mp_free_node (MP mp, pointer p, halfword s) { /* variable-size node - liberation */ - pointer q; /* |lmp_link(rover)| */ - node_size(p)=s; mp_link(p)=empty_flag; -@^inner loop@> - q=lmp_link(mp->rover); lmp_link(p)=q; rmp_link(p)=mp->rover; /* set both links */ - lmp_link(mp->rover)=p; rmp_link(q)=p; /* insert |p| into the ring */ - mp->var_used-=s; /* maintain statistics */ +static mp_sym get_mp_sym_sym (MP mp, mp_node p) { + mp_symbolic_node pp = (mp_symbolic_node) p; + (void) mp; + assert (pp->type == mp_symbol_node); + return pp->sym; } -@* \[11] Memory layout. -Some areas of |mem| are dedicated to fixed usage, since static allocation is -more efficient than dynamic allocation when we can get away with it. For -example, locations |0| to |1| are always used to store a -two-word dummy token whose second word is zero. -The following macro definitions accomplish the static allocation by giving -symbolic names to the fixed positions. Static variable-size nodes appear -in locations |0| through |lo_mem_stat_max|, and static single-word nodes -appear in locations |hi_mem_stat_min| through |mem_top|, inclusive. +@ @<Declarations@>= +static void do_set_mp_sym_info (MP mp, mp_node A, halfword B); +static halfword get_mp_sym_info (MP mp, mp_node p); +static void do_set_mp_sym_sym (MP mp, mp_node A, mp_sym B); +static mp_sym get_mp_sym_sym (MP mp, mp_node p); -@d sentinel mp->mem_top /* end of sorted lists */ -@d temp_head (mp->mem_top-1) /* head of a temporary list of some kind */ -@d hold_head (mp->mem_top-2) /* head of a temporary list of another kind */ +@ Symbolic nodes also have |name_type|, which is a short enumeration -@(mpmp.h@>= -#define spec_head (mp->mem_top-3) /* head of a list of unprocessed \&{special} items */ -#define null_dash (2) /* the first two words are reserved for a null value */ -#define dep_head (null_dash+3) /* we will define |dash_node_size=3| */ -#define zero_val (dep_head+2) /* two words for a permanently zero value */ -#define temp_val (zero_val+2) /* two words for a temporary value node */ -#define end_attr temp_val /* we use |end_attr+2| only */ -#define inf_val (end_attr+2) /* and |inf_val+1| only */ -#define bad_vardef (inf_val+2) /* two words for \&{vardef} error recovery */ -#define lo_mem_stat_max (bad_vardef+1) /* largest statically - allocated word in the variable-size |mem| */ -#define hi_mem_stat_min (mp->mem_top-3) /* smallest statically allocated word in - the one-word |mem| */ - -@ The following code gets the dynamic part of |mem| off to a good start, -when \MP\ is initializing itself the slow way. - -@<Initialize table entries (done by \.{INIMP} only)@>= -mp->rover=lo_mem_stat_max+1; /* initialize the dynamic memory */ -mp_link(mp->rover)=empty_flag; -node_size(mp->rover)=1000; /* which is a 1000-word available node */ -lmp_link(mp->rover)=mp->rover; rmp_link(mp->rover)=mp->rover; -mp->lo_mem_max=mp->rover+1000; -mp_link(mp->lo_mem_max)=null; mp_info(mp->lo_mem_max)=null; -for (k=hi_mem_stat_min;k<=(int)mp->mem_top;k++) { - mp->mem[k]=mp->mem[mp->lo_mem_max]; /* clear list heads */ -} -mp->avail=null; mp->mem_end=mp->mem_top; -mp->hi_mem_min=hi_mem_stat_min; /* initialize the one-word memory */ -mp->var_used=lo_mem_stat_max+1; -mp->dyn_used=mp->mem_top+1-(hi_mem_stat_min); /* initialize statistics */ - -@ The procedure |flush_list(p)| frees an entire linked list of one-word -nodes that starts at a given position, until coming to |sentinel| or a -pointer that is not in the one-word region. Another procedure, -|flush_node_list|, frees an entire linked list of one-word and two-word -nodes, until coming to a |null| pointer. +@<Types...@>= +enum { + mp_normal_sym = 0, + mp_internal_sym, /* for values of internals */ + mp_macro_sym, /* for macro names */ + mp_expr_sym, /* for macro parameters if type |expr| */ + mp_suffix_sym, /* for macro parameters if type |suffix| */ + mp_text_sym, /* for macro parameters if type |text| */ +} mp_sym_name_types; + +@ The function |get_symbolic_node| returns a pointer to a new symbolic node whose +|link| field is null. @^inner loop@> -@c -static void mp_flush_list (MP mp,pointer p) { /* makes list of single-word nodes available */ - pointer q,r; /* list traversers */ - if ( p>=mp->hi_mem_min ) if ( p!=sentinel ) { - r=p; - do { - q=r; r=mp_link(r); - decr(mp->dyn_used); - if ( r<mp->hi_mem_min ) break; - } while (r!=sentinel); - /* now |q| is the last node on the list */ - mp_link(q)=mp->avail; mp->avail=p; - } -} -@# -static void mp_flush_node_list (MP mp,pointer p) { - pointer q; /* the node being recycled */ - while ( p!=null ){ - q=p; p=mp_link(p); - if ( q<mp->hi_mem_min ) - mp_free_node(mp, q,2); - else - free_avail(q); - } +@d symbolic_node_size sizeof(mp_symbolic_node_data) +@c +static mp_node mp_get_symbolic_node (MP mp) { + mp_symbolic_node p = xmalloc (1, symbolic_node_size); + add_var_used (symbolic_node_size); + memset (p, 0, symbolic_node_size); + p->type = mp_symbol_node; + p->name_type = mp_normal_sym; + FUNCTION_TRACE2 ("%p = mp_get_symbolic_node()\n", p); + return (mp_node) p; } -@ If \MP\ is extended improperly, the |mem| array might get screwed up. -For example, some pointers might be wrong, or some ``dead'' nodes might not -have been freed when the last reference to them disappeared. Procedures -|check_mem| and |search_mem| are available to help diagnose such -problems. These procedures make use of two arrays called |free| and -|was_free| that are present only if \MP's debugging routines have -been included. (You may want to decrease the size of |mem| while you -@^debugging@> -are debugging.) - -Because |boolean|s are typedef-d as ints, it is better to use -unsigned chars here. - -@<Glob...@>= -unsigned char *free; /* free cells */ -unsigned char *was_free; /* previously free cells */ -pointer was_mem_end; pointer was_lo_max; pointer was_hi_min; - /* previous |mem_end|, |lo_mem_max|,and |hi_mem_min| */ -boolean panicking; /* do we want to check memory constantly? */ -@ @<Allocate or initialize ...@>= -mp->free = xmalloc ((mp->mem_max+1),sizeof (unsigned char)); -mp->was_free = xmalloc ((mp->mem_max+1), sizeof (unsigned char)); - -@ @<Dealloc variables@>= -xfree(mp->free); -xfree(mp->was_free); +@ Conversely, when some node |p| of size |s| is no longer needed, +the operation |free_node(p,s)| will make its words available, by inserting +|p| as a new empty node just before where |rover| now points. -@ @<Allocate or ...@>= -mp->was_hi_min=mp->mem_max; -mp->panicking=false; +A symbolic node is recycled by calling |free_symbolic_node|. -@ @<Declarations@>= -static void mp_reallocate_memory(MP mp, int l) ; +@d mp_free_symbolic_node(mp, A) mp_free_node(mp, (A), symbolic_node_size) -@ @c -static void mp_reallocate_memory(MP mp, int l) { - XREALLOC(mp->free, l, unsigned char); - XREALLOC(mp->was_free, l, unsigned char); - if (mp->mem) { - int newarea = l-mp->mem_max; - XREALLOC(mp->mem, l, memory_word); - memset (mp->mem+(mp->mem_max+1),0,(size_t)(sizeof(memory_word)*((unsigned)newarea))); - } else { - XREALLOC(mp->mem, l, memory_word); - memset(mp->mem,0,(size_t)(sizeof(memory_word)*(unsigned)(l+1))); - } - mp->mem_max = l; - if (mp->ini_version) - mp->mem_top = l; +@c +void mp_free_node (MP mp, mp_node p, size_t siz) { /* node liberation */ + FUNCTION_TRACE3 ("mp_free_node(%p,%d)\n", p, siz); + mp->var_used -= siz; + xfree (p); /* do more later */ } +@ @<Internal library declarations@>= +void mp_free_node (MP mp, mp_node p, size_t siz); -@ Procedure |check_mem| makes sure that the available space lists of -|mem| are well formed, and it optionally prints out all locations -that are reserved now but were free the last time this procedure was called. - -@c -#if 0 -static void mp_check_mem (MP mp,boolean print_locs ) { - pointer p,q,r; /* current locations of interest in |mem| */ - boolean clobbered; /* is something amiss? */ - for (p=0;p<=mp->lo_mem_max;p++) { - mp->free[p]=false; /* you can probably do this faster */ - } - for (p=mp->hi_mem_min;p<= mp->mem_end;p++) { - mp->free[p]=false; /* ditto */ - } - @<Check single-word |avail| list@>; - @<Check variable-size |avail| list@>; - @<Check flags of unavailable nodes@>; - @<Check the list of linear dependencies@>; - if ( print_locs ) { - @<Print newly busy locations@>; - } - (void)memcpy(mp->was_free, mp->free, (size_t)(sizeof(char)*(unsigned)(mp->mem_end+1))); - mp->was_mem_end=mp->mem_end; - mp->was_lo_max=mp->lo_mem_max; - mp->was_hi_min=mp->hi_mem_min; -} -#endif - -@ @<Check single-word...@>= -p=mp->avail; q=null; clobbered=false; -while ( p!=null ) { - if ( (p>mp->mem_end)||(p<mp->hi_mem_min) ) clobbered=true; - else if ( mp->free[p] ) clobbered=true; - if ( clobbered ) { - mp_print_nl(mp, "AVAIL list clobbered at "); -@.AVAIL list clobbered...@> - mp_print_int(mp, q); break; - } - mp->free[p]=true; q=p; p=mp_link(q); -} - -@ @<Check variable-size...@>= -p=mp->rover; q=null; clobbered=false; -do { - if ( (p>=mp->lo_mem_max)||(p<0) ) clobbered=true; - else if ( (rmp_link(p)>=mp->lo_mem_max)||(rmp_link(p)<0) ) clobbered=true; - else if ( !(is_empty(p))||(node_size(p)<2)|| - (p+node_size(p)>mp->lo_mem_max)|| (lmp_link(rmp_link(p))!=p) ) clobbered=true; - if ( clobbered ) { - mp_print_nl(mp, "Double-AVAIL list clobbered at "); -@.Double-AVAIL list clobbered...@> - mp_print_int(mp, q); break; - } - for (q=p;q<=p+node_size(p)-1;q++) { /* mark all locations free */ - if ( mp->free[q] ) { - mp_print_nl(mp, "Doubly free location at "); -@.Doubly free location...@> - mp_print_int(mp, q); break; - } - mp->free[q]=true; - } - q=p; p=rmp_link(p); -} while (p!=mp->rover) +@ Same redirection trick as above +@d mp_info(A) get_mp_info(mp,(A)) +@d set_mp_info(A,B) do_set_mp_info(mp,(A),(B)) -@ @<Check flags...@>= -p=0; -while ( p<=mp->lo_mem_max ) { /* node |p| should not be empty */ - if ( is_empty(p) ) { - mp_print_nl(mp, "Bad flag at "); mp_print_int(mp, p); -@.Bad flag...@> - } - while ( (p<=mp->lo_mem_max) && ! mp->free[p] ) p++; - while ( (p<=mp->lo_mem_max) && mp->free[p] ) p++; +@c +static void do_set_mp_info (MP mp, mp_node p, halfword v) { + (void) mp; + p->info = v; } - -@ @<Print newly busy...@>= -{ - @<Do intialization required before printing new busy locations@>; - mp_print_nl(mp, "New busy locs:"); -@.New busy locs@> - for (p=0;p<= mp->lo_mem_max;p++ ) { - if ( ! mp->free[p] && ((p>mp->was_lo_max) || mp->was_free[p]) ) { - @<Indicate that |p| is a new busy location@>; - } - } - for (p=mp->hi_mem_min;p<=mp->mem_end;p++ ) { - if ( ! mp->free[p] && - ((p<mp->was_hi_min) || (p>mp->was_mem_end) || mp->was_free[p]) ) { - @<Indicate that |p| is a new busy location@>; - } - } - @<Finish printing new busy locations@>; +halfword get_mp_info (MP mp, mp_node p) { + (void) mp; + return p->info; } -@ There might be many new busy locations so we are careful to print contiguous -blocks compactly. During this operation |q| is the last new busy location and -|r| is the start of the block containing |q|. -@<Indicate that |p| is a new busy location@>= -{ - if ( p>q+1 ) { - if ( q>r ) { - mp_print(mp, ".."); mp_print_int(mp, q); - } - mp_print_char(mp, xord(' ')); mp_print_int(mp, p); - r=p; - } - q=p; -} +@ @<Declarations@>= +static halfword get_mp_info (MP mp, mp_node p); +static void do_set_mp_info (MP mp, mp_node p, halfword v); -@ @<Do intialization required before printing new busy locations@>= -q=mp->mem_max; r=mp->mem_max -@ @<Finish printing new busy locations@>= -if ( q>r ) { - mp_print(mp, ".."); mp_print_int(mp, q); -} +@* Memory layout. +Some nodes are created statically, since static allocation is +more efficient than dynamic allocation when we can get away with it. -@ The |search_mem| procedure attempts to answer the question ``Who points -to node~|p|?'' In doing so, it fetches |link| and |info| fields of |mem| -that might not be of type |two_halves|. Strictly speaking, this is -undefined, and it can lead to ``false drops'' (words that seem to -point to |p| purely by coincidence). But for debugging purposes, we want -to rule out the places that do {\sl not\/} point to |p|, so a few false -drops are tolerable. +@<Glob...@>= +mp_node null_dash; +mp_value_node dep_head; +mp_node inf_val; +mp_node zero_val; +mp_node temp_val; +mp_node end_attr; +mp_node bad_vardef; +mp_node temp_head; +mp_node hold_head; +mp_node spec_head; + +@ The following code gets the memory off to a good start. + +@<Initialize table entries@>= +mp->spec_head = mp_get_symbolic_node (mp); +mp->last_pending = mp->spec_head; +mp->temp_head = mp_get_symbolic_node (mp); +mp->hold_head = mp_get_symbolic_node (mp); + +@ @<Free table entries@>= +mp_free_symbolic_node (mp, mp->spec_head); +mp_free_symbolic_node (mp, mp->temp_head); +mp_free_symbolic_node (mp, mp->hold_head); + +@ The procedure |flush_node_list(p)| frees an entire linked list of +nodes that starts at a given position, until coming to a |NULL| pointer. +@^inner loop@> @c -#if 0 -void mp_search_mem (MP mp, pointer p) { /* look for pointers to |p| */ - integer q; /* current position being searched */ - for (q=0;q<=mp->lo_mem_max;q++) { - if ( mp_link(q)==p ){ - mp_print_nl(mp, "LINK("); mp_print_int(mp, q); mp_print_char(mp, xord(')')); - } - if ( mp_info(q)==p ) { - mp_print_nl(mp, "INFO("); mp_print_int(mp, q); mp_print_char(mp, xord(')')); - } - } - for (q=mp->hi_mem_min;q<=mp->mem_end;q++) { - if ( mp_link(q)==p ) { - mp_print_nl(mp, "LINK("); mp_print_int(mp, q); mp_print_char(mp, xord(')')); - } - if ( mp_info(q)==p ) { - mp_print_nl(mp, "INFO("); mp_print_int(mp, q); mp_print_char(mp, xord(')')); - } +static void mp_flush_node_list (MP mp, mp_node p) { + mp_node q; /* the node being recycled */ + FUNCTION_TRACE2 ("mp_flush_node_list(%p)\n", p); + while (p != NULL) { + q = p; + p = p->link; + if (q->type != mp_symbol_node) + mp_free_node (mp, q, token_node_size); + else + mp_free_symbolic_node (mp, q); } - @<Search |eqtb| for equivalents equal to |p|@>; } -#endif -@ Just before \.{INIMP} writes out the memory, it sorts the doubly linked -available space list. The list is probably very short at such times, so a -simple insertion sort is used. The smallest available location will be -pointed to by |rover|, the next-smallest by |rmp_link(rover)|, etc. -@<Internal library ...@>= -void mp_sort_avail (MP mp); - -@ @c -void mp_sort_avail (MP mp) { /* sorts the available variable-size nodes - by location */ - pointer p,q,r; /* indices into |mem| */ - pointer old_rover; /* initial |rover| setting */ - p=mp_get_node(mp, 010000000000); /* merge adjacent free areas */ - p=rmp_link(mp->rover); rmp_link(mp->rover)=max_halfword; old_rover=mp->rover; - while ( p!=old_rover ) { - @<Sort |p| into the list starting at |rover| - and advance |p| to |rmp_link(p)|@>; - } - p=mp->rover; - while ( rmp_link(p)!=max_halfword ) { - lmp_link(rmp_link(p))=p; p=rmp_link(p); - }; - rmp_link(p)=mp->rover; lmp_link(mp->rover)=p; -} - -@ The following |while| loop is guaranteed to -terminate, since the list that starts at -|rover| ends with |max_halfword| during the sorting procedure. - -@<Sort |p|...@>= -if ( p<mp->rover ) { - q=p; p=rmp_link(q); rmp_link(q)=mp->rover; mp->rover=q; -} else { - q=mp->rover; - while ( rmp_link(q)<p ) q=rmp_link(q); - r=rmp_link(p); rmp_link(p)=rmp_link(q); rmp_link(q)=p; p=r; -} - - -@* \[12] The command codes. +@* The command codes. Before we can go much further, we need to define symbolic names for the internal code numbers that represent the various commands obeyed by \MP. These codes are somewhat arbitrary, but not completely so. For example, @@ -4828,6 +3299,7 @@ At any rate, here is the list, for future reference. @d stop 84 /* end a job (\&{end}, \&{dump}), must be |end_group+1| */ @d max_command_code stop @d outer_tag (max_command_code+1) /* protection code added to command code */ +@d undefined_cs (max_command_code+2) /* protection code added to command code */ @<Types...@>= typedef int command_code; @@ -4841,101 +3313,233 @@ parts must have types between |transform_type| and |pair_type|; all other types must be smaller than |transform_type|; and among the types that are not unknown or vacuous, the smallest two must be |boolean_type| and |string_type| in that order. - + @d undefined 0 /* no type has been declared */ @d unknown_tag 1 /* this constant is added to certain type codes below */ @d unknown_types mp_unknown_boolean: case mp_unknown_string: case mp_unknown_pen: case mp_unknown_picture: case mp_unknown_path -@<Types...@>= -enum mp_variable_type { -mp_vacuous = 1, /* no expression was present */ -mp_boolean_type, /* \&{boolean} with a known value */ -mp_unknown_boolean, -mp_string_type, /* \&{string} with a known value */ -mp_unknown_string, -mp_pen_type, /* \&{pen} with a known value */ -mp_unknown_pen, -mp_path_type, /* \&{path} with a known value */ -mp_unknown_path, -mp_picture_type, /* \&{picture} with a known value */ -mp_unknown_picture, -mp_transform_type, /* \&{transform} variable or capsule */ -mp_color_type, /* \&{color} variable or capsule */ -mp_cmykcolor_type, /* \&{cmykcolor} variable or capsule */ -mp_pair_type, /* \&{pair} variable or capsule */ -mp_numeric_type, /* variable that has been declared \&{numeric} but not used */ -mp_known, /* \&{numeric} with a known value */ -mp_dependent, /* a linear combination with |fraction| coefficients */ -mp_proto_dependent, /* a linear combination with |scaled| coefficients */ -mp_independent, /* \&{numeric} with unknown value */ -mp_token_list, /* variable name or suffix argument or text argument */ -mp_structured, /* variable with subscripts and attributes */ -mp_unsuffixed_macro, /* variable defined with \&{vardef} but no \.{\AT!\#} */ -mp_suffixed_macro /* variable defined with \&{vardef} and \.{\AT!\#} */ -} ; +@<Enumeration types@>= +typedef enum { + mp_vacuous = 1, /* no expression was present */ + mp_boolean_type, /* \&{boolean} with a known value */ + mp_unknown_boolean, + mp_string_type, /* \&{string} with a known value */ + mp_unknown_string, + mp_pen_type, /* \&{pen} with a known value */ + mp_unknown_pen, + mp_path_type, /* \&{path} with a known value */ + mp_unknown_path, + mp_picture_type, /* \&{picture} with a known value */ + mp_unknown_picture, + mp_transform_type, /* \&{transform} variable or capsule */ + mp_color_type, /* \&{color} variable or capsule */ + mp_cmykcolor_type, /* \&{cmykcolor} variable or capsule */ + mp_pair_type, /* \&{pair} variable or capsule */ + mp_numeric_type, /* variable that has been declared \&{numeric} but not used */ + mp_known, /* \&{numeric} with a known value */ + mp_dependent, /* a linear combination with |fraction| coefficients */ + mp_proto_dependent, /* a linear combination with |scaled| coefficients */ + mp_independent, /* \&{numeric} with unknown value */ + mp_token_list, /* variable name or suffix argument or text argument */ + mp_structured, /* variable with subscripts and attributes */ + mp_unsuffixed_macro, /* variable defined with \&{vardef} but no \.{\AT!\#} */ + mp_suffixed_macro, /* variable defined with \&{vardef} and \.{\AT!\#} */ +/* here are some generic node types */ + mp_symbol_node, + mp_token_node_type, + mp_value_node_type, + mp_attr_node_type, + mp_subscr_node_type, + mp_pair_node_type, + mp_transform_node_type, + mp_color_node_type, + mp_cmykcolor_node_type, +/* it is important that the next 7 items remain in this order, for export */ + mp_fill_node_type, + mp_stroked_node_type, + mp_text_node_type, + mp_start_clip_node_type, + mp_start_bounds_node_type, + mp_stop_clip_node_type, + mp_stop_bounds_node_type, + mp_dash_node_type, + mp_dep_node_type, + mp_if_node_type, + mp_edge_header_node_type, +} mp_variable_type; @ @<Declarations@>= -static void mp_print_type (MP mp,quarterword t) ; +static void mp_print_type (MP mp, quarterword t); @ @<Basic printing procedures@>= -void mp_print_type (MP mp,quarterword t) { +static const char *mp_type_string (quarterword t) { + const char *s = NULL; switch (t) { - case mp_vacuous:mp_print(mp, "vacuous"); break; - case mp_boolean_type:mp_print(mp, "boolean"); break; - case mp_unknown_boolean:mp_print(mp, "unknown boolean"); break; - case mp_string_type:mp_print(mp, "string"); break; - case mp_unknown_string:mp_print(mp, "unknown string"); break; - case mp_pen_type:mp_print(mp, "pen"); break; - case mp_unknown_pen:mp_print(mp, "unknown pen"); break; - case mp_path_type:mp_print(mp, "path"); break; - case mp_unknown_path:mp_print(mp, "unknown path"); break; - case mp_picture_type:mp_print(mp, "picture"); break; - case mp_unknown_picture:mp_print(mp, "unknown picture"); break; - case mp_transform_type:mp_print(mp, "transform"); break; - case mp_color_type:mp_print(mp, "color"); break; - case mp_cmykcolor_type:mp_print(mp, "cmykcolor"); break; - case mp_pair_type:mp_print(mp, "pair"); break; - case mp_known:mp_print(mp, "known numeric"); break; - case mp_dependent:mp_print(mp, "dependent"); break; - case mp_proto_dependent:mp_print(mp, "proto-dependent"); break; - case mp_numeric_type:mp_print(mp, "numeric"); break; - case mp_independent:mp_print(mp, "independent"); break; - case mp_token_list:mp_print(mp, "token list"); break; - case mp_structured:mp_print(mp, "mp_structured"); break; - case mp_unsuffixed_macro:mp_print(mp, "unsuffixed macro"); break; - case mp_suffixed_macro:mp_print(mp, "suffixed macro"); break; - default: mp_print(mp, "undefined"); break; - } -} - -@ Values inside \MP\ are stored in two-word nodes that have a |name_type| + case undefined: + s = "undefined"; + break; + case mp_vacuous: + s = "vacuous"; + break; + case mp_boolean_type: + s = "boolean"; + break; + case mp_unknown_boolean: + s = "unknown boolean"; + break; + case mp_string_type: + s = "string"; + break; + case mp_unknown_string: + s = "unknown string"; + break; + case mp_pen_type: + s = "pen"; + break; + case mp_unknown_pen: + s = "unknown pen"; + break; + case mp_path_type: + s = "path"; + break; + case mp_unknown_path: + s = "unknown path"; + break; + case mp_picture_type: + s = "picture"; + break; + case mp_unknown_picture: + s = "unknown picture"; + break; + case mp_transform_type: + s = "transform"; + break; + case mp_color_type: + s = "color"; + break; + case mp_cmykcolor_type: + s = "cmykcolor"; + break; + case mp_pair_type: + s = "pair"; + break; + case mp_known: + s = "known numeric"; + break; + case mp_dependent: + s = "dependent"; + break; + case mp_proto_dependent: + s = "proto-dependent"; + break; + case mp_numeric_type: + s = "numeric"; + break; + case mp_independent: + s = "independent"; + break; + case mp_token_list: + s = "token list"; + break; + case mp_structured: + s = "mp_structured"; + break; + case mp_unsuffixed_macro: + s = "unsuffixed macro"; + break; + case mp_suffixed_macro: + s = "suffixed macro"; + break; + case mp_symbol_node: + s = "symbol node"; + break; + case mp_token_node_type: + s = "token node"; + break; + case mp_value_node_type: + s = "value node"; + break; + case mp_attr_node_type: + s = "attribute node"; + break; + case mp_subscr_node_type: + s = "subscript node"; + break; + case mp_fill_node_type: + s = "fill node"; + break; + case mp_stroked_node_type: + s = "stroked node"; + break; + case mp_text_node_type: + s = "text node"; + break; + case mp_start_clip_node_type: + s = "start clip node"; + break; + case mp_start_bounds_node_type: + s = "start bounds node"; + break; + case mp_stop_clip_node_type: + s = "stop clip node"; + break; + case mp_stop_bounds_node_type: + s = "stop bounds node"; + break; + case mp_dash_node_type: + s = "dash node"; + break; + case mp_dep_node_type: + s = "dependency node"; + break; + case mp_if_node_type: + s = "if node"; + break; + case mp_edge_header_node_type: + s = "edge header node"; + break; + default: + assert (0); + break; + } + return s; +} +void mp_print_type (MP mp, quarterword t) { + if (t >= 0 && t <= mp_edge_header_node_type) + mp_print (mp, mp_type_string (t)); + else + mp_print (mp, "unknown"); +} + + +@ Values inside \MP\ are stored in non-symbolic nodes that have a |name_type| as well as a |type|. The possibilities for |name_type| are defined here; they will be explained in more detail later. @<Types...@>= enum mp_name_types { - mp_root=0, /* |name_type| at the top level of a variable */ - mp_saved_root, /* same, when the variable has been saved */ - mp_structured_root, /* |name_type| where a |mp_structured| branch occurs */ - mp_subscr, /* |name_type| in a subscript node */ - mp_attr, /* |name_type| in an attribute node */ - mp_x_part_sector, /* |name_type| in the \&{xpart} of a node */ - mp_y_part_sector, /* |name_type| in the \&{ypart} of a node */ - mp_xx_part_sector, /* |name_type| in the \&{xxpart} of a node */ - mp_xy_part_sector, /* |name_type| in the \&{xypart} of a node */ - mp_yx_part_sector, /* |name_type| in the \&{yxpart} of a node */ - mp_yy_part_sector, /* |name_type| in the \&{yypart} of a node */ - mp_red_part_sector, /* |name_type| in the \&{redpart} of a node */ - mp_green_part_sector, /* |name_type| in the \&{greenpart} of a node */ - mp_blue_part_sector, /* |name_type| in the \&{bluepart} of a node */ - mp_cyan_part_sector, /* |name_type| in the \&{redpart} of a node */ - mp_magenta_part_sector, /* |name_type| in the \&{greenpart} of a node */ - mp_yellow_part_sector, /* |name_type| in the \&{bluepart} of a node */ - mp_black_part_sector, /* |name_type| in the \&{greenpart} of a node */ - mp_grey_part_sector, /* |name_type| in the \&{bluepart} of a node */ - mp_capsule, /* |name_type| in stashed-away subexpressions */ - mp_token /* |name_type| in a numeric token or string token */ + mp_root = 0, /* |name_type| at the top level of a variable */ + mp_saved_root, /* same, when the variable has been saved */ + mp_structured_root, /* |name_type| where a |mp_structured| branch occurs */ + mp_subscr, /* |name_type| in a subscript node */ + mp_attr, /* |name_type| in an attribute node */ + mp_x_part_sector, /* |name_type| in the \&{xpart} of a node */ + mp_y_part_sector, /* |name_type| in the \&{ypart} of a node */ + mp_xx_part_sector, /* |name_type| in the \&{xxpart} of a node */ + mp_xy_part_sector, /* |name_type| in the \&{xypart} of a node */ + mp_yx_part_sector, /* |name_type| in the \&{yxpart} of a node */ + mp_yy_part_sector, /* |name_type| in the \&{yypart} of a node */ + mp_red_part_sector, /* |name_type| in the \&{redpart} of a node */ + mp_green_part_sector, /* |name_type| in the \&{greenpart} of a node */ + mp_blue_part_sector, /* |name_type| in the \&{bluepart} of a node */ + mp_cyan_part_sector, /* |name_type| in the \&{redpart} of a node */ + mp_magenta_part_sector, /* |name_type| in the \&{greenpart} of a node */ + mp_yellow_part_sector, /* |name_type| in the \&{bluepart} of a node */ + mp_black_part_sector, /* |name_type| in the \&{greenpart} of a node */ + mp_grey_part_sector, /* |name_type| in the \&{bluepart} of a node */ + mp_capsule, /* |name_type| in stashed-away subexpressions */ + mp_token /* |name_type| in a numeric token or string token */ }; @ Primitive operations that produce values have a secondary identification @@ -5054,206 +3658,419 @@ values they test for. @d envelope_of 130 /* operation code for \.{envelope} */ @d glyph_infont 131 /* operation code for \.{glyph} */ -@c static void mp_print_op (MP mp,quarterword c) { - if (c<=mp_numeric_type ) { - mp_print_type(mp, c); +@c +static void mp_print_op (MP mp, quarterword c) { + if (c <= mp_numeric_type) { + mp_print_type (mp, c); } else { switch (c) { - case true_code:mp_print(mp, "true"); break; - case false_code:mp_print(mp, "false"); break; - case null_picture_code:mp_print(mp, "nullpicture"); break; - case null_pen_code:mp_print(mp, "nullpen"); break; - case read_string_op:mp_print(mp, "readstring"); break; - case pen_circle:mp_print(mp, "pencircle"); break; - case normal_deviate:mp_print(mp, "normaldeviate"); break; - case read_from_op:mp_print(mp, "readfrom"); break; - case close_from_op:mp_print(mp, "closefrom"); break; - case odd_op:mp_print(mp, "odd"); break; - case known_op:mp_print(mp, "known"); break; - case unknown_op:mp_print(mp, "unknown"); break; - case not_op:mp_print(mp, "not"); break; - case decimal:mp_print(mp, "decimal"); break; - case reverse:mp_print(mp, "reverse"); break; - case make_path_op:mp_print(mp, "makepath"); break; - case make_pen_op:mp_print(mp, "makepen"); break; - case oct_op:mp_print(mp, "oct"); break; - case hex_op:mp_print(mp, "hex"); break; - case ASCII_op:mp_print(mp, "ASCII"); break; - case char_op:mp_print(mp, "char"); break; - case length_op:mp_print(mp, "length"); break; - case turning_op:mp_print(mp, "turningnumber"); break; - case x_part:mp_print(mp, "xpart"); break; - case y_part:mp_print(mp, "ypart"); break; - case xx_part:mp_print(mp, "xxpart"); break; - case xy_part:mp_print(mp, "xypart"); break; - case yx_part:mp_print(mp, "yxpart"); break; - case yy_part:mp_print(mp, "yypart"); break; - case red_part:mp_print(mp, "redpart"); break; - case green_part:mp_print(mp, "greenpart"); break; - case blue_part:mp_print(mp, "bluepart"); break; - case cyan_part:mp_print(mp, "cyanpart"); break; - case magenta_part:mp_print(mp, "magentapart"); break; - case yellow_part:mp_print(mp, "yellowpart"); break; - case black_part:mp_print(mp, "blackpart"); break; - case grey_part:mp_print(mp, "greypart"); break; - case color_model_part:mp_print(mp, "colormodel"); break; - case font_part:mp_print(mp, "fontpart"); break; - case text_part:mp_print(mp, "textpart"); break; - case path_part:mp_print(mp, "pathpart"); break; - case pen_part:mp_print(mp, "penpart"); break; - case dash_part:mp_print(mp, "dashpart"); break; - case sqrt_op:mp_print(mp, "sqrt"); break; - case mp_m_exp_op:mp_print(mp, "mexp"); break; - case mp_m_log_op:mp_print(mp, "mlog"); break; - case sin_d_op:mp_print(mp, "sind"); break; - case cos_d_op:mp_print(mp, "cosd"); break; - case floor_op:mp_print(mp, "floor"); break; - case uniform_deviate:mp_print(mp, "uniformdeviate"); break; - case char_exists_op:mp_print(mp, "charexists"); break; - case font_size:mp_print(mp, "fontsize"); break; - case ll_corner_op:mp_print(mp, "llcorner"); break; - case lr_corner_op:mp_print(mp, "lrcorner"); break; - case ul_corner_op:mp_print(mp, "ulcorner"); break; - case ur_corner_op:mp_print(mp, "urcorner"); break; - case arc_length:mp_print(mp, "arclength"); break; - case angle_op:mp_print(mp, "angle"); break; - case cycle_op:mp_print(mp, "cycle"); break; - case filled_op:mp_print(mp, "filled"); break; - case stroked_op:mp_print(mp, "stroked"); break; - case textual_op:mp_print(mp, "textual"); break; - case clipped_op:mp_print(mp, "clipped"); break; - case bounded_op:mp_print(mp, "bounded"); break; - case plus:mp_print_char(mp, xord('+')); break; - case minus:mp_print_char(mp, xord('-')); break; - case times:mp_print_char(mp, xord('*')); break; - case over:mp_print_char(mp, xord('/')); break; - case pythag_add:mp_print(mp, "++"); break; - case pythag_sub:mp_print(mp, "+-+"); break; - case or_op:mp_print(mp, "or"); break; - case and_op:mp_print(mp, "and"); break; - case less_than:mp_print_char(mp, xord('<')); break; - case less_or_equal:mp_print(mp, "<="); break; - case greater_than:mp_print_char(mp, xord('>')); break; - case greater_or_equal:mp_print(mp, ">="); break; - case equal_to:mp_print_char(mp, xord('=')); break; - case unequal_to:mp_print(mp, "<>"); break; - case concatenate:mp_print(mp, "&"); break; - case rotated_by:mp_print(mp, "rotated"); break; - case slanted_by:mp_print(mp, "slanted"); break; - case scaled_by:mp_print(mp, "scaled"); break; - case shifted_by:mp_print(mp, "shifted"); break; - case transformed_by:mp_print(mp, "transformed"); break; - case x_scaled:mp_print(mp, "xscaled"); break; - case y_scaled:mp_print(mp, "yscaled"); break; - case z_scaled:mp_print(mp, "zscaled"); break; - case in_font:mp_print(mp, "infont"); break; - case intersect:mp_print(mp, "intersectiontimes"); break; - case substring_of:mp_print(mp, "substring"); break; - case subpath_of:mp_print(mp, "subpath"); break; - case direction_time_of:mp_print(mp, "directiontime"); break; - case point_of:mp_print(mp, "point"); break; - case precontrol_of:mp_print(mp, "precontrol"); break; - case postcontrol_of:mp_print(mp, "postcontrol"); break; - case pen_offset_of:mp_print(mp, "penoffset"); break; - case arc_time_of:mp_print(mp, "arctime"); break; - case mp_version:mp_print(mp, "mpversion"); break; - case envelope_of:mp_print(mp, "envelope"); break; - case glyph_infont:mp_print(mp, "glyph"); break; - default: mp_print(mp, ".."); break; + case true_code: + mp_print (mp, "true"); + break; + case false_code: + mp_print (mp, "false"); + break; + case null_picture_code: + mp_print (mp, "nullpicture"); + break; + case null_pen_code: + mp_print (mp, "nullpen"); + break; + case read_string_op: + mp_print (mp, "readstring"); + break; + case pen_circle: + mp_print (mp, "pencircle"); + break; + case normal_deviate: + mp_print (mp, "normaldeviate"); + break; + case read_from_op: + mp_print (mp, "readfrom"); + break; + case close_from_op: + mp_print (mp, "closefrom"); + break; + case odd_op: + mp_print (mp, "odd"); + break; + case known_op: + mp_print (mp, "known"); + break; + case unknown_op: + mp_print (mp, "unknown"); + break; + case not_op: + mp_print (mp, "not"); + break; + case decimal: + mp_print (mp, "decimal"); + break; + case reverse: + mp_print (mp, "reverse"); + break; + case make_path_op: + mp_print (mp, "makepath"); + break; + case make_pen_op: + mp_print (mp, "makepen"); + break; + case oct_op: + mp_print (mp, "oct"); + break; + case hex_op: + mp_print (mp, "hex"); + break; + case ASCII_op: + mp_print (mp, "ASCII"); + break; + case char_op: + mp_print (mp, "char"); + break; + case length_op: + mp_print (mp, "length"); + break; + case turning_op: + mp_print (mp, "turningnumber"); + break; + case x_part: + mp_print (mp, "xpart"); + break; + case y_part: + mp_print (mp, "ypart"); + break; + case xx_part: + mp_print (mp, "xxpart"); + break; + case xy_part: + mp_print (mp, "xypart"); + break; + case yx_part: + mp_print (mp, "yxpart"); + break; + case yy_part: + mp_print (mp, "yypart"); + break; + case red_part: + mp_print (mp, "redpart"); + break; + case green_part: + mp_print (mp, "greenpart"); + break; + case blue_part: + mp_print (mp, "bluepart"); + break; + case cyan_part: + mp_print (mp, "cyanpart"); + break; + case magenta_part: + mp_print (mp, "magentapart"); + break; + case yellow_part: + mp_print (mp, "yellowpart"); + break; + case black_part: + mp_print (mp, "blackpart"); + break; + case grey_part: + mp_print (mp, "greypart"); + break; + case color_model_part: + mp_print (mp, "colormodel"); + break; + case font_part: + mp_print (mp, "fontpart"); + break; + case text_part: + mp_print (mp, "textpart"); + break; + case path_part: + mp_print (mp, "pathpart"); + break; + case pen_part: + mp_print (mp, "penpart"); + break; + case dash_part: + mp_print (mp, "dashpart"); + break; + case sqrt_op: + mp_print (mp, "sqrt"); + break; + case mp_m_exp_op: + mp_print (mp, "mexp"); + break; + case mp_m_log_op: + mp_print (mp, "mlog"); + break; + case sin_d_op: + mp_print (mp, "sind"); + break; + case cos_d_op: + mp_print (mp, "cosd"); + break; + case floor_op: + mp_print (mp, "floor"); + break; + case uniform_deviate: + mp_print (mp, "uniformdeviate"); + break; + case char_exists_op: + mp_print (mp, "charexists"); + break; + case font_size: + mp_print (mp, "fontsize"); + break; + case ll_corner_op: + mp_print (mp, "llcorner"); + break; + case lr_corner_op: + mp_print (mp, "lrcorner"); + break; + case ul_corner_op: + mp_print (mp, "ulcorner"); + break; + case ur_corner_op: + mp_print (mp, "urcorner"); + break; + case arc_length: + mp_print (mp, "arclength"); + break; + case angle_op: + mp_print (mp, "angle"); + break; + case cycle_op: + mp_print (mp, "cycle"); + break; + case filled_op: + mp_print (mp, "filled"); + break; + case stroked_op: + mp_print (mp, "stroked"); + break; + case textual_op: + mp_print (mp, "textual"); + break; + case clipped_op: + mp_print (mp, "clipped"); + break; + case bounded_op: + mp_print (mp, "bounded"); + break; + case plus: + mp_print_char (mp, xord ('+')); + break; + case minus: + mp_print_char (mp, xord ('-')); + break; + case times: + mp_print_char (mp, xord ('*')); + break; + case over: + mp_print_char (mp, xord ('/')); + break; + case pythag_add: + mp_print (mp, "++"); + break; + case pythag_sub: + mp_print (mp, "+-+"); + break; + case or_op: + mp_print (mp, "or"); + break; + case and_op: + mp_print (mp, "and"); + break; + case less_than: + mp_print_char (mp, xord ('<')); + break; + case less_or_equal: + mp_print (mp, "<="); + break; + case greater_than: + mp_print_char (mp, xord ('>')); + break; + case greater_or_equal: + mp_print (mp, ">="); + break; + case equal_to: + mp_print_char (mp, xord ('=')); + break; + case unequal_to: + mp_print (mp, "<>"); + break; + case concatenate: + mp_print (mp, "&"); + break; + case rotated_by: + mp_print (mp, "rotated"); + break; + case slanted_by: + mp_print (mp, "slanted"); + break; + case scaled_by: + mp_print (mp, "scaled"); + break; + case shifted_by: + mp_print (mp, "shifted"); + break; + case transformed_by: + mp_print (mp, "transformed"); + break; + case x_scaled: + mp_print (mp, "xscaled"); + break; + case y_scaled: + mp_print (mp, "yscaled"); + break; + case z_scaled: + mp_print (mp, "zscaled"); + break; + case in_font: + mp_print (mp, "infont"); + break; + case intersect: + mp_print (mp, "intersectiontimes"); + break; + case substring_of: + mp_print (mp, "substring"); + break; + case subpath_of: + mp_print (mp, "subpath"); + break; + case direction_time_of: + mp_print (mp, "directiontime"); + break; + case point_of: + mp_print (mp, "point"); + break; + case precontrol_of: + mp_print (mp, "precontrol"); + break; + case postcontrol_of: + mp_print (mp, "postcontrol"); + break; + case pen_offset_of: + mp_print (mp, "penoffset"); + break; + case arc_time_of: + mp_print (mp, "arctime"); + break; + case mp_version: + mp_print (mp, "mpversion"); + break; + case envelope_of: + mp_print (mp, "envelope"); + break; + case glyph_infont: + mp_print (mp, "glyph"); + break; + default: + mp_print (mp, ".."); + break; } } } + @ \MP\ also has a bunch of internal parameters that a user might want to fuss with. Every such parameter has an identifying code number, defined here. @<Types...@>= enum mp_given_internal { - mp_output_template=1, /* a string set up by \&{outputtemplate} */ - mp_output_format, /* the output format set up by \&{outputformat} */ - mp_job_name, /* the perceived jobname, as set up from the options stucture, - the name of the input file, or by \&{jobname} */ - mp_tracing_titles, /* show titles online when they appear */ - mp_tracing_equations, /* show each variable when it becomes known */ - mp_tracing_capsules, /* show capsules too */ - mp_tracing_choices, /* show the control points chosen for paths */ - mp_tracing_specs, /* show path subdivision prior to filling with polygonal a pen */ - mp_tracing_commands, /* show commands and operations before they are performed */ - mp_tracing_restores, /* show when a variable or internal is restored */ - mp_tracing_macros, /* show macros before they are expanded */ - mp_tracing_output, /* show digitized edges as they are output */ - mp_tracing_stats, /* show memory usage at end of job */ - mp_tracing_lost_chars, /* show characters that aren't \&{infont} */ - mp_tracing_online, /* show long diagnostics on terminal and in the log file */ - mp_year, /* the current year (e.g., 1984) */ - mp_month, /* the current month (e.g., 3 $\equiv$ March) */ - mp_day, /* the current day of the month */ - mp_time, /* the number of minutes past midnight when this job started */ - mp_hour, /* the number of hours past midnight when this job started */ - mp_minute, /* the number of minutes in that hour when this job started */ - mp_char_code, /* the number of the next character to be output */ - mp_char_ext, /* the extension code of the next character to be output */ - mp_char_wd, /* the width of the next character to be output */ - mp_char_ht, /* the height of the next character to be output */ - mp_char_dp, /* the depth of the next character to be output */ - mp_char_ic, /* the italic correction of the next character to be output */ - mp_design_size, /* the unit of measure used for |mp_char_wd..mp_char_ic|, in points */ - mp_pausing, /* positive to display lines on the terminal before they are read */ - mp_showstopping, /* positive to stop after each \&{show} command */ - mp_fontmaking, /* positive if font metric output is to be produced */ - mp_linejoin, /* as in \ps: 0 for mitered, 1 for round, 2 for beveled */ - mp_linecap, /* as in \ps: 0 for butt, 1 for round, 2 for square */ - mp_miterlimit, /* controls miter length as in \ps */ - mp_warning_check, /* controls error message when variable value is large */ - mp_boundary_char, /* the right boundary character for ligatures */ - mp_prologues, /* positive to output conforming PostScript using built-in fonts */ - mp_true_corners, /* positive to make \&{llcorner} etc. ignore \&{setbounds} */ - mp_default_color_model, /* the default color model for unspecified items */ + mp_output_template = 1, /* a string set up by \&{outputtemplate} */ + mp_output_format, /* the output format set up by \&{outputformat} */ + mp_job_name, /* the perceived jobname, as set up from the options stucture, + the name of the input file, or by \&{jobname} */ + mp_tracing_titles, /* show titles online when they appear */ + mp_tracing_equations, /* show each variable when it becomes known */ + mp_tracing_capsules, /* show capsules too */ + mp_tracing_choices, /* show the control points chosen for paths */ + mp_tracing_specs, /* show path subdivision prior to filling with polygonal a pen */ + mp_tracing_commands, /* show commands and operations before they are performed */ + mp_tracing_restores, /* show when a variable or internal is restored */ + mp_tracing_macros, /* show macros before they are expanded */ + mp_tracing_output, /* show digitized edges as they are output */ + mp_tracing_stats, /* show memory usage at end of job */ + mp_tracing_lost_chars, /* show characters that aren't \&{infont} */ + mp_tracing_online, /* show long diagnostics on terminal and in the log file */ + mp_year, /* the current year (e.g., 1984) */ + mp_month, /* the current month (e.g., 3 $\equiv$ March) */ + mp_day, /* the current day of the month */ + mp_time, /* the number of minutes past midnight when this job started */ + mp_hour, /* the number of hours past midnight when this job started */ + mp_minute, /* the number of minutes in that hour when this job started */ + mp_char_code, /* the number of the next character to be output */ + mp_char_ext, /* the extension code of the next character to be output */ + mp_char_wd, /* the width of the next character to be output */ + mp_char_ht, /* the height of the next character to be output */ + mp_char_dp, /* the depth of the next character to be output */ + mp_char_ic, /* the italic correction of the next character to be output */ + mp_design_size, /* the unit of measure used for |mp_char_wd..mp_char_ic|, in points */ + mp_pausing, /* positive to display lines on the terminal before they are read */ + mp_showstopping, /* positive to stop after each \&{show} command */ + mp_fontmaking, /* positive if font metric output is to be produced */ + mp_linejoin, /* as in \ps: 0 for mitered, 1 for round, 2 for beveled */ + mp_linecap, /* as in \ps: 0 for butt, 1 for round, 2 for square */ + mp_miterlimit, /* controls miter length as in \ps */ + mp_warning_check, /* controls error message when variable value is large */ + mp_boundary_char, /* the right boundary character for ligatures */ + mp_prologues, /* positive to output conforming PostScript using built-in fonts */ + mp_true_corners, /* positive to make \&{llcorner} etc. ignore \&{setbounds} */ + mp_default_color_model, /* the default color model for unspecified items */ mp_restore_clip_color, - mp_procset, /* wether or not create PostScript command shortcuts */ - mp_gtroffmode /* whether the user specified |-troff| on the command line */ + mp_procset, /* wether or not create PostScript command shortcuts */ + mp_gtroffmode /* whether the user specified |-troff| on the command line */ }; +typedef struct { + mp_value v; + char *intname; +} mp_internal; + + +@ @(mpmp.h@>= +#define internal_value(A) mp->internal[(A)].v.data.val +#define internal_string(A) mp->internal[(A)].v.data.str +#define internal_name(A) mp->internal[(A)].intname +#define internal_type(A) mp->internal[(A)].v.type @ @d max_given_internal mp_gtroffmode @<Glob...@>= -scaled *internal; /* the values of internal quantities */ -int *int_type; /* their types */ -char **int_name; /* their names */ -int int_ptr; /* the maximum internal quantity defined so far */ -int max_internal; /* current maximum number of internal quantities */ +mp_internal *internal; /* the values of internal quantities */ +int int_ptr; /* the maximum internal quantity defined so far */ +int max_internal; /* current maximum number of internal quantities */ @ @<Option variables@>= -int troff_mode; +int troff_mode; @ @<Allocate or initialize ...@>= -mp->max_internal=2*max_given_internal; -mp->internal = xmalloc ((mp->max_internal+1), sizeof(scaled)); -memset(mp->internal,0,(size_t)(mp->max_internal+1)* sizeof(scaled)); -mp->int_name = xmalloc ((mp->max_internal+1), sizeof(char *)); -memset(mp->int_name,0,(size_t)(mp->max_internal+1) * sizeof(char *)); -mp->int_type = xmalloc ((mp->max_internal+1), sizeof(int)); -memset(mp->int_type,0,(size_t)(mp->max_internal+1) * sizeof(int)); +mp->max_internal = 2 * max_given_internal; +mp->internal = xmalloc ((mp->max_internal + 1), sizeof (mp_internal)); +memset (mp->internal, 0, + (size_t) (mp->max_internal + 1) * sizeof (mp_internal)); { int i; - for (i=1;i<=max_given_internal;i++) - mp->int_type[i]=mp_known; + for (i = 1; i <= max_given_internal; i++) + internal_type (i) = mp_known; } -mp->int_type[mp_output_format]=mp_string_type; -mp->int_type[mp_output_template]=mp_string_type; -mp->int_type[mp_job_name]=mp_string_type; -mp->troff_mode=(opt->troff_mode>0 ? true : false); +internal_type (mp_output_format) = mp_string_type; +internal_type (mp_output_template) = mp_string_type; +internal_type (mp_job_name) = mp_string_type; +mp->troff_mode = (opt->troff_mode > 0 ? true : false); @ @<Exported function ...@>= -int mp_troff_mode(MP mp); +int mp_troff_mode (MP mp); @ @c -int mp_troff_mode(MP mp) { return mp->troff_mode; } +int mp_troff_mode (MP mp) { + return mp->troff_mode; +} + @ @<Set initial ...@>= -mp->int_ptr=max_given_internal; +mp->int_ptr = max_given_internal; @ The symbolic names for internal quantities are put into \MP's hash table by using a routine called |primitive|, which will be defined later. Let us @@ -5261,90 +4078,92 @@ enter them now, so that we don't have to list all those names again anywhere else. @<Put each of \MP's primitives into the hash table@>= -mp_primitive(mp, "tracingtitles",internal_quantity,mp_tracing_titles); -@:tracingtitles_}{\&{tracingtitles} primitive@> -mp_primitive(mp, "tracingequations",internal_quantity,mp_tracing_equations); -@:mp_tracing_equations_}{\&{tracingequations} primitive@> -mp_primitive(mp, "tracingcapsules",internal_quantity,mp_tracing_capsules); -@:mp_tracing_capsules_}{\&{tracingcapsules} primitive@> -mp_primitive(mp, "tracingchoices",internal_quantity,mp_tracing_choices); -@:mp_tracing_choices_}{\&{tracingchoices} primitive@> -mp_primitive(mp, "tracingspecs",internal_quantity,mp_tracing_specs); -@:mp_tracing_specs_}{\&{tracingspecs} primitive@> -mp_primitive(mp, "tracingcommands",internal_quantity,mp_tracing_commands); -@:mp_tracing_commands_}{\&{tracingcommands} primitive@> -mp_primitive(mp, "tracingrestores",internal_quantity,mp_tracing_restores); -@:mp_tracing_restores_}{\&{tracingrestores} primitive@> -mp_primitive(mp, "tracingmacros",internal_quantity,mp_tracing_macros); -@:mp_tracing_macros_}{\&{tracingmacros} primitive@> -mp_primitive(mp, "tracingoutput",internal_quantity,mp_tracing_output); -@:mp_tracing_output_}{\&{tracingoutput} primitive@> -mp_primitive(mp, "tracingstats",internal_quantity,mp_tracing_stats); -@:mp_tracing_stats_}{\&{tracingstats} primitive@> -mp_primitive(mp, "tracinglostchars",internal_quantity,mp_tracing_lost_chars); -@:mp_tracing_lost_chars_}{\&{tracinglostchars} primitive@> -mp_primitive(mp, "tracingonline",internal_quantity,mp_tracing_online); -@:mp_tracing_online_}{\&{tracingonline} primitive@> -mp_primitive(mp, "year",internal_quantity,mp_year); -@:mp_year_}{\&{year} primitive@> -mp_primitive(mp, "month",internal_quantity,mp_month); -@:mp_month_}{\&{month} primitive@> -mp_primitive(mp, "day",internal_quantity,mp_day); -@:mp_day_}{\&{day} primitive@> -mp_primitive(mp, "time",internal_quantity,mp_time); -@:time_}{\&{time} primitive@> -mp_primitive(mp, "hour",internal_quantity,mp_hour); -@:hour_}{\&{hour} primitive@> -mp_primitive(mp, "minute",internal_quantity,mp_minute); -@:minute_}{\&{minute} primitive@> -mp_primitive(mp, "charcode",internal_quantity,mp_char_code); -@:mp_char_code_}{\&{charcode} primitive@> -mp_primitive(mp, "charext",internal_quantity,mp_char_ext); -@:mp_char_ext_}{\&{charext} primitive@> -mp_primitive(mp, "charwd",internal_quantity,mp_char_wd); -@:mp_char_wd_}{\&{charwd} primitive@> -mp_primitive(mp, "charht",internal_quantity,mp_char_ht); -@:mp_char_ht_}{\&{charht} primitive@> -mp_primitive(mp, "chardp",internal_quantity,mp_char_dp); -@:mp_char_dp_}{\&{chardp} primitive@> -mp_primitive(mp, "charic",internal_quantity,mp_char_ic); -@:mp_char_ic_}{\&{charic} primitive@> -mp_primitive(mp, "designsize",internal_quantity,mp_design_size); -@:mp_design_size_}{\&{designsize} primitive@> -mp_primitive(mp, "pausing",internal_quantity,mp_pausing); -@:mp_pausing_}{\&{pausing} primitive@> -mp_primitive(mp, "showstopping",internal_quantity,mp_showstopping); -@:mp_showstopping_}{\&{showstopping} primitive@> -mp_primitive(mp, "fontmaking",internal_quantity,mp_fontmaking); -@:mp_fontmaking_}{\&{fontmaking} primitive@> -mp_primitive(mp, "linejoin",internal_quantity,mp_linejoin); -@:mp_linejoin_}{\&{linejoin} primitive@> -mp_primitive(mp, "linecap",internal_quantity,mp_linecap); -@:mp_linecap_}{\&{linecap} primitive@> -mp_primitive(mp, "miterlimit",internal_quantity,mp_miterlimit); -@:mp_miterlimit_}{\&{miterlimit} primitive@> -mp_primitive(mp, "warningcheck",internal_quantity,mp_warning_check); -@:mp_warning_check_}{\&{warningcheck} primitive@> -mp_primitive(mp, "boundarychar",internal_quantity,mp_boundary_char); -@:mp_boundary_char_}{\&{boundarychar} primitive@> -mp_primitive(mp, "prologues",internal_quantity,mp_prologues); -@:mp_prologues_}{\&{prologues} primitive@> -mp_primitive(mp, "truecorners",internal_quantity,mp_true_corners); -@:mp_true_corners_}{\&{truecorners} primitive@> -mp_primitive(mp, "mpprocset",internal_quantity,mp_procset); -@:mp_procset_}{\&{mpprocset} primitive@> -mp_primitive(mp, "troffmode",internal_quantity,mp_gtroffmode); -@:troffmode_}{\&{troffmode} primitive@> -mp_primitive(mp, "defaultcolormodel",internal_quantity,mp_default_color_model); -@:mp_default_color_model_}{\&{defaultcolormodel} primitive@> -mp_primitive(mp, "restoreclipcolor",internal_quantity,mp_restore_clip_color); -@:mp_restore_clip_color_}{\&{restoreclipcolor} primitive@> -mp_primitive(mp, "outputtemplate",internal_quantity,mp_output_template); -@:mp_output_template_}{\&{outputtemplate} primitive@> -mp_primitive(mp, "outputformat",internal_quantity,mp_output_format); -@:mp_output_format_}{\&{outputformat} primitive@> -mp_primitive(mp, "jobname",internal_quantity,mp_job_name); +mp_primitive (mp, "tracingtitles", internal_quantity, mp_tracing_titles); +@:tracingtitles_}{\&{tracingtitles} primitive@>; +mp_primitive (mp, "tracingequations", internal_quantity, mp_tracing_equations); +@:mp_tracing_equations_}{\&{tracingequations} primitive@>; +mp_primitive (mp, "tracingcapsules", internal_quantity, mp_tracing_capsules); +@:mp_tracing_capsules_}{\&{tracingcapsules} primitive@>; +mp_primitive (mp, "tracingchoices", internal_quantity, mp_tracing_choices); +@:mp_tracing_choices_}{\&{tracingchoices} primitive@>; +mp_primitive (mp, "tracingspecs", internal_quantity, mp_tracing_specs); +@:mp_tracing_specs_}{\&{tracingspecs} primitive@>; +mp_primitive (mp, "tracingcommands", internal_quantity, mp_tracing_commands); +@:mp_tracing_commands_}{\&{tracingcommands} primitive@>; +mp_primitive (mp, "tracingrestores", internal_quantity, mp_tracing_restores); +@:mp_tracing_restores_}{\&{tracingrestores} primitive@>; +mp_primitive (mp, "tracingmacros", internal_quantity, mp_tracing_macros); +@:mp_tracing_macros_}{\&{tracingmacros} primitive@>; +mp_primitive (mp, "tracingoutput", internal_quantity, mp_tracing_output); +@:mp_tracing_output_}{\&{tracingoutput} primitive@>; +mp_primitive (mp, "tracingstats", internal_quantity, mp_tracing_stats); +@:mp_tracing_stats_}{\&{tracingstats} primitive@>; +mp_primitive (mp, "tracinglostchars", internal_quantity, mp_tracing_lost_chars); +@:mp_tracing_lost_chars_}{\&{tracinglostchars} primitive@>; +mp_primitive (mp, "tracingonline", internal_quantity, mp_tracing_online); +@:mp_tracing_online_}{\&{tracingonline} primitive@>; +mp_primitive (mp, "year", internal_quantity, mp_year); +@:mp_year_}{\&{year} primitive@>; +mp_primitive (mp, "month", internal_quantity, mp_month); +@:mp_month_}{\&{month} primitive@>; +mp_primitive (mp, "day", internal_quantity, mp_day); +@:mp_day_}{\&{day} primitive@>; +mp_primitive (mp, "time", internal_quantity, mp_time); +@:time_}{\&{time} primitive@>; +mp_primitive (mp, "hour", internal_quantity, mp_hour); +@:hour_}{\&{hour} primitive@>; +mp_primitive (mp, "minute", internal_quantity, mp_minute); +@:minute_}{\&{minute} primitive@>; +mp_primitive (mp, "charcode", internal_quantity, mp_char_code); +@:mp_char_code_}{\&{charcode} primitive@>; +mp_primitive (mp, "charext", internal_quantity, mp_char_ext); +@:mp_char_ext_}{\&{charext} primitive@>; +mp_primitive (mp, "charwd", internal_quantity, mp_char_wd); +@:mp_char_wd_}{\&{charwd} primitive@>; +mp_primitive (mp, "charht", internal_quantity, mp_char_ht); +@:mp_char_ht_}{\&{charht} primitive@>; +mp_primitive (mp, "chardp", internal_quantity, mp_char_dp); +@:mp_char_dp_}{\&{chardp} primitive@>; +mp_primitive (mp, "charic", internal_quantity, mp_char_ic); +@:mp_char_ic_}{\&{charic} primitive@>; +mp_primitive (mp, "designsize", internal_quantity, mp_design_size); +@:mp_design_size_}{\&{designsize} primitive@>; +mp_primitive (mp, "pausing", internal_quantity, mp_pausing); +@:mp_pausing_}{\&{pausing} primitive@>; +mp_primitive (mp, "showstopping", internal_quantity, mp_showstopping); +@:mp_showstopping_}{\&{showstopping} primitive@>; +mp_primitive (mp, "fontmaking", internal_quantity, mp_fontmaking); +@:mp_fontmaking_}{\&{fontmaking} primitive@>; +mp_primitive (mp, "linejoin", internal_quantity, mp_linejoin); +@:mp_linejoin_}{\&{linejoin} primitive@>; +mp_primitive (mp, "linecap", internal_quantity, mp_linecap); +@:mp_linecap_}{\&{linecap} primitive@>; +mp_primitive (mp, "miterlimit", internal_quantity, mp_miterlimit); +@:mp_miterlimit_}{\&{miterlimit} primitive@>; +mp_primitive (mp, "warningcheck", internal_quantity, mp_warning_check); +@:mp_warning_check_}{\&{warningcheck} primitive@>; +mp_primitive (mp, "boundarychar", internal_quantity, mp_boundary_char); +@:mp_boundary_char_}{\&{boundarychar} primitive@>; +mp_primitive (mp, "prologues", internal_quantity, mp_prologues); +@:mp_prologues_}{\&{prologues} primitive@>; +mp_primitive (mp, "truecorners", internal_quantity, mp_true_corners); +@:mp_true_corners_}{\&{truecorners} primitive@>; +mp_primitive (mp, "mpprocset", internal_quantity, mp_procset); +@:mp_procset_}{\&{mpprocset} primitive@>; +mp_primitive (mp, "troffmode", internal_quantity, mp_gtroffmode); +@:troffmode_}{\&{troffmode} primitive@>; +mp_primitive (mp, "defaultcolormodel", internal_quantity, + mp_default_color_model); +@:mp_default_color_model_}{\&{defaultcolormodel} primitive@>; +mp_primitive (mp, "restoreclipcolor", internal_quantity, mp_restore_clip_color); +@:mp_restore_clip_color_}{\&{restoreclipcolor} primitive@>; +mp_primitive (mp, "outputtemplate", internal_quantity, mp_output_template); +@:mp_output_template_}{\&{outputtemplate} primitive@>; +mp_primitive (mp, "outputformat", internal_quantity, mp_output_format); +@:mp_output_format_}{\&{outputformat} primitive@>; +mp_primitive (mp, "jobname", internal_quantity, mp_job_name); @:mp_job_name_}{\&{jobname} primitive@> + @ Colors can be specified in four color models. In the special case of |no_model|, MetaPost does not output any color operator to @@ -5360,66 +4179,80 @@ the user so far in the game. @(mplib.h@>= enum mp_color_model { - mp_no_model=1, - mp_grey_model=3, - mp_rgb_model=5, - mp_cmyk_model=7, - mp_uninitialized_model=9 + mp_no_model = 1, + mp_grey_model = 3, + mp_rgb_model = 5, + mp_cmyk_model = 7, + mp_uninitialized_model = 9 }; -@ @<Initialize table entries (done by \.{INIMP} only)@>= -mp->internal[mp_default_color_model]=(mp_rgb_model*unity); -mp->internal[mp_restore_clip_color]=unity; -mp->internal[mp_output_template]=intern("%j.%c"); -mp->internal[mp_output_format]=intern("eps"); +@ @<Initialize table entries@>= +internal_value (mp_default_color_model) = (mp_rgb_model * unity); +internal_value (mp_restore_clip_color) = unity; +internal_string (mp_output_template) = mp_intern (mp, "%j.%c"); +internal_string (mp_output_format) = mp_intern (mp, "eps"); +#if 0 +internal_value (mp_tracing_titles) = 3 * unity; +internal_value (mp_tracing_equations) = 3 * unity; +internal_value (mp_tracing_capsules) = 3 * unity; +internal_value (mp_tracing_choices) = 3 * unity; +internal_value (mp_tracing_specs) = 3 * unity; +internal_value (mp_tracing_commands) = 3 * unity; +internal_value (mp_tracing_restores) = 3 * unity; +internal_value (mp_tracing_macros) = 3 * unity; +internal_value (mp_tracing_output) = 3 * unity; +internal_value (mp_tracing_stats) = 3 * unity; +internal_value (mp_tracing_lost_chars) = 3 * unity; +internal_value (mp_tracing_online) = 3 * unity; +#endif @ Well, we do have to list the names one more time, for use in symbolic printouts. @<Initialize table...@>= -mp->int_name[mp_tracing_titles]=xstrdup("tracingtitles"); -mp->int_name[mp_tracing_equations]=xstrdup("tracingequations"); -mp->int_name[mp_tracing_capsules]=xstrdup("tracingcapsules"); -mp->int_name[mp_tracing_choices]=xstrdup("tracingchoices"); -mp->int_name[mp_tracing_specs]=xstrdup("tracingspecs"); -mp->int_name[mp_tracing_commands]=xstrdup("tracingcommands"); -mp->int_name[mp_tracing_restores]=xstrdup("tracingrestores"); -mp->int_name[mp_tracing_macros]=xstrdup("tracingmacros"); -mp->int_name[mp_tracing_output]=xstrdup("tracingoutput"); -mp->int_name[mp_tracing_stats]=xstrdup("tracingstats"); -mp->int_name[mp_tracing_lost_chars]=xstrdup("tracinglostchars"); -mp->int_name[mp_tracing_online]=xstrdup("tracingonline"); -mp->int_name[mp_year]=xstrdup("year"); -mp->int_name[mp_month]=xstrdup("month"); -mp->int_name[mp_day]=xstrdup("day"); -mp->int_name[mp_time]=xstrdup("time"); -mp->int_name[mp_hour]=xstrdup("hour"); -mp->int_name[mp_minute]=xstrdup("minute"); -mp->int_name[mp_char_code]=xstrdup("charcode"); -mp->int_name[mp_char_ext]=xstrdup("charext"); -mp->int_name[mp_char_wd]=xstrdup("charwd"); -mp->int_name[mp_char_ht]=xstrdup("charht"); -mp->int_name[mp_char_dp]=xstrdup("chardp"); -mp->int_name[mp_char_ic]=xstrdup("charic"); -mp->int_name[mp_design_size]=xstrdup("designsize"); -mp->int_name[mp_pausing]=xstrdup("pausing"); -mp->int_name[mp_showstopping]=xstrdup("showstopping"); -mp->int_name[mp_fontmaking]=xstrdup("fontmaking"); -mp->int_name[mp_linejoin]=xstrdup("linejoin"); -mp->int_name[mp_linecap]=xstrdup("linecap"); -mp->int_name[mp_miterlimit]=xstrdup("miterlimit"); -mp->int_name[mp_warning_check]=xstrdup("warningcheck"); -mp->int_name[mp_boundary_char]=xstrdup("boundarychar"); -mp->int_name[mp_prologues]=xstrdup("prologues"); -mp->int_name[mp_true_corners]=xstrdup("truecorners"); -mp->int_name[mp_default_color_model]=xstrdup("defaultcolormodel"); -mp->int_name[mp_procset]=xstrdup("mpprocset"); -mp->int_name[mp_gtroffmode]=xstrdup("troffmode"); -mp->int_name[mp_restore_clip_color]=xstrdup("restoreclipcolor"); -mp->int_name[mp_output_template]=xstrdup("outputtemplate"); -mp->int_name[mp_output_format]=xstrdup("outputformat"); -mp->int_name[mp_job_name]=xstrdup("jobname"); +internal_name (mp_tracing_titles) = xstrdup ("tracingtitles"); +internal_name (mp_tracing_equations) = xstrdup ("tracingequations"); +internal_name (mp_tracing_capsules) = xstrdup ("tracingcapsules"); +internal_name (mp_tracing_choices) = xstrdup ("tracingchoices"); +internal_name (mp_tracing_specs) = xstrdup ("tracingspecs"); +internal_name (mp_tracing_commands) = xstrdup ("tracingcommands"); +internal_name (mp_tracing_restores) = xstrdup ("tracingrestores"); +internal_name (mp_tracing_macros) = xstrdup ("tracingmacros"); +internal_name (mp_tracing_output) = xstrdup ("tracingoutput"); +internal_name (mp_tracing_stats) = xstrdup ("tracingstats"); +internal_name (mp_tracing_lost_chars) = xstrdup ("tracinglostchars"); +internal_name (mp_tracing_online) = xstrdup ("tracingonline"); +internal_name (mp_year) = xstrdup ("year"); +internal_name (mp_month) = xstrdup ("month"); +internal_name (mp_day) = xstrdup ("day"); +internal_name (mp_time) = xstrdup ("time"); +internal_name (mp_hour) = xstrdup ("hour"); +internal_name (mp_minute) = xstrdup ("minute"); +internal_name (mp_char_code) = xstrdup ("charcode"); +internal_name (mp_char_ext) = xstrdup ("charext"); +internal_name (mp_char_wd) = xstrdup ("charwd"); +internal_name (mp_char_ht) = xstrdup ("charht"); +internal_name (mp_char_dp) = xstrdup ("chardp"); +internal_name (mp_char_ic) = xstrdup ("charic"); +internal_name (mp_design_size) = xstrdup ("designsize"); +internal_name (mp_pausing) = xstrdup ("pausing"); +internal_name (mp_showstopping) = xstrdup ("showstopping"); +internal_name (mp_fontmaking) = xstrdup ("fontmaking"); +internal_name (mp_linejoin) = xstrdup ("linejoin"); +internal_name (mp_linecap) = xstrdup ("linecap"); +internal_name (mp_miterlimit) = xstrdup ("miterlimit"); +internal_name (mp_warning_check) = xstrdup ("warningcheck"); +internal_name (mp_boundary_char) = xstrdup ("boundarychar"); +internal_name (mp_prologues) = xstrdup ("prologues"); +internal_name (mp_true_corners) = xstrdup ("truecorners"); +internal_name (mp_default_color_model) = xstrdup ("defaultcolormodel"); +internal_name (mp_procset) = xstrdup ("mpprocset"); +internal_name (mp_gtroffmode) = xstrdup ("troffmode"); +internal_name (mp_restore_clip_color) = xstrdup ("restoreclipcolor"); +internal_name (mp_output_template) = xstrdup ("outputtemplate"); +internal_name (mp_output_format) = xstrdup ("outputformat"); +internal_name (mp_job_name) = xstrdup ("jobname"); @ The following procedure, which is called just before \MP\ initializes its input and output, establishes the initial values of the date and time. @@ -5428,21 +4261,21 @@ input and output, establishes the initial values of the date and time. Note that the values are |scaled| integers. Hence \MP\ can no longer be used after the year 32767. -@c -static void mp_fix_date_and_time (MP mp) { +@c +static void mp_fix_date_and_time (MP mp) { time_t aclock = time ((time_t *) 0); struct tm *tmptr = localtime (&aclock); - mp->internal[mp_time]= - (tmptr->tm_hour*60+tmptr->tm_min)*unity; /* minutes since midnight */ - mp->internal[mp_hour]= (tmptr->tm_hour)*unity; /* hours since midnight */ - mp->internal[mp_minute]= (tmptr->tm_min)*unity; /* minutes since the hour */ - mp->internal[mp_day]=(tmptr->tm_mday)*unity; /* fourth day of the month */ - mp->internal[mp_month]=(tmptr->tm_mon+1)*unity; /* seventh month of the year */ - mp->internal[mp_year]=(tmptr->tm_year+1900)*unity; /* Anno Domini */ + internal_value (mp_time) = (tmptr->tm_hour * 60 + tmptr->tm_min) * unity; /* minutes since midnight */ + internal_value (mp_hour) = (tmptr->tm_hour) * unity; /* hours since midnight */ + internal_value (mp_minute) = (tmptr->tm_min) * unity; /* minutes since the hour */ + internal_value (mp_day) = (tmptr->tm_mday) * unity; /* fourth day of the month */ + internal_value (mp_month) = (tmptr->tm_mon + 1) * unity; /* seventh month of the year */ + internal_value (mp_year) = (tmptr->tm_year + 1900) * unity; /* Anno Domini */ } + @ @<Declarations@>= -static void mp_fix_date_and_time (MP mp) ; +static void mp_fix_date_and_time (MP mp); @ \MP\ is occasionally supposed to print diagnostic information that goes only into the transcript file, unless |mp_tracing_online| is positive. @@ -5450,26 +4283,31 @@ Now that we have defined |mp_tracing_online| we can define two routines that adjust the destination of print commands: @<Declarations@>= -static void mp_begin_diagnostic (MP mp) ; -static void mp_end_diagnostic (MP mp,boolean blank_line); -static void mp_print_diagnostic (MP mp, const char *s, const char *t, boolean nuline) ; +static void mp_begin_diagnostic (MP mp); +static void mp_end_diagnostic (MP mp, boolean blank_line); +static void mp_print_diagnostic (MP mp, const char *s, const char *t, + boolean nuline); @ @<Basic printing...@>= -void mp_begin_diagnostic (MP mp) { /* prepare to do some tracing */ - mp->old_setting=mp->selector; - if ((mp->internal[mp_tracing_online]<=0)&&(mp->selector==term_and_log)){ - decr(mp->selector); - if ( mp->history==mp_spotless ) mp->history=mp_warning_issued; +void mp_begin_diagnostic (MP mp) { /* prepare to do some tracing */ + mp->old_setting = mp->selector; + if ((internal_value (mp_tracing_online) <= 0) + && (mp->selector == term_and_log)) { + decr (mp->selector); + if (mp->history == mp_spotless) + mp->history = mp_warning_issued; } } @# -void mp_end_diagnostic (MP mp,boolean blank_line) { +void mp_end_diagnostic (MP mp, boolean blank_line) { /* restore proper conditions after tracing */ - mp_print_nl(mp, ""); - if ( blank_line ) mp_print_ln(mp); - mp->selector=mp->old_setting; + mp_print_nl (mp, ""); + if (blank_line) + mp_print_ln (mp); + mp->selector = mp->old_setting; } + @ @<Glob...@>= @@ -5480,14 +4318,19 @@ printing, as follows. (The parameter |s| is typically |"Path"| or |"Cycle spec"|, etc.) @<Basic printing...@>= -void mp_print_diagnostic (MP mp, const char *s, const char *t, boolean nuline) { - mp_begin_diagnostic(mp); - if ( nuline ) mp_print_nl(mp, s); else mp_print(mp, s); - mp_print(mp, " at line "); - mp_print_int(mp, mp_true_line(mp)); - mp_print(mp, t); mp_print_char(mp, xord(':')); +void mp_print_diagnostic (MP mp, const char *s, const char *t, boolean nuline) { + mp_begin_diagnostic (mp); + if (nuline) + mp_print_nl (mp, s); + else + mp_print (mp, s); + mp_print (mp, " at line "); + mp_print_int (mp, mp_true_line (mp)); + mp_print (mp, t); + mp_print_char (mp, xord (':')); } + @ The 256 |ASCII_code| characters are grouped into classes by means of the |char_class| table. Individual class numbers have no semantic or syntactic significance, except in a few instances defined here. @@ -5508,7 +4351,7 @@ class numbers in nonstandard extensions of \MP. @d max_class 20 /* the largest class number */ @<Glob...@>= -int char_class[256]; /* the class numbers */ +int char_class[256]; /* the class numbers */ @ If changes are made to accommodate non-ASCII character sets, they should follow the guidelines in Appendix~C of {\sl The {\logos METAFONT\/}book}. @@ -5516,264 +4359,273 @@ follow the guidelines in Appendix~C of {\sl The {\logos METAFONT\/}book}. @^system dependencies@> @<Set initial ...@>= -for (k='0';k<='9';k++) - mp->char_class[k]=digit_class; -mp->char_class['.']=period_class; -mp->char_class[' ']=space_class; -mp->char_class['%']=percent_class; -mp->char_class['"']=string_class; -mp->char_class[',']=5; -mp->char_class[';']=6; -mp->char_class['(']=7; -mp->char_class[')']=right_paren_class; -for (k='A';k<= 'Z';k++ ) - mp->char_class[k]=letter_class; -for (k='a';k<='z';k++) - mp->char_class[k]=letter_class; -mp->char_class['_']=letter_class; -mp->char_class['<']=10; -mp->char_class['=']=10; -mp->char_class['>']=10; -mp->char_class[':']=10; -mp->char_class['|']=10; -mp->char_class['`']=11; -mp->char_class['\'']=11; -mp->char_class['+']=12; -mp->char_class['-']=12; -mp->char_class['/']=13; -mp->char_class['*']=13; -mp->char_class['\\']=13; -mp->char_class['!']=14; -mp->char_class['?']=14; -mp->char_class['#']=15; -mp->char_class['&']=15; -mp->char_class['@@']=15; -mp->char_class['$']=15; -mp->char_class['^']=16; -mp->char_class['~']=16; -mp->char_class['[']=left_bracket_class; -mp->char_class[']']=right_bracket_class; -mp->char_class['{']=19; -mp->char_class['}']=19; -for (k=0;k<' ';k++) - mp->char_class[k]=invalid_class; -mp->char_class['\t']=space_class; -mp->char_class['\f']=space_class; -for (k=127;k<=255;k++) - mp->char_class[k]=invalid_class; - -@* \[13] The hash table. -Symbolic tokens are stored and retrieved by means of a fairly standard hash -table algorithm called the method of ``coalescing lists'' (cf.\ Algorithm 6.4C -in {\sl The Art of Computer Programming\/}). Once a symbolic token enters the -table, it is never removed. - -The actual sequence of characters forming a symbolic token is -stored in the |str_pool| array together with all the other strings. An -auxiliary array |hash| consists of items with two halfword fields per -word. The first of these, called |mp_next(p)|, points to the next identifier -belonging to the same coalesced list as the identifier corresponding to~|p|; -and the other, called |text(p)|, points to the |str_start| entry for -|p|'s identifier. If position~|p| of the hash table is empty, we have -|text(p)=0|; if position |p| is either empty or the end of a coalesced -hash list, we have |mp_next(p)=0|. - -An auxiliary pointer variable called |hash_used| is maintained in such a -way that all locations |p>=hash_used| are nonempty. The global variable -|st_count| tells how many symbolic tokens have been defined, if statistics -are being kept. - -The first 256 locations of |hash| are reserved for symbols of length one. - -There's a parallel array called |eqtb| that contains the current equivalent -values of each symbolic token. The entries of this array consist of -two halfwords called |eq_type| (a command code) and |equiv| (a secondary -piece of information that qualifies the |eq_type|). - -@d eq_type(A) mp->eqtb[(A)].lh /* the current ``meaning'' of a symbolic token */ -@d equiv(A) mp->eqtb[(A)].rh /* parametric part of a token's meaning */ -@d hash_is_full (mp->hash_used==hash_base) /* are all positions occupied? */ - -@(mpmp.h@>= -#define mp_next(A) mp->hash[(A)].lh /* link for coalesced lists */ -#define text(A) mp->hash[(A)].rh /* string number for symbolic token name */ -#define hash_base 257 /* hashing actually starts here */ +for (k = '0'; k <= '9'; k++) + mp->char_class[k] = digit_class; +mp->char_class['.'] = period_class; +mp->char_class[' '] = space_class; +mp->char_class['%'] = percent_class; +mp->char_class['"'] = string_class; +mp->char_class[','] = 5; +mp->char_class[';'] = 6; +mp->char_class['('] = 7; +mp->char_class[')'] = right_paren_class; +for (k = 'A'; k <= 'Z'; k++) + mp->char_class[k] = letter_class; +for (k = 'a'; k <= 'z'; k++) + mp->char_class[k] = letter_class; +mp->char_class['_'] = letter_class; +mp->char_class['<'] = 10; +mp->char_class['='] = 10; +mp->char_class['>'] = 10; +mp->char_class[':'] = 10; +mp->char_class['|'] = 10; +mp->char_class['`'] = 11; +mp->char_class['\''] = 11; +mp->char_class['+'] = 12; +mp->char_class['-'] = 12; +mp->char_class['/'] = 13; +mp->char_class['*'] = 13; +mp->char_class['\\'] = 13; +mp->char_class['!'] = 14; +mp->char_class['?'] = 14; +mp->char_class['#'] = 15; +mp->char_class['&'] = 15; +mp->char_class['@@'] = 15; +mp->char_class['$'] = 15; +mp->char_class['^'] = 16; +mp->char_class['~'] = 16; +mp->char_class['['] = left_bracket_class; +mp->char_class[']'] = right_bracket_class; +mp->char_class['{'] = 19; +mp->char_class['}'] = 19; +for (k = 0; k < ' '; k++) + mp->char_class[k] = invalid_class; +mp->char_class['\t'] = space_class; +mp->char_class['\f'] = space_class; +for (k = 127; k <= 255; k++) + mp->char_class[k] = invalid_class; + +@* The hash table. + +Symbolic tokens are stored in and retrieved from an AVL tree. This +is not as fast as an actual hash table, but it is easily extensible. + +A symbolic token contains a pointer to the |str_number| that +contains the string representation of the symbol, a |halfword| +that holds the current command value of the token, and an +|mp_value| for the associated equivalent. + +@d text(A) (A)->text /* string number for symbolic token name */ +@d eq_type(A) (A)->type /* the current ``meaning'' of a symbolic token */ +@d equiv(A) (A)->v.data.val /* parametric part of a token's meaning */ +@d equiv_node(A) (A)->v.data.node /* parametric part of a token's meaning */ +@d equiv_sym(A) (A)->v.data.sym /* parametric part of a token's meaning */ + +@ @<Types...@>= +typedef struct mp_symbol_entry { + halfword type; + mp_value v; + str_number text; +} mp_symbol_entry; @ @<Glob...@>= -pointer hash_used; /* allocation pointer for |hash| */ -integer st_count; /* total number of known identifiers */ +integer st_count; /* total number of known identifiers */ +avl_tree symbols; /* avl tree of symbolic tokens */ +avl_tree frozen_symbols; /* avl tree of frozen symbolic tokens */ +mp_sym frozen_bad_vardef; +mp_sym frozen_colon; +mp_sym frozen_end_def; +mp_sym frozen_end_for; +mp_sym frozen_end_group; +mp_sym frozen_etex; +mp_sym frozen_fi; +mp_sym frozen_inaccessible; +mp_sym frozen_left_bracket; +mp_sym frozen_mpx_break; +mp_sym frozen_repeat_loop; +mp_sym frozen_right_delimiter; +mp_sym frozen_semicolon; +mp_sym frozen_slash; +mp_sym frozen_undefined; + + +@ Here are the functions needed for the avl construction. -@ Certain entries in the hash table are ``frozen'' and not redefinable, -since they are used in error recovery. - -@(mpmp.h@>= -#define hash_top (integer)(hash_base+mp->hash_size) /* the first location of the frozen area */ -#define frozen_inaccessible hash_top /* |hash| location to protect the frozen area */ -#define frozen_repeat_loop (hash_top+1) /* |hash| location of a loop-repeat token */ -#define frozen_right_delimiter (hash_top+2) /* |hash| location of a permanent `\.)' */ -#define frozen_left_bracket (hash_top+3) /* |hash| location of a permanent `\.[' */ -#define frozen_slash (hash_top+4) /* |hash| location of a permanent `\./' */ -#define frozen_colon (hash_top+5) /* |hash| location of a permanent `\.:' */ -#define frozen_semicolon (hash_top+6) /* |hash| location of a permanent `\.;' */ -#define frozen_end_for (hash_top+7) /* |hash| location of a permanent \&{endfor} */ -#define frozen_end_def (hash_top+8) /* |hash| location of a permanent \&{enddef} */ -#define frozen_fi (hash_top+9) /* |hash| location of a permanent \&{fi} */ -#define frozen_end_group (hash_top+10) /* |hash| location of a permanent `\.{endgroup}' */ -#define frozen_etex (hash_top+11) /* |hash| location of a permanent \&{etex} */ -#define frozen_mpx_break (hash_top+12) /* |hash| location of a permanent \&{mpxbreak} */ -#define frozen_bad_vardef (hash_top+13) /* |hash| location of `\.{a bad variable}' */ -#define frozen_undefined (hash_top+14) /* |hash| location that never gets defined */ -#define hash_end (integer)(hash_top+14) /* the actual size of the |hash| and |eqtb| arrays */ +@<Declarations@>= +static int comp_symbols_entry (void *p, const void *pa, const void *pb); +static void *copy_symbols_entry (const void *p); +static void *delete_symbols_entry (void *p); -@ @<Glob...@>= -two_halves *hash; /* the hash table */ -two_halves *eqtb; /* the equivalents */ +@ The avl comparison function is a straightword version of |strcmp|, +except that checks for the string lengths first. -@ @<Allocate or initialize ...@>= -mp->hash = xmalloc((hash_end+1),sizeof(two_halves)); -mp->eqtb = xmalloc((hash_end+1),sizeof(two_halves)); +@c +static int comp_symbols_entry (void *p, const void *pa, const void *pb) { + const mp_symbol_entry *a = (const mp_symbol_entry *) pa; + const mp_symbol_entry *b = (const mp_symbol_entry *) pb; + (void) p; + if (a->text->len != b->text->len) { + return (a->text->len > b->text->len ? 1 : -1); + } + return strncmp ((const char *) a->text->str, (const char *) b->text->str, + a->text->len); +} -@ @<Dealloc variables@>= -xfree(mp->hash); -xfree(mp->eqtb); -@ @<Set init...@>= -mp_next(1)=0; text(1)=0; eq_type(1)=tag_token; equiv(1)=null; -for (k=2;k<=hash_end;k++) { - mp->hash[k]=mp->hash[1]; mp->eqtb[k]=mp->eqtb[1]; -} - -@ @<Initialize table entries...@>= -mp->hash_used=frozen_inaccessible; /* nothing is used */ -mp->st_count=0; -text(frozen_bad_vardef)=intern("a bad variable"); -text(frozen_etex)=intern("etex"); -text(frozen_mpx_break)=intern("mpxbreak"); -text(frozen_fi)=intern("fi"); -text(frozen_end_group)=intern("endgroup"); -text(frozen_end_def)=intern("enddef"); -text(frozen_end_for)=intern("endfor"); -text(frozen_semicolon)=intern(";"); -text(frozen_colon)=intern(":"); -text(frozen_slash)=intern("/"); -text(frozen_left_bracket)=intern("["); -text(frozen_right_delimiter)=intern(")"); -text(frozen_inaccessible)=intern(" INACCESSIBLE"); -eq_type(frozen_right_delimiter)=right_delimiter; - -@ @<Check the ``constant'' values...@>= -if ( hash_end+mp->max_internal>max_halfword ) mp->bad=17; - -@ The value of |hash_prime| should be roughly 85\pct! of |hash_size|, and it -should be a prime number. The theory of hashing tells us to expect fewer -than two table probes, on the average, when the search is successful. -[See J.~S. Vitter, {\sl Journal of the ACM\/ \bf30} (1983), 231--258.] -@^Vitter, Jeffrey Scott@> +@ Copying a symbol happens when an item is inserted into an AVL tree. +The |text| needs to be deep copied, every thing else can be reassigned. @c -static integer mp_compute_hash (MP mp, const char *s, int l) { - integer k; - integer h = *s; - for (k=1;k<l;k++){ - h=h+h+(*(s+k)); - while ( h>=mp->hash_prime ) h=h-mp->hash_prime; - } - return h; +static void *copy_symbols_entry (const void *p) { + mp_sym ff; + const mp_symbol_entry *fp; + fp = (const mp_symbol_entry *) p; + ff = malloc (sizeof (mp_symbol_entry)); + if (ff == NULL) + return NULL; + ff->text = copy_strings_entry (fp->text); + if (ff->text == NULL) + return NULL; + ff->v = fp->v; + ff->type = fp->type; + return ff; } -@ Here is the subroutine that searches the hash table for an identifier -that matches a given string of length~|l| appearing in |buffer[j.. -(j+l-1)]|. If the identifier is not found, it is inserted; hence it -will always be found, and the corresponding hash table address -will be returned. - -@c -static pointer mp_id_lookup (MP mp,integer j, integer l) { /* search the hash table */ - integer h; /* hash code */ - pointer p; /* index in |hash| array */ - pointer k; /* index in |buffer| array */ - if (l==1) { - @<Treat special case of length 1 and |break|@>; - } - @<Compute the hash code |h|@>; - p=h+hash_base; /* we start searching here; note that |0<=h<hash_prime| */ - while (true) { - if (text(p)>0 && length(text(p))==l && mp_str_eq_buf(mp, text(p),j)) - break; - if ( mp_next(p)==0 ) { - @<Insert a new symbolic token after |p|, then - make |p| point to it and |break|@>; - } - p=mp_next(p); - } - return p; +@ In the current implementation, symbols are not freed until the +end of the run. + +@c +static void *delete_symbols_entry (void *p) { + mp_sym ff = (mp_sym) p; + delete_strings_entry (ff->text); + mp_xfree (ff); + return NULL; } -@ @<Treat special case of length 1...@>= - p=mp->buffer[j]+1; text(p)=p-1; return p; +@ @<Allocate or initialize ...@>= +mp->symbols = avl_create (comp_symbols_entry, + copy_symbols_entry, + delete_symbols_entry, malloc, free, NULL); +mp->frozen_symbols = avl_create (comp_symbols_entry, + copy_symbols_entry, + delete_symbols_entry, malloc, free, NULL); -@ @<Insert a new symbolic...@>= -{ -if ( text(p)>0 ) { - do { - if ( hash_is_full ) - mp_overflow(mp, "hash size",(integer)mp->hash_size); -@:MetaPost capacity exceeded hash size}{\quad hash size@> - decr(mp->hash_used); - } while (text(mp->hash_used)!=0); /* search for an empty location in |hash| */ - mp_next(p)=mp->hash_used; - p=mp->hash_used; -} -str_room(l); -for (k=j;k<j+l;k++) { - append_char(mp->buffer[k]); -} -text(p)=mp_make_string(mp); -mp->str_ref[text(p)]=max_str_ref; -incr(mp->st_count); -break; -} +@ @<Dealloc variables@>= +if (mp->symbols != NULL) + avl_destroy (mp->symbols); +if (mp->frozen_symbols != NULL) + avl_destroy (mp->frozen_symbols); -@ @<Compute the hash code |h|@>= -h=mp_compute_hash(mp, (char *)mp->buffer+j, l) +@ Actually creating symbols is done by |id_lookup|, but in order to +do so it needs a way to create a new, empty symbol structure. +@<Declarations@>= +static mp_sym new_symbols_entry (MP mp, unsigned char *nam, size_t len); + +@ @c +static mp_sym new_symbols_entry (MP mp, unsigned char *nam, size_t len) { + mp_sym ff; + ff = mp_xmalloc (mp, 1, sizeof (mp_symbol_entry)); + memset (ff, 0, sizeof (mp_symbol_entry)); + ff->text = new_strings_entry (mp); + ff->text->str = nam; + ff->text->len = len; + ff->type = tag_token; + ff->v.type = mp_known; + FUNCTION_TRACE4 ("%p = new_symbols_entry(\"%s\",%d)\n", ff, nam, len); + return ff; +} + + +@ Certain symbols are ``frozen'' and not redefinable, since they are +used +in error recovery. + +@<Initialize table entries@>= +mp->st_count = 0; +mp->frozen_bad_vardef = +mp_frozen_primitive (mp, "a bad variable", tag_token, 0); +mp->frozen_right_delimiter = mp_frozen_primitive (mp, ")", right_delimiter, 0); +mp->frozen_inaccessible = +mp_frozen_primitive (mp, " INACCESSIBLE", tag_token, 0); +mp->frozen_undefined = mp_frozen_primitive (mp, " UNDEFINED", tag_token, 0); + +@ Here is the subroutine that searches the avl tree for an identifier +that matches a given string of length~|l| appearing in |buffer[j.. +(j+l-1)]|. If the identifier is not found, it is inserted if +|insert_new| is |true|, and the corresponding symbol will be returned. -@ @<Search |eqtb| for equivalents equal to |p|@>= -for (q=1;q<=hash_end;q++) { - if ( equiv(q)==p ) { - mp_print_nl(mp, "EQUIV("); - mp_print_int(mp, q); - mp_print_char(mp, xord(')')); +There are two variations on the lookup function: one for the normal +symbol table, and one for the table of error recovery symbols. + +@c +static mp_sym mp_do_id_lookup (MP mp, avl_tree symbols, const char *j, + size_t l, boolean insert_new) { + /* search an avl tree */ + mp_sym s, str; + unsigned char *nam = (unsigned char *) mp_xstrldup (mp, j, l); + s = new_symbols_entry (mp, nam, l); + str = (mp_sym) avl_find (s, symbols); + if (str == NULL && insert_new) { + mp->st_count++; + assert (avl_ins (s, symbols, avl_false) > 0); + str = (mp_sym) avl_find (s, symbols); } + delete_symbols_entry (s); + return str; +} +static mp_sym mp_id_lookup (MP mp, char *j, size_t l, boolean insert_new) { + /* search the normal symbol table */ + return mp_do_id_lookup (mp, mp->symbols, j, l, insert_new); +} +static mp_sym mp_frozen_id_lookup (MP mp, const char *j, size_t l, + boolean insert_new) { + /* search the error recovery symbol table */ + return mp_do_id_lookup (mp, mp->frozen_symbols, j, l, insert_new); } + @ We need to put \MP's ``primitive'' symbolic tokens into the hash table, together with their command code (which will be the |eq_type|) and an operand (which will be the |equiv|). The |primitive| procedure does this, in a way that no \MP\ user can. The global value |cur_sym| contains the new |eqtb| pointer after |primitive| has acted. -@c +@c static void mp_primitive (MP mp, const char *ss, halfword c, halfword o) { - pool_pointer k; /* index into |str_pool| */ - quarterword j; /* index into |buffer| */ - quarterword l; /* length of the string */ - str_number s; - s = intern(ss); - k=mp->str_start[s]; l=(quarterword)(str_stop(s)-k); - /* we will move |s| into the (empty) |buffer| */ - for (j=0;j<=l-1;j++) { - mp->buffer[j]=mp->str_pool[k+j]; - } - mp->cur_sym=mp_id_lookup(mp, 0,l); - if ( s>=256 ) { /* we don't want to have the string twice */ - mp_flush_string(mp, text(mp->cur_sym)); text(mp->cur_sym)=s; - }; - eq_type(mp->cur_sym)=c; - equiv(mp->cur_sym)=o; + char *s = mp_xstrdup (mp, ss); + mp->cur_sym = mp_id_lookup (mp, s, strlen (s), true); + mp_xfree (s); + eq_type (mp->cur_sym) = c; + equiv (mp->cur_sym) = o; +} + + +@ Some other symbolic tokens only exist for error recovery. + +@c +static mp_sym mp_frozen_primitive (MP mp, const char *ss, halfword c, + halfword o) { + mp_sym str = mp_frozen_id_lookup (mp, ss, strlen (ss), true); + str->type = c; + str->v.data.val = o; + return str; +} + + +@ This routine returns |true| if the argument is an un-redefinable symbol +because it is one of the error recovery tokens (as explained elsewhere, +|frozen_inaccessible| actuall is redefinable). + +@c +static boolean mp_is_frozen (MP mp, mp_sym sym) { + mp_sym temp = mp_frozen_id_lookup (mp, (char *) sym->text->str, sym->text->len, false); + if (temp==mp->frozen_inaccessible) + return false; + return (temp == sym); } @@ -5782,90 +4634,95 @@ by their |eq_type| alone. These primitives are loaded into the hash table as follows: @<Put each of \MP's primitives into the hash table@>= -mp_primitive(mp, "..",path_join,0); -@:.._}{\.{..} primitive@> -mp_primitive(mp, "[",left_bracket,0); mp->eqtb[frozen_left_bracket]=mp->eqtb[mp->cur_sym]; -@:[ }{\.{[} primitive@> -mp_primitive(mp, "]",right_bracket,0); -@:] }{\.{]} primitive@> -mp_primitive(mp, "}",right_brace,0); -@:]]}{\.{\char`\}} primitive@> -mp_primitive(mp, "{",left_brace,0); -@:][}{\.{\char`\{} primitive@> -mp_primitive(mp, ":",colon,0); mp->eqtb[frozen_colon]=mp->eqtb[mp->cur_sym]; -@:: }{\.{:} primitive@> -mp_primitive(mp, "::",double_colon,0); -@::: }{\.{::} primitive@> -mp_primitive(mp, "||:",bchar_label,0); -@:::: }{\.{\char'174\char'174:} primitive@> -mp_primitive(mp, ":=",assignment,0); -@::=_}{\.{:=} primitive@> -mp_primitive(mp, ",",comma,0); -@:, }{\., primitive@> -mp_primitive(mp, ";",semicolon,0); mp->eqtb[frozen_semicolon]=mp->eqtb[mp->cur_sym]; -@:; }{\.; primitive@> -mp_primitive(mp, "\\",relax,0); -@:]]\\}{\.{\char`\\} primitive@> -@# -mp_primitive(mp, "addto",add_to_command,0); -@:add_to_}{\&{addto} primitive@> -mp_primitive(mp, "atleast",at_least,0); -@:at_least_}{\&{atleast} primitive@> -mp_primitive(mp, "begingroup",begin_group,0); mp->bg_loc=mp->cur_sym; -@:begin_group_}{\&{begingroup} primitive@> -mp_primitive(mp, "controls",controls,0); -@:controls_}{\&{controls} primitive@> -mp_primitive(mp, "curl",curl_command,0); -@:curl_}{\&{curl} primitive@> -mp_primitive(mp, "delimiters",delimiters,0); -@:delimiters_}{\&{delimiters} primitive@> -mp_primitive(mp, "endgroup",end_group,0); - mp->eqtb[frozen_end_group]=mp->eqtb[mp->cur_sym]; mp->eg_loc=mp->cur_sym; -@:endgroup_}{\&{endgroup} primitive@> -mp_primitive(mp, "everyjob",every_job_command,0); -@:every_job_}{\&{everyjob} primitive@> -mp_primitive(mp, "exitif",exit_test,0); -@:exit_if_}{\&{exitif} primitive@> -mp_primitive(mp, "expandafter",expand_after,0); -@:expand_after_}{\&{expandafter} primitive@> -mp_primitive(mp, "interim",interim_command,0); -@:interim_}{\&{interim} primitive@> -mp_primitive(mp, "let",let_command,0); -@:let_}{\&{let} primitive@> -mp_primitive(mp, "newinternal",new_internal,0); -@:new_internal_}{\&{newinternal} primitive@> -mp_primitive(mp, "of",of_token,0); -@:of_}{\&{of} primitive@> -mp_primitive(mp, "randomseed",mp_random_seed,0); -@:mp_random_seed_}{\&{randomseed} primitive@> -mp_primitive(mp, "save",save_command,0); -@:save_}{\&{save} primitive@> -mp_primitive(mp, "scantokens",scan_tokens,0); -@:scan_tokens_}{\&{scantokens} primitive@> -mp_primitive(mp, "shipout",ship_out_command,0); -@:ship_out_}{\&{shipout} primitive@> -mp_primitive(mp, "skipto",skip_to,0); -@:skip_to_}{\&{skipto} primitive@> -mp_primitive(mp, "special",special_command,0); -@:special}{\&{special} primitive@> -mp_primitive(mp, "fontmapfile",special_command,1); -@:fontmapfile}{\&{fontmapfile} primitive@> -mp_primitive(mp, "fontmapline",special_command,2); -@:fontmapline}{\&{fontmapline} primitive@> -mp_primitive(mp, "step",step_token,0); -@:step_}{\&{step} primitive@> -mp_primitive(mp, "str",str_op,0); -@:str_}{\&{str} primitive@> -mp_primitive(mp, "tension",tension,0); -@:tension_}{\&{tension} primitive@> -mp_primitive(mp, "to",to_token,0); -@:to_}{\&{to} primitive@> -mp_primitive(mp, "until",until_token,0); -@:until_}{\&{until} primitive@> -mp_primitive(mp, "within",within_token,0); -@:within_}{\&{within} primitive@> -mp_primitive(mp, "write",write_command,0); +mp_primitive (mp, "..", path_join, 0); +@:.._}{\.{..} primitive@>; +mp_primitive (mp, "[", left_bracket, 0); +mp->frozen_left_bracket = mp_frozen_primitive (mp, "[", left_bracket, 0); +@:[ }{\.{[} primitive@>; +mp_primitive (mp, "]", right_bracket, 0); +@:] }{\.{]} primitive@>; +mp_primitive (mp, "}", right_brace, 0); +@:]]}{\.{\char`\}} primitive@>; +mp_primitive (mp, "{", left_brace, 0); +@:][}{\.{\char`\{} primitive@>; +mp_primitive (mp, ":", colon, 0); +mp->frozen_colon = mp_frozen_primitive (mp, ":", colon, 0); +@:: }{\.{:} primitive@>; +mp_primitive (mp, "::", double_colon, 0); +@::: }{\.{::} primitive@>; +mp_primitive (mp, "||:", bchar_label, 0); +@:::: }{\.{\char'174\char'174:} primitive@>; +mp_primitive (mp, ":=", assignment, 0); +@::=_}{\.{:=} primitive@>; +mp_primitive (mp, ",", comma, 0); +@:, }{\., primitive@>; +mp_primitive (mp, ";", semicolon, 0); +mp->frozen_semicolon = mp_frozen_primitive (mp, ";", semicolon, 0); +@:; }{\.; primitive@>; +mp_primitive (mp, "\\", relax, 0); +@:]]\\}{\.{\char`\\} primitive@>; +mp_primitive (mp, "addto", add_to_command, 0); +@:add_to_}{\&{addto} primitive@>; +mp_primitive (mp, "atleast", at_least, 0); +@:at_least_}{\&{atleast} primitive@>; +mp_primitive (mp, "begingroup", begin_group, 0); +mp->bg_loc = mp->cur_sym; +@:begin_group_}{\&{begingroup} primitive@>; +mp_primitive (mp, "controls", controls, 0); +@:controls_}{\&{controls} primitive@>; +mp_primitive (mp, "curl", curl_command, 0); +@:curl_}{\&{curl} primitive@>; +mp_primitive (mp, "delimiters", delimiters, 0); +@:delimiters_}{\&{delimiters} primitive@>; +mp_primitive (mp, "endgroup", end_group, 0); +mp->eg_loc = mp->cur_sym; +mp->frozen_end_group = mp_frozen_primitive (mp, "endgroup", end_group, 0); +@:endgroup_}{\&{endgroup} primitive@>; +mp_primitive (mp, "everyjob", every_job_command, 0); +@:every_job_}{\&{everyjob} primitive@>; +mp_primitive (mp, "exitif", exit_test, 0); +@:exit_if_}{\&{exitif} primitive@>; +mp_primitive (mp, "expandafter", expand_after, 0); +@:expand_after_}{\&{expandafter} primitive@>; +mp_primitive (mp, "interim", interim_command, 0); +@:interim_}{\&{interim} primitive@>; +mp_primitive (mp, "let", let_command, 0); +@:let_}{\&{let} primitive@>; +mp_primitive (mp, "newinternal", new_internal, 0); +@:new_internal_}{\&{newinternal} primitive@>; +mp_primitive (mp, "of", of_token, 0); +@:of_}{\&{of} primitive@>; +mp_primitive (mp, "randomseed", mp_random_seed, 0); +@:mp_random_seed_}{\&{randomseed} primitive@>; +mp_primitive (mp, "save", save_command, 0); +@:save_}{\&{save} primitive@>; +mp_primitive (mp, "scantokens", scan_tokens, 0); +@:scan_tokens_}{\&{scantokens} primitive@>; +mp_primitive (mp, "shipout", ship_out_command, 0); +@:ship_out_}{\&{shipout} primitive@>; +mp_primitive (mp, "skipto", skip_to, 0); +@:skip_to_}{\&{skipto} primitive@>; +mp_primitive (mp, "special", special_command, 0); +@:special}{\&{special} primitive@>; +mp_primitive (mp, "fontmapfile", special_command, 1); +@:fontmapfile}{\&{fontmapfile} primitive@>; +mp_primitive (mp, "fontmapline", special_command, 2); +@:fontmapline}{\&{fontmapline} primitive@>; +mp_primitive (mp, "step", step_token, 0); +@:step_}{\&{step} primitive@>; +mp_primitive (mp, "str", str_op, 0); +@:str_}{\&{str} primitive@>; +mp_primitive (mp, "tension", tension, 0); +@:tension_}{\&{tension} primitive@>; +mp_primitive (mp, "to", to_token, 0); +@:to_}{\&{to} primitive@>; +mp_primitive (mp, "until", until_token, 0); +@:until_}{\&{until} primitive@>; +mp_primitive (mp, "within", within_token, 0); +@:within_}{\&{within} primitive@>; +mp_primitive (mp, "write", write_command, 0); @:write_}{\&{write} primitive@> + @ Each primitive has a corresponding inverse, so that it is possible to display the cryptic numeric contents of |eqtb| in symbolic form. @@ -5874,154 +4731,299 @@ straightforward code that forms part of the |print_cmd_mod| routine explained below. @<Cases of |print_cmd_mod| for symbolic printing of primitives@>= -case add_to_command:mp_print(mp, "addto"); break; -case assignment:mp_print(mp, ":="); break; -case at_least:mp_print(mp, "atleast"); break; -case bchar_label:mp_print(mp, "||:"); break; -case begin_group:mp_print(mp, "begingroup"); break; -case colon:mp_print(mp, ":"); break; -case comma:mp_print(mp, ","); break; -case controls:mp_print(mp, "controls"); break; -case curl_command:mp_print(mp, "curl"); break; -case delimiters:mp_print(mp, "delimiters"); break; -case double_colon:mp_print(mp, "::"); break; -case end_group:mp_print(mp, "endgroup"); break; -case every_job_command:mp_print(mp, "everyjob"); break; -case exit_test:mp_print(mp, "exitif"); break; -case expand_after:mp_print(mp, "expandafter"); break; -case interim_command:mp_print(mp, "interim"); break; -case left_brace:mp_print(mp, "{"); break; -case left_bracket:mp_print(mp, "["); break; -case let_command:mp_print(mp, "let"); break; -case new_internal:mp_print(mp, "newinternal"); break; -case of_token:mp_print(mp, "of"); break; -case path_join:mp_print(mp, ".."); break; -case mp_random_seed:mp_print(mp, "randomseed"); break; -case relax:mp_print_char(mp, xord('\\')); break; -case right_brace:mp_print_char(mp, xord('}')); break; -case right_bracket:mp_print_char(mp, xord(']')); break; -case save_command:mp_print(mp, "save"); break; -case scan_tokens:mp_print(mp, "scantokens"); break; -case semicolon:mp_print_char(mp, xord(';')); break; -case ship_out_command:mp_print(mp, "shipout"); break; -case skip_to:mp_print(mp, "skipto"); break; -case special_command: if ( m==2 ) mp_print(mp, "fontmapline"); else - if ( m==1 ) mp_print(mp, "fontmapfile"); else - mp_print(mp, "special"); break; -case step_token:mp_print(mp, "step"); break; -case str_op:mp_print(mp, "str"); break; -case tension:mp_print(mp, "tension"); break; -case to_token:mp_print(mp, "to"); break; -case until_token:mp_print(mp, "until"); break; -case within_token:mp_print(mp, "within"); break; -case write_command:mp_print(mp, "write"); break; +case add_to_command: +mp_print (mp, "addto"); +break; +case assignment: +mp_print (mp, ":="); +break; +case at_least: +mp_print (mp, "atleast"); +break; +case bchar_label: +mp_print (mp, "||:"); +break; +case begin_group: +mp_print (mp, "begingroup"); +break; +case colon: +mp_print (mp, ":"); +break; +case comma: +mp_print (mp, ","); +break; +case controls: +mp_print (mp, "controls"); +break; +case curl_command: +mp_print (mp, "curl"); +break; +case delimiters: +mp_print (mp, "delimiters"); +break; +case double_colon: +mp_print (mp, "::"); +break; +case end_group: +mp_print (mp, "endgroup"); +break; +case every_job_command: +mp_print (mp, "everyjob"); +break; +case exit_test: +mp_print (mp, "exitif"); +break; +case expand_after: +mp_print (mp, "expandafter"); +break; +case interim_command: +mp_print (mp, "interim"); +break; +case left_brace: +mp_print (mp, "{"); +break; +case left_bracket: +mp_print (mp, "["); +break; +case let_command: +mp_print (mp, "let"); +break; +case new_internal: +mp_print (mp, "newinternal"); +break; +case of_token: +mp_print (mp, "of"); +break; +case path_join: +mp_print (mp, ".."); +break; +case mp_random_seed: +mp_print (mp, "randomseed"); +break; +case relax: +mp_print_char (mp, xord ('\\')); +break; +case right_brace: +mp_print_char (mp, xord ('}')); +break; +case right_bracket: +mp_print_char (mp, xord (']')); +break; +case save_command: +mp_print (mp, "save"); +break; +case scan_tokens: +mp_print (mp, "scantokens"); +break; +case semicolon: +mp_print_char (mp, xord (';')); +break; +case ship_out_command: +mp_print (mp, "shipout"); +break; +case skip_to: +mp_print (mp, "skipto"); +break; +case special_command: +if (m == 2) + mp_print (mp, "fontmapline"); +else if (m == 1) + mp_print (mp, "fontmapfile"); +else + mp_print (mp, "special"); +break; +case step_token: +mp_print (mp, "step"); +break; +case str_op: +mp_print (mp, "str"); +break; +case tension: +mp_print (mp, "tension"); +break; +case to_token: +mp_print (mp, "to"); +break; +case until_token: +mp_print (mp, "until"); +break; +case within_token: +mp_print (mp, "within"); +break; +case write_command: +mp_print (mp, "write"); +break; @ We will deal with the other primitives later, at some point in the program where their |eq_type| and |equiv| values are more meaningful. For example, the primitives for macro definitions will be loaded when we consider the -routines that define macros. -It is easy to find where each particular +routines that define macros. It is easy to find where each particular primitive was treated by looking in the index at the end; for example, the section where |"def"| entered |eqtb| is listed under `\&{def} primitive'. -@* \[14] Token lists. -A \MP\ token is either symbolic or numeric or a string, or it denotes -a macro parameter or capsule; so there are five corresponding ways to encode it +@* Token lists. + +A \MP\ token is either symbolic or numeric or a string, or it denotes a macro +parameter or capsule or an internal; so there are six corresponding ways to +encode it internally: @^token@> -internally: (1)~A symbolic token whose hash code is~|p| -is represented by the number |p|, in the |info| field of a single-word -node in~|mem|. (2)~A numeric token whose |scaled| value is~|v| is -represented in a two-word node of~|mem|; the |type| field is |known|, + +(1)~A symbolic token for symbol |p| is represented by the pointer |p|, +in the |sym_sym| field of a symbolic node in~|mem|. The |type| field is |symbol_node|; +and it has a |name_type| to differentiate various subtypes of symbolic tokens, +which is usually |normal_sym|, but |macro_sym| for macro names. + +(2)~A numeric token whose |scaled| value is~|v| is +represented in a non-symbolic node of~|mem|; the |type| field is |known|, the |name_type| field is |token|, and the |value| field holds~|v|. -The fact that this token appears in a two-word node rather than a -one-word node is, of course, clear from the node address. -(3)~A string token is also represented in a two-word node; the |type| + +(3)~A string token is also represented in a non-symbolic node; the |type| field is |mp_string_type|, the |name_type| field is |token|, and the -|value| field holds the corresponding |str_number|. (4)~Capsules have -|name_type=capsule|, and their |type| and |value| fields represent -arbitrary values (in ways to be explained later). (5)~Macro parameters -are like symbolic tokens in that they appear in |info| fields of -one-word nodes. The $k$th parameter is represented by |expr_base+k| if it -is of type \&{expr}, or by |suffix_base+k| if it is of type \&{suffix}, or -by |text_base+k| if it is of type \&{text}. (Here |0<=k<param_size|.) -Actual values of these parameters are kept in a separate stack, as we will -see later. The constants |expr_base|, |suffix_base|, and |text_base| are, -of course, chosen so that there will be no confusion between symbolic -tokens and parameters of various types. - -Note that -the `\\{type}' field of a node has nothing to do with ``type'' in a +|value| field holds the corresponding |str_number|. + +(4)~Capsules have |name_type=capsule|, and their |type| and |value| fields +represent arbitrary values, with |type| different from |symbol_node| +(in ways to be explained later). + +(5)~Macro parameters appear in |sym_info| fields of symbolic nodes. The |type| +field is |symbol_node|; the $k$th parameter is represented by |k| in |sym_info|; +and |expr_sym| in |name_type|, if it is of type \&{expr}, or |suffix_sym| if it +is of type \&{suffix}, or by |text_sym| if it is of type \&{text}. + +(6)~The $k$th internal is also represented by |k| in |sym_info|; the |type| field is +|symbol_node| as for the other symbolic tokens; and |internal_sym| is its |name_type|; + +Actual values of the parameters and internals are kept in a separate +stack, as we will see later. + +Note that the `\\{type}' field of a node has nothing to do with ``type'' in a printer's sense. It's curious that the same word is used in such different ways. -@d mp_type(A) mp->mem[(A)].hh.b0 /* identifies what kind of value this is */ -@d mp_name_type(A) mp->mem[(A)].hh.b1 /* a clue to the name of this value */ -@d token_node_size 2 /* the number of words in a large token node */ -@d value_loc(A) ((A)+1) /* the word that contains the |value| field */ -@d value(A) mp->mem[value_loc((A))].cint /* the value stored in a large token node */ -@d expr_base (hash_end+1) /* code for the zeroth \&{expr} parameter */ -@d suffix_base (expr_base+mp->param_size) /* code for the zeroth \&{suffix} parameter */ -@d text_base (suffix_base+mp->param_size) /* code for the zeroth \&{text} parameter */ +@d token_node_size sizeof(mp_token_node_data) /* the number of words in a large token node */ + +@d value_sym(A) ((mp_token_node)(A))->data.sym /* the sym stored in a large token node */ +@d value(A) ((mp_token_node)(A))->data.val /* the value stored in a large token node */ + +@d set_value(A,B) do { /* store the value in a large token node */ + knot_value(A)=NULL; + str_value(A)=NULL; + value_node(A)=NULL; + value(A)=(B); + } while (0) -@<Check the ``constant''...@>= -if ( text_base+mp->param_size>max_halfword ) mp->bad=18; +@d value_node(A) ((mp_token_node)(A))->data.node /* the value stored in a large token node */ -@ We have set aside a two word node beginning at |null| so that we can have -|value(null)=0|. We will make use of this coincidence later. +@d set_value_node(A,B) do { /* store the value in a large token node */ + knot_value(A)=NULL; + str_value(A)=NULL; + value_node(A)=(B); + value(A)=0; + } while (0) + +@d str_value(A) ((mp_token_node)(A))->data.str /* the value stored in a large token node */ + +@d set_str_value(A,B) do { /* store the value in a large token node */ + knot_value(A)=NULL; + str_value(A)=(B); + value_node(A)=NULL; + value(A)=0; + } while (0) + +@d knot_value(A) ((mp_token_node)(A))->data.p /* the value stored in a large token node */ + +@d set_knot_value(A,B) do { /* store the value in a large token node */ + knot_value(A)=(B); + str_value(A)=NULL; + value_node(A)=NULL; + value(A)=0; + } while (0) + + +@(mpmp.h@>= +typedef struct mp_token_node_data { + NODE_BODY; + mp_value_data data; +} mp_token_node_data; +typedef struct mp_token_node_data *mp_token_node; + +@ +@c +static mp_node mp_get_token_node (MP mp) { + mp_token_node p = (mp_token_node) xmalloc (1, token_node_size); + add_var_used (token_node_size); + memset (p, 0, token_node_size); + p->type = mp_token_node_type; + FUNCTION_TRACE2 ("%p = mp_get_token_node()\n", p); + return (mp_node) p; +} -@<Initialize table entries...@>= -mp_link(null)=null; value(null)=0; @ A numeric token is created by the following trivial routine. -@c -static pointer mp_new_num_tok (MP mp,scaled v) { - pointer p; /* the new node */ - p=mp_get_node(mp, token_node_size); value(p)=v; - mp_type(p)=mp_known; mp_name_type(p)=mp_token; +@c +static mp_node mp_new_num_tok (MP mp, scaled v) { + mp_node p; /* the new node */ + p = mp_get_token_node (mp); + set_value (p, v); + p->type = mp_known; + p->name_type = mp_token; return p; } + @ A token list is a singly linked list of nodes in |mem|, where each node contains a token and a link. Here's a subroutine that gets rid of a token list when it is no longer needed. -@c static void mp_flush_token_list (MP mp,pointer p) { - pointer q; /* the node being recycled */ - while ( p!=null ) { - q=p; p=mp_link(p); - if ( q>=mp->hi_mem_min ) { - free_avail(q); - } else { - switch (mp_type(q)) { - case mp_vacuous: case mp_boolean_type: case mp_known: +@c +static void mp_flush_token_list (MP mp, mp_node p) { + mp_node q; /* the node being recycled */ + FUNCTION_TRACE2 ("mp_flush_token_list(%p)\n", p); + while (p != NULL) { + q = p; + p = mp_link (p); + if (mp_type (q) == mp_symbol_node) { + mp_free_symbolic_node (mp, q); + } else { + switch (mp_type (q)) { + case mp_vacuous: + case mp_boolean_type: + case mp_known: break; case mp_string_type: - delete_str_ref(value(q)); + delete_str_ref (str_value (q)); break; - case unknown_types: case mp_pen_type: case mp_path_type: - case mp_picture_type: case mp_pair_type: case mp_color_type: - case mp_cmykcolor_type: case mp_transform_type: case mp_dependent: - case mp_proto_dependent: case mp_independent: - mp_recycle_value(mp,q); + case unknown_types: + case mp_pen_type: + case mp_path_type: + case mp_picture_type: + case mp_pair_type: + case mp_color_type: + case mp_cmykcolor_type: + case mp_transform_type: + case mp_dependent: + case mp_proto_dependent: + case mp_independent: + mp_recycle_value (mp, q); break; - default: mp_confusion(mp, "token"); -@:this can't happen token}{\quad token@> + default: + mp_confusion (mp, "token"); +@:this can't happen token}{\quad token@>; } - mp_free_node(mp, q,token_node_size); + mp_free_node (mp, q, token_node_size); } } } + @ The procedure |show_token_list|, which prints a symbolic form of the token list that starts at a given node |p|, illustrates these conventions. The token list being displayed should not begin with a reference -count. However, the procedure is intended to be fairly robust, so that if the -memory links are awry or if |p| is not really a pointer to a token list, -almost nothing catastrophic can happen. +count. -An additional parameter |q| is also given; this parameter is either null +An additional parameter |q| is also given; this parameter is either NULL or it points to a node in the token list where a certain magic computation -takes place that will be explained later. (Basically, |q| is non-null when +takes place that will be explained later. (Basically, |q| is non-NULL when we are printing the two-line context information at the time of an error message; |q| marks the place corresponding to where the second line should begin.) @@ -6037,90 +5039,106 @@ Unusual entries are printed in the form of all-caps tokens preceded by a space, e.g., `\.{\char`\ BAD}'. @<Declarations@>= -static void mp_show_token_list (MP mp, integer p, integer q, integer l, - integer null_tally) ; +static void mp_show_token_list (MP mp, mp_node p, mp_node q, integer l, + integer null_tally); @ @c -void mp_show_token_list (MP mp, integer p, integer q, integer l, +void mp_show_token_list (MP mp, mp_node p, mp_node q, integer l, integer null_tally) { - quarterword class,c; /* the |char_class| of previous and new tokens */ - integer r,v; /* temporary registers */ - class=percent_class; - mp->tally=null_tally; - while ( (p!=null) && (mp->tally<l) ) { - if ( p==q ) + quarterword class, c; /* the |char_class| of previous and new tokens */ + integer r, v; /* temporary registers */ + class = percent_class; + mp->tally = null_tally; + while ((p != NULL) && (mp->tally < l)) { + if (p == q) @<Do magic computation@>; @<Display token |p| and set |c| to its class; but |return| if there are problems@>; - class=c; p=mp_link(p); + class = c; + p = mp_link (p); } - if ( p!=null ) - mp_print(mp, " ETC."); -@.ETC@> + if (p != NULL) + mp_print (mp, " ETC."); +@.ETC@>; return; } + @ @<Display token |p| and set |c| to its class...@>= -c=letter_class; /* the default */ -if ( (p<0)||(p>mp->mem_end) ) { - mp_print(mp, " CLOBBERED"); return; -@.CLOBBERED@> -} -if ( p<mp->hi_mem_min ) { - @<Display two-word token@>; -} else { - r=mp_info(p); - if ( r>=expr_base ) { - @<Display a parameter token@>; +c = letter_class; /* the default */ +if (mp_type (p) != mp_symbol_node) { + @<Display non-symbolic token@>; +} else { + if (mp_name_type (p) == mp_expr_sym || + mp_name_type (p) == mp_suffix_sym || mp_name_type (p) == mp_text_sym) { + r = mp_sym_info (p); + if (mp_name_type (p) == mp_expr_sym) { + mp_print (mp, "(EXPR"); +@.EXPR@> + } else if (mp_name_type (p) == mp_suffix_sym) { + mp_print (mp, "(SUFFIX"); +@.SUFFIX@> + } else { + mp_print (mp, "(TEXT"); +@.TEXT@> + } + mp_print_int (mp, r); + mp_print_char (mp, xord (')')); + c = right_paren_class; } else { - if ( r<1 ) { - if ( r==0 ) { - @<Display a collective subscript@> - } else { - mp_print(mp, " IMPOSSIBLE"); -@.IMPOSSIBLE@> - } - } else { - r=text(r); - if ( (r<0)||(r>mp->max_str_ptr) ) { - mp_print(mp, " NONEXISTENT"); + mp_sym sr = mp_sym_sym (p); + if (sr == 0) { + @<Display a collective subscript@> + } else { + str_number rr = text (sr); + if (rr == NULL) { + mp_print (mp, " NONEXISTENT"); @.NONEXISTENT@> } else { - @<Print string |r| as a symbolic token + @<Print string |r| as a symbolic token and set |c| to its class@>; } } } } -@ @<Display two-word token@>= -if ( mp_name_type(p)==mp_token ) { - if ( mp_type(p)==mp_known ) { + +@ @<Display non-symbolic token@>= +if (mp_name_type (p) == mp_token) { + if (mp_type (p) == mp_known) { @<Display a numeric token@>; - } else if ( mp_type(p)!=mp_string_type ) { - mp_print(mp, " BAD"); + } else if (mp_type (p) != mp_string_type) { + mp_print (mp, " BAD"); @.BAD@> - } else { - mp_print_char(mp, xord('"')); mp_print_str(mp, value(p)); mp_print_char(mp, xord('"')); - c=string_class; - } -} else if ((mp_name_type(p)!=mp_capsule)||(mp_type(p)<mp_vacuous)||(mp_type(p)>mp_independent) ) { - mp_print(mp, " BAD"); -} else { - mp_print_capsule(mp,p); c=right_paren_class; + } else { + mp_print_char (mp, xord ('"')); + mp_print_str (mp, str_value (p)); + mp_print_char (mp, xord ('"')); + c = string_class; + } +} else if ((mp_name_type (p) != mp_capsule) || (mp_type (p) < mp_vacuous) + || (mp_type (p) > mp_independent)) { + mp_print (mp, " BAD"); +} else { + mp_print_capsule (mp, p); + c = right_paren_class; } + @ @<Display a numeric token@>= -if ( class==digit_class ) - mp_print_char(mp, xord(' ')); -v=value(p); -if ( v<0 ){ - if ( class==left_bracket_class ) - mp_print_char(mp, xord(' ')); - mp_print_char(mp, xord('[')); mp_print_scaled(mp, v); mp_print_char(mp, xord(']')); - c=right_bracket_class; -} else { - mp_print_scaled(mp, v); c=digit_class; +if (class == digit_class) + mp_print_char (mp, xord (' ')); +v = value (p); +if (v < 0) { + if (class == left_bracket_class) + mp_print_char (mp, xord (' ')); + mp_print_char (mp, xord ('[')); + mp_print_scaled (mp, v); + mp_print_char (mp, xord (']')); + c = right_bracket_class; +} else { + mp_print_scaled (mp, v); + c = digit_class; } @@ -6130,54 +5148,49 @@ it is convenient to let |mp_info(p)=0| stand for `\.{[]}'. @<Display a collective subscript@>= { -if ( class==left_bracket_class ) - mp_print_char(mp, xord(' ')); -mp_print(mp, "[]"); c=right_bracket_class; -} - -@ @<Display a parameter token@>= -{ -if ( r<suffix_base ) { - mp_print(mp, "(EXPR"); r=r-(expr_base); -@.EXPR@> -} else if ( r<text_base ) { - mp_print(mp, "(SUFFIX"); r=r-(suffix_base); -@.SUFFIX@> -} else { - mp_print(mp, "(TEXT"); r=r-(text_base); -@.TEXT@> -} -mp_print_int(mp, r); mp_print_char(mp, xord(')')); c=right_paren_class; + if (class == left_bracket_class) + mp_print_char (mp, xord (' ')); + mp_print (mp, "[]"); + c = right_bracket_class; } @ @<Print string |r| as a symbolic token...@>= -{ -c=(quarterword)mp->char_class[mp->str_pool[mp->str_start[r]]]; -if ( c==class ) { - switch (c) { - case letter_class:mp_print_char(mp, xord('.')); break; - case isolated_classes: break; - default: mp_print_char(mp, xord(' ')); break; +{ + c = (quarterword) mp->char_class[(rr->str[0])]; + if (c == class) { + switch (c) { + case letter_class: + mp_print_char (mp, xord ('.')); + break; + case isolated_classes: + break; + default: + mp_print_char (mp, xord (' ')); + break; + } } + mp_print_str (mp, rr); } -mp_print_str(mp, r); -} + @ @<Declarations@>= -static void mp_print_capsule (MP mp, pointer p); +static void mp_print_capsule (MP mp, mp_node p); @ @<Declare miscellaneous procedures that were declared |forward|@>= -void mp_print_capsule (MP mp, pointer p) { - mp_print_char(mp, xord('(')); mp_print_exp(mp,p,0); mp_print_char(mp, xord(')')); +void mp_print_capsule (MP mp, mp_node p) { + mp_print_char (mp, xord ('(')); + mp_print_exp (mp, p, 0); + mp_print_char (mp, xord (')')); } + @ Macro definitions are kept in \MP's memory in the form of token lists -that have a few extra one-word nodes at the beginning. +that have a few extra symbolic nodes at the beginning. The first node contains a reference count that is used to tell when the list is no longer needed. To emphasize the fact that a reference count is -present, we shall refer to the |info| field of this special node as the +present, we shall refer to the |sym_info| field of this special node as the |ref_count| field. @^reference counts@> @@ -6185,9 +5198,10 @@ The next node or nodes after the reference count serve to describe the formal parameters. They consist of zero or more parameter tokens followed by a code for the type of macro. -@d ref_count mp_info +@d ref_count(A) mp_sym_info(A) /* reference count preceding a macro definition or picture header */ -@d add_mac_ref(A) incr(ref_count((A))) /* make a new reference to a macro list */ +@d add_mac_ref(A) set_mp_sym_info((A),ref_count((A))+1) /* make a new reference to a macro list */ +@d decr_mac_ref(A) set_mp_sym_info((A),ref_count((A))-1) /* remove a reference to a macro list */ @d general_macro 0 /* preface to a macro defined with a parameter list */ @d primary_macro 1 /* preface to a macro with a \&{primary} parameter */ @d secondary_macro 2 /* preface to a macro with a \&{secondary} parameter */ @@ -6197,54 +5211,79 @@ by a code for the type of macro. undelimited `\&{expr} |x| \&{of}~|y|' parameters */ @d suffix_macro 6 /* preface to a macro with an undelimited \&{suffix} parameter */ @d text_macro 7 /* preface to a macro with an undelimited \&{text} parameter */ +@d expr_param 8 +@d suffix_param 9 +@d text_param 10 -@c -static void mp_delete_mac_ref (MP mp,pointer p) { +@c +static void mp_delete_mac_ref (MP mp, mp_node p) { /* |p| points to the reference count of a macro list that is - losing one reference */ - if ( ref_count(p)==null ) mp_flush_token_list(mp, p); - else decr(ref_count(p)); + losing one reference */ + if (ref_count (p) == 0) + mp_flush_token_list (mp, p); + else + decr_mac_ref (p); } + @ The following subroutine displays a macro, given a pointer to its reference count. -@c -static void mp_show_macro (MP mp, pointer p, integer q, integer l) { - pointer r; /* temporary storage */ - p=mp_link(p); /* bypass the reference count */ - while ( mp_info(p)>text_macro ){ - r=mp_link(p); mp_link(p)=null; - mp_show_token_list(mp, p,null,l,0); mp_link(p)=r; p=r; - if ( l>0 ) l=l-mp->tally; else return; - } /* control printing of `\.{ETC.}' */ -@.ETC@> - mp->tally=0; - switch(mp_info(p)) { - case general_macro:mp_print(mp, "->"); break; +@c +static void mp_show_macro (MP mp, mp_node p, mp_node q, integer l) { + mp_node r; /* temporary storage */ + p = mp_link (p); /* bypass the reference count */ + while (mp_name_type (p) != mp_macro_sym) { + r = mp_link (p); + mp_link (p) = NULL; + mp_show_token_list (mp, p, NULL, l, 0); + mp_link (p) = r; + p = r; + if (l > 0) + l = l - mp->tally; + else + return; + } /* control printing of `\.{ETC.}' */ +@.ETC@>; + mp->tally = 0; + switch (mp_sym_info (p)) { + case general_macro: + mp_print (mp, "->"); + break; @.->@> - case primary_macro: case secondary_macro: case tertiary_macro: - mp_print_char(mp, xord('<')); - mp_print_cmd_mod(mp, param_type,mp_info(p)); - mp_print(mp, ">->"); + case primary_macro: + case secondary_macro: + case tertiary_macro: + mp_print_char (mp, xord ('<')); + mp_print_cmd_mod (mp, param_type, mp_sym_info (p)); + mp_print (mp, ">->"); + break; + case expr_macro: + mp_print (mp, "<expr>->"); + break; + case of_macro: + mp_print (mp, "<expr>of<primary>->"); break; - case expr_macro:mp_print(mp, "<expr>->"); break; - case of_macro:mp_print(mp, "<expr>of<primary>->"); break; - case suffix_macro:mp_print(mp, "<suffix>->"); break; - case text_macro:mp_print(mp, "<text>->"); break; - } /* there are no other cases */ - mp_show_token_list(mp, mp_link(p),q,l-mp->tally,0); + case suffix_macro: + mp_print (mp, "<suffix>->"); + break; + case text_macro: + mp_print (mp, "<text>->"); + break; + } /* there are no other cases */ + mp_show_token_list (mp, mp_link (p), q, l - mp->tally, 0); } -@* \[15] Data structures for variables. + +@* Data structures for variables. The variables of \MP\ programs can be simple, like `\.x', or they can combine the structural properties of arrays and records, like `\.{x20a.b}'. A \MP\ user assigns a type to a variable like \.{x20a.b} by saying, for example, `\.{boolean} \.{x[]a.b}'. It's time for us to study how such things are represented inside of the computer. -Each variable value occupies two consecutive words, either in a two-word -node called a value node, or as a two-word subfield of a larger node. One +Each variable value occupies two consecutive words, either in a non-symbolic +node called a value node, or as a non-symbolic subfield of a larger node. One of those two words is called the |value| field; it is an integer, containing either a |scaled| numeric value or the representation of some other type of quantity. (It might also be subdivided into halfwords, in @@ -6267,31 +5306,96 @@ and the second word is broken into two pointer fields called |attr_head| and |subscr_head|. Those fields point to additional nodes that contain structural information, as we shall see. -@d subscr_head_loc(A) (A)+1 /* where |value|, |subscr_head| and |attr_head| are */ -@d attr_head(A) mp_info(subscr_head_loc((A))) /* pointer to attribute info */ -@d subscr_head(A) mp_link(subscr_head_loc((A))) /* pointer to subscript info */ -@d value_node_size 2 /* the number of words in a value node */ +TH Note: DEK and JDH had a nice theoretical split between |value|, +|attr| and |subscr| nodes, as documented above and further +below. However, all three types had a bad habit of transmuting into +each other in practice while pointers to them still lived on +elsewhere, so using three different C structures is simply not +workable. All three are now represented as a single C structure called +|mp_value_node|. + +There is a union in this structure in the interest of space +saving: |subscript_| and |hashloc_| are mutually exclusive. + +Actually, so are |attr_head_| + |subscr_head_| on one side and and +|value_| on the other, but because of all the access macros that are +used in the code base to get at values, those cannot be folded into a +union (yet); this would have required creating a similar union in +|mp_token_node| where it would only serve to confuse things. + +Finally, |parent_| only applies in |attr| nodes (the ones that have +|hashloc_|), but creating an extra substructure inside the union just +for that does not save space and the extra complication in the +structure is not worth the minimal extra code clarification. + +@d attr_head(A) ((mp_value_node)(A))->attr_head_ /* pointer to attribute info */ +@d set_attr_head(A,B) do { + mp_node d = (B); + /* |printf("set attrhead of %p to %p on %d\n",A,d,__LINE__);| */ + attr_head((A)) = d; +} while (0) +@d subscr_head(A) ((mp_value_node)(A))->subscr_head_ /* pointer to subscript info */ +@d set_subscr_head(A,B) do { + mp_node d = (B); + /* |printf("set subcrhead of %p to %p on %d\n",A,d,__LINE__);| */ + subscr_head((A)) = d; +} while (0) + +@(mpmp.h@>= +typedef struct mp_value_node_data { + NODE_BODY; + mp_value_data data; + union { + scaled subscript_; + mp_sym hashloc_; + } v; + mp_node parent_; + mp_node attr_head_; + mp_node subscr_head_; +} mp_value_node_data; + +@ @<Declarations@>= +static mp_node mp_get_value_node (MP mp); + +@ It would have been nicer to make |mp_get_value_node| return +|mp_value_node| variables, but with |eqtb| as it stands that +became messy: lots of typecasts. So, it returns a simple +|mp_node| for now. + +@d value_node_size sizeof(struct mp_value_node_data) /* the number of words in a value node */ +@d mp_free_value_node(a,b) mp_free_node(a,b,value_node_size) + +@c +static mp_node mp_get_value_node (MP mp) { + mp_node p = xmalloc (1, value_node_size); + add_var_used (value_node_size); + memset (p, 0, value_node_size); + mp_type (p) = mp_value_node_type; + FUNCTION_TRACE2 ("%p = mp_get_value_node()\n", p); + return p; +} + @ An attribute node is three words long. Two of these words contain |type| and |value| fields as described above, and the third word contains -additional information: There is an |attr_loc| field, which contains the +additional information: There is an |hashloc| field, which contains the hash address of the token that names this attribute; and there's also a |parent| field, which points to the value node of |mp_structured| type at the next higher level (i.e., at the level to which this attribute is subsidiary). The |name_type| in an attribute node is `|attr|'. The |link| field points to the next attribute with the same parent; these are -arranged in increasing order, so that |attr_loc(mp_link(p))>attr_loc(p)|. The -final attribute node links to the constant |end_attr|, whose |attr_loc| +arranged in increasing order, so that |hashloc(mp_link(p))>hashloc(p)|. The +final attribute node links to the constant |end_attr|, whose |hashloc| field is greater than any legal hash address. The |attr_head| in the parent points to a node whose |name_type| is |mp_structured_root|; this -node represents the null attribute, i.e., the variable that is relevant +node represents the NULL attribute, i.e., the variable that is relevant when no attributes are attached to the parent. The |attr_head| node has the fields of either a value node, a subscript node, or an attribute node, depending on what the parent would be if it were not structured; but the subscript and attribute fields are ignored, so it effectively contains only the data of a value node. The |link| field in this special node points to an attribute -node whose |attr_loc| field is zero; the latter node represents a collective +node whose |hashloc| field is zero; the latter node represents a collective subscript `\.{[]}' attached to the parent, and its |link| field points to the first non-special attribute node (or to |end_attr| if there are none). @@ -6315,7 +5419,7 @@ Suppose that `\.x' and `\.{x.a}' and `\.{x[]b}' and `\.{x5}' and `\.{x20b}' have been mentioned in a user's program, where \.{x[]b} has been declared to be of \&{boolean} type. Let |h(x)|, |h(a)|, and |h(b)| be the hash addresses of \.x, \.a, and~\.b. Then -|eq_type(h(x))=name| and |equiv(h(x))=p|, where |p|~is a two-word value +|eq_type(h(x))=name| and |equiv(h(x))=p|, where |p|~is a non-symbolic value node with |mp_name_type(p)=root| and |mp_link(p)=h(x)|. We have |type(p)=mp_structured|, |attr_head(p)=q|, and |subscr_head(p)=r|, where |q| points to a value node and |r| to a subscript node. (Are you still following this? Use @@ -6323,18 +5427,18 @@ a pencil to draw a diagram.) The lone variable `\.x' is represented by |type(q)| and |value(q)|; furthermore |mp_name_type(q)=mp_structured_root| and |mp_link(q)=q1|, where |q1| points to an attribute node representing `\.{x[]}'. Thus |mp_name_type(q1)=attr|, -|attr_loc(q1)=collective_subscript=0|, |parent(q1)=p|, +|hashloc(q1)=collective_subscript=0|, |parent(q1)=p|, |type(q1)=mp_structured|, |attr_head(q1)=qq|, and |subscr_head(q1)=qq1|; |qq| is a three-word ``attribute-as-value'' node with |type(qq)=numeric_type| (assuming that \.{x5} is numeric, because |qq| represents `\.{x[]}' with no further attributes), |mp_name_type(qq)=structured_root|, -|attr_loc(qq)=0|, |parent(qq)=p|, and +|hashloc(qq)=0|, |parent(qq)=p|, and |mp_link(qq)=qq1|. (Now pay attention to the next part.) Node |qq1| is an attribute node representing `\.{x[][]}', which has never yet occurred; its |type| field is |undefined|, and its |value| field is -undefined. We have |mp_name_type(qq1)=attr|, |attr_loc(qq1)=collective_subscript|, +undefined. We have |mp_name_type(qq1)=attr|, |hashloc(qq1)=collective_subscript|, |parent(qq1)=q1|, and |mp_link(qq1)=qq2|. Since |qq2| represents -`\.{x[]b}', |type(qq2)=mp_unknown_boolean|; also |attr_loc(qq2)=h(b)|, +`\.{x[]b}', |type(qq2)=mp_unknown_boolean|; also |hashloc(qq2)=h(b)|, |parent(qq2)=q1|, |mp_name_type(qq2)=attr|, |mp_link(qq2)=end_attr|. (Maybe colored lines will help untangle your picture.) Node |r| is a subscript node with |type| and |value| @@ -6361,17 +5465,50 @@ subscript attributes do not carry actual values except for macro identifiers; branches of the structure below subscript nodes do not carry significant information in their collective subscript attributes. -@d attr_loc_loc(A) ((A)+2) /* where the |attr_loc| and |parent| fields are */ -@d attr_loc(A) mp_info(attr_loc_loc((A))) /* hash address of this attribute */ -@d parent(A) mp_link(attr_loc_loc((A))) /* pointer to |mp_structured| variable */ -@d subscript_loc(A) ((A)+2) /* where the |subscript| field lives */ -@d subscript(A) mp->mem[subscript_loc((A))].sc /* subscript of this variable */ -@d attr_node_size 3 /* the number of words in an attribute node */ -@d subscr_node_size 3 /* the number of words in a subscript node */ -@d collective_subscript 0 /* code for the attribute `\.{[]}' */ +@d hashloc(A) ((mp_value_node)(A))->v.hashloc_ /* hash address of this attribute */ +@d set_hashloc(A,B) do { + /* |printf ("set attrloc of %p to %d on %d\n", (A), d, __LINE__);| */ + ((mp_value_node)(A))->v.hashloc_ = (mp_sym)(B); + } while (0) +@d parent(A) (A)->parent_ /* pointer to |mp_structured| variable */ + +@ +@d mp_free_attr_node(a,b) mp_free_node(a,b,value_node_size) + +@c +static mp_value_node mp_get_attr_node (MP mp) { + mp_value_node p = (mp_value_node) mp_get_value_node (mp); + mp_type (p) = mp_attr_node_type; + return p; +} + + +@ Setting the |hashloc| field of |end_attr| to a value greater than +any legal hash address is done by assigning $-1$ typecasted to +|mp_sym|, hopefully resulting in all bits being set. On systems that +support negative pointer values or where typecasting $-1$ does not +result in all bits in a pointer being set, something else needs to be done. +@^system dependencies@> @<Initialize table...@>= -attr_loc(end_attr)=hash_end+1; parent(end_attr)=null; +mp->end_attr = (mp_node) mp_get_attr_node (mp); +set_hashloc (mp->end_attr, -1); +parent ((mp_value_node) mp->end_attr) = NULL; + +@ @<Free table...@>= +mp_free_attr_node (mp, mp->end_attr); + +@ +@d collective_subscript 0 /* code for the attribute `\.{[]}' */ +@d subscript(A) ((mp_value_node)(A))->v.subscript_ /* subscript of this variable */ + +@c +static mp_value_node mp_get_subscr_node (MP mp) { + mp_value_node p = (mp_value_node) mp_get_value_node (mp); + mp_type (p) = mp_subscr_node_type; + return p; +} + @ Variables of type \&{pair} will have values that point to four-word nodes containing two numeric values. The first of these values has @@ -6379,204 +5516,464 @@ nodes containing two numeric values. The first of these values has the |link| in the first points back to the node whose |value| points to this four-word node. +@d x_part_loc(A) ((mp_pair_node)(A))->x_part_ /* where the \&{xpart} is found in a pair node */ +@d y_part_loc(A) ((mp_pair_node)(A))->y_part_ /* where the \&{ypart} is found in a pair node */ + +@(mpmp.h@>= +typedef struct mp_pair_node_data { + NODE_BODY; + mp_node x_part_; + mp_node y_part_; +} mp_pair_node_data; +typedef struct mp_pair_node_data *mp_pair_node; + +@ +@d pair_node_size sizeof(struct mp_pair_node_data) /* the number of words in a subscript node */ + +@c +static mp_node mp_get_pair_node (MP mp) { + mp_pair_node p = (mp_pair_node) xmalloc (1, pair_node_size); + add_var_used (pair_node_size); + memset (p, 0, pair_node_size); + mp_type (p) = mp_pair_node_type; + FUNCTION_TRACE2("get_pair_node(): %p\n", p); + return (mp_node) p; +} + + +@ If |type(p)=mp_pair_type| or if |value(p)=NULL|, the procedure call |init_pair_node(p)| will +allocate a pair node for~|p|. The individual parts of such nodes are initially of type +|mp_independent|. + +@c +static void mp_init_pair_node (MP mp, mp_node p) { + mp_node q; /* the new node */ + mp_type (p) = mp_pair_type; + q = mp_get_pair_node (mp); + y_part_loc (q) = mp_get_value_node (mp); + new_indep (y_part_loc (q)); /* sets |type(q)| and |value(q)| */ + mp_name_type (y_part_loc (q)) = (quarterword) (mp_y_part_sector); + mp_link (y_part_loc (q)) = p; + x_part_loc (q) = mp_get_value_node (mp); + new_indep (x_part_loc (q)); /* sets |type(q)| and |value(q)| */ + mp_name_type (x_part_loc (q)) = (quarterword) (mp_x_part_sector); + mp_link (x_part_loc (q)) = p; + set_value_node (p, q); +} + + +@ Variables of type \&{transform} are similar, but in this case their |value| points to a 12-word node containing six values, identified by |x_part_sector|, |y_part_sector|, |mp_xx_part_sector|, |mp_xy_part_sector|, |mp_yx_part_sector|, and |mp_yy_part_sector|. -Finally, variables of type \&{color} have 3~values in 6~words -identified by |mp_red_part_sector|, |mp_green_part_sector|, and |mp_blue_part_sector|. -When an entire structured variable is saved, the |root| indication -is temporarily replaced by |saved_root|. +@d tx_part_loc(A) ((mp_transform_node)(A))->tx_part_ /* where the \&{xpart} is found in a transform node */ +@d ty_part_loc(A) ((mp_transform_node)(A))->ty_part_ /* where the \&{ypart} is found in a transform node */ +@d xx_part_loc(A) ((mp_transform_node)(A))->xx_part_ /* where the \&{xxpart} is found in a transform node */ +@d xy_part_loc(A) ((mp_transform_node)(A))->xy_part_ /* where the \&{xypart} is found in a transform node */ +@d yx_part_loc(A) ((mp_transform_node)(A))->yx_part_ /* where the \&{yxpart} is found in a transform node */ +@d yy_part_loc(A) ((mp_transform_node)(A))->yy_part_ /* where the \&{yypart} is found in a transform node */ -Some variables have no name; they just are used for temporary storage -while expressions are being evaluated. We call them {\sl capsules}. +@(mpmp.h@>= +typedef struct mp_transform_node_data { + NODE_BODY; + mp_node tx_part_; + mp_node ty_part_; + mp_node xx_part_; + mp_node yx_part_; + mp_node xy_part_; + mp_node yy_part_; +} mp_transform_node_data; +typedef struct mp_transform_node_data *mp_transform_node; -@d x_part_loc(A) (A) /* where the \&{xpart} is found in a pair or transform node */ -@d y_part_loc(A) ((A)+2) /* where the \&{ypart} is found in a pair or transform node */ -@d xx_part_loc(A) ((A)+4) /* where the \&{xxpart} is found in a transform node */ -@d xy_part_loc(A) ((A)+6) /* where the \&{xypart} is found in a transform node */ -@d yx_part_loc(A) ((A)+8) /* where the \&{yxpart} is found in a transform node */ -@d yy_part_loc(A) ((A)+10) /* where the \&{yypart} is found in a transform node */ -@d red_part_loc(A) (A) /* where the \&{redpart} is found in a color node */ -@d green_part_loc(A) ((A)+2) /* where the \&{greenpart} is found in a color node */ -@d blue_part_loc(A) ((A)+4) /* where the \&{bluepart} is found in a color node */ -@d cyan_part_loc(A) (A) /* where the \&{cyanpart} is found in a color node */ -@d magenta_part_loc(A) ((A)+2) /* where the \&{magentapart} is found in a color node */ -@d yellow_part_loc(A) ((A)+4) /* where the \&{yellowpart} is found in a color node */ -@d black_part_loc(A) ((A)+6) /* where the \&{blackpart} is found in a color node */ -@d grey_part_loc(A) (A) /* where the \&{greypart} is found in a color node */ -@# -@d pair_node_size 4 /* the number of words in a pair node */ -@d transform_node_size 12 /* the number of words in a transform node */ -@d color_node_size 6 /* the number of words in a color node */ -@d cmykcolor_node_size 8 /* the number of words in a color node */ +@ +@d transform_node_size sizeof(struct mp_transform_node_data) /* the number of words in a subscript node */ -@<Glob...@>= -quarterword big_node_size[mp_pair_type+1]; -quarterword sector0[mp_pair_type+1]; -quarterword sector_offset[mp_black_part_sector+1]; +@c +static mp_node mp_get_transform_node (MP mp) { + mp_transform_node p = (mp_transform_node) xmalloc (1, transform_node_size); + add_var_used (transform_node_size); + memset (p, 0, transform_node_size); + mp_type (p) = mp_transform_node_type; + return (mp_node) p; +} -@ The |sector0| array gives for each big node type, |name_type| values -for its first subfield; the |sector_offset| array gives for each -|name_type| value, the offset from the first subfield in words; -and the |big_node_size| array gives the size in words for each type of -big node. -@<Set init...@>= -mp->big_node_size[mp_transform_type]=transform_node_size; -mp->big_node_size[mp_pair_type]=pair_node_size; -mp->big_node_size[mp_color_type]=color_node_size; -mp->big_node_size[mp_cmykcolor_type]=cmykcolor_node_size; -mp->sector0[mp_transform_type]=mp_x_part_sector; -mp->sector0[mp_pair_type]=mp_x_part_sector; -mp->sector0[mp_color_type]=mp_red_part_sector; -mp->sector0[mp_cmykcolor_type]=mp_cyan_part_sector; -for (k=mp_x_part_sector;k<= mp_yy_part_sector;k++ ) { - mp->sector_offset[k]=(quarterword)(2*(k-mp_x_part_sector)); -} -for (k=mp_red_part_sector;k<= mp_blue_part_sector ; k++) { - mp->sector_offset[k]=(quarterword)(2*(k-mp_red_part_sector)); -} -for (k=mp_cyan_part_sector;k<= mp_black_part_sector;k++ ) { - mp->sector_offset[k]=(quarterword)(2*(k-mp_cyan_part_sector)); -} - -@ If |type(p)=mp_pair_type| or |mp_transform_type| and if |value(p)=null|, the -procedure call |init_big_node(p)| will allocate a pair or transform node -for~|p|. The individual parts of such nodes are initially of type -|mp_independent|. +@ @c +static void mp_init_transform_node (MP mp, mp_node p) { + mp_node q; /* the new node */ + mp_type (p) = mp_transform_type; + q = mp_get_transform_node (mp); /* big node */ + yy_part_loc (q) = mp_get_value_node (mp); + new_indep (yy_part_loc (q)); /* sets |type(q)| and |value(q)| */ + mp_name_type (yy_part_loc (q)) = (quarterword) (mp_yy_part_sector); + mp_link (yy_part_loc (q)) = p; + yx_part_loc (q) = mp_get_value_node (mp); + new_indep (yx_part_loc (q)); /* sets |type(q)| and |value(q)| */ + mp_name_type (yx_part_loc (q)) = (quarterword) (mp_yx_part_sector); + mp_link (yx_part_loc (q)) = p; + xy_part_loc (q) = mp_get_value_node (mp); + new_indep (xy_part_loc (q)); /* sets |type(q)| and |value(q)| */ + mp_name_type (xy_part_loc (q)) = (quarterword) (mp_xy_part_sector); + mp_link (xy_part_loc (q)) = p; + xx_part_loc (q) = mp_get_value_node (mp); + new_indep (xx_part_loc (q)); /* sets |type(q)| and |value(q)| */ + mp_name_type (xx_part_loc (q)) = (quarterword) (mp_xx_part_sector); + mp_link (xx_part_loc (q)) = p; + ty_part_loc (q) = mp_get_value_node (mp); + new_indep (ty_part_loc (q)); /* sets |type(q)| and |value(q)| */ + mp_name_type (ty_part_loc (q)) = (quarterword) (mp_y_part_sector); + mp_link (ty_part_loc (q)) = p; + tx_part_loc (q) = mp_get_value_node (mp); + new_indep (tx_part_loc (q)); /* sets |type(q)| and |value(q)| */ + mp_name_type (tx_part_loc (q)) = (quarterword) (mp_x_part_sector); + mp_link (tx_part_loc (q)) = p; + set_value_node (p, q); +} -@c -static void mp_init_big_node (MP mp,pointer p) { - pointer q; /* the new node */ - quarterword s; /* its size */ - s=mp->big_node_size[mp_type(p)]; q=mp_get_node(mp, s); - do { - s=(quarterword)(s-2); - @<Make variable |q+s| newly independent@>; - mp_name_type(q+s)=(quarterword)(halfp(s)+mp->sector0[mp_type(p)]); - mp_link(q+s)=null; - } while (s!=0); - mp_link(q)=p; value(p)=q; + +@ +Variables of type \&{color} have 3~values in 6~words identified by |mp_red_part_sector|, +|mp_green_part_sector|, and |mp_blue_part_sector|. + +@d red_part_loc(A) ((mp_color_node)(A))->red_part_ /* where the \&{redpart} is found in a color node */ +@d green_part_loc(A) ((mp_color_node)(A))->green_part_ /* where the \&{greenpart} is found in a color node */ +@d blue_part_loc(A) ((mp_color_node)(A))->blue_part_ /* where the \&{bluepart} is found in a color node */ + +@d grey_part_loc(A) red_part_loc(A) /* where the \&{greypart} is found in a color node */ + +@(mpmp.h@>= +typedef struct mp_color_node_data { + NODE_BODY; + mp_node red_part_; + mp_node green_part_; + mp_node blue_part_; +} mp_color_node_data; +typedef struct mp_color_node_data *mp_color_node; + +@ +@d color_node_size sizeof(struct mp_color_node_data) /* the number of words in a subscript node */ + +@c +static mp_node mp_get_color_node (MP mp) { + mp_color_node p = (mp_color_node) xmalloc (1, color_node_size); + add_var_used (color_node_size); + memset (p, 0, color_node_size); + mp_type (p) = mp_color_node_type; + p->link = NULL; + return (mp_node) p; +} + + +@ +@c +static void mp_init_color_node (MP mp, mp_node p) { + mp_node q; /* the new node */ + mp_type (p) = mp_color_type; + q = mp_get_color_node (mp); /* big node */ + blue_part_loc (q) = mp_get_value_node (mp); + new_indep (blue_part_loc (q)); /* sets |type(q)| and |value(q)| */ + mp_name_type (blue_part_loc (q)) = (quarterword) (mp_blue_part_sector); + mp_link (blue_part_loc (q)) = p; + green_part_loc (q) = mp_get_value_node (mp); + new_indep (green_part_loc (q)); /* sets |type(q)| and |value(q)| */ + mp_name_type (y_part_loc (q)) = (quarterword) (mp_green_part_sector); + mp_link (green_part_loc (q)) = p; + red_part_loc (q) = mp_get_value_node (mp); + new_indep (red_part_loc (q)); /* sets |type(q)| and |value(q)| */ + mp_name_type (red_part_loc (q)) = (quarterword) (mp_red_part_sector); + mp_link (red_part_loc (q)) = p; + set_value_node (p, q); +} + + +@ Finally, variables of type |cmykcolor|. + +@d cyan_part_loc(A) ((mp_cmykcolor_node)(A))->cyan_part_ /* where the \&{cyanpart} is found in a color node */ +@d magenta_part_loc(A) ((mp_cmykcolor_node)(A))->magenta_part_ /* where the \&{magentapart} is found in a color node */ +@d yellow_part_loc(A) ((mp_cmykcolor_node)(A))->yellow_part_ /* where the \&{yellowpart} is found in a color node */ +@d black_part_loc(A) ((mp_cmykcolor_node)(A))->black_part_ /* where the \&{blackpart} is found in a color node */ + +@(mpmp.h@>= +typedef struct mp_cmykcolor_node_data { + NODE_BODY; + mp_node cyan_part_; + mp_node magenta_part_; + mp_node yellow_part_; + mp_node black_part_; +} mp_cmykcolor_node_data; +typedef struct mp_cmykcolor_node_data *mp_cmykcolor_node; + +@ +@d cmykcolor_node_size sizeof(struct mp_cmykcolor_node_data) /* the number of words in a subscript node */ + +@c +static mp_node mp_get_cmykcolor_node (MP mp) { + mp_cmykcolor_node p = (mp_cmykcolor_node) xmalloc (1, cmykcolor_node_size); + add_var_used (cmykcolor_node_size); + memset (p, 0, cmykcolor_node_size); + mp_type (p) = mp_cmykcolor_node_type; + p->link = NULL; + return (mp_node) p; } + +@ +@c +static void mp_init_cmykcolor_node (MP mp, mp_node p) { + mp_node q; /* the new node */ + mp_type (p) = mp_cmykcolor_type; + q = mp_get_cmykcolor_node (mp); /* big node */ + black_part_loc (q) = mp_get_value_node (mp); + new_indep (black_part_loc (q)); /* sets |type(q)| and |value(q)| */ + mp_name_type (black_part_loc (q)) = (quarterword) (mp_black_part_sector); + mp_link (black_part_loc (q)) = p; + yellow_part_loc (q) = mp_get_value_node (mp); + new_indep (yellow_part_loc (q)); /* sets |type(q)| and |value(q)| */ + mp_name_type (yellow_part_loc (q)) = (quarterword) (mp_yellow_part_sector); + mp_link (yellow_part_loc (q)) = p; + magenta_part_loc (q) = mp_get_value_node (mp); + new_indep (magenta_part_loc (q)); /* sets |type(q)| and |value(q)| */ + mp_name_type (magenta_part_loc (q)) = (quarterword) (mp_magenta_part_sector); + mp_link (magenta_part_loc (q)) = p; + cyan_part_loc (q) = mp_get_value_node (mp); + new_indep (cyan_part_loc (q)); /* sets |type(q)| and |value(q)| */ + mp_name_type (cyan_part_loc (q)) = (quarterword) (mp_cyan_part_sector); + mp_link (cyan_part_loc (q)) = p; + set_value_node (p, q); +} + + +@ When an entire structured variable is saved, the |root| indication +is temporarily replaced by |saved_root|. + +Some variables have no name; they just are used for temporary storage +while expressions are being evaluated. We call them {\sl capsules}. + @ The |id_transform| function creates a capsule for the identity transformation. -@c -static pointer mp_id_transform (MP mp) { - pointer p,q,r; /* list manipulation registers */ - p=mp_get_node(mp, value_node_size); mp_type(p)=mp_transform_type; - mp_name_type(p)=mp_capsule; value(p)=null; mp_init_big_node(mp, p); q=value(p); - r=q+transform_node_size; - do { - r=r-2; - mp_type(r)=mp_known; value(r)=0; - } while (r!=q); - value(xx_part_loc(q))=unity; - value(yy_part_loc(q))=unity; +@c +static mp_node mp_id_transform (MP mp) { + mp_node p, q; /* list manipulation registers */ + p = mp_get_value_node (mp); + mp_name_type (p) = mp_capsule; + set_value (p, 0); /* todo: this was |null| */ + mp_init_transform_node (mp, p); + q = value_node (p); + mp_type (tx_part_loc (q)) = mp_known; + set_value (tx_part_loc (q), 0); + mp_type (ty_part_loc (q)) = mp_known; + set_value (ty_part_loc (q), 0); + mp_type (xy_part_loc (q)) = mp_known; + set_value (xy_part_loc (q), 0); + mp_type (yx_part_loc (q)) = mp_known; + set_value (yx_part_loc (q), 0); + mp_type (xx_part_loc (q)) = mp_known; + set_value (xx_part_loc (q), unity); + mp_type (yy_part_loc (q)) = mp_known; + set_value (yy_part_loc (q), unity); return p; } + @ Tokens are of type |tag_token| when they first appear, but they point -to |null| until they are first used as the root of a variable. +to |NULL| until they are first used as the root of a variable. The following subroutine establishes the root node on such grand occasions. -@c -static void mp_new_root (MP mp,pointer x) { - pointer p; /* the new node */ - p=mp_get_node(mp, value_node_size); mp_type(p)=undefined; mp_name_type(p)=mp_root; - mp_link(p)=x; equiv(x)=p; +@c +static void mp_new_root (MP mp, mp_sym x) { + mp_node p; /* the new node */ + p = mp_get_value_node (mp); + mp_type (p) = undefined; + mp_name_type (p) = mp_root; + value_sym (p) = x; + equiv_node (x) = p; } + @ These conventions for variable representation are illustrated by the |print_variable_name| routine, which displays the full name of a -variable given only a pointer to its two-word value packet. +variable given only a pointer to its value. @<Declarations@>= -static void mp_print_variable_name (MP mp, pointer p); +static void mp_print_variable_name (MP mp, mp_node p); -@ @c -void mp_print_variable_name (MP mp, pointer p) { - pointer q; /* a token list that will name the variable's suffix */ - pointer r; /* temporary for token list creation */ - while ( mp_name_type(p)>=mp_x_part_sector ) { +@ @c +void mp_print_variable_name (MP mp, mp_node p) { + mp_node q; /* a token list that will name the variable's suffix */ + mp_node r; /* temporary for token list creation */ + while (mp_name_type (p) >= mp_x_part_sector) { @<Preface the output with a part specifier; |return| in the case of a capsule@>; } - q=null; - while ( mp_name_type(p)>mp_saved_root ) { + q = NULL; + while (mp_name_type (p) > mp_saved_root) { @<Ascend one level, pushing a token onto list |q| and replacing |p| by its parent@>; } - r=mp_get_avail(mp); mp_info(r)=mp_link(p); mp_link(r)=q; - if ( mp_name_type(p)==mp_saved_root ) mp_print(mp, "(SAVED)"); -@.SAVED@> - mp_show_token_list(mp, r,null,el_gordo,mp->tally); - mp_flush_token_list(mp, r); + /* now |link(p)| is the hash address of |p|, and + |name_type(p)| is either |root| or |saved_root|. + Have to prepend a token to |q| for |show_token_list|. + */ + r = mp_get_symbolic_node (mp); + set_mp_sym_sym (r, value_sym (p)); + mp_link (r) = q; + if (mp_name_type (p) == mp_saved_root) + mp_print (mp, "(SAVED)"); +@.SAVED@>; + mp_show_token_list (mp, r, NULL, max_integer, mp->tally); + mp_flush_token_list (mp, r); } + @ @<Ascend one level, pushing a token onto list |q|...@>= -{ - if ( mp_name_type(p)==mp_subscr ) { - r=mp_new_num_tok(mp, subscript(p)); - do { - p=mp_link(p); - } while (mp_name_type(p)!=mp_attr); - } else if ( mp_name_type(p)==mp_structured_root ) { - p=mp_link(p); goto FOUND; - } else { - if ( mp_name_type(p)!=mp_attr ) mp_confusion(mp, "var"); -@:this can't happen var}{\quad var@> - r=mp_get_avail(mp); mp_info(r)=attr_loc(p); - } - mp_link(r)=q; q=r; -FOUND: - p=parent(p); +{ + if (mp_name_type (p) == mp_subscr) { + r = mp_new_num_tok (mp, subscript (p)); + do { + p = mp_link (p); + } while (mp_name_type (p) != mp_attr); + } else if (mp_name_type (p) == mp_structured_root) { + p = mp_link (p); + goto FOUND; + } else { + if (mp_name_type (p) != mp_attr) + mp_confusion (mp, "var"); +@:this can't happen var}{\quad var@>; + r = mp_get_symbolic_node (mp); + set_mp_sym_sym (r, hashloc (p)); /* the hash address */ + } + set_mp_link (r, q); + q = r; +FOUND: + p = parent ((mp_value_node) p); } + @ @<Preface the output with a part specifier...@>= -{ switch (mp_name_type(p)) { - case mp_x_part_sector: mp_print_char(mp, xord('x')); break; - case mp_y_part_sector: mp_print_char(mp, xord('y')); break; - case mp_xx_part_sector: mp_print(mp, "xx"); break; - case mp_xy_part_sector: mp_print(mp, "xy"); break; - case mp_yx_part_sector: mp_print(mp, "yx"); break; - case mp_yy_part_sector: mp_print(mp, "yy"); break; - case mp_red_part_sector: mp_print(mp, "red"); break; - case mp_green_part_sector: mp_print(mp, "green"); break; - case mp_blue_part_sector: mp_print(mp, "blue"); break; - case mp_cyan_part_sector: mp_print(mp, "cyan"); break; - case mp_magenta_part_sector: mp_print(mp, "magenta"); break; - case mp_yellow_part_sector: mp_print(mp, "yellow"); break; - case mp_black_part_sector: mp_print(mp, "black"); break; - case mp_grey_part_sector: mp_print(mp, "grey"); break; - case mp_capsule: - mp_print(mp, "%CAPSULE"); mp_print_int(mp, p-null); return; +{ + switch (mp_name_type (p)) { + case mp_x_part_sector: + mp_print (mp, "x"); + break; + case mp_y_part_sector: + mp_print (mp, "y"); + break; + case mp_xx_part_sector: + mp_print (mp, "xx"); + break; + case mp_xy_part_sector: + mp_print (mp, "xy"); + break; + case mp_yx_part_sector: + mp_print (mp, "yx"); + break; + case mp_yy_part_sector: + mp_print (mp, "yy"); + break; + case mp_red_part_sector: + mp_print (mp, "red"); + break; + case mp_green_part_sector: + mp_print (mp, "green"); + break; + case mp_blue_part_sector: + mp_print (mp, "blue"); + break; + case mp_cyan_part_sector: + mp_print (mp, "cyan"); + break; + case mp_magenta_part_sector: + mp_print (mp, "magenta"); + break; + case mp_yellow_part_sector: + mp_print (mp, "yellow"); + break; + case mp_black_part_sector: + mp_print (mp, "black"); + break; + case mp_grey_part_sector: + mp_print (mp, "grey"); + break; + case mp_capsule: + { + char pval[19]; /* allow 64bit pointers, + "0x" */ + mp_print (mp, "%CAPSULE"); + sprintf (pval, "%p", p); + mp_print (mp, pval); + return; + } break; @.CAPSULE@> - } /* there are no other cases */ - mp_print(mp, "part "); - p=mp_link(p-mp->sector_offset[mp_name_type(p)]); + } /* there are no other cases */ + mp_print (mp, "part "); + p = mp_link (p); } + @ The |interesting| function returns |true| if a given variable is not in a capsule, or if the user wants to trace capsules. -@c -static boolean mp_interesting (MP mp,pointer p) { - quarterword t; /* a |name_type| */ - if ( mp->internal[mp_tracing_capsules]>0 ) { +@c +static boolean mp_interesting (MP mp, mp_node p) { + quarterword t; /* a |name_type| */ + if (internal_value (mp_tracing_capsules) > 0) { return true; - } else { - t=mp_name_type(p); - if ( t>=mp_x_part_sector ) if ( t!=mp_capsule ) - t=mp_name_type(mp_link(p-mp->sector_offset[t])); - return (t!=mp_capsule); + } else { + t = mp_name_type (p); + if (t >= mp_x_part_sector && t != mp_capsule) { + switch (t) { + case mp_x_part_sector: + t = mp_name_type (mp_link (x_part_loc (p))); + break; + case mp_y_part_sector: + t = mp_name_type (mp_link (y_part_loc (p))); + break; + case mp_xx_part_sector: + t = mp_name_type (mp_link (xx_part_loc (p))); + break; + case mp_xy_part_sector: + t = mp_name_type (mp_link (xy_part_loc (p))); + break; + case mp_yx_part_sector: + t = mp_name_type (mp_link (yx_part_loc (p))); + break; + case mp_yy_part_sector: + t = mp_name_type (mp_link (yy_part_loc (p))); + break; + case mp_red_part_sector: + t = mp_name_type (mp_link (red_part_loc (p))); + break; + case mp_green_part_sector: + t = mp_name_type (mp_link (green_part_loc (p))); + break; + case mp_blue_part_sector: + t = mp_name_type (mp_link (blue_part_loc (p))); + break; + case mp_cyan_part_sector: + t = mp_name_type (mp_link (cyan_part_loc (p))); + break; + case mp_magenta_part_sector: + t = mp_name_type (mp_link (magenta_part_loc (p))); + break; + case mp_yellow_part_sector: + t = mp_name_type (mp_link (yellow_part_loc (p))); + break; + case mp_black_part_sector: + t = mp_name_type (mp_link (black_part_loc (p))); + break; + case mp_grey_part_sector: + t = mp_name_type (mp_link (grey_part_loc (p))); + break; + } + } } + return (t != mp_capsule); } + @ Now here is a subroutine that converts an unstructured type into an equivalent structured type, by inserting a |mp_structured| node that is capable of growing. This operation is done only when |mp_name_type(p)=root|, @@ -6586,66 +5983,105 @@ The procedure returns a pointer to the new node that has taken node~|p|'s place in the structure. Node~|p| itself does not move, nor are its |value| or |type| fields changed in any way. -@c -static pointer mp_new_structure (MP mp,pointer p) { - pointer q,r=0; /* list manipulation registers */ - switch (mp_name_type(p)) { - case mp_root: - q=mp_link(p); r=mp_get_node(mp, value_node_size); equiv(q)=r; +@c +static mp_node mp_new_structure (MP mp, mp_node p) { + mp_node q, r = NULL; /* list manipulation registers */ + mp_sym qq = NULL; + switch (mp_name_type (p)) { + case mp_root: + { + qq = value_sym (p); + r = mp_get_value_node (mp); + equiv_node (qq) = r; + } break; - case mp_subscr: + case mp_subscr: @<Link a new subscript node |r| in place of node |p|@>; break; - case mp_attr: + case mp_attr: @<Link a new attribute node |r| in place of node |p|@>; break; - default: - mp_confusion(mp, "struct"); -@:this can't happen struct}{\quad struct@> + default: + mp_confusion (mp, "struct"); +@:this can't happen struct}{\quad struct@>; break; } - mp_link(r)=mp_link(p); mp_type(r)=mp_structured; mp_name_type(r)=mp_name_type(p); - attr_head(r)=p; mp_name_type(p)=mp_structured_root; - q=mp_get_node(mp, attr_node_size); mp_link(p)=q; subscr_head(r)=q; - parent(q)=r; mp_type(q)=undefined; mp_name_type(q)=mp_attr; mp_link(q)=end_attr; - attr_loc(q)=collective_subscript; + set_mp_link (r, mp_link (p)); + value_sym (r) = value_sym (p); + mp_type (r) = mp_structured; + mp_name_type (r) = mp_name_type (p); + set_attr_head (r, p); + mp_name_type (p) = mp_structured_root; + { + mp_value_node qqr = mp_get_attr_node (mp); + set_mp_link (p, (mp_node) qqr); + set_subscr_head (r, (mp_node) qqr); + parent (qqr) = r; + mp_type (qqr) = undefined; + mp_name_type (qqr) = mp_attr; + set_mp_link (qqr, mp->end_attr); + set_hashloc (qqr, collective_subscript); + } return r; } + @ @<Link a new subscript node |r| in place of node |p|@>= -{ - q=p; - do { - q=mp_link(q); - } while (mp_name_type(q)!=mp_attr); - q=parent(q); r=subscr_head_loc(q); /* |mp_link(r)=subscr_head(q)| */ - do { - q=r; r=mp_link(r); - } while (r!=p); - r=mp_get_node(mp, subscr_node_size); - mp_link(q)=r; subscript(r)=subscript(p); +{ + mp_node q_new; + q = p; + do { + q = mp_link (q); + } while (mp_name_type (q) != mp_attr); + q = parent ((mp_value_node) q); + r = mp->temp_head; + set_mp_link (r, subscr_head (q)); + do { + q_new = r; + r = mp_link (r); + } while (r != p); + r = (mp_node) mp_get_subscr_node (mp); + if (q_new == mp->temp_head) { + subscr_head (q) = r; + } else { + set_mp_link (q_new, r); + } + subscript (r) = subscript (p); } + @ If the attribute is |collective_subscript|, there are two pointers to node~|p|, so we must change both of them. @<Link a new attribute node |r| in place of node |p|@>= -{ - q=parent(p); r=attr_head(q); - do { - q=r; r=mp_link(r); - } while (r!=p); - r=mp_get_node(mp, attr_node_size); mp_link(q)=r; - mp->mem[attr_loc_loc(r)]=mp->mem[attr_loc_loc(p)]; /* copy |attr_loc| and |parent| */ - if ( attr_loc(p)==collective_subscript ) { - q=subscr_head_loc(parent(p)); - while ( mp_link(q)!=p ) q=mp_link(q); - mp_link(q)=r; +{ + mp_value_node rr; + q = parent ((mp_value_node) p); + r = attr_head (q); + do { + q = r; + r = mp_link (r); + } while (r != p); + rr = mp_get_attr_node (mp); + r = (mp_node) rr; + set_mp_link (q, (mp_node) rr); + set_hashloc (rr, hashloc (p)); + parent (rr) = parent ((mp_value_node) p); + if (hashloc (p) == collective_subscript) { + q = mp->temp_head; + set_mp_link (q, subscr_head (parent ((mp_value_node) p))); + while (mp_link (q) != p) + q = mp_link (q); + if (q == mp->temp_head) + subscr_head (parent ((mp_value_node) p)) = (mp_node) rr; + else + set_mp_link (q, (mp_node) rr); } } + @ The |find_variable| routine is given a pointer~|t| to a nonempty token -list of suffixes; it returns a pointer to the corresponding two-word +list of suffixes; it returns a pointer to the corresponding non-symbolic value. For example, if |t| points to token \.x followed by a numeric token containing the value~7, |find_variable| finds where the value of \.{x7} is stored in memory. This may seem a simple task, and it @@ -6654,56 +6090,75 @@ Indeed, \.x may never have even been subscripted before; complexities arise with respect to updating the collective subscript information. If a macro type is detected anywhere along path~|t|, or if the first -item on |t| isn't a |tag_token|, the value |null| is returned. -Otherwise |p| will be a non-null pointer to a node such that +item on |t| isn't a |tag_token|, the value |NULL| is returned. +Otherwise |p| will be a non-NULL pointer to a node such that |undefined<type(p)<mp_structured|. -@d abort_find { return null; } +@d abort_find { return NULL; } -@c -static pointer mp_find_variable (MP mp,pointer t) { - pointer p,q,r,s; /* nodes in the ``value'' line */ - pointer pp,qq,rr,ss; /* nodes in the ``collective'' line */ - integer n; /* subscript or attribute */ - memory_word save_word; /* temporary storage for a word of |mem| */ -@^inner loop@> - p=mp_info(t); t=mp_link(t); - if ( (eq_type(p) % outer_tag) != tag_token ) abort_find; - if ( equiv(p)==null ) mp_new_root(mp, p); - p=equiv(p); pp=p; - while ( t!=null ) { +@c +static mp_node mp_find_variable (MP mp, mp_node t) { + mp_node p, q, r, s; /* nodes in the ``value'' line */ + mp_sym p_sym; + mp_node pp, qq, rr, ss; /* nodes in the ``collective'' line */ + integer n; /* subscript or attribute */ +@^inner loop@>; + p_sym = mp_sym_sym (t); + t = mp_link (t); + if ((eq_type (p_sym) % outer_tag) != tag_token) + abort_find; + if (equiv_node (p_sym) == NULL) + mp_new_root (mp, p_sym); + p = equiv_node (p_sym); + pp = p; + while (t != NULL) { @<Make sure that both nodes |p| and |pp| are of |mp_structured| type@>; - if ( t<mp->hi_mem_min ) { + if (mp_type (t) != mp_symbol_node) { @<Descend one level for the subscript |value(t)|@> } else { - @<Descend one level for the attribute |mp_info(t)|@>; + @<Descend one level for the attribute |mp_sym_info(t)|@>; } - t=mp_link(t); + t = mp_link (t); } - if ( mp_type(pp)>=mp_structured ) { - if ( mp_type(pp)==mp_structured ) pp=attr_head(pp); else abort_find; + if (mp_type (pp) >= mp_structured) { + if (mp_type (pp) == mp_structured) + pp = attr_head (pp); + else + abort_find; + } + if (mp_type (p) == mp_structured) + p = attr_head (p); + if (mp_type (p) == undefined) { + if (mp_type (pp) == undefined) { + mp_type (pp) = mp_numeric_type; + set_value (pp, 0); /* todo: this was |null| */ + } + mp_type (p) = mp_type (pp); + set_value (p, 0); /* todo: this was |null| */ } - if ( mp_type(p)==mp_structured ) p=attr_head(p); - if ( mp_type(p)==undefined ) { - if ( mp_type(pp)==undefined ) { mp_type(pp)=mp_numeric_type; value(pp)=null; }; - mp_type(p)=mp_type(pp); value(p)=null; - }; return p; } + @ Although |pp| and |p| begin together, they diverge when a subscript occurs; |pp|~stays in the collective line while |p|~goes through actual subscript values. @<Make sure that both nodes |p| and |pp|...@>= -if ( mp_type(pp)!=mp_structured ) { - if ( mp_type(pp)>mp_structured ) abort_find; - ss=mp_new_structure(mp, pp); - if ( p==pp ) p=ss; - pp=ss; -}; /* now |type(pp)=mp_structured| */ -if ( mp_type(p)!=mp_structured ) /* it cannot be |>mp_structured| */ - p=mp_new_structure(mp, p) /* now |type(p)=mp_structured| */ +{ + if (mp_type (pp) != mp_structured) { + if (mp_type (pp) > mp_structured) + abort_find; + ss = mp_new_structure (mp, pp); + if (p == pp) + p = ss; + pp = ss; + } /* now |type(pp)=mp_structured| */ + if (mp_type (p) != mp_structured) { /* it cannot be |>mp_structured| */ + p = mp_new_structure (mp, p); /* now |type(p)=mp_structured| */ + } +} + @ We want this part of the program to be reasonably fast, in case there are @^inner loop@> @@ -6712,52 +6167,81 @@ we store an ``infinite'' value in the word that appears at the end of the subscript list, even though that word isn't part of a subscript node. @<Descend one level for the subscript |value(t)|@>= -{ - n=value(t); - pp=mp_link(attr_head(pp)); /* now |attr_loc(pp)=collective_subscript| */ - q=mp_link(attr_head(p)); save_word=mp->mem[subscript_loc(q)]; - subscript(q)=el_gordo; s=subscr_head_loc(p); /* |mp_link(s)=subscr_head(p)| */ - do { - r=s; s=mp_link(s); - } while (n>subscript(s)); - if ( n==subscript(s) ) { - p=s; - } else { - p=mp_get_node(mp, subscr_node_size); mp_link(r)=p; mp_link(p)=s; - subscript(p)=n; mp_name_type(p)=mp_subscr; mp_type(p)=undefined; +{ + halfword save_subscript; /* temporary storage */ + n = value (t); + pp = mp_link (attr_head (pp)); /* now |hashloc(pp)=collective_subscript| */ + q = mp_link (attr_head (p)); + save_subscript = subscript (q); + subscript (q) = EL_GORDO; + s = mp->temp_head; + set_mp_link (s, subscr_head (p)); + do { + r = s; + s = mp_link (s); + } while (n > subscript (s)); + if (n == subscript (s)) { + p = s; + } else { + mp_value_node pp = mp_get_subscr_node (mp); + if (r == mp->temp_head) + set_subscr_head (p, (mp_node) pp); + else + set_mp_link (r, (mp_node) pp); + set_mp_link (pp, s); + subscript (pp) = n; + mp_name_type (pp) = mp_subscr; + mp_type (pp) = undefined; + p = (mp_node) pp; } - mp->mem[subscript_loc(q)]=save_word; + subscript (q) = save_subscript; } -@ @<Descend one level for the attribute |mp_info(t)|@>= -{ - n=mp_info(t); - ss=attr_head(pp); - do { - rr=ss; ss=mp_link(ss); - } while (n>attr_loc(ss)); - if ( n<attr_loc(ss) ) { - qq=mp_get_node(mp, attr_node_size); mp_link(rr)=qq; mp_link(qq)=ss; - attr_loc(qq)=n; mp_name_type(qq)=mp_attr; mp_type(qq)=undefined; - parent(qq)=pp; ss=qq; - } - if ( p==pp ) { - p=ss; pp=ss; - } else { - pp=ss; s=attr_head(p); - do { - r=s; s=mp_link(s); - } while (n>attr_loc(s)); - if ( n==attr_loc(s) ) { - p=s; - } else { - q=mp_get_node(mp, attr_node_size); mp_link(r)=q; mp_link(q)=s; - attr_loc(q)=n; mp_name_type(q)=mp_attr; mp_type(q)=undefined; - parent(q)=p; p=q; + +@ @<Descend one level for the attribute |mp_sym_info(t)|@>= +{ + mp_sym nn = mp_sym_sym (t); + ss = attr_head (pp); + do { + rr = ss; + ss = mp_link (ss); + } while (nn > hashloc (ss)); + if (nn < hashloc (ss)) { + qq = (mp_node) mp_get_attr_node (mp); + set_mp_link (rr, qq); + set_mp_link (qq, ss); + set_hashloc (qq, nn); + mp_name_type (qq) = mp_attr; + mp_type (qq) = undefined; + parent ((mp_value_node) qq) = pp; + ss = qq; + } + if (p == pp) { + p = ss; + pp = ss; + } else { + pp = ss; + s = attr_head (p); + do { + r = s; + s = mp_link (s); + } while (nn > hashloc (s)); + if (nn == hashloc (s)) { + p = s; + } else { + q = (mp_node) mp_get_attr_node (mp); + set_mp_link (r, q); + set_mp_link (q, s); + set_hashloc (q, nn); + mp_name_type (q) = mp_attr; + mp_type (q) = undefined; + parent ((mp_value_node) q) = p; + p = q; } } } + @ Variables lose their former values when they appear in a type declaration, or when they are defined to be macros or \&{let} equal to something else. A subroutine will be defined later that recycles the storage associated @@ -6775,136 +6259,179 @@ to handle such examples is to use recursion; so that's what we~do. @^recursion@> Parameter |p| points to the root information of the variable; -parameter |t| points to a list of one-word nodes that represent +parameter |t| points to a list of symbolic nodes that represent suffixes, with |info=collective_subscript| for subscripts. @<Declarations@>= -static void mp_flush_cur_exp (MP mp,scaled v) ; - -@ @c -static void mp_flush_variable (MP mp,pointer p, pointer t, boolean discard_suffixes) { - pointer q,r; /* list manipulation */ - halfword n; /* attribute to match */ - while ( t!=null ) { - if ( mp_type(p)!=mp_structured ) return; - n=mp_info(t); t=mp_link(t); - if ( n==collective_subscript ) { - r=subscr_head_loc(p); q=mp_link(r); /* |q=subscr_head(p)| */ - while ( mp_name_type(q)==mp_subscr ){ - mp_flush_variable(mp, q,t,discard_suffixes); - if ( t==null ) { - if ( mp_type(q)==mp_structured ) r=q; - else { mp_link(r)=mp_link(q); mp_free_node(mp, q,subscr_node_size); } +static void mp_flush_cur_exp (MP mp, mp_value v); + +@ @c +static void mp_flush_variable (MP mp, mp_node p, mp_node t, + boolean discard_suffixes) { + mp_node q, r; /* list manipulation */ + mp_sym n; /* attribute to match */ + while (t != NULL) { + if (mp_type (p) != mp_structured) + return; + n = mp_sym_sym (t); + t = mp_link (t); + if (n == collective_subscript) { + r = mp->temp_head; + mp_link (r) = subscr_head (p); + q = mp_link (r); + while (mp_name_type (q) == mp_subscr) { + mp_flush_variable (mp, q, t, discard_suffixes); + if (t == NULL) { + if (mp_type (q) == mp_structured) { + r = q; + } else { + set_mp_link (r, mp_link (q)); + mp_free_node (mp, q, value_node_size); + } } else { - r=q; + r = q; } - q=mp_link(r); + q = mp_link (r); } + /* fix |subscr_head| if it was already present */ + if (q==mp_link(mp->temp_head)) + set_subscr_head (p, q); } - p=attr_head(p); - do { - r=p; p=mp_link(p); - } while (attr_loc(p)<n); - if ( attr_loc(p)!=n ) return; + p = attr_head (p); + do { + r = p; + p = mp_link (p); + } while (hashloc (p) < n); + if (hashloc (p) != n) + return; } - if ( discard_suffixes ) { - mp_flush_below_variable(mp, p); - } else { - if ( mp_type(p)==mp_structured ) p=attr_head(p); - mp_recycle_value(mp, p); + if (discard_suffixes) { + mp_flush_below_variable (mp, p); + } else { + if (mp_type (p) == mp_structured) + p = attr_head (p); + mp_recycle_value (mp, p); } } + @ The next procedure is simpler; it wipes out everything but |p| itself, which becomes undefined. @<Declarations@>= -static void mp_flush_below_variable (MP mp, pointer p); +static void mp_flush_below_variable (MP mp, mp_node p); @ @c -void mp_flush_below_variable (MP mp,pointer p) { - pointer q,r; /* list manipulation registers */ - if ( mp_type(p)!=mp_structured ) { - mp_recycle_value(mp, p); /* this sets |type(p)=undefined| */ - } else { - q=subscr_head(p); - while ( mp_name_type(q)==mp_subscr ) { - mp_flush_below_variable(mp, q); r=q; q=mp_link(q); - mp_free_node(mp, r,subscr_node_size); +void mp_flush_below_variable (MP mp, mp_node p) { + mp_node q, r; /* list manipulation registers */ + FUNCTION_TRACE2 ("mp_flush_below_variable(%p)\n", p); + if (mp_type (p) != mp_structured) { + mp_recycle_value (mp, p); /* this sets |type(p)=undefined| */ + } else { + q = subscr_head (p); + while (mp_name_type (q) == mp_subscr) { + mp_flush_below_variable (mp, q); + r = q; + q = mp_link (q); + mp_free_node (mp, r, value_node_size); } - r=attr_head(p); q=mp_link(r); mp_recycle_value(mp, r); - if ( mp_name_type(p)<=mp_saved_root ) mp_free_node(mp, r,value_node_size); - else mp_free_node(mp, r,subscr_node_size); - /* we assume that |subscr_node_size=attr_node_size| */ - do { - mp_flush_below_variable(mp, q); r=q; q=mp_link(q); mp_free_node(mp, r,attr_node_size); - } while (q!=end_attr); - mp_type(p)=undefined; + r = attr_head (p); + q = mp_link (r); + mp_recycle_value (mp, r); + mp_free_node (mp, r, value_node_size); + do { + mp_flush_below_variable (mp, q); + r = q; + q = mp_link (q); + mp_free_node (mp, r, value_node_size); + } while (q != mp->end_attr); + mp_type (p) = undefined; } } + @ Just before assigning a new value to a variable, we will recycle the old value and make the old value undefined. The |und_type| routine determines what type of undefined value should be given, based on the current type before recycling. -@c -static quarterword mp_und_type (MP mp,pointer p) { - switch (mp_type(p)) { - case undefined: case mp_vacuous: +@c +static quarterword mp_und_type (MP mp, mp_node p) { + (void) mp; + switch (mp_type (p)) { + case mp_vacuous: return undefined; - case mp_boolean_type: case mp_unknown_boolean: + case mp_boolean_type: + case mp_unknown_boolean: return mp_unknown_boolean; - case mp_string_type: case mp_unknown_string: + case mp_string_type: + case mp_unknown_string: return mp_unknown_string; - case mp_pen_type: case mp_unknown_pen: + case mp_pen_type: + case mp_unknown_pen: return mp_unknown_pen; - case mp_path_type: case mp_unknown_path: + case mp_path_type: + case mp_unknown_path: return mp_unknown_path; - case mp_picture_type: case mp_unknown_picture: + case mp_picture_type: + case mp_unknown_picture: return mp_unknown_picture; - case mp_transform_type: case mp_color_type: case mp_cmykcolor_type: - case mp_pair_type: case mp_numeric_type: - return mp_type(p); - case mp_known: case mp_dependent: case mp_proto_dependent: case mp_independent: + case mp_transform_type: + case mp_color_type: + case mp_cmykcolor_type: + case mp_pair_type: + case mp_numeric_type: + return mp_type (p); + case mp_known: + case mp_dependent: + case mp_proto_dependent: + case mp_independent: return mp_numeric_type; - } /* there are no other cases */ + default: /* there are no other valid cases, but please the compiler */ + return 0; + } return 0; } + @ The |clear_symbol| routine is used when we want to redefine the equivalent of a symbolic token. It must remove any variable structure or macro definition that is currently attached to that symbol. If the |saving| parameter is true, a subsidiary structure is saved instead of destroyed. -@c -static void mp_clear_symbol (MP mp,pointer p, boolean saving) { - pointer q; /* |equiv(p)| */ - q=equiv(p); - switch (eq_type(p) % outer_tag) { +@c +static void mp_clear_symbol (MP mp, mp_sym p, boolean saving) { + mp_node q; /* |equiv(p)| */ + FUNCTION_TRACE3 ("mp_clear_symbol(%p,%d)\n", p, saving); + q = equiv_node (p); + switch (eq_type (p) % outer_tag) { case defined_macro: case secondary_primary_macro: case tertiary_secondary_macro: - case expression_tertiary_macro: - if ( ! saving ) mp_delete_mac_ref(mp, q); + case expression_tertiary_macro: + if (!saving) + mp_delete_mac_ref (mp, q); break; case tag_token: - if ( q!=null ) { - if ( saving ) { - mp_name_type(q)=mp_saved_root; - } else { - mp_flush_below_variable(mp, q); - mp_free_node(mp,q,value_node_size); + if (q != NULL) { + if (saving) { + mp_name_type (q) = mp_saved_root; + } else { + mp_flush_below_variable (mp, q); + mp_free_node (mp, q, value_node_size); } } break; default: break; } - mp->eqtb[p]=mp->eqtb[frozen_undefined]; + equiv (p) = mp->frozen_undefined->v.data.val; + equiv_node (p) = NULL; + eq_type (p) = mp->frozen_undefined->type; } -@* \[16] Saving and restoring equivalents. + +@* Saving and restoring equivalents. The nested structure given by \&{begingroup} and \&{endgroup} allows |eqtb| entries to be saved and restored, so that temporary changes can be made without difficulty. When the user requests a current value to @@ -6917,119 +6444,157 @@ distinguished by their |info| fields. If |p| points to a saved item, then \smallskip\hang -|mp_info(p)=0| stands for a group boundary; each \&{begingroup} contributes +|p->info=0| stands for a group boundary; each \&{begingroup} contributes such an item to the save stack and each \&{endgroup} cuts back the stack until the most recent such entry has been removed. \smallskip\hang -|mp_info(p)=q|, where |1<=q<=hash_end|, means that |mem[p+1]| holds the former +|p->type=mp_normal_sym| and |p->info=q|, means that |p->equiv| holds the former contents of |eqtb[q]|. Such save stack entries are generated by \&{save} commands. \smallskip\hang -|mp_info(p)=hash_end+q|, where |q>0|, means that |value(p)| is a |scaled| -integer to be restored to internal parameter number~|q|. Such entries +|p->type=mp_internal_sym| and |p->info=q|, where |q>0|, means that |p->value| is a |mp_internal| +to be restored to internal parameter number~|q|. Such entries are generated by \&{interim} commands. \smallskip\noindent The global variable |save_ptr| points to the top item on the save stack. -@d save_node_size 2 /* number of words per non-boundary save-stack node */ -@d saved_equiv(A) mp->mem[(A)+1].hh /* where an |eqtb| entry gets saved */ -@d save_boundary_item(A) { (A)=mp_get_avail(mp); mp_info((A))=0; - mp_link((A))=mp->save_ptr; mp->save_ptr=(A); - } +@<Types...@>= +typedef struct mp_save_data { + halfword info; + mp_sym sym; + quarterword type; + mp_internal value; + halfword equiv; + halfword eq_type; + mp_node equiv_n; + struct mp_save_data *link; +} mp_save_data; -@<Glob...@>= -pointer save_ptr; /* the most recently saved item */ +@ @<Glob...@>= +mp_save_data *save_ptr; /* the most recently saved item */ + +@ @<Set init...@>= +mp->save_ptr = NULL; + +@ Saving a boundary item +@c +static void mp_save_boundary (MP mp) { + mp_save_data *p; /* temporary register */ + FUNCTION_TRACE1 ("mp_save_boundary ()\n"); + p = xmalloc (1, sizeof (mp_save_data)); + p->info = 0; + p->link = mp->save_ptr; + mp->save_ptr = p; +} -@ @<Set init...@>=mp->save_ptr=null; @ The |save_variable| routine is given a hash address |q|; it salts this address in the save stack, together with its current equivalent, then makes token~|q| behave as though it were brand new. -Nothing is stacked when |save_ptr=null|, however; there's no way to remove +Nothing is stacked when |save_ptr=NULL|, however; there's no way to remove things from the stack when the program is not inside a group, so there's no point in wasting the space. -@c -static void mp_save_variable (MP mp,pointer q) { - pointer p; /* temporary register */ - if ( mp->save_ptr!=null ){ - p=mp_get_node(mp, save_node_size); mp_info(p)=q; mp_link(p)=mp->save_ptr; - saved_equiv(p)=mp->eqtb[q]; mp->save_ptr=p; +@c +static void mp_save_variable (MP mp, mp_sym q) { + mp_save_data *p; /* temporary register */ + FUNCTION_TRACE2 ("mp_save_variable (%p)\n", q); + if (mp->save_ptr != NULL) { + p = xmalloc (1, sizeof (mp_save_data)); + p->info = 1; + p->sym = q; + p->type = mp_normal_sym; + p->link = mp->save_ptr; + p->equiv = equiv (q); + p->eq_type = eq_type (q); + p->equiv_n = equiv_node (q); + mp->save_ptr = p; } - mp_clear_symbol(mp, q,(mp->save_ptr!=null)); + mp_clear_symbol (mp, q, (mp->save_ptr != NULL)); } + @ Similarly, |save_internal| is given the location |q| of an internal quantity like |mp_tracing_pens|. It creates a save stack entry of the third kind. -@c -static void mp_save_internal (MP mp,halfword q) { - pointer p; /* new item for the save stack */ - if ( mp->save_ptr!=null ){ - p=mp_get_node(mp, save_node_size); - mp_info(p)=hash_end+q; - mp_link(p)=mp->save_ptr; - value(p)=mp->internal[q]; - mp->save_ptr=p; +@c +static void mp_save_internal (MP mp, halfword q) { + mp_save_data *p; /* new item for the save stack */ + FUNCTION_TRACE2 ("mp_save_internal (%p)\n", q); + if (mp->save_ptr != NULL) { + p = xmalloc (1, sizeof (mp_save_data)); + p->info = q; + p->type = mp_internal_sym; + p->link = mp->save_ptr; + p->value = mp->internal[q]; + mp->save_ptr = p; } } + @ At the end of a group, the |unsave| routine restores all of the saved equivalents in reverse order. This routine will be called only when there is at least one boundary item on the save stack. -@c +@c static void mp_unsave (MP mp) { - pointer q; /* index to saved item */ - pointer p; /* temporary register */ - while ( mp_info(mp->save_ptr)!=0 ) { - q=mp_info(mp->save_ptr); - if ( q>hash_end ) { - if ( mp->internal[mp_tracing_restores]>0 ) { - mp_begin_diagnostic(mp); - mp_print_nl(mp, "{restoring "); - mp_print(mp, mp->int_name[q-(hash_end)]); - mp_print_char(mp, xord('=')); - if (mp->int_type[q-(hash_end)]==mp_known) { - mp_print_scaled(mp, value(mp->save_ptr)); - } else if (mp->int_type[q-(hash_end)]==mp_string_type) { - char *s = mp_str(mp, value(mp->save_ptr)); - mp_print(mp, s); - free(s); + mp_save_data *p; /* saved item */ + FUNCTION_TRACE1 ("mp_unsave ()\n"); + while (mp->save_ptr->info != 0) { + halfword q = mp->save_ptr->info; + if (mp->save_ptr->type == mp_internal_sym) { + if (internal_value (mp_tracing_restores) > 0) { + mp_begin_diagnostic (mp); + mp_print_nl (mp, "{restoring "); + mp_print (mp, internal_name (q)); + mp_print_char (mp, xord ('=')); + if (internal_type (q) == mp_known) { + mp_print_scaled (mp, mp->save_ptr->value.v.data.val); + } else if (internal_type (q) == mp_string_type) { + char *s = mp_str (mp, mp->save_ptr->value.v.data.str); + mp_print (mp, s); } else { - mp_confusion(mp,"internal_restore"); + mp_confusion (mp, "internal_restore"); } - mp_print_char(mp, xord('}')); - mp_end_diagnostic(mp, false); + mp_print_char (mp, xord ('}')); + mp_end_diagnostic (mp, false); } - mp->internal[q-(hash_end)]=value(mp->save_ptr); - } else { - if ( mp->internal[mp_tracing_restores]>0 ) { - mp_begin_diagnostic(mp); - mp_print_nl(mp, "{restoring "); - mp_print_text(q); - mp_print_char(mp, xord('}')); - mp_end_diagnostic(mp, false); + mp->internal[q] = mp->save_ptr->value; + } else { + mp_sym q = mp->save_ptr->sym; + if (internal_value (mp_tracing_restores) > 0) { + mp_begin_diagnostic (mp); + mp_print_nl (mp, "{restoring "); + mp_print_text (q); + mp_print_char (mp, xord ('}')); + mp_end_diagnostic (mp, false); } - mp_clear_symbol(mp, q,false); - mp->eqtb[q]=saved_equiv(mp->save_ptr); - if ( eq_type(q) % outer_tag==tag_token ) { - p=equiv(q); - if ( p!=null ) mp_name_type(p)=mp_root; + mp_clear_symbol (mp, q, false); + equiv (q) = mp->save_ptr->equiv; + eq_type (q) = mp->save_ptr->eq_type; + equiv_node (q) = mp->save_ptr->equiv_n; + if (eq_type (q) % outer_tag == tag_token) { + mp_node pp = equiv_node (q); + if (pp != NULL) + mp_name_type (pp) = mp_root; } } - p=mp_link(mp->save_ptr); - mp_free_node(mp, mp->save_ptr,save_node_size); mp->save_ptr=p; + p = mp->save_ptr->link; + xfree (mp->save_ptr); + mp->save_ptr = p; } - p=mp_link(mp->save_ptr); free_avail(mp->save_ptr); mp->save_ptr=p; + p = mp->save_ptr->link; + xfree (mp->save_ptr); + mp->save_ptr = p; } -@* \[17] Data structures for paths. + +@* Data structures for paths. When a \MP\ user specifies a path, \MP\ will create a list of knots and control points for the associated cubic spline curves. If the knots are $z_0$, $z_1$, \dots, $z_n$, there are control points @@ -7040,7 +6605,7 @@ $$\eqalign{z(t)&=B(z_k,z_k^+,z_{k+1}^-,z_{k+1};t)\cr &=(1-t)^3z_k+3(1-t)^2tz_k^++3(1-t)t^2z_{k+1}^-+t^3z_{k+1}\cr}$$ for |0<=t<=1|. -There is an 8-word node for each knot $z_k$, containing one word of +There is a 8-word node for each knot $z_k$, containing one word of control information and six words for the |x| and |y| coordinates of $z_k^-$ and $z_k$ and~$z_k^+$. The control information appears in the |mp_left_type| and |mp_right_type| fields, which each occupy a quarter of @@ -7055,31 +6620,48 @@ is not closed, the |mp_left_type| of knot~0 and the |mp_right_type| of knot~|n| are equal to |endpoint|. In the latter case the |link| in knot~|n| points to knot~0, and the control points $z_0^-$ and $z_n^+$ are not used. -@d mp_left_type(A) mp->mem[(A)].hh.b0 /* characterizes the path entering this knot */ -@d mp_right_type(A) mp->mem[(A)].hh.b1 /* characterizes the path leaving this knot */ -@d mp_x_coord(A) mp->mem[(A)+1].sc /* the |x| coordinate of this knot */ -@d mp_y_coord(A) mp->mem[(A)+2].sc /* the |y| coordinate of this knot */ -@d mp_left_x(A) mp->mem[(A)+3].sc /* the |x| coordinate of previous control point */ -@d mp_left_y(A) mp->mem[(A)+4].sc /* the |y| coordinate of previous control point */ -@d mp_right_x(A) mp->mem[(A)+5].sc /* the |x| coordinate of next control point */ -@d mp_right_y(A) mp->mem[(A)+6].sc /* the |y| coordinate of next control point */ -@d x_loc(A) ((A)+1) /* where the |x| coordinate is stored in a knot */ -@d y_loc(A) ((A)+2) /* where the |y| coordinate is stored in a knot */ -@d knot_coord(A) mp->mem[(A)].sc /* |x| or |y| coordinate given |x_loc| or |y_loc| */ -@d left_coord(A) mp->mem[(A)+2].sc - /* coordinate of previous control point given |x_loc| or |y_loc| */ -@d right_coord(A) mp->mem[(A)+4].sc - /* coordinate of next control point given |x_loc| or |y_loc| */ -@d knot_node_size 8 /* number of words in a knot node */ - -@(mplib.h@>= +@d mp_x_coord(A) (A)->x_coord /* the |x| coordinate of this knot */ +@d mp_y_coord(A) (A)->y_coord /* the |y| coordinate of this knot */ +@d mp_left_x(A) (A)->left_x /* the |x| coordinate of previous control point */ +@d mp_left_y(A) (A)->left_y /* the |y| coordinate of previous control point */ +@d mp_right_x(A) (A)->right_x /* the |x| coordinate of next control point */ +@d mp_right_y(A) (A)->right_y /* the |y| coordinate of next control point */ +@d mp_next_knot(A) (A)->next /* the next knot in this list */ +@d mp_left_type(A) (A)->data.types.left_type /* characterizes the path entering this knot */ +@d mp_right_type(A) (A)->data.types.right_type /* characterizes the path leaving this knot */ +@d mp_prev_knot(A) (A)->data.prev /* the previous knot in this list (only for pens) */ +@d mp_knot_info(A) (A)->data.info /* temporary info, used during splitting */ + +@<Exported types...@>= +typedef struct mp_knot_data *mp_knot; +typedef struct mp_knot_data { + scaled x_coord; + scaled y_coord; + scaled left_x; + scaled left_y; + scaled right_x; + scaled right_y; + mp_knot next; + union { + struct { + unsigned short left_type; + unsigned short right_type; + } types; + mp_knot prev; + signed int info; + } data; + unsigned char originator; +} mp_knot_data; + + +@ @(mplib.h@>= enum mp_knot_type { - mp_endpoint=0, /* |mp_left_type| at path beginning and |mp_right_type| at path end */ - mp_explicit, /* |mp_left_type| or |mp_right_type| when control points are known */ - mp_given, /* |mp_left_type| or |mp_right_type| when a direction is given */ - mp_curl, /* |mp_left_type| or |mp_right_type| when a curl is desired */ - mp_open, /* |mp_left_type| or |mp_right_type| when \MP\ should choose the direction */ - mp_end_cycle + mp_endpoint = 0, /* |mp_left_type| at path beginning and |mp_right_type| at path end */ + mp_explicit, /* |mp_left_type| or |mp_right_type| when control points are known */ + mp_given, /* |mp_left_type| or |mp_right_type| when a direction is given */ + mp_curl, /* |mp_left_type| or |mp_right_type| when a curl is desired */ + mp_open, /* |mp_left_type| or |mp_right_type| when \MP\ should choose the direction */ + mp_end_cycle }; @ Before the B\'ezier control points have been calculated, the memory @@ -7157,12 +6739,12 @@ one knot from another, we will set |originator(p):=mp_metapost_user| when it appeared in the actual metapost program, and |originator(p):=mp_program_code| in all other cases. -@d mp_originator(A) mp->mem[(A)+7].hh.b0 /* the creator of this knot */ +@d mp_originator(A) (A)->originator /* the creator of this knot */ @<Exported types@>= enum mp_knot_originator { - mp_program_code=0, /* not created by a user */ - mp_metapost_user /* created by a user */ + mp_program_code = 0, /* not created by a user */ + mp_metapost_user /* created by a user */ }; @ Here is a routine that prints a given knot list @@ -7170,57 +6752,61 @@ in symbolic form. It illustrates the conventions discussed above, and checks for anomalies that might arise while \MP\ is being debugged. @<Declarations@>= -static void mp_pr_path (MP mp,pointer h); +static void mp_pr_path (MP mp, mp_knot h); @ @c -void mp_pr_path (MP mp,pointer h) { - pointer p,q; /* for list traversal */ - p=h; - do { - q=mp_link(p); - if ( (p==null)||(q==null) ) { - mp_print_nl(mp, "???"); return; /* this won't happen */ +void mp_pr_path (MP mp, mp_knot h) { + mp_knot p, q; /* for list traversal */ + p = h; + do { + q = mp_next_knot (p); + if ((p == NULL) || (q == NULL)) { + mp_print_nl (mp, "???"); + return; /* this won't happen */ @.???@> } @<Print information for adjacent knots |p| and |q|@>; DONE1: - p=q; - if ( (p!=h)||(mp_left_type(h)!=mp_endpoint) ) { + p = q; + if ((p != h) || (mp_left_type (h) != mp_endpoint)) { @<Print two dots, followed by |given| or |curl| if present@>; } - } while (p!=h); - if ( mp_left_type(h)!=mp_endpoint ) - mp_print(mp, "cycle"); + } while (p != h); + if (mp_left_type (h) != mp_endpoint) + mp_print (mp, "cycle"); } + @ @<Print information for adjacent knots...@>= -mp_print_two(mp, mp_x_coord(p),mp_y_coord(p)); -switch (mp_right_type(p)) { -case mp_endpoint: - if ( mp_left_type(p)==mp_open ) mp_print(mp, "{open?}"); /* can't happen */ -@.open?@> - if ( (mp_left_type(q)!=mp_endpoint)||(q!=h) ) q=null; /* force an error */ +mp_print_two (mp, mp_x_coord (p), mp_y_coord (p)); +switch (mp_right_type (p)) { +case mp_endpoint: + if (mp_left_type (p) == mp_open) + mp_print (mp, "{open?}"); /* can't happen */ +@.open?@>; + if ((mp_left_type (q) != mp_endpoint) || (q != h)) + q = NULL; /* force an error */ goto DONE1; break; -case mp_explicit: +case mp_explicit: @<Print control points between |p| and |q|, then |goto done1|@>; break; -case mp_open: +case mp_open: @<Print information for a curve that begins |open|@>; break; case mp_curl: -case mp_given: +case mp_given: @<Print information for a curve that begins |curl| or |given|@>; break; default: - mp_print(mp, "???"); /* can't happen */ -@.???@> + mp_print (mp, "???"); /* can't happen */ +@.???@>; break; } -if ( mp_left_type(q)<=mp_explicit ) { - mp_print(mp, "..control?"); /* can't happen */ +if (mp_left_type (q) <= mp_explicit) { + mp_print (mp, "..control?"); /* can't happen */ @.control?@> -} else if ( (right_tension(p)!=unity)||(left_tension(q)!=unity) ) { +} else if ((right_tension (p) != unity) || (left_tension (q) != unity)) { @<Print tension between |p| and |q|@>; } @@ -7228,47 +6814,58 @@ if ( mp_left_type(q)<=mp_explicit ) { were |scaled|, the magnitude of a |given| direction vector will be~4096. @<Print two dots...@>= -{ - mp_print_nl(mp, " .."); - if ( mp_left_type(p)==mp_given ) { - mp_n_sin_cos(mp, left_given(p)); mp_print_char(mp, xord('{')); - mp_print_scaled(mp, mp->n_cos); mp_print_char(mp, xord(',')); - mp_print_scaled(mp, mp->n_sin); mp_print_char(mp, xord('}')); - } else if ( mp_left_type(p)==mp_curl ){ - mp_print(mp, "{curl "); - mp_print_scaled(mp, left_curl(p)); mp_print_char(mp, xord('}')); +{ + fraction n_sin; + fraction n_cos; + mp_print_nl (mp, " .."); + if (mp_left_type (p) == mp_given) { + mp_n_sin_cos (mp, left_given (p), &n_cos, &n_sin); + mp_print_char (mp, xord ('{')); + mp_print_scaled (mp, n_cos); + mp_print_char (mp, xord (',')); + mp_print_scaled (mp, n_sin); + mp_print_char (mp, xord ('}')); + } else if (mp_left_type (p) == mp_curl) { + mp_print (mp, "{curl "); + mp_print_scaled (mp, left_curl (p)); + mp_print_char (mp, xord ('}')); } } + @ @<Print tension between |p| and |q|@>= -{ - mp_print(mp, "..tension "); - if ( right_tension(p)<0 ) mp_print(mp, "atleast"); - mp_print_scaled(mp, abs(right_tension(p))); - if ( right_tension(p)!=left_tension(q) ){ - mp_print(mp, " and "); - if ( left_tension(q)<0 ) mp_print(mp, "atleast"); - mp_print_scaled(mp, abs(left_tension(q))); +{ + mp_print (mp, "..tension "); + if (right_tension (p) < 0) + mp_print (mp, "atleast"); + mp_print_scaled (mp, abs (right_tension (p))); + if (right_tension (p) != left_tension (q)) { + mp_print (mp, " and "); + if (left_tension (q) < 0) + mp_print (mp, "atleast"); + mp_print_scaled (mp, abs (left_tension (q))); } } + @ @<Print control points between |p| and |q|, then |goto done1|@>= -{ - mp_print(mp, "..controls "); - mp_print_two(mp, mp_right_x(p),mp_right_y(p)); - mp_print(mp, " and "); - if ( mp_left_type(q)!=mp_explicit ) { - mp_print(mp, "??"); /* can't happen */ +{ + mp_print (mp, "..controls "); + mp_print_two (mp, mp_right_x (p), mp_right_y (p)); + mp_print (mp, " and "); + if (mp_left_type (q) != mp_explicit) { + mp_print (mp, "??"); /* can't happen */ @.??@> } else { - mp_print_two(mp, mp_left_x(q),mp_left_y(q)); + mp_print_two (mp, mp_left_x (q), mp_left_y (q)); } goto DONE1; } + @ @<Print information for a curve that begins |open|@>= -if ( (mp_left_type(p)!=mp_explicit)&&(mp_left_type(p)!=mp_open) ) { - mp_print(mp, "{open?}"); /* can't happen */ +if ((mp_left_type (p) != mp_explicit) && (mp_left_type (p) != mp_open)) { + mp_print (mp, "{open?}"); /* can't happen */ @.open?@> } @@ -7276,136 +6873,102 @@ if ( (mp_left_type(p)!=mp_explicit)&&(mp_left_type(p)!=mp_open) ) { \MP's default curl is present. @<Print information for a curve that begins |curl|...@>= -{ - if ( mp_left_type(p)==mp_open ) - mp_print(mp, "??"); /* can't happen */ -@.??@> - if ( mp_right_type(p)==mp_curl ) { - mp_print(mp, "{curl "); mp_print_scaled(mp, right_curl(p)); - } else { - mp_n_sin_cos(mp, right_given(p)); mp_print_char(mp, xord('{')); - mp_print_scaled(mp, mp->n_cos); mp_print_char(mp, xord(',')); - mp_print_scaled(mp, mp->n_sin); +{ + if (mp_left_type (p) == mp_open) + mp_print (mp, "??"); /* can't happen */ +@.??@>; + if (mp_right_type (p) == mp_curl) { + mp_print (mp, "{curl "); + mp_print_scaled (mp, right_curl (p)); + } else { + fraction n_sin; + fraction n_cos; + mp_n_sin_cos (mp, right_given (p), &n_cos, &n_sin); + mp_print_char (mp, xord ('{')); + mp_print_scaled (mp, n_cos); + mp_print_char (mp, xord (',')); + mp_print_scaled (mp, n_sin); } - mp_print_char(mp, xord('}')); + mp_print_char (mp, xord ('}')); } + @ It is convenient to have another version of |pr_path| that prints the path as a diagnostic message. @<Declarations@>= -static void mp_print_path (MP mp,pointer h, const char *s, boolean nuline) ; +static void mp_print_path (MP mp, mp_knot h, const char *s, boolean nuline); @ @c -void mp_print_path (MP mp,pointer h, const char *s, boolean nuline) { - mp_print_diagnostic(mp, "Path", s, nuline); mp_print_ln(mp); -@.Path at line...@> - mp_pr_path(mp, h); - mp_end_diagnostic(mp, true); +void mp_print_path (MP mp, mp_knot h, const char *s, boolean nuline) { + mp_print_diagnostic (mp, "Path", s, nuline); + mp_print_ln (mp); +@.Path at line...@>; + mp_pr_path (mp, h); + mp_end_diagnostic (mp, true); } -@ If we want to duplicate a knot node, we can say |copy_knot|: -@c -static pointer mp_copy_knot (MP mp,pointer p) { - pointer q; /* the copy */ - int k; /* runs through the words of a knot node */ - q=mp_get_node(mp, knot_node_size); - for (k=0;k<knot_node_size;k++) { - mp->mem[q+k]=mp->mem[p+k]; - } - mp_originator(q)=mp_originator(p); +@ @<Declarations@>= +static mp_knot mp_new_knot (MP mp); + +@ @c +static mp_knot mp_new_knot (MP mp) { + mp_knot q = mp_xmalloc (mp, 1, sizeof (struct mp_knot_data)); return q; } -@ The |copy_path| routine makes a clone of a given path. -@c -static pointer mp_copy_path (MP mp, pointer p) { - pointer q,pp,qq; /* for list manipulation */ - q=mp_copy_knot(mp, p); - qq=q; pp=mp_link(p); - while ( pp!=p ) { - mp_link(qq)=mp_copy_knot(mp, pp); - qq=mp_link(qq); - pp=mp_link(pp); - } - mp_link(qq)=q; +@ If we want to duplicate a knot node, we can say |copy_knot|: + +@c +static mp_knot mp_copy_knot (MP mp, mp_knot p) { + mp_knot q; /* the copy */ + q = mp_new_knot (mp); + memcpy (q, p, sizeof (struct mp_knot_data)); + mp_next_knot (q) = NULL; return q; } -@ Just before |ship_out|, knot lists are exported for printing. +@ The |copy_path| routine makes a clone of a given path. -The |gr_XXXX| macros are defined in |mppsout.h|. - -@c -static mp_knot *mp_export_knot (MP mp,pointer p) { - mp_knot *q; /* the copy */ - if (p==null) - return NULL; - q = xmalloc(1, sizeof (mp_knot)); - memset(q,0,sizeof (mp_knot)); - gr_left_type(q) = (unsigned short)mp_left_type(p); - gr_right_type(q) = (unsigned short)mp_right_type(p); - gr_x_coord(q) = mp_x_coord(p); - gr_y_coord(q) = mp_y_coord(p); - gr_left_x(q) = mp_left_x(p); - gr_left_y(q) = mp_left_y(p); - gr_right_x(q) = mp_right_x(p); - gr_right_y(q) = mp_right_y(p); - gr_originator(q) = (unsigned char)mp_originator(p); +@c +static mp_knot mp_copy_path (MP mp, mp_knot p) { + mp_knot q, pp, qq; /* for list manipulation */ + if (p == NULL) + return NULL; + q = mp_copy_knot (mp, p); + qq = q; + pp = mp_next_knot (p); + while (pp != p) { + mp_next_knot (qq) = mp_copy_knot (mp, pp); + qq = mp_next_knot (qq); + pp = mp_next_knot (pp); + } + mp_next_knot (qq) = q; return q; } -static pointer mp_import_knot (MP mp, mp_knot *q) { - pointer p; /* the copy */ - if (q==NULL) - return null; - p=mp_get_node(mp, knot_node_size); - mp_left_type(p) = (quarterword)gr_left_type(q); - mp_right_type(p) = (quarterword)gr_right_type(q); - mp_x_coord(p) = gr_x_coord(q); - mp_y_coord(p) = gr_y_coord(q); - mp_left_x(p) = gr_left_x(q); - mp_left_y(p) = gr_left_y(q); - mp_right_x(p) = gr_right_x(q); - mp_right_y(p) = gr_right_y(q); - mp_originator(p) = gr_originator(q); - return p; -} + + +@ Just before |ship_out|, knot lists are exported for printing. @ The |export_knot_list| routine therefore also makes a clone of a given path. -@c -static mp_knot *mp_export_knot_list (MP mp, pointer p) { - mp_knot *q, *qq; /* for list manipulation */ - pointer pp; /* for list manipulation */ - if (p==null) - return NULL; - q=mp_export_knot(mp, p); - qq=q; pp=mp_link(p); - while ( pp!=p ) { - gr_next_knot(qq)=mp_export_knot(mp, pp); - qq=gr_next_knot(qq); - pp=mp_link(pp); - } - gr_next_knot(qq)=q; +@c +static mp_knot mp_export_knot_list (MP mp, mp_knot p) { + mp_knot q; /* the exported copy */ + if (p == NULL) + return NULL; + q = mp_copy_path (mp, p); return q; } -static pointer mp_import_knot_list (MP mp, mp_knot *q) { - mp_knot *qq; /* for list manipulation */ - pointer p, pp; /* for list manipulation */ - if (q==NULL) - return null; - p=mp_import_knot(mp, q); - pp=p; qq=gr_link(q); - while ( qq!=q ) { - mp_link(pp)=mp_import_knot(mp, qq); - qq=gr_next_knot(qq); - pp=mp_link(pp); - } - mp_link(pp)=p; +static mp_knot mp_import_knot_list (MP mp, mp_knot q) { + mp_knot p; /* the imported copy */ + if (q == NULL) + return NULL; + p = mp_copy_path (mp, q); return p; } @@ -7418,45 +6981,61 @@ this trick makes it easier to implement `\&{doublepath}'. All node types are assumed to be |endpoint| or |explicit| only. -@c -static pointer mp_htap_ypoc (MP mp,pointer p) { - pointer q,pp,qq,rr; /* for list manipulation */ - q=mp_get_node(mp, knot_node_size); /* this will correspond to |p| */ - qq=q; pp=p; - while (1) { - mp_right_type(qq)=mp_left_type(pp); mp_left_type(qq)=mp_right_type(pp); - mp_x_coord(qq)=mp_x_coord(pp); mp_y_coord(qq)=mp_y_coord(pp); - mp_right_x(qq)=mp_left_x(pp); mp_right_y(qq)=mp_left_y(pp); - mp_left_x(qq)=mp_right_x(pp); mp_left_y(qq)=mp_right_y(pp); - mp_originator(qq)=mp_originator(pp); - if ( mp_link(pp)==p ) { - mp_link(q)=qq; mp->path_tail=pp; return q; +@c +static mp_knot mp_htap_ypoc (MP mp, mp_knot p) { + mp_knot q, pp, qq, rr; /* for list manipulation */ + q = mp_new_knot (mp); /* this will correspond to |p| */ + qq = q; + pp = p; + while (1) { + mp_right_type (qq) = mp_left_type (pp); + mp_left_type (qq) = mp_right_type (pp); + mp_x_coord (qq) = mp_x_coord (pp); + mp_y_coord (qq) = mp_y_coord (pp); + mp_right_x (qq) = mp_left_x (pp); + mp_right_y (qq) = mp_left_y (pp); + mp_left_x (qq) = mp_right_x (pp); + mp_left_y (qq) = mp_right_y (pp); + mp_originator (qq) = mp_originator (pp); + if (mp_next_knot (pp) == p) { + mp_next_knot (q) = qq; + mp->path_tail = pp; + return q; } - rr=mp_get_node(mp, knot_node_size); mp_link(rr)=qq; qq=rr; pp=mp_link(pp); + rr = mp_new_knot (mp); + mp_next_knot (rr) = qq; + qq = rr; + pp = mp_next_knot (pp); } } + @ @<Glob...@>= -pointer path_tail; /* the node that links to the beginning of a path */ +mp_knot path_tail; /* the node that links to the beginning of a path */ @ When a cyclic list of knot nodes is no longer needed, it can be recycled by calling the following subroutine. @<Declarations@>= -static void mp_toss_knot_list (MP mp,pointer p) ; +static void mp_toss_knot_list (MP mp, mp_knot p); @ @c -void mp_toss_knot_list (MP mp,pointer p) { - pointer q; /* the node being freed */ - pointer r; /* the next node */ - q=p; - do { - r=mp_link(q); - mp_free_node(mp, q,knot_node_size); q=r; - } while (q!=p); +void mp_toss_knot_list (MP mp, mp_knot p) { + mp_knot q; /* the node being freed */ + mp_knot r; /* the next node */ + (void) mp; + if (p == NULL) + return; + q = p; + do { + r = mp_next_knot (q); + mp_xfree (q); + q = r; + } while (q != p); } -@* \[18] Choosing control points. + +@* Choosing control points. Now we must actually delve into one of \MP's more difficult routines, the |make_choices| procedure that chooses angles and control points for the splines of a curve when the user has not specified them explicitly. @@ -7467,100 +7046,115 @@ A path decomposes into independent segments at ``breakpoint'' knots, which are knots whose left and right angles are both prespecified in some way (i.e., their |mp_left_type| and |mp_right_type| aren't both open). -@c -static void mp_make_choices (MP mp,pointer knots) { - pointer h; /* the first breakpoint */ - pointer p,q; /* consecutive breakpoints being processed */ +@c +static void mp_make_choices (MP mp, mp_knot knots) { + mp_knot h; /* the first breakpoint */ + mp_knot p, q; /* consecutive breakpoints being processed */ @<Other local variables for |make_choices|@>; - check_arith; /* make sure that |arith_error=false| */ - if ( mp->internal[mp_tracing_choices]>0 ) - mp_print_path(mp, knots,", before choices",true); + check_arith; /* make sure that |arith_error=false| */ + if (internal_value (mp_tracing_choices) > 0) + mp_print_path (mp, knots, ", before choices", true); @<If consecutive knots are equal, join them explicitly@>; @<Find the first breakpoint, |h|, on the path; insert an artificial breakpoint if the path is an unbroken cycle@>; - p=h; - do { + p = h; + do { @<Fill in the control points between |p| and the next breakpoint, then advance |p| to that breakpoint@>; - } while (p!=h); - if ( mp->internal[mp_tracing_choices]>0 ) - mp_print_path(mp, knots,", after choices",true); - if ( mp->arith_error ) { + } while (p != h); + if (internal_value (mp_tracing_choices) > 0) + mp_print_path (mp, knots, ", after choices", true); + if (mp->arith_error) { @<Report an unexpected problem during the choice-making@>; } } + @ @<Report an unexpected problem during the choice...@>= -{ - print_err("Some number got too big"); -@.Some number got too big@> - help2("The path that I just computed is out of range.", - "So it will probably look funny. Proceed, for a laugh."); - mp_put_get_error(mp); mp->arith_error=false; +{ + print_err ("Some number got too big"); +@.Some number got too big@>; + help2 ("The path that I just computed is out of range.", + "So it will probably look funny. Proceed, for a laugh."); + mp_put_get_error (mp); + mp->arith_error = false; } + @ Two knots in a row with the same coordinates will always be joined by an explicit ``curve'' whose control points are identical with the knots. @<If consecutive knots are equal, join them explicitly@>= -p=knots; -do { - q=mp_link(p); - if ( mp_x_coord(p)==mp_x_coord(q) && - mp_y_coord(p)==mp_y_coord(q) && mp_right_type(p)>mp_explicit ) { - mp_right_type(p)=mp_explicit; - if ( mp_left_type(p)==mp_open ) { - mp_left_type(p)=mp_curl; left_curl(p)=unity; +p = knots; +do { + q = mp_next_knot (p); + if (mp_x_coord (p) == mp_x_coord (q) && + mp_y_coord (p) == mp_y_coord (q) && mp_right_type (p) > mp_explicit) { + mp_right_type (p) = mp_explicit; + if (mp_left_type (p) == mp_open) { + mp_left_type (p) = mp_curl; + left_curl (p) = unity; } - mp_left_type(q)=mp_explicit; - if ( mp_right_type(q)==mp_open ) { - mp_right_type(q)=mp_curl; right_curl(q)=unity; + mp_left_type (q) = mp_explicit; + if (mp_right_type (q) == mp_open) { + mp_right_type (q) = mp_curl; + right_curl (q) = unity; } - mp_right_x(p)=mp_x_coord(p); mp_left_x(q)=mp_x_coord(p); - mp_right_y(p)=mp_y_coord(p); mp_left_y(q)=mp_y_coord(p); + mp_right_x (p) = mp_x_coord (p); + mp_left_x (q) = mp_x_coord (p); + mp_right_y (p) = mp_y_coord (p); + mp_left_y (q) = mp_y_coord (p); } - p=q; -} while (p!=knots) + p = q; +} while (p != knots) @ If there are no breakpoints, it is necessary to compute the direction angles around an entire cycle. In this case the |mp_left_type| of the first node is temporarily changed to |end_cycle|. @<Find the first breakpoint, |h|, on the path...@>= -h=knots; -while (1) { - if ( mp_left_type(h)!=mp_open ) break; - if ( mp_right_type(h)!=mp_open ) break; - h=mp_link(h); - if ( h==knots ) { - mp_left_type(h)=mp_end_cycle; break; +h = knots; +while (1) { + if (mp_left_type (h) != mp_open) + break; + if (mp_right_type (h) != mp_open) + break; + h = mp_next_knot (h); + if (h == knots) { + mp_left_type (h) = mp_end_cycle; + break; } } + @ If |mp_right_type(p)<given| and |q=mp_link(p)|, we must have |mp_right_type(p)=mp_left_type(q)=mp_explicit| or |endpoint|. @<Fill in the control points between |p| and the next breakpoint...@>= -q=mp_link(p); -if ( mp_right_type(p)>=mp_given ) { - while ( (mp_left_type(q)==mp_open)&&(mp_right_type(q)==mp_open) ) q=mp_link(q); +q = mp_next_knot (p); +if (mp_right_type (p) >= mp_given) { + while ((mp_left_type (q) == mp_open) && (mp_right_type (q) == mp_open)) + q = mp_next_knot (q); @<Fill in the control information between consecutive breakpoints |p| and |q|@>; -} else if ( mp_right_type(p)==mp_endpoint ) { +} else if (mp_right_type (p) == mp_endpoint) { @<Give reasonable values for the unused control points between |p| and~|q|@>; } -p=q +p = q @ This step makes it possible to transform an explicitly computed path without checking the |mp_left_type| and |mp_right_type| fields. @<Give reasonable values for the unused control points between |p| and~|q|@>= -{ - mp_right_x(p)=mp_x_coord(p); mp_right_y(p)=mp_y_coord(p); - mp_left_x(q)=mp_x_coord(q); mp_left_y(q)=mp_y_coord(q); +{ + mp_right_x (p) = mp_x_coord (p); + mp_right_y (p) = mp_y_coord (p); + mp_left_x (q) = mp_x_coord (q); + mp_left_y (q) = mp_y_coord (q); } + @ Before we can go further into the way choices are made, we need to consider the underlying theory. The basic ideas implemented in |make_choices| are due to John Hobby, who introduced the notion of ``mock curvature'' @@ -7678,7 +7272,8 @@ called |solve_choices|, which has been introduced to keep @<Calculate the turning angles $\psi_k$ and the distances $d_{k,k+1}$; set $n$ to the length of the path@>; @<Remove |open| types at the breakpoints@>; -mp_solve_choices(mp, p,q,n) +mp_solve_choices (mp, p, q, n) + @ It's convenient to precompute quantities that will be needed several times later. The values of |delta_x[k]| and |delta_y[k]| will be the @@ -7687,51 +7282,59 @@ coordinates of $z\k-z_k$, and the magnitude of this vector will be and $z\k-z_k$ will be stored in |psi[k]|. @<Glob...@>= -int path_size; /* maximum number of knots between breakpoints of a path */ +int path_size; /* maximum number of knots between breakpoints of a path */ scaled *delta_x; scaled *delta_y; -scaled *delta; /* knot differences */ -angle *psi; /* turning angles */ +scaled *delta; /* knot differences */ +angle *psi; /* turning angles */ @ @<Dealloc variables@>= -xfree(mp->delta_x); -xfree(mp->delta_y); -xfree(mp->delta); -xfree(mp->psi); +xfree (mp->delta_x); +xfree (mp->delta_y); +xfree (mp->delta); +xfree (mp->psi); @ @<Other local variables for |make_choices|@>= - int k,n; /* current and final knot numbers */ - pointer s,t; /* registers for list traversal */ - scaled delx,dely; /* directions where |open| meets |explicit| */ - fraction sine,cosine; /* trig functions of various angles */ +int k, n; /* current and final knot numbers */ +mp_knot s, t; /* registers for list traversal */ +scaled delx, dely; /* directions where |open| meets |explicit| */ +fraction sine, cosine; /* trig functions of various angles */ @ @<Calculate the turning angles...@>= { RESTART: - k=0; s=p; n=mp->path_size; - do { - t=mp_link(s); - mp->delta_x[k]=mp_x_coord(t)-mp_x_coord(s); - mp->delta_y[k]=mp_y_coord(t)-mp_y_coord(s); - mp->delta[k]=mp_pyth_add(mp, mp->delta_x[k],mp->delta_y[k]); - if ( k>0 ) { - sine=mp_make_fraction(mp, mp->delta_y[k-1],mp->delta[k-1]); - cosine=mp_make_fraction(mp, mp->delta_x[k-1],mp->delta[k-1]); - mp->psi[k]=mp_n_arg(mp, mp_take_fraction(mp, mp->delta_x[k],cosine)+ - mp_take_fraction(mp, mp->delta_y[k],sine), - mp_take_fraction(mp, mp->delta_y[k],cosine)- - mp_take_fraction(mp, mp->delta_x[k],sine)); + k = 0; + s = p; + n = mp->path_size; + do { + t = mp_next_knot (s); + mp->delta_x[k] = mp_x_coord (t) - mp_x_coord (s); + mp->delta_y[k] = mp_y_coord (t) - mp_y_coord (s); + mp->delta[k] = mp_pyth_add (mp, mp->delta_x[k], mp->delta_y[k]); + if (k > 0) { + sine = mp_make_fraction (mp, mp->delta_y[k - 1], mp->delta[k - 1]); + cosine = mp_make_fraction (mp, mp->delta_x[k - 1], mp->delta[k - 1]); + mp->psi[k] = mp_n_arg (mp, mp_take_fraction (mp, mp->delta_x[k], cosine) + + mp_take_fraction (mp, mp->delta_y[k], sine), + mp_take_fraction (mp, mp->delta_y[k], cosine) - + mp_take_fraction (mp, mp->delta_x[k], sine)); } - incr(k); s=t; - if ( k==mp->path_size ) { - mp_reallocate_paths(mp, mp->path_size+(mp->path_size/4)); - goto RESTART; /* retry, loop size has changed */ + incr (k); + s = t; + if (k == mp->path_size) { + mp_reallocate_paths (mp, mp->path_size + (mp->path_size / 4)); + goto RESTART; /* retry, loop size has changed */ } - if ( s==q ) n=k; - } while (!((k>=n)&&(mp_left_type(s)!=mp_end_cycle))); - if ( k==n ) mp->psi[n]=0; else mp->psi[k]=mp->psi[1]; + if (s == q) + n = k; + } while (!((k >= n) && (mp_left_type (s) != mp_end_cycle))); + if (k == n) + mp->psi[n] = 0; + else + mp->psi[k] = mp->psi[1]; } + @ When we get to this point of the code, |mp_right_type(p)| is either |given| or |curl| or |open|. If it is |open|, we must have |mp_left_type(p)=mp_end_cycle| or |mp_left_type(p)=mp_explicit|. In the latter @@ -7743,20 +7346,26 @@ Similarly, |mp_left_type(q)| is either |given| or |curl| or |open| or |mp_end_cycle|. The |open| possibility is reduced either to |given| or to |curl|. @<Remove |open| types at the breakpoints@>= -if ( mp_left_type(q)==mp_open ) { - delx=mp_right_x(q)-mp_x_coord(q); dely=mp_right_y(q)-mp_y_coord(q); - if ( (delx==0)&&(dely==0) ) { - mp_left_type(q)=mp_curl; left_curl(q)=unity; - } else { - mp_left_type(q)=mp_given; left_given(q)=mp_n_arg(mp, delx,dely); +if (mp_left_type (q) == mp_open) { + delx = mp_right_x (q) - mp_x_coord (q); + dely = mp_right_y (q) - mp_y_coord (q); + if ((delx == 0) && (dely == 0)) { + mp_left_type (q) = mp_curl; + left_curl (q) = unity; + } else { + mp_left_type (q) = mp_given; + left_given (q) = mp_n_arg (mp, delx, dely); } } -if ( (mp_right_type(p)==mp_open)&&(mp_left_type(p)==mp_explicit) ) { - delx=mp_x_coord(p)-mp_left_x(p); dely=mp_y_coord(p)-mp_left_y(p); - if ( (delx==0)&&(dely==0) ) { - mp_right_type(p)=mp_curl; right_curl(p)=unity; - } else { - mp_right_type(p)=mp_given; right_given(p)=mp_n_arg(mp, delx,dely); +if ((mp_right_type (p) == mp_open) && (mp_left_type (p) == mp_explicit)) { + delx = mp_x_coord (p) - mp_left_x (p); + dely = mp_y_coord (p) - mp_left_y (p); + if ((delx == 0) && (dely == 0)) { + mp_right_type (p) = mp_curl; + right_curl (p) = unity; + } else { + mp_right_type (p) = mp_given; + right_given (p) = mp_n_arg (mp, delx, dely); } } @@ -7795,16 +7404,16 @@ and~|k+1|. The $u$'s and $w$'s are scaled by $2^{28}$, i.e., they are of type |fraction|; the $\theta$'s and $v$'s are of type |angle|. @<Glob...@>= -angle *theta; /* values of $\theta_k$ */ -fraction *uu; /* values of $u_k$ */ -angle *vv; /* values of $v_k$ */ -fraction *ww; /* values of $w_k$ */ +angle *theta; /* values of $\theta_k$ */ +fraction *uu; /* values of $u_k$ */ +angle *vv; /* values of $v_k$ */ +fraction *ww; /* values of $w_k$ */ @ @<Dealloc variables@>= -xfree(mp->theta); -xfree(mp->uu); -xfree(mp->vv); -xfree(mp->ww); +xfree (mp->theta); +xfree (mp->uu); +xfree (mp->vv); +xfree (mp->ww); @ @<Declarations@>= static void mp_reallocate_paths (MP mp, int l); @@ -7813,37 +7422,41 @@ static void mp_reallocate_paths (MP mp, int l); void mp_reallocate_paths (MP mp, int l) { XREALLOC (mp->delta_x, l, scaled); XREALLOC (mp->delta_y, l, scaled); - XREALLOC (mp->delta, l, scaled); - XREALLOC (mp->psi, l, angle); - XREALLOC (mp->theta, l, angle); - XREALLOC (mp->uu, l, fraction); - XREALLOC (mp->vv, l, angle); - XREALLOC (mp->ww, l, fraction); + XREALLOC (mp->delta, l, scaled); + XREALLOC (mp->psi, l, angle); + XREALLOC (mp->theta, l, angle); + XREALLOC (mp->uu, l, fraction); + XREALLOC (mp->vv, l, angle); + XREALLOC (mp->ww, l, fraction); mp->path_size = l; } + @ Our immediate problem is to get the ball rolling by setting up the first equation or by realizing that no equations are needed, and to fit this initialization into a framework suitable for the overall computation. @<Declarations@>= -static void mp_solve_choices (MP mp,pointer p, pointer q, halfword n) ; +static void mp_solve_choices (MP mp, mp_knot p, mp_knot q, halfword n); @ @c -void mp_solve_choices (MP mp,pointer p, pointer q, halfword n) { - int k; /* current knot number */ - pointer r,s,t; /* registers for list traversal */ +void mp_solve_choices (MP mp, mp_knot p, mp_knot q, halfword n) { + int k; /* current knot number */ + mp_knot r, s, t; /* registers for list traversal */ @<Other local variables for |solve_choices|@>; - k=0; s=p; r=0; - while (1) { - t=mp_link(s); - if ( k==0 ) { + k = 0; + s = p; + r = 0; + while (1) { + t = mp_next_knot (s); + if (k == 0) { @<Get the linear equations started; or |return| with the control points in place, if linear equations needn't be solved@> - } else { - switch (mp_left_type(s)) { - case mp_end_cycle: case mp_open: + } else { + switch (mp_left_type (s)) { + case mp_end_cycle: + case mp_open: @<Set up equation to match mock curvatures at $z_k$; then |goto found| with $\theta_n$ adjusted to equal $\theta_0$, if a cycle has ended@>; @@ -7856,38 +7469,44 @@ void mp_solve_choices (MP mp,pointer p, pointer q, halfword n) { @<Calculate the given value of $\theta_n$ and |goto found|@>; break; - } /* there are no other cases */ + } /* there are no other cases */ } - r=s; s=t; incr(k); + r = s; + s = t; + incr (k); } FOUND: @<Finish choosing angles and assigning control points@>; } + @ On the first time through the loop, we have |k=0| and |r| is not yet defined. The first linear equation, if any, will have $A_0=B_0=0$. @<Get the linear equations started...@>= -switch (mp_right_type(s)) { -case mp_given: - if ( mp_left_type(t)==mp_given ) { +switch (mp_right_type (s)) { +case mp_given: + if (mp_left_type (t) == mp_given) { @<Reduce to simple case of two givens and |return|@> } else { @<Set up the equation for a given value of $\theta_0$@>; } break; -case mp_curl: - if ( mp_left_type(t)==mp_curl ) { +case mp_curl: + if (mp_left_type (t) == mp_curl) { @<Reduce to simple case of straight line and |return|@> } else { @<Set up the equation for a curl at $\theta_0$@>; } break; -case mp_open: - mp->uu[0]=0; mp->vv[0]=0; mp->ww[0]=fraction_one; +case mp_open: + mp->uu[0] = 0; + mp->vv[0] = 0; + mp->ww[0] = fraction_one; /* this begins a cycle */ break; -} /* there are no other cases */ +} /* there are no other cases */ + @ The general equation that specifies equality of mock curvature at $z_k$ is $$A_k\theta_{k-1}+(B_k+C_k)\theta_k+D_k\theta\k=-B_k\psi_k-D_k\psi\k,$$ @@ -7906,41 +7525,50 @@ The calculations will be performed in several registers that provide temporary storage for intermediate quantities. @<Other local variables for |solve_choices|@>= -fraction aa,bb,cc,ff,acc; /* temporary registers */ -scaled dd,ee; /* likewise, but |scaled| */ -scaled lt,rt; /* tension values */ +fraction aa, bb, cc, ff, acc; /* temporary registers */ +scaled dd, ee; /* likewise, but |scaled| */ +scaled lt, rt; /* tension values */ @ @<Set up equation to match mock curvatures...@>= -{ @<Calculate the values $\\{aa}=A_k/B_k$, $\\{bb}=D_k/C_k$, +{ + @<Calculate the values $\\{aa}=A_k/B_k$, $\\{bb}=D_k/C_k$, $\\{dd}=(3-\alpha_{k-1})d_{k,k+1}$, $\\{ee}=(3-\beta\k)d_{k-1,k}$, and $\\{cc}=(B_k-u_{k-1}A_k)/B_k$@>; @<Calculate the ratio $\\{ff}=C_k/(C_k+B_k-u_{k-1}A_k)$@>; - mp->uu[k]=mp_take_fraction(mp, ff,bb); + mp->uu[k] = mp_take_fraction (mp, ff, bb); @<Calculate the values of $v_k$ and $w_k$@>; - if ( mp_left_type(s)==mp_end_cycle ) { + if (mp_left_type (s) == mp_end_cycle) { @<Adjust $\theta_n$ to equal $\theta_0$ and |goto found|@>; } } + @ Since tension values are never less than 3/4, the values |aa| and |bb| computed here are never more than 4/5. @<Calculate the values $\\{aa}=...@>= -if ( abs(right_tension(r))==unity) { - aa=fraction_half; dd=2*mp->delta[k]; -} else { - aa=mp_make_fraction(mp, unity,3*abs(right_tension(r))-unity); - dd=mp_take_fraction(mp, mp->delta[k], - fraction_three-mp_make_fraction(mp, unity,abs(right_tension(r)))); -} -if ( abs(left_tension(t))==unity ){ - bb=fraction_half; ee=2*mp->delta[k-1]; -} else { - bb=mp_make_fraction(mp, unity,3*abs(left_tension(t))-unity); - ee=mp_take_fraction(mp, mp->delta[k-1], - fraction_three-mp_make_fraction(mp, unity,abs(left_tension(t)))); -} -cc=fraction_one-mp_take_fraction(mp, mp->uu[k-1],aa) +if (abs (right_tension (r)) == unity) { + aa = fraction_half; + dd = 2 * mp->delta[k]; +} else { + aa = mp_make_fraction (mp, unity, 3 * abs (right_tension (r)) - unity); + dd = mp_take_fraction (mp, mp->delta[k], + fraction_three - mp_make_fraction (mp, unity, + abs (right_tension + (r)))); +} +if (abs (left_tension (t)) == unity) { + bb = fraction_half; + ee = 2 * mp->delta[k - 1]; +} else { + bb = mp_make_fraction (mp, unity, 3 * abs (left_tension (t)) - unity); + ee = mp_take_fraction (mp, mp->delta[k - 1], + fraction_three - mp_make_fraction (mp, unity, + abs (left_tension + (t)))); +} +cc = fraction_one - mp_take_fraction (mp, mp->uu[k - 1], aa) + @ The ratio to be calculated in this step can be written in the form $$\beta_k^2\cdot\\{ee}\over\beta_k^2\cdot\\{ee}+\alpha_k^2\cdot @@ -7949,19 +7577,22 @@ because of the quantities just calculated. The values of |dd| and |ee| will not be needed after this step has been performed. @<Calculate the ratio $\\{ff}=C_k/(C_k+B_k-u_{k-1}A_k)$@>= -dd=mp_take_fraction(mp, dd,cc); lt=abs(left_tension(s)); rt=abs(right_tension(s)); -if ( lt!=rt ) { /* $\beta_k^{-1}\ne\alpha_k^{-1}$ */ - if ( lt<rt ) { - ff=mp_make_fraction(mp, lt,rt); - ff=mp_take_fraction(mp, ff,ff); /* $\alpha_k^2/\beta_k^2$ */ - dd=mp_take_fraction(mp, dd,ff); - } else { - ff=mp_make_fraction(mp, rt,lt); - ff=mp_take_fraction(mp, ff,ff); /* $\beta_k^2/\alpha_k^2$ */ - ee=mp_take_fraction(mp, ee,ff); +dd = mp_take_fraction (mp, dd, cc); +lt = abs (left_tension (s)); +rt = abs (right_tension (s)); +if (lt != rt) { /* $\beta_k^{-1}\ne\alpha_k^{-1}$ */ + if (lt < rt) { + ff = mp_make_fraction (mp, lt, rt); + ff = mp_take_fraction (mp, ff, ff); /* $\alpha_k^2/\beta_k^2$ */ + dd = mp_take_fraction (mp, dd, ff); + } else { + ff = mp_make_fraction (mp, rt, lt); + ff = mp_take_fraction (mp, ff, ff); /* $\beta_k^2/\alpha_k^2$ */ + ee = mp_take_fraction (mp, ee, ff); } } -ff=mp_make_fraction(mp, ee,ee+dd) +ff = mp_make_fraction (mp, ee, ee + dd) + @ The value of $u_{k-1}$ will be |<=1| except when $k=1$ and the previous equation was specified by a curl. In that case we must use a special @@ -7972,20 +7603,23 @@ case. The curl equation makes $w_0=0$ and $v_0=-u_0\psi_1$, hence $-B_1\psi_1-A_1v_0=-(B_1-u_0A_1)\psi_1=-\\{cc}\cdot B_1\psi_1$. @<Calculate the values of $v_k$ and $w_k$@>= -acc=-mp_take_fraction(mp, mp->psi[k+1],mp->uu[k]); -if ( mp_right_type(r)==mp_curl ) { - mp->ww[k]=0; - mp->vv[k]=acc-mp_take_fraction(mp, mp->psi[1],fraction_one-ff); -} else { - ff=mp_make_fraction(mp, fraction_one-ff,cc); /* this is - $B_k/(C_k+B_k-u_{k-1}A_k)<5$ */ - acc=acc-mp_take_fraction(mp, mp->psi[k],ff); - ff=mp_take_fraction(mp, ff,aa); /* this is $A_k/(C_k+B_k-u_{k-1}A_k)$ */ - mp->vv[k]=acc-mp_take_fraction(mp, mp->vv[k-1],ff); - if ( mp->ww[k-1]==0 ) mp->ww[k]=0; - else mp->ww[k]=-mp_take_fraction(mp, mp->ww[k-1],ff); +acc = -mp_take_fraction (mp, mp->psi[k + 1], mp->uu[k]); +if (mp_right_type (r) == mp_curl) { + mp->ww[k] = 0; + mp->vv[k] = acc - mp_take_fraction (mp, mp->psi[1], fraction_one - ff); +} else { + ff = mp_make_fraction (mp, fraction_one - ff, cc); /* this is + $B_k/(C_k+B_k-u_{k-1}A_k)<5$ */ + acc = acc - mp_take_fraction (mp, mp->psi[k], ff); + ff = mp_take_fraction (mp, ff, aa); /* this is $A_k/(C_k+B_k-u_{k-1}A_k)$ */ + mp->vv[k] = acc - mp_take_fraction (mp, mp->vv[k - 1], ff); + if (mp->ww[k - 1] == 0) + mp->ww[k] = 0; + else + mp->ww[k] = -mp_take_fraction (mp, mp->ww[k - 1], ff); } + @ When a complete cycle has been traversed, we have $\theta_k+u_k\theta\k= v_k+w_k\theta_0$, for |1<=k<=n|. We would like to determine the value of $\theta_n$ and reduce the system to the form $\theta_k+u_k\theta\k=v_k$ @@ -7999,58 +7633,77 @@ $$\eqalign{\theta_n&=v_n+w_n\theta_0-u_n\theta_1=\cdots\cr so we can solve for $\theta_n=\theta_0$. @<Adjust $\theta_n$ to equal $\theta_0$ and |goto found|@>= -{ -aa=0; bb=fraction_one; /* we have |k=n| */ -do { decr(k); -if ( k==0 ) k=n; - aa=mp->vv[k]-mp_take_fraction(mp, aa,mp->uu[k]); - bb=mp->ww[k]-mp_take_fraction(mp, bb,mp->uu[k]); -} while (k!=n); /* now $\theta_n=\\{aa}+\\{bb}\cdot\theta_n$ */ -aa=mp_make_fraction(mp, aa,fraction_one-bb); -mp->theta[n]=aa; mp->vv[0]=aa; -for (k=1;k<n;k++) { - mp->vv[k]=mp->vv[k]+mp_take_fraction(mp, aa,mp->ww[k]); -} -goto FOUND; +{ + aa = 0; + bb = fraction_one; /* we have |k=n| */ + do { + decr (k); + if (k == 0) + k = n; + aa = mp->vv[k] - mp_take_fraction (mp, aa, mp->uu[k]); + bb = mp->ww[k] - mp_take_fraction (mp, bb, mp->uu[k]); + } while (k != n); /* now $\theta_n=\\{aa}+\\{bb}\cdot\theta_n$ */ + aa = mp_make_fraction (mp, aa, fraction_one - bb); + mp->theta[n] = aa; + mp->vv[0] = aa; + for (k = 1; k < n; k++) { + mp->vv[k] = mp->vv[k] + mp_take_fraction (mp, aa, mp->ww[k]); + } + goto FOUND; } + @ @d reduce_angle(A) if ( abs((A))>one_eighty_deg ) { if ( (A)>0 ) (A)=(A)-three_sixty_deg; else (A)=(A)+three_sixty_deg; } @<Calculate the given value of $\theta_n$...@>= -{ - mp->theta[n]=left_given(s)-mp_n_arg(mp, mp->delta_x[n-1],mp->delta_y[n-1]); - reduce_angle(mp->theta[n]); +{ + mp->theta[n] = + left_given (s) - mp_n_arg (mp, mp->delta_x[n - 1], mp->delta_y[n - 1]); + reduce_angle (mp->theta[n]); goto FOUND; } + @ @<Set up the equation for a given value of $\theta_0$@>= -{ - mp->vv[0]=right_given(s)-mp_n_arg(mp, mp->delta_x[0],mp->delta_y[0]); - reduce_angle(mp->vv[0]); - mp->uu[0]=0; mp->ww[0]=0; +{ + mp->vv[0] = right_given (s) - mp_n_arg (mp, mp->delta_x[0], mp->delta_y[0]); + reduce_angle (mp->vv[0]); + mp->uu[0] = 0; + mp->ww[0] = 0; } + @ @<Set up the equation for a curl at $\theta_0$@>= -{ cc=right_curl(s); lt=abs(left_tension(t)); rt=abs(right_tension(s)); - if ( (rt==unity)&&(lt==unity) ) - mp->uu[0]=mp_make_fraction(mp, cc+cc+unity,cc+two); - else - mp->uu[0]=mp_curl_ratio(mp, cc,rt,lt); - mp->vv[0]=-mp_take_fraction(mp, mp->psi[1],mp->uu[0]); mp->ww[0]=0; +{ + cc = right_curl (s); + lt = abs (left_tension (t)); + rt = abs (right_tension (s)); + if ((rt == unity) && (lt == unity)) + mp->uu[0] = mp_make_fraction (mp, cc + cc + unity, cc + two); + else + mp->uu[0] = mp_curl_ratio (mp, cc, rt, lt); + mp->vv[0] = -mp_take_fraction (mp, mp->psi[1], mp->uu[0]); + mp->ww[0] = 0; } + @ @<Set up equation for a curl at $\theta_n$...@>= -{ cc=left_curl(s); lt=abs(left_tension(s)); rt=abs(right_tension(r)); - if ( (rt==unity)&&(lt==unity) ) - ff=mp_make_fraction(mp, cc+cc+unity,cc+two); - else - ff=mp_curl_ratio(mp, cc,lt,rt); - mp->theta[n]=-mp_make_fraction(mp, mp_take_fraction(mp, mp->vv[n-1],ff), - fraction_one-mp_take_fraction(mp, ff,mp->uu[n-1])); +{ + cc = left_curl (s); + lt = abs (left_tension (s)); + rt = abs (right_tension (r)); + if ((rt == unity) && (lt == unity)) + ff = mp_make_fraction (mp, cc + cc + unity, cc + two); + else + ff = mp_curl_ratio (mp, cc, lt, rt); + mp->theta[n] = + -mp_make_fraction (mp, mp_take_fraction (mp, mp->vv[n - 1], ff), + fraction_one - mp_take_fraction (mp, ff, mp->uu[n - 1])); goto FOUND; } + @ The |curl_ratio| subroutine has three arguments, which our previous notation encourages us to call $\gamma$, $\alpha^{-1}$, and $\beta^{-1}$. It is a somewhat tedious program to calculate @@ -8061,46 +7714,58 @@ is necessary only if the curl and tension are both large.) The values of $\alpha$ and $\beta$ will be at most~4/3. @<Declarations@>= -static fraction mp_curl_ratio (MP mp,scaled gamma, scaled a_tension, - scaled b_tension) ; +static fraction mp_curl_ratio (MP mp, scaled gamma, scaled a_tension, + scaled b_tension); @ @c -fraction mp_curl_ratio (MP mp,scaled gamma, scaled a_tension, - scaled b_tension) { - fraction alpha,beta,num,denom,ff; /* registers */ - alpha=mp_make_fraction(mp, unity,a_tension); - beta=mp_make_fraction(mp, unity,b_tension); - if ( alpha<=beta ) { - ff=mp_make_fraction(mp, alpha,beta); ff=mp_take_fraction(mp, ff,ff); - gamma=mp_take_fraction(mp, gamma,ff); - beta=beta / 010000; /* convert |fraction| to |scaled| */ - denom=mp_take_fraction(mp, gamma,alpha)+three-beta; - num=mp_take_fraction(mp, gamma,fraction_three-alpha)+beta; - } else { - ff=mp_make_fraction(mp, beta,alpha); ff=mp_take_fraction(mp, ff,ff); - beta=mp_take_fraction(mp, beta,ff) / 010000; /* convert |fraction| to |scaled| */ - denom=mp_take_fraction(mp, gamma,alpha)+(ff / 1365)-beta; - /* $1365\approx 2^{12}/3$ */ - num=mp_take_fraction(mp, gamma,fraction_three-alpha)+beta; - } - if ( num>=denom+denom+denom+denom ) return fraction_four; - else return mp_make_fraction(mp, num,denom); +fraction mp_curl_ratio (MP mp, scaled gamma, scaled a_tension, scaled b_tension) { + fraction alpha, beta, num, denom, ff; /* registers */ + alpha = mp_make_fraction (mp, unity, a_tension); + beta = mp_make_fraction (mp, unity, b_tension); + if (alpha <= beta) { + ff = mp_make_fraction (mp, alpha, beta); + ff = mp_take_fraction (mp, ff, ff); + gamma = mp_take_fraction (mp, gamma, ff); + beta = beta / 010000; /* convert |fraction| to |scaled| */ + denom = mp_take_fraction (mp, gamma, alpha) + three - beta; + num = mp_take_fraction (mp, gamma, fraction_three - alpha) + beta; + } else { + ff = mp_make_fraction (mp, beta, alpha); + ff = mp_take_fraction (mp, ff, ff); + beta = mp_take_fraction (mp, beta, ff) / 010000; /* convert |fraction| to |scaled| */ + denom = mp_take_fraction (mp, gamma, alpha) + (ff / 1365) - beta; + /* $1365\approx 2^{12}/3$ */ + num = mp_take_fraction (mp, gamma, fraction_three - alpha) + beta; + } + if (num >= denom + denom + denom + denom) + return fraction_four; + else + return mp_make_fraction (mp, num, denom); } + @ We're in the home stretch now. @<Finish choosing angles and assigning control points@>= -for (k=n-1;k>=0;k--) { - mp->theta[k]=mp->vv[k]-mp_take_fraction(mp,mp->theta[k+1],mp->uu[k]); -} -s=p; k=0; -do { - t=mp_link(s); - mp_n_sin_cos(mp, mp->theta[k]); mp->st=mp->n_sin; mp->ct=mp->n_cos; - mp_n_sin_cos(mp, -mp->psi[k+1]-mp->theta[k+1]); mp->sf=mp->n_sin; mp->cf=mp->n_cos; - mp_set_controls(mp, s,t,k); - incr(k); s=t; -} while (k!=n) +for (k = n - 1; k >= 0; k--) { + mp->theta[k] = mp->vv[k] - mp_take_fraction (mp, mp->theta[k + 1], mp->uu[k]); +} +s = p; +k = 0; +do { + fraction n_sin; + fraction n_cos; + t = mp_next_knot (s); + mp_n_sin_cos (mp, mp->theta[k], &n_cos, &n_sin); + mp->st = n_sin; + mp->ct = n_cos; + mp_n_sin_cos (mp, -mp->psi[k + 1] - mp->theta[k + 1], &n_cos, &n_sin); + mp->sf = n_sin; + mp->cf = n_cos; + mp_set_controls (mp, s, t, k); + incr (k); + s = t; +} while (k != n) @ The |set_controls| routine actually puts the control points into a pair of consecutive nodes |p| and~|q|. Global variables are used to @@ -8111,96 +7776,135 @@ $\cos\phi$ needed in this calculation. fraction st; fraction ct; fraction sf; -fraction cf; /* sines and cosines */ +fraction cf; /* sines and cosines */ @ @<Declarations@>= -static void mp_set_controls (MP mp,pointer p, pointer q, integer k); +static void mp_set_controls (MP mp, mp_knot p, mp_knot q, integer k); @ @c -void mp_set_controls (MP mp,pointer p, pointer q, integer k) { - fraction rr,ss; /* velocities, divided by thrice the tension */ - scaled lt,rt; /* tensions */ - fraction sine; /* $\sin(\theta+\phi)$ */ - lt=abs(left_tension(q)); rt=abs(right_tension(p)); - rr=mp_velocity(mp, mp->st,mp->ct,mp->sf,mp->cf,rt); - ss=mp_velocity(mp, mp->sf,mp->cf,mp->st,mp->ct,lt); - if ( (right_tension(p)<0)||(left_tension(q)<0) ) { +void mp_set_controls (MP mp, mp_knot p, mp_knot q, integer k) { + fraction rr, ss; /* velocities, divided by thrice the tension */ + scaled lt, rt; /* tensions */ + fraction sine; /* $\sin(\theta+\phi)$ */ + lt = abs (left_tension (q)); + rt = abs (right_tension (p)); + rr = mp_velocity (mp, mp->st, mp->ct, mp->sf, mp->cf, rt); + ss = mp_velocity (mp, mp->sf, mp->cf, mp->st, mp->ct, lt); + if ((right_tension (p) < 0) || (left_tension (q) < 0)) { @<Decrease the velocities, if necessary, to stay inside the bounding triangle@>; } - mp_right_x(p)=mp_x_coord(p)+mp_take_fraction(mp, - mp_take_fraction(mp, mp->delta_x[k],mp->ct)- - mp_take_fraction(mp, mp->delta_y[k],mp->st),rr); - mp_right_y(p)=mp_y_coord(p)+mp_take_fraction(mp, - mp_take_fraction(mp, mp->delta_y[k],mp->ct)+ - mp_take_fraction(mp, mp->delta_x[k],mp->st),rr); - mp_left_x(q)=mp_x_coord(q)-mp_take_fraction(mp, - mp_take_fraction(mp, mp->delta_x[k],mp->cf)+ - mp_take_fraction(mp, mp->delta_y[k],mp->sf),ss); - mp_left_y(q)=mp_y_coord(q)-mp_take_fraction(mp, - mp_take_fraction(mp, mp->delta_y[k],mp->cf)- - mp_take_fraction(mp, mp->delta_x[k],mp->sf),ss); - mp_right_type(p)=mp_explicit; mp_left_type(q)=mp_explicit; + mp_right_x (p) = mp_x_coord (p) + mp_take_fraction (mp, + mp_take_fraction (mp, + mp->delta_x + [k], + mp->ct) + - mp_take_fraction (mp, + mp->delta_y + [k], + mp-> + st), + rr); + mp_right_y (p) = + mp_y_coord (p) + mp_take_fraction (mp, + mp_take_fraction (mp, mp->delta_y[k], + mp->ct) + + mp_take_fraction (mp, mp->delta_x[k], + mp->st), rr); + mp_left_x (q) = + mp_x_coord (q) - mp_take_fraction (mp, + mp_take_fraction (mp, mp->delta_x[k], + mp->cf) + + mp_take_fraction (mp, mp->delta_y[k], + mp->sf), ss); + mp_left_y (q) = + mp_y_coord (q) - mp_take_fraction (mp, + mp_take_fraction (mp, mp->delta_y[k], + mp->cf) - + mp_take_fraction (mp, mp->delta_x[k], + mp->sf), ss); + mp_right_type (p) = mp_explicit; + mp_left_type (q) = mp_explicit; } + @ The boundedness conditions $\\{rr}\L\sin\phi\,/\sin(\theta+\phi)$ and $\\{ss}\L\sin\theta\,/\sin(\theta+\phi)$ are to be enforced if $\sin\theta$, $\sin\phi$, and $\sin(\theta+\phi)$ all have the same sign. Otherwise there is no ``bounding triangle.'' @<Decrease the velocities, if necessary...@>= -if (((mp->st>=0)&&(mp->sf>=0))||((mp->st<=0)&&(mp->sf<=0)) ) { - sine=mp_take_fraction(mp, abs(mp->st),mp->cf)+ - mp_take_fraction(mp, abs(mp->sf),mp->ct); - if ( sine>0 ) { - sine=mp_take_fraction(mp, sine,fraction_one+unity); /* safety factor */ - if ( right_tension(p)<0 ) - if ( mp_ab_vs_cd(mp, abs(mp->sf),fraction_one,rr,sine)<0 ) - rr=mp_make_fraction(mp, abs(mp->sf),sine); - if ( left_tension(q)<0 ) - if ( mp_ab_vs_cd(mp, abs(mp->st),fraction_one,ss,sine)<0 ) - ss=mp_make_fraction(mp, abs(mp->st),sine); +if (((mp->st >= 0) && (mp->sf >= 0)) || ((mp->st <= 0) && (mp->sf <= 0))) { + sine = mp_take_fraction (mp, abs (mp->st), mp->cf) + + mp_take_fraction (mp, abs (mp->sf), mp->ct); + if (sine > 0) { + sine = mp_take_fraction (mp, sine, fraction_one + unity); /* safety factor */ + if (right_tension (p) < 0) + if (mp_ab_vs_cd (mp, abs (mp->sf), fraction_one, rr, sine) < 0) + rr = mp_make_fraction (mp, abs (mp->sf), sine); + if (left_tension (q) < 0) + if (mp_ab_vs_cd (mp, abs (mp->st), fraction_one, ss, sine) < 0) + ss = mp_make_fraction (mp, abs (mp->st), sine); } } @ Only the simple cases remain to be handled. @<Reduce to simple case of two givens and |return|@>= -{ - aa=mp_n_arg(mp, mp->delta_x[0],mp->delta_y[0]); - mp_n_sin_cos(mp, right_given(p)-aa); mp->ct=mp->n_cos; mp->st=mp->n_sin; - mp_n_sin_cos(mp, left_given(q)-aa); mp->cf=mp->n_cos; mp->sf=-mp->n_sin; - mp_set_controls(mp, p,q,0); return; +{ + fraction n_sin; + fraction n_cos; + aa = mp_n_arg (mp, mp->delta_x[0], mp->delta_y[0]); + mp_n_sin_cos (mp, right_given (p) - aa, &n_cos, &n_sin); + mp->ct = n_cos; + mp->st = n_sin; + mp_n_sin_cos (mp, left_given (q) - aa, &n_cos, &n_sin); + mp->cf = n_cos; + mp->sf = -n_sin; + mp_set_controls (mp, p, q, 0); + return; } + @ @<Reduce to simple case of straight line and |return|@>= -{ - mp_right_type(p)=mp_explicit; mp_left_type(q)=mp_explicit; - lt=abs(left_tension(q)); rt=abs(right_tension(p)); - if ( rt==unity ) { - if ( mp->delta_x[0]>=0 ) mp_right_x(p)=mp_x_coord(p)+((mp->delta_x[0]+1) / 3); - else mp_right_x(p)=mp_x_coord(p)+((mp->delta_x[0]-1) / 3); - if ( mp->delta_y[0]>=0 ) mp_right_y(p)=mp_y_coord(p)+((mp->delta_y[0]+1) / 3); - else mp_right_y(p)=mp_y_coord(p)+((mp->delta_y[0]-1) / 3); - } else { - ff=mp_make_fraction(mp, unity,3*rt); /* $\alpha/3$ */ - mp_right_x(p)=mp_x_coord(p)+mp_take_fraction(mp, mp->delta_x[0],ff); - mp_right_y(p)=mp_y_coord(p)+mp_take_fraction(mp, mp->delta_y[0],ff); - } - if ( lt==unity ) { - if ( mp->delta_x[0]>=0 ) mp_left_x(q)=mp_x_coord(q)-((mp->delta_x[0]+1) / 3); - else mp_left_x(q)=mp_x_coord(q)-((mp->delta_x[0]-1) / 3); - if ( mp->delta_y[0]>=0 ) mp_left_y(q)=mp_y_coord(q)-((mp->delta_y[0]+1) / 3); - else mp_left_y(q)=mp_y_coord(q)-((mp->delta_y[0]-1) / 3); - } else { - ff=mp_make_fraction(mp, unity,3*lt); /* $\beta/3$ */ - mp_left_x(q)=mp_x_coord(q)-mp_take_fraction(mp, mp->delta_x[0],ff); - mp_left_y(q)=mp_y_coord(q)-mp_take_fraction(mp, mp->delta_y[0],ff); +{ + mp_right_type (p) = mp_explicit; + mp_left_type (q) = mp_explicit; + lt = abs (left_tension (q)); + rt = abs (right_tension (p)); + if (rt == unity) { + if (mp->delta_x[0] >= 0) + mp_right_x (p) = mp_x_coord (p) + ((mp->delta_x[0] + 1) / 3); + else + mp_right_x (p) = mp_x_coord (p) + ((mp->delta_x[0] - 1) / 3); + if (mp->delta_y[0] >= 0) + mp_right_y (p) = mp_y_coord (p) + ((mp->delta_y[0] + 1) / 3); + else + mp_right_y (p) = mp_y_coord (p) + ((mp->delta_y[0] - 1) / 3); + } else { + ff = mp_make_fraction (mp, unity, 3 * rt); /* $\alpha/3$ */ + mp_right_x (p) = mp_x_coord (p) + mp_take_fraction (mp, mp->delta_x[0], ff); + mp_right_y (p) = mp_y_coord (p) + mp_take_fraction (mp, mp->delta_y[0], ff); + } + if (lt == unity) { + if (mp->delta_x[0] >= 0) + mp_left_x (q) = mp_x_coord (q) - ((mp->delta_x[0] + 1) / 3); + else + mp_left_x (q) = mp_x_coord (q) - ((mp->delta_x[0] - 1) / 3); + if (mp->delta_y[0] >= 0) + mp_left_y (q) = mp_y_coord (q) - ((mp->delta_y[0] + 1) / 3); + else + mp_left_y (q) = mp_y_coord (q) - ((mp->delta_y[0] - 1) / 3); + } else { + ff = mp_make_fraction (mp, unity, 3 * lt); /* $\beta/3$ */ + mp_left_x (q) = mp_x_coord (q) - mp_take_fraction (mp, mp->delta_x[0], ff); + mp_left_y (q) = mp_y_coord (q) - mp_take_fraction (mp, mp->delta_y[0], ff); } return; } -@* \[19] Measuring paths. + +@* Measuring paths. \MP's \&{llcorner}, \&{lrcorner}, \&{ulcorner}, and \&{urcorner} operators allow the user to measure the bounding box of anything that can go into a picture. It's easy to get rough bounds on the $x$ and $y$ extent of a path @@ -8238,25 +7942,33 @@ is already negative at |t=0|), |crossing_point| returns the value zero. @d no_crossing { return (fraction_one+1); } @d one_crossing { return fraction_one; } @d zero_crossing { return 0; } -@d mp_crossing_point(M,A,B,C) mp_do_crossing_point(A,B,C) - -@c static fraction mp_do_crossing_point (integer a, integer b, integer c) { - integer d; /* recursive counter */ - integer x,xx,x0,x1,x2; /* temporary registers for bisection */ - if ( a<0 ) zero_crossing; - if ( c>=0 ) { - if ( b>=0 ) { - if ( c>0 ) { no_crossing; } - else if ( (a==0)&&(b==0) ) { no_crossing;} - else { one_crossing; } + +@c +static fraction mp_crossing_point (MP mp, integer a, integer b, integer c) { + integer d; /* recursive counter */ + integer x, xx, x0, x1, x2; /* temporary registers for bisection */ + if (a < 0) + zero_crossing; + if (c >= 0) { + if (b >= 0) { + if (c > 0) { + no_crossing; + } else if ((a == 0) && (b == 0)) { + no_crossing; + } else { + one_crossing; + } } - if ( a==0 ) zero_crossing; - } else if ( a==0 ) { - if ( b<=0 ) zero_crossing; + if (a == 0) + zero_crossing; + } else if (a == 0) { + if (b <= 0) + zero_crossing; } @<Use bisection to find the crossing point, if one exists@>; } + @ The general bisection method is quite simple when $n=2$, hence |crossing_point| does not take much time. At each stage in the recursion we have a subinterval defined by |l| and~|j| such that @@ -8280,23 +7992,35 @@ will occur if the inputs satisfy $a<2^{30}$, $\vert a-b\vert<2^{30}$, and $\vert b-c\vert<2^{30}$. @<Use bisection to find the crossing point...@>= -d=1; x0=a; x1=a-b; x2=b-c; -do { - x=half(x1+x2); - if ( x1-x0>x0 ) { - x2=x; x0+=x0; d+=d; - } else { - xx=x1+x-x0; - if ( xx>x0 ) { - x2=x; x0+=x0; d+=d; - } else { - x0=x0-xx; - if ( x<=x0 ) { if ( x+x2<=x0 ) no_crossing; } - x1=x; d=d+d+1; +d = 1; +x0 = a; +x1 = a - b; +x2 = b - c; +do { + x = half (x1 + x2); + if (x1 - x0 > x0) { + x2 = x; + x0 += x0; + d += d; + } else { + xx = x1 + x - x0; + if (xx > x0) { + x2 = x; + x0 += x0; + d += d; + } else { + x0 = x0 - xx; + if (x <= x0) { + if (x + x2 <= x0) + no_crossing; + } + x1 = x; + d = d + d + 1; } } -} while (d<fraction_one); -return (d-fraction_one) +} while (d < fraction_one); +return (d - fraction_one) + @ Here is a routine that computes the $x$ or $y$ coordinate of the point on a cubic corresponding to the |fraction| value~|t|. @@ -8306,26 +8030,35 @@ It is convenient to define a \.{WEB} macro |t_of_the_way| such that @d t_of_the_way(A,B) ((A)-mp_take_fraction(mp,((A)-(B)),t)) -@c static scaled mp_eval_cubic (MP mp,pointer p, pointer q, fraction t) { - scaled x1,x2,x3; /* intermediate values */ - x1=t_of_the_way(knot_coord(p),right_coord(p)); - x2=t_of_the_way(right_coord(p),left_coord(q)); - x3=t_of_the_way(left_coord(q),knot_coord(q)); - x1=t_of_the_way(x1,x2); - x2=t_of_the_way(x2,x3); - return t_of_the_way(x1,x2); +@c +static scaled mp_eval_cubic (MP mp, mp_knot p, mp_knot q, quarterword c, + fraction t) { + scaled x1, x2, x3; /* intermediate values */ + if (c == mp_x_code) { + x1 = t_of_the_way (mp_x_coord (p), mp_right_x (p)); + x2 = t_of_the_way (mp_right_x (p), mp_left_x (q)); + x3 = t_of_the_way (mp_left_x (q), mp_x_coord (q)); + } else { + x1 = t_of_the_way (mp_y_coord (p), mp_right_y (p)); + x2 = t_of_the_way (mp_right_y (p), mp_left_y (q)); + x3 = t_of_the_way (mp_left_y (q), mp_y_coord (q)); + } + x1 = t_of_the_way (x1, x2); + x2 = t_of_the_way (x2, x3); + return t_of_the_way (x1, x2); } + @ The actual bounding box information is stored in global variables. Since it is convenient to address the $x$ and $y$ information separately, we define arrays indexed by |x_code..y_code| and use macros to give them more convenient names. @<Types...@>= -enum mp_bb_code { - mp_x_code=0, /* index for |minx| and |maxx| */ - mp_y_code /* index for |miny| and |maxy| */ -} ; +enum mp_bb_code { + mp_x_code = 0, /* index for |minx| and |maxx| */ + mp_y_code /* index for |miny| and |maxy| */ +}; @ @d mp_minx mp->bbmin[mp_x_code] @@ -8334,8 +8067,8 @@ enum mp_bb_code { @d mp_maxy mp->bbmax[mp_y_code] @<Glob...@>= -scaled bbmin[mp_y_code+1]; -scaled bbmax[mp_y_code+1]; +scaled bbmin[mp_y_code + 1]; +scaled bbmax[mp_y_code + 1]; /* the result of procedures that compute bounding box information */ @ Now we're ready for the key part of the bounding box computation. @@ -8347,57 +8080,86 @@ for $0<t\le1$. In other words, the procedure adjusts the bounds to accommodate |knot_coord(q)| and any extremes over the range $0<t<1$. The |c| parameter is |x_code| or |y_code|. -@c static void mp_bound_cubic (MP mp,pointer p, pointer q, quarterword c) { +@c +static void mp_bound_cubic (MP mp, mp_knot p, mp_knot q, quarterword c) { boolean wavy; /* whether we need to look for extremes */ - scaled del1,del2,del3,del,dmax; /* proportional to the control - points of a quadratic derived from a cubic */ - fraction t,tt; /* where a quadratic crosses zero */ - scaled x; /* a value that |bbmin[c]| and |bbmax[c]| must accommodate */ - x=knot_coord(q); + scaled del1, del2, del3, del, dmax; /* proportional to the control + points of a quadratic derived from a cubic */ + fraction t, tt; /* where a quadratic crosses zero */ + scaled x; /* a value that |bbmin[c]| and |bbmax[c]| must accommodate */ + x = (c == mp_x_code ? mp_x_coord (q) : mp_y_coord (q)); @<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>; @<Check the control points against the bounding box and set |wavy:=true| if any of them lie outside@>; - if ( wavy ) { - del1=right_coord(p)-knot_coord(p); - del2=left_coord(q)-right_coord(p); - del3=knot_coord(q)-left_coord(q); + if (wavy) { + if (c == mp_x_code) { + del1 = mp_right_x (p) - mp_x_coord (p); + del2 = mp_left_x (q) - mp_right_x (p); + del3 = mp_x_coord (q) - mp_left_x (q); + } else { + del1 = mp_right_y (p) - mp_y_coord (p); + del2 = mp_left_y (q) - mp_right_y (p); + del3 = mp_y_coord (q) - mp_left_y (q); + } @<Scale up |del1|, |del2|, and |del3| for greater accuracy; also set |del| to the first nonzero element of |(del1,del2,del3)|@>; - if ( del<0 ) { - negate(del1); negate(del2); negate(del3); + if (del < 0) { + negate (del1); + negate (del2); + negate (del3); }; - t=mp_crossing_point(mp, del1,del2,del3); - if ( t<fraction_one ) { + t = mp_crossing_point (mp, del1, del2, del3); + if (t < fraction_one) { @<Test the extremes of the cubic against the bounding box@>; } } } + @ @<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>= -if ( x<mp->bbmin[c] ) mp->bbmin[c]=x; -if ( x>mp->bbmax[c] ) mp->bbmax[c]=x +if (x < mp->bbmin[c]) + mp->bbmin[c] = x; +if (x > mp->bbmax[c]) + mp->bbmax[c] = x @ @<Check the control points against the bounding box and set...@>= -wavy=true; -if ( mp->bbmin[c]<=right_coord(p) ) - if ( right_coord(p)<=mp->bbmax[c] ) - if ( mp->bbmin[c]<=left_coord(q) ) - if ( left_coord(q)<=mp->bbmax[c] ) - wavy=false +wavy = true; +if (c == mp_x_code) { + if (mp->bbmin[c] <= mp_right_x (p)) + if (mp_right_x (p) <= mp->bbmax[c]) + if (mp->bbmin[c] <= mp_left_x (q)) + if (mp_left_x (q) <= mp->bbmax[c]) + wavy = false; +} else { + if (mp->bbmin[c] <= mp_right_y (p)) + if (mp_right_y (p) <= mp->bbmax[c]) + if (mp->bbmin[c] <= mp_left_y (q)) + if (mp_left_y (q) <= mp->bbmax[c]) + wavy = false; +} + @ If |del1=del2=del3=0|, it's impossible to obey the title of this section. We just set |del=0| in that case. @<Scale up |del1|, |del2|, and |del3| for greater accuracy...@>= -if ( del1!=0 ) del=del1; -else if ( del2!=0 ) del=del2; -else del=del3; -if ( del!=0 ) { - dmax=abs(del1); - if ( abs(del2)>dmax ) dmax=abs(del2); - if ( abs(del3)>dmax ) dmax=abs(del3); - while ( dmax<fraction_half ) { - dmax+=dmax; del1+=del1; del2+=del2; del3+=del3; +if (del1 != 0) + del = del1; +else if (del2 != 0) + del = del2; +else + del = del3; +if (del != 0) { + dmax = abs (del1); + if (abs (del2) > dmax) + dmax = abs (del2); + if (abs (del3) > dmax) + dmax = abs (del3); + while (dmax < fraction_half) { + dmax += dmax; + del1 += del1; + del2 += del2; + del3 += del3; } } @@ -8408,41 +8170,49 @@ But rounding error could make it slightly positive in which case we must cut it to zero to avoid confusion. @<Test the extremes of the cubic against the bounding box@>= -{ - x=mp_eval_cubic(mp, p,q,t); +{ + x = mp_eval_cubic (mp, p, q, c, t); @<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>; - del2=t_of_the_way(del2,del3); - /* now |0,del2,del3| represent the derivative on the remaining interval */ - if ( del2>0 ) del2=0; - tt=mp_crossing_point(mp, 0,-del2,-del3); - if ( tt<fraction_one ) { + del2 = t_of_the_way (del2, del3); + /* now |0,del2,del3| represent the derivative on the remaining interval */ + if (del2 > 0) + del2 = 0; + tt = mp_crossing_point (mp, 0, -del2, -del3); + if (tt < fraction_one) { @<Test the second extreme against the bounding box@>; } } + @ @<Test the second extreme against the bounding box@>= { - x=mp_eval_cubic(mp, p,q,t_of_the_way(tt,fraction_one)); + x = mp_eval_cubic (mp, p, q, c, t_of_the_way (tt, fraction_one)); @<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>; } + @ Finding the bounding box of a path is basically a matter of applying |bound_cubic| twice for each pair of adjacent knots. -@c static void mp_path_bbox (MP mp,pointer h) { - pointer p,q; /* a pair of adjacent knots */ - mp_minx=mp_x_coord(h); mp_miny=mp_y_coord(h); - mp_maxx=mp_minx; mp_maxy=mp_miny; - p=h; - do { - if ( mp_right_type(p)==mp_endpoint ) return; - q=mp_link(p); - mp_bound_cubic(mp, x_loc(p),x_loc(q),mp_x_code); - mp_bound_cubic(mp, y_loc(p),y_loc(q),mp_y_code); - p=q; - } while (p!=h); +@c +static void mp_path_bbox (MP mp, mp_knot h) { + mp_knot p, q; /* a pair of adjacent knots */ + mp_minx = mp_x_coord (h); + mp_miny = mp_y_coord (h); + mp_maxx = mp_minx; + mp_maxy = mp_miny; + p = h; + do { + if (mp_right_type (p) == mp_endpoint) + return; + q = mp_next_knot (p); + mp_bound_cubic (mp, p, q, mp_x_code); + mp_bound_cubic (mp, p, q, mp_y_code); + p = q; + } while (p != h); } + @ Another important way to measure a path is to find its arc length. This is best done by using the general bisection algorithm to subdivide the path until obtaining ``well behaved'' subpaths whose arc lengths can be approximated @@ -8494,7 +8264,7 @@ Thus the return value is either an arc length less than |a_goal| or, if the arc length would be at least |a_goal|, it returns a time value decreased by |two|. This allows the caller to use the sign of the result to distinguish between arc lengths and time values. On certain types of overflow, it is -possible for |a_goal| and the result of |arc_test| both to be |el_gordo|. +possible for |a_goal| and the result of |arc_test| both to be |EL_GORDO|. Otherwise, the result is always less than |a_goal|. Rather than halving the control point coordinates on each recursive call to @@ -8511,15 +8281,15 @@ for ${1\over3}\vb\dot B(0)\vb$, ${2\over3}\vb\dot B({1\over2})\vb$, and ${1\over3}\vb\dot B(1)\vb$. These quantities are relatively expensive to compute and they are needed in different instances of |arc_test|. -@c -static scaled mp_arc_test (MP mp, scaled dx0, scaled dy0, scaled dx1, scaled dy1, - scaled dx2, scaled dy2, scaled v0, scaled v02, - scaled v2, scaled a_goal, scaled tol) { - boolean simple; /* are the control points confined to a $90^\circ$ sector? */ - scaled dx01, dy01, dx12, dy12, dx02, dy02; /* bisection results */ +@c +static scaled mp_arc_test (MP mp, scaled dx0, scaled dy0, scaled dx1, + scaled dy1, scaled dx2, scaled dy2, scaled v0, + scaled v02, scaled v2, scaled a_goal, scaled tol) { + boolean simple; /* are the control points confined to a $90^\circ$ sector? */ + scaled dx01, dy01, dx12, dy12, dx02, dy02; /* bisection results */ scaled v002, v022; - /* twice the velocity magnitudes at $t={1\over4}$ and $t={3\over4}$ */ - scaled arc; /* best arc length estimate before recursion */ + /* twice the velocity magnitudes at $t={1\over4}$ and $t={3\over4}$ */ + scaled arc; /* best arc length estimate before recursion */ @<Other local variables in |arc_test|@>; @<Bisect the B\'ezier quadratic given by |dx0|, |dy0|, |dx1|, |dy1|, |dx2|, |dy2|@>; @@ -8527,11 +8297,11 @@ static scaled mp_arc_test (MP mp, scaled dx0, scaled dy0, scaled dx1, scaled dy1 set |arc_test| and |return|@>; @<Test if the control points are confined to one quadrant or rotating them $45^\circ$ would put them in one quadrant. Then set |simple| appropriately@>; - if ( simple && (abs(arc-v02-halfp(v0+v2)) <= tol) ) { - if ( arc < a_goal ) { + if (simple && (abs (arc - v02 - halfp (v0 + v2)) <= tol)) { + if (arc < a_goal) { return arc; } else { - @<Estimate when the arc length reaches |a_goal| and set |arc_test| to + @<Estimate when the arc length reaches |a_goal| and set |arc_test| to that time minus |two|@>; } } else { @@ -8539,51 +8309,54 @@ static scaled mp_arc_test (MP mp, scaled dx0, scaled dy0, scaled dx1, scaled dy1 } } + @ The |tol| value should by multiplied by $\sqrt 2$ before making recursive calls, but $1.5$ is an adequate approximation. It is best to avoid using |make_fraction| in this inner loop. @^inner loop@> @<Use one or two recursive calls to compute the |arc_test| function@>= -{ +{ @<Set |a_new| and |a_aux| so their sum is |2*a_goal| and |a_new| is as large as possible@>; - tol = tol + halfp(tol); - a = mp_arc_test(mp, dx0,dy0, dx01,dy01, dx02,dy02, v0, v002, - halfp(v02), a_new, tol); - if ( a<0 ) { - return (-halfp(two-a)); - } else { + tol = tol + halfp (tol); + a = mp_arc_test (mp, dx0, dy0, dx01, dy01, dx02, dy02, v0, v002, + halfp (v02), a_new, tol); + if (a < 0) { + return (-halfp (two - a)); + } else { @<Update |a_new| to reduce |a_new+a_aux| by |a|@>; - b = mp_arc_test(mp, dx02,dy02, dx12,dy12, dx2,dy2, - halfp(v02), v022, v2, a_new, tol); - if ( b<0 ) - return (-halfp(-b) - half_unit); - else - return (a + half(b-a)); + b = mp_arc_test (mp, dx02, dy02, dx12, dy12, dx2, dy2, + halfp (v02), v022, v2, a_new, tol); + if (b < 0) + return (-halfp (-b) - half_unit); + else + return (a + half (b - a)); } } + @ @<Other local variables in |arc_test|@>= -scaled a,b; /* results of recursive calls */ -scaled a_new,a_aux; /* the sum of these gives the |a_goal| */ +scaled a, b; /* results of recursive calls */ +scaled a_new, a_aux; /* the sum of these gives the |a_goal| */ @ @<Set |a_new| and |a_aux| so their sum is |2*a_goal| and |a_new| is...@>= -a_aux = el_gordo - a_goal; -if ( a_goal > a_aux ) { +a_aux = EL_GORDO - a_goal; +if (a_goal > a_aux) { a_aux = a_goal - a_aux; - a_new = el_gordo; -} else { + a_new = EL_GORDO; +} else { a_new = a_goal + a_goal; a_aux = 0; } + @ There is no need to maintain |a_aux| at this point so we use it as a temporary to force the additions and subtractions to be done in an order that avoids overflow. @<Update |a_new| to reduce |a_new+a_aux| by |a|@>= -if ( a > a_aux ) { +if (a > a_aux) { a_aux = a_aux - a; a_new = a_new + a_aux; } @@ -8594,46 +8367,50 @@ this assumption by requiring the norm of each $({\it dx},{\it dy})$ pair to obey this bound. Note that recursive calls will maintain this invariant. @<Bisect the B\'ezier quadratic given by |dx0|, |dy0|, |dx1|, |dy1|,...@>= -dx01 = half(dx0 + dx1); -dx12 = half(dx1 + dx2); -dx02 = half(dx01 + dx12); -dy01 = half(dy0 + dy1); -dy12 = half(dy1 + dy2); -dy02 = half(dy01 + dy12) +dx01 = half (dx0 + dx1); +dx12 = half (dx1 + dx2); +dx02 = half (dx01 + dx12); +dy01 = half (dy0 + dy1); +dy12 = half (dy1 + dy2); +dy02 = half (dy01 + dy12) + -@ We should be careful to keep |arc<el_gordo| so that calling |arc_test| with -|a_goal=el_gordo| is guaranteed to yield the arc length. +@ We should be careful to keep |arc<EL_GORDO| so that calling |arc_test| with +|a_goal=EL_GORDO| is guaranteed to yield the arc length. @<Initialize |v002|, |v022|, and the arc length estimate |arc|;...@>= -v002 = mp_pyth_add(mp, dx01+half(dx0+dx02), dy01+half(dy0+dy02)); -v022 = mp_pyth_add(mp, dx12+half(dx02+dx2), dy12+half(dy02+dy2)); -tmp = halfp(v02+2); -arc1 = v002 + half(halfp(v0+tmp) - v002); -arc = v022 + half(halfp(v2+tmp) - v022); -if ( (arc < el_gordo-arc1) ) { - arc = arc+arc1; -} else { +v002 = mp_pyth_add (mp, dx01 + half (dx0 + dx02), dy01 + half (dy0 + dy02)); +v022 = mp_pyth_add (mp, dx12 + half (dx02 + dx2), dy12 + half (dy02 + dy2)); +tmp = halfp (v02 + 2); +arc1 = v002 + half (halfp (v0 + tmp) - v002); +arc = v022 + half (halfp (v2 + tmp) - v022); +if ((arc < EL_GORDO - arc1)) { + arc = arc + arc1; +} else { mp->arith_error = true; - if ( a_goal==el_gordo ) return (el_gordo); - else return (-two); + if (a_goal == EL_GORDO) + return (EL_GORDO); + else + return (-two); } + @ @<Other local variables in |arc_test|@>= -scaled tmp, tmp2; /* all purpose temporary registers */ -scaled arc1; /* arc length estimate for the first half */ +scaled tmp, tmp2; /* all purpose temporary registers */ +scaled arc1; /* arc length estimate for the first half */ @ @<Test if the control points are confined to one quadrant or rotating...@>= -simple = ((dx0>=0) && (dx1>=0) && (dx2>=0)) || - ((dx0<=0) && (dx1<=0) && (dx2<=0)); -if ( simple ) - simple = ((dy0>=0) && (dy1>=0) && (dy2>=0)) || - ((dy0<=0) && (dy1<=0) && (dy2<=0)); -if ( ! simple ) { - simple = ((dx0>=dy0) && (dx1>=dy1) && (dx2>=dy2)) || - ((dx0<=dy0) && (dx1<=dy1) && (dx2<=dy2)); - if ( simple ) - simple = ((-dx0>=dy0) && (-dx1>=dy1) && (-dx2>=dy2)) || - ((-dx0<=dy0) && (-dx1<=dy1) && (-dx2<=dy2)); +simple = ((dx0 >= 0) && (dx1 >= 0) && (dx2 >= 0)) || + ((dx0 <= 0) && (dx1 <= 0) && (dx2 <= 0)); +if (simple) + simple = ((dy0 >= 0) && (dy1 >= 0) && (dy2 >= 0)) || + ((dy0 <= 0) && (dy1 <= 0) && (dy2 <= 0)); +if (!simple) { + simple = ((dx0 >= dy0) && (dx1 >= dy1) && (dx2 >= dy2)) || + ((dx0 <= dy0) && (dx1 <= dy1) && (dx2 <= dy2)); + if (simple) + simple = ((-dx0 >= dy0) && (-dx1 >= dy1) && (-dx2 >= dy2)) || + ((-dx0 <= dy0) && (-dx1 <= dy1) && (-dx2 <= dy2)); } @ Since Simpson's rule is based on approximating the integrand by a parabola, @@ -8675,19 +8452,22 @@ $d\tau\over dt$. We shall define a function |solve_rising_cubic| that finds $\tau$ given $a$, $b$, $c$, and $x$. @<Estimate when the arc length reaches |a_goal| and set |arc_test| to...@>= -{ +{ tmp = (v02 + 2) / 4; - if ( a_goal<=arc1 ) { - tmp2 = halfp(v0); - return - (halfp(mp_solve_rising_cubic(mp, tmp2, arc1-tmp2-tmp, tmp, a_goal))- two); - } else { - tmp2 = halfp(v2); + if (a_goal <= arc1) { + tmp2 = halfp (v0); + return + (halfp (mp_solve_rising_cubic (mp, tmp2, arc1 - tmp2 - tmp, tmp, a_goal)) + - two); + } else { + tmp2 = halfp (v2); return ((half_unit - two) + - halfp(mp_solve_rising_cubic(mp, tmp, arc-arc1-tmp-tmp2, tmp2, a_goal-arc1))); + halfp (mp_solve_rising_cubic + (mp, tmp, arc - arc1 - tmp - tmp2, tmp2, a_goal - arc1))); } } + @ Here is the |solve_rising_cubic| routine that finds the time~$t$ when $$ B(0, a, a+b, a+b+c; t) = x. $$ This routine is based on |crossing_point| but is simplified by the @@ -8697,92 +8477,121 @@ it and proceed with binary search. This finds a time when the function value reaches |x| and the slope is positive. @<Declarations@>= -static scaled mp_solve_rising_cubic (MP mp,scaled a, scaled b, scaled c, scaled x) ; +static scaled mp_solve_rising_cubic (MP mp, scaled a, scaled b, scaled c, + scaled x); @ @c -scaled mp_solve_rising_cubic (MP mp,scaled a, scaled b, scaled c, scaled x) { - scaled ab, bc, ac; /* bisection results */ - integer t; /* $2^k+q$ where unscaled answer is in $[q2^{-k},(q+1)2^{-k})$ */ - integer xx; /* temporary for updating |x| */ - if ( (a<0) || (c<0) ) mp_confusion(mp, "rising?"); -@:this can't happen rising?}{\quad rising?@> - if ( x<=0 ) { - return 0; - } else if ( x >= a+b+c ) { +scaled mp_solve_rising_cubic (MP mp, scaled a, scaled b, scaled c, scaled x) { + scaled ab, bc, ac; /* bisection results */ + integer t; /* $2^k+q$ where unscaled answer is in $[q2^{-k},(q+1)2^{-k})$ */ + integer xx; /* temporary for updating |x| */ + if ((a < 0) || (c < 0)) + mp_confusion (mp, "rising?"); +@:this can't happen rising?}{\quad rising?@>; + if (x <= 0) { + return 0; + } else if (x >= a + b + c) { return unity; - } else { + } else { t = 1; @<Rescale if necessary to make sure |a|, |b|, and |c| are all less than - |el_gordo div 3|@>; - do { - t+=t; + |EL_GORDO div 3|@>; + do { + t += t; @<Subdivide the B\'ezier quadratic defined by |a|, |b|, |c|@>; xx = x - a - ab - ac; - if ( xx < -x ) { x+=x; b=ab; c=ac; } - else { x = x + xx; a=ac; b=bc; t = t+1; }; + if (xx < -x) { + x += x; + b = ab; + c = ac; + } else { + x = x + xx; + a = ac; + b = bc; + t = t + 1; + }; } while (t < unity); return (t - unity); } } + @ @<Subdivide the B\'ezier quadratic defined by |a|, |b|, |c|@>= -ab = half(a+b); -bc = half(b+c); -ac = half(ab+bc) +ab = half (a + b); +bc = half (b + c); +ac = half (ab + bc) + + +@ The upper bound on |a|, |b|, and |c|: -@ @d one_third_el_gordo 05252525252 /* upper bound on |a|, |b|, and |c| */ +@d one_third_EL_GORDO ((math_data *)mp->math)->one_third_max_scaled_ @<Rescale if necessary to make sure |a|, |b|, and |c| are all less than...@>= -while ((a>one_third_el_gordo)||(b>one_third_el_gordo)||(c>one_third_el_gordo)) { - a = halfp(a); - b = half(b); - c = halfp(c); - x = halfp(x); +while ((a > one_third_EL_GORDO) || (b > one_third_EL_GORDO) + || (c > one_third_EL_GORDO)) { + a = halfp (a); + b = half (b); + c = halfp (c); + x = halfp (x); } + @ It is convenient to have a simpler interface to |arc_test| that requires no unnecessary arguments and ensures that each $({\it dx},{\it dy})$ pair has length less than |fraction_four|. @d arc_tol 16 /* quit when change in arc length estimate reaches this */ -@c static scaled mp_do_arc_test (MP mp,scaled dx0, scaled dy0, scaled dx1, - scaled dy1, scaled dx2, scaled dy2, scaled a_goal) { - scaled v0,v1,v2; /* length of each $({\it dx},{\it dy})$ pair */ - scaled v02; /* twice the norm of the quadratic at $t={1\over2}$ */ - v0 = mp_pyth_add(mp, dx0,dy0); - v1 = mp_pyth_add(mp, dx1,dy1); - v2 = mp_pyth_add(mp, dx2,dy2); - if ( (v0>=fraction_four) || (v1>=fraction_four) || (v2>=fraction_four) ) { +@c +static scaled mp_do_arc_test (MP mp, scaled dx0, scaled dy0, scaled dx1, + scaled dy1, scaled dx2, scaled dy2, scaled a_goal) { + scaled v0, v1, v2; /* length of each $({\it dx},{\it dy})$ pair */ + scaled v02; /* twice the norm of the quadratic at $t={1\over2}$ */ + v0 = mp_pyth_add (mp, dx0, dy0); + v1 = mp_pyth_add (mp, dx1, dy1); + v2 = mp_pyth_add (mp, dx2, dy2); + if ((v0 >= fraction_four) || (v1 >= fraction_four) || (v2 >= fraction_four)) { mp->arith_error = true; - if ( a_goal==el_gordo ) return el_gordo; - else return (-two); - } else { - v02 = mp_pyth_add(mp, dx1+half(dx0+dx2), dy1+half(dy0+dy2)); - return (mp_arc_test(mp, dx0,dy0, dx1,dy1, dx2,dy2, - v0, v02, v2, a_goal, arc_tol)); + if (a_goal == EL_GORDO) + return EL_GORDO; + else + return (-two); + } else { + v02 = mp_pyth_add (mp, dx1 + half (dx0 + dx2), dy1 + half (dy0 + dy2)); + return (mp_arc_test (mp, dx0, dy0, dx1, dy1, dx2, dy2, + v0, v02, v2, a_goal, arc_tol)); } } + @ Now it is easy to find the arc length of an entire path. -@c static scaled mp_get_arc_length (MP mp,pointer h) { - pointer p,q; /* for traversing the path */ - scaled a,a_tot; /* current and total arc lengths */ +@c +static scaled mp_get_arc_length (MP mp, mp_knot h) { + mp_knot p, q; /* for traversing the path */ + scaled a, a_tot; /* current and total arc lengths */ a_tot = 0; p = h; - while ( mp_right_type(p)!=mp_endpoint ){ - q = mp_link(p); - a = mp_do_arc_test(mp, mp_right_x(p)-mp_x_coord(p), mp_right_y(p)-mp_y_coord(p), - mp_left_x(q)-mp_right_x(p), mp_left_y(q)-mp_right_y(p), - mp_x_coord(q)-mp_left_x(q), mp_y_coord(q)-mp_left_y(q), el_gordo); - a_tot = mp_slow_add(mp, a, a_tot); - if ( q==h ) break; else p=q; + while (mp_right_type (p) != mp_endpoint) { + q = mp_next_knot (p); + a = + mp_do_arc_test (mp, mp_right_x (p) - mp_x_coord (p), + mp_right_y (p) - mp_y_coord (p), + mp_left_x (q) - mp_right_x (p), + mp_left_y (q) - mp_right_y (p), + mp_x_coord (q) - mp_left_x (q), + mp_y_coord (q) - mp_left_y (q), EL_GORDO); + a_tot = mp_slow_add (mp, a, a_tot); + if (q == h) + break; + else + p = q; } check_arith; return a_tot; } + @ The inverse operation of finding the time on a path~|h| when the arc length reaches some value |arc0| can also be accomplished via |do_arc_test|. Some care is required to handle very large times or negative times on cyclic paths. For @@ -8794,26 +8603,32 @@ time value greater than the length of the path. Since it could be much greater, we must be prepared to compute the arc length of path~|h| and divide this into |arc0| to find how many multiples of the length of path~|h| to add. -@c static scaled mp_get_arc_time (MP mp,pointer h, scaled arc0) { - pointer p,q; /* for traversing the path */ +@c +static scaled mp_get_arc_time (MP mp, mp_knot h, scaled arc0) { + mp_knot p, q; /* for traversing the path */ scaled t_tot; /* accumulator for the result */ - scaled t; /* the result of |do_arc_test| */ - scaled arc; /* portion of |arc0| not used up so far */ - integer n; /* number of extra times to go around the cycle */ - if ( arc0<0 ) { + scaled t; /* the result of |do_arc_test| */ + scaled arc; /* portion of |arc0| not used up so far */ + integer n; /* number of extra times to go around the cycle */ + if (arc0 < 0) { @<Deal with a negative |arc0| value and |return|@>; } - if ( arc0==el_gordo ) decr(arc0); + if (arc0 == EL_GORDO) + decr (arc0); t_tot = 0; arc = arc0; p = h; - while ( (mp_right_type(p)!=mp_endpoint) && (arc>0) ) { - q = mp_link(p); - t = mp_do_arc_test(mp, mp_right_x(p)-mp_x_coord(p), mp_right_y(p)-mp_y_coord(p), - mp_left_x(q)-mp_right_x(p), mp_left_y(q)-mp_right_y(p), - mp_x_coord(q)-mp_left_x(q), mp_y_coord(q)-mp_left_y(q), arc); + while ((mp_right_type (p) != mp_endpoint) && (arc > 0)) { + q = mp_next_knot (p); + t = + mp_do_arc_test (mp, mp_right_x (p) - mp_x_coord (p), + mp_right_y (p) - mp_y_coord (p), + mp_left_x (q) - mp_right_x (p), + mp_left_y (q) - mp_right_y (p), + mp_x_coord (q) - mp_left_x (q), + mp_y_coord (q) - mp_left_y (q), arc); @<Update |arc| and |t_tot| after |do_arc_test| has just returned |t|@>; - if ( q==h ) { + if (q == h) { @<Update |t_tot| and |arc| to avoid going around the cyclic path too many times but set |arith_error:=true| and |goto done| on overflow@>; @@ -8824,36 +8639,44 @@ we must be prepared to compute the arc length of path~|h| and divide this into return t_tot; } + @ @<Update |arc| and |t_tot| after |do_arc_test| has just returned |t|@>= -if ( t<0 ) { t_tot = t_tot + t + two; arc = 0; } -else { t_tot = t_tot + unity; arc = arc - t; } +if (t < 0) { + t_tot = t_tot + t + two; + arc = 0; +} else { + t_tot = t_tot + unity; + arc = arc - t; +} + @ @<Deal with a negative |arc0| value and |return|@>= -{ - if ( mp_left_type(h)==mp_endpoint ) { - t_tot=0; - } else { - p = mp_htap_ypoc(mp, h); - t_tot = -mp_get_arc_time(mp, p, -arc0); - mp_toss_knot_list(mp, p); +{ + if (mp_left_type (h) == mp_endpoint) { + t_tot = 0; + } else { + p = mp_htap_ypoc (mp, h); + t_tot = -mp_get_arc_time (mp, p, -arc0); + mp_toss_knot_list (mp, p); } check_arith; return t_tot; } + @ @<Update |t_tot| and |arc| to avoid going around the cyclic...@>= -if ( arc>0 ) { +if (arc > 0) { n = arc / (arc0 - arc); - arc = arc - n*(arc0 - arc); - if ( t_tot > (el_gordo / (n+1)) ) { - mp->arith_error = true; - check_arith; - return el_gordo; + arc = arc - n * (arc0 - arc); + if (t_tot > (EL_GORDO / (n + 1))) { + mp->arith_error = true; + check_arith; + return EL_GORDO; } - t_tot = (n + 1)*t_tot; + t_tot = (n + 1) * t_tot; } -@* \[20] Data structures for pens. +@* Data structures for pens. A Pen in \MP\ can be either elliptical or polygonal. Elliptical pens result in \ps\ \&{stroke} commands, while anything drawn with a polygonal pen is @:stroke}{\&{stroke} command@> @@ -8876,32 +8699,31 @@ room for the extra back pointer because we do not need the so that certain procedures can operate on both pens and paths. In particular, pens can be copied using |copy_path| and recycled using |toss_knot_list|. -@d knil mp_info - /* this replaces the |mp_left_type| and |mp_right_type| fields in a pen knot */ - @ The |make_pen| procedure turns a path into a pen by initializing -the |knil| pointers and making sure the knots form a convex polygon. +the |prev_knot| pointers and making sure the knots form a convex polygon. Thus each cubic in the given path becomes a straight line and the control points are ignored. If the path is not cyclic, the ends are connected by a straight line. @d copy_pen(A) mp_make_pen(mp, mp_copy_path(mp, (A)),false) -@c -static pointer mp_make_pen (MP mp,pointer h, boolean need_hull) { - pointer p,q; /* two consecutive knots */ - q=h; - do { - p=q; q=mp_link(q); - knil(q)=p; - } while (q!=h); - if ( need_hull ){ - h=mp_convex_hull(mp, h); +@c +static mp_knot mp_make_pen (MP mp, mp_knot h, boolean need_hull) { + mp_knot p, q; /* two consecutive knots */ + q = h; + do { + p = q; + q = mp_next_knot (q); + mp_prev_knot (q) = p; + } while (q != h); + if (need_hull) { + h = mp_convex_hull (mp, h); @<Make sure |h| isn't confused with an elliptical pen@>; } return h; } + @ The only information required about an elliptical pen is the overall transformation that has been applied to the original \&{pencircle}. @:pencircle_}{\&{pencircle} primitive@> @@ -8909,147 +8731,165 @@ Since it suffices to keep track of how the three points $(0,0)$, $(1,0)$, and $(0,1)$ are transformed, an elliptical pen can be stored in a single knot node and transformed as if it were a path. -@d pen_is_elliptical(A) ((A)==mp_link((A))) - -@c -static pointer mp_get_pen_circle (MP mp,scaled diam) { - pointer h; /* the knot node to return */ - h=mp_get_node(mp, knot_node_size); - mp_link(h)=h; knil(h)=h; - mp_originator(h)=mp_program_code; - mp_x_coord(h)=0; mp_y_coord(h)=0; - mp_left_x(h)=diam; mp_left_y(h)=0; - mp_right_x(h)=0; mp_right_y(h)=diam; +@d pen_is_elliptical(A) ((A)==mp_next_knot((A))) + +@c +static mp_knot mp_get_pen_circle (MP mp, scaled diam) { + mp_knot h; /* the knot node to return */ + h = mp_new_knot (mp); + mp_next_knot (h) = h; + mp_prev_knot (h) = h; + mp_originator (h) = mp_program_code; + mp_x_coord (h) = 0; + mp_y_coord (h) = 0; + mp_left_x (h) = diam; + mp_left_y (h) = 0; + mp_right_x (h) = 0; + mp_right_y (h) = diam; return h; } + @ If the polygon being returned by |make_pen| has only one vertex, it will be interpreted as an elliptical pen. This is no problem since a degenerate polygon can equally well be thought of as a degenerate ellipse. We need only initialize the |mp_left_x|, |mp_left_y|, |mp_right_x|, and |mp_right_y| fields. @<Make sure |h| isn't confused with an elliptical pen@>= -if ( pen_is_elliptical( h) ){ - mp_left_x(h)=mp_x_coord(h); mp_left_y(h)=mp_y_coord(h); - mp_right_x(h)=mp_x_coord(h); mp_right_y(h)=mp_y_coord(h); +if (pen_is_elliptical (h)) { + mp_left_x (h) = mp_x_coord (h); + mp_left_y (h) = mp_y_coord (h); + mp_right_x (h) = mp_x_coord (h); + mp_right_y (h) = mp_y_coord (h); } @ Printing a polygonal pen is very much like printing a path @<Declarations@>= -static void mp_pr_pen (MP mp,pointer h) ; +static void mp_pr_pen (MP mp, mp_knot h); @ @c -void mp_pr_pen (MP mp,pointer h) { - pointer p,q; /* for list traversal */ - if ( pen_is_elliptical(h) ) { +void mp_pr_pen (MP mp, mp_knot h) { + mp_knot p, q; /* for list traversal */ + if (pen_is_elliptical (h)) { @<Print the elliptical pen |h|@>; - } else { - p=h; - do { - mp_print_two(mp, mp_x_coord(p),mp_y_coord(p)); - mp_print_nl(mp, " .. "); + } else { + p = h; + do { + mp_print_two (mp, mp_x_coord (p), mp_y_coord (p)); + mp_print_nl (mp, " .. "); @<Advance |p| making sure the links are OK and |return| if there is a problem@>; - } while (p!=h); - mp_print(mp, "cycle"); + } while (p != h); + mp_print (mp, "cycle"); } } + @ @<Advance |p| making sure the links are OK and |return| if there is...@>= -q=mp_link(p); -if ( (q==null) || (knil(q)!=p) ) { - mp_print_nl(mp, "???"); return; /* this won't happen */ +q = mp_next_knot (p); +if ((q == NULL) || (mp_prev_knot (q) != p)) { + mp_print_nl (mp, "???"); + return; /* this won't happen */ @.???@> } -p=q +p = q @ @<Print the elliptical pen |h|@>= -{ -mp_print(mp, "pencircle transformed ("); -mp_print_scaled(mp, mp_x_coord(h)); -mp_print_char(mp, xord(',')); -mp_print_scaled(mp, mp_y_coord(h)); -mp_print_char(mp, xord(',')); -mp_print_scaled(mp, mp_left_x(h)-mp_x_coord(h)); -mp_print_char(mp, xord(',')); -mp_print_scaled(mp, mp_right_x(h)-mp_x_coord(h)); -mp_print_char(mp, xord(',')); -mp_print_scaled(mp, mp_left_y(h)-mp_y_coord(h)); -mp_print_char(mp, xord(',')); -mp_print_scaled(mp, mp_right_y(h)-mp_y_coord(h)); -mp_print_char(mp, xord(')')); +{ + mp_print (mp, "pencircle transformed ("); + mp_print_scaled (mp, mp_x_coord (h)); + mp_print_char (mp, xord (',')); + mp_print_scaled (mp, mp_y_coord (h)); + mp_print_char (mp, xord (',')); + mp_print_scaled (mp, mp_left_x (h) - mp_x_coord (h)); + mp_print_char (mp, xord (',')); + mp_print_scaled (mp, mp_right_x (h) - mp_x_coord (h)); + mp_print_char (mp, xord (',')); + mp_print_scaled (mp, mp_left_y (h) - mp_y_coord (h)); + mp_print_char (mp, xord (',')); + mp_print_scaled (mp, mp_right_y (h) - mp_y_coord (h)); + mp_print_char (mp, xord (')')); } + @ Here us another version of |pr_pen| that prints the pen as a diagnostic message. @<Declarations@>= -static void mp_print_pen (MP mp,pointer h, const char *s, boolean nuline) ; +static void mp_print_pen (MP mp, mp_knot h, const char *s, boolean nuline); @ @c -void mp_print_pen (MP mp,pointer h, const char *s, boolean nuline) { - mp_print_diagnostic(mp, "Pen",s,nuline); mp_print_ln(mp); -@.Pen at line...@> - mp_pr_pen(mp, h); - mp_end_diagnostic(mp, true); +void mp_print_pen (MP mp, mp_knot h, const char *s, boolean nuline) { + mp_print_diagnostic (mp, "Pen", s, nuline); + mp_print_ln (mp); +@.Pen at line...@>; + mp_pr_pen (mp, h); + mp_end_diagnostic (mp, true); } + @ Making a polygonal pen into a path involves restoring the |mp_left_type| and |mp_right_type| fields and setting the control points so as to make a polygonal path. -@c -static void mp_make_path (MP mp,pointer h) { - pointer p; /* for traversing the knot list */ - quarterword k; /* a loop counter */ +@c +static void mp_make_path (MP mp, mp_knot h) { + mp_knot p; /* for traversing the knot list */ + quarterword k; /* a loop counter */ @<Other local variables in |make_path|@>; - if ( pen_is_elliptical(h) ) { + if (pen_is_elliptical (h)) { @<Make the elliptical pen |h| into a path@>; - } else { - p=h; - do { - mp_left_type(p)=mp_explicit; - mp_right_type(p)=mp_explicit; + } else { + p = h; + do { + mp_left_type (p) = mp_explicit; + mp_right_type (p) = mp_explicit; @<copy the coordinates of knot |p| into its control points@>; - p=mp_link(p); - } while (p!=h); + p = mp_next_knot (p); + } while (p != h); } } + @ @<copy the coordinates of knot |p| into its control points@>= -mp_left_x(p)=mp_x_coord(p); -mp_left_y(p)=mp_y_coord(p); -mp_right_x(p)=mp_x_coord(p); -mp_right_y(p)=mp_y_coord(p) +mp_left_x (p) = mp_x_coord (p); +mp_left_y (p) = mp_y_coord (p); +mp_right_x (p) = mp_x_coord (p); +mp_right_y (p) = mp_y_coord (p) + @ We need an eight knot path to get a good approximation to an ellipse. @<Make the elliptical pen |h| into a path@>= -{ +{ @<Extract the transformation parameters from the elliptical pen~|h|@>; - p=h; - for (k=0;k<=7;k++ ) { + p = h; + for (k = 0; k <= 7; k++) { @<Initialize |p| as the |k|th knot of a circle of unit diameter, transforming it appropriately@>; - if ( k==7 ) mp_link(p)=h; else mp_link(p)=mp_get_node(mp, knot_node_size); - p=mp_link(p); + if (k == 7) + mp_next_knot (p) = h; + else + mp_next_knot (p) = mp_new_knot (mp); + p = mp_next_knot (p); } } + @ @<Extract the transformation parameters from the elliptical pen~|h|@>= -center_x=mp_x_coord(h); -center_y=mp_y_coord(h); -width_x=mp_left_x(h)-center_x; -width_y=mp_left_y(h)-center_y; -height_x=mp_right_x(h)-center_x; -height_y=mp_right_y(h)-center_y +center_x = mp_x_coord (h); +center_y = mp_y_coord (h); +width_x = mp_left_x (h) - center_x; +width_y = mp_left_y (h) - center_y; +height_x = mp_right_x (h) - center_x; +height_y = mp_right_y (h) - center_y @ @<Other local variables in |make_path|@>= -scaled center_x,center_y; /* translation parameters for an elliptical pen */ -scaled width_x,width_y; /* the effect of a unit change in $x$ */ -scaled height_x,height_y; /* the effect of a unit change in $y$ */ -scaled dx,dy; /* the vector from knot |p| to its right control point */ +scaled center_x, center_y; /* translation parameters for an elliptical pen */ +scaled width_x, width_y; /* the effect of a unit change in $x$ */ +scaled height_x, height_y; /* the effect of a unit change in $y$ */ +scaled dx, dy; /* the vector from knot |p| to its right control point */ integer kk; /* |k| advanced $270^\circ$ around the ring (cf. $\sin\theta=\cos(\theta+270)$) */ @@ -9058,26 +8898,26 @@ find the point $k/8$ of the way around the circle and the direction vector to use there. @<Initialize |p| as the |k|th knot of a circle of unit diameter,...@>= -kk=(k+6)% 8; -mp_x_coord(p)=center_x+mp_take_fraction(mp, mp->half_cos[k],width_x) - +mp_take_fraction(mp, mp->half_cos[kk],height_x); -mp_y_coord(p)=center_y+mp_take_fraction(mp, mp->half_cos[k],width_y) - +mp_take_fraction(mp, mp->half_cos[kk],height_y); -dx=-mp_take_fraction(mp, mp->d_cos[kk],width_x) - +mp_take_fraction(mp, mp->d_cos[k],height_x); -dy=-mp_take_fraction(mp, mp->d_cos[kk],width_y) - +mp_take_fraction(mp, mp->d_cos[k],height_y); -mp_right_x(p)=mp_x_coord(p)+dx; -mp_right_y(p)=mp_y_coord(p)+dy; -mp_left_x(p)=mp_x_coord(p)-dx; -mp_left_y(p)=mp_y_coord(p)-dy; -mp_left_type(p)=mp_explicit; -mp_right_type(p)=mp_explicit; -mp_originator(p)=mp_program_code +kk = (k + 6) % 8; +mp_x_coord (p) = center_x + mp_take_fraction (mp, mp->half_cos[k], width_x) + + mp_take_fraction (mp, mp->half_cos[kk], height_x); +mp_y_coord (p) = center_y + mp_take_fraction (mp, mp->half_cos[k], width_y) + + mp_take_fraction (mp, mp->half_cos[kk], height_y); +dx = -mp_take_fraction (mp, mp->d_cos[kk], width_x) + + mp_take_fraction (mp, mp->d_cos[k], height_x); +dy = -mp_take_fraction (mp, mp->d_cos[kk], width_y) + + mp_take_fraction (mp, mp->d_cos[k], height_y); +mp_right_x (p) = mp_x_coord (p) + dx; +mp_right_y (p) = mp_y_coord (p) + dy; +mp_left_x (p) = mp_x_coord (p) - dx; +mp_left_y (p) = mp_y_coord (p) - dy; +mp_left_type (p) = mp_explicit; +mp_right_type (p) = mp_explicit; +mp_originator (p) = mp_program_code @ @<Glob...@>= -fraction half_cos[8]; /* ${1\over2}\cos(45k)$ */ -fraction d_cos[8]; /* a magic constant times $\cos(45k)$ */ +fraction half_cos[8]; /* ${1\over2}\cos(45k)$ */ +fraction d_cos[8]; /* a magic constant times $\cos(45k)$ */ @ The magic constant for |d_cos| is the distance between $({1\over2},0)$ and $({1\over4}\sqrt2,{1\over4}\sqrt2)$ times the result of the |velocity| @@ -9087,21 +8927,22 @@ $$ d = {\sqrt{2-\sqrt2}\over 3+3\cos22.5^\circ} $$ @<Set init...@>= -mp->half_cos[0]=fraction_half; -mp->half_cos[1]=94906266; /* $2^{26}\sqrt2\approx94906265.62$ */ -mp->half_cos[2]=0; -mp->d_cos[0]=35596755; /* $2^{28}d\approx35596754.69$ */ -mp->d_cos[1]=25170707; /* $2^{27}\sqrt2\,d\approx25170706.63$ */ -mp->d_cos[2]=0; -for (k=3;k<= 4;k++ ) { - mp->half_cos[k]=-mp->half_cos[4-k]; - mp->d_cos[k]=-mp->d_cos[4-k]; +mp->half_cos[0] = fraction_half; +mp->half_cos[1] = 94906266; /* $2^{26}\sqrt2\approx94906265.62$ */ +mp->half_cos[2] = 0; +mp->d_cos[0] = 35596755; /* $2^{28}d\approx35596754.69$ */ +mp->d_cos[1] = 25170707; /* $2^{27}\sqrt2\,d\approx25170706.63$ */ +mp->d_cos[2] = 0; +for (k = 3; k <= 4; k++) { + mp->half_cos[k] = -mp->half_cos[4 - k]; + mp->d_cos[k] = -mp->d_cos[4 - k]; } -for (k=5;k<= 7;k++ ) { - mp->half_cos[k]=mp->half_cos[8-k]; - mp->d_cos[k]=mp->d_cos[8-k]; +for (k = 5; k <= 7; k++) { + mp->half_cos[k] = mp->half_cos[8 - k]; + mp->d_cos[k] = mp->d_cos[8 - k]; } + @ The |convex_hull| function forces a pen polygon to be convex when it is returned by |make_pen| and after any subsequent transformation where rounding error might allow the convexity to be lost. @@ -9109,21 +8950,21 @@ The convex hull algorithm used here is described by F.~P. Preparata and M.~I. Shamos [{\sl Computational Geometry}, Springer-Verlag, 1985]. @<Declarations@>= -static pointer mp_convex_hull (MP mp,pointer h); +static mp_knot mp_convex_hull (MP mp, mp_knot h); @ @c -pointer mp_convex_hull (MP mp,pointer h) { /* Make a polygonal pen convex */ - pointer l,r; /* the leftmost and rightmost knots */ - pointer p,q; /* knots being scanned */ - pointer s; /* the starting point for an upcoming scan */ - scaled dx,dy; /* a temporary pointer */ - if ( pen_is_elliptical(h) ) { - return h; - } else { +mp_knot mp_convex_hull (MP mp, mp_knot h) { /* Make a polygonal pen convex */ + mp_knot l, r; /* the leftmost and rightmost knots */ + mp_knot p, q; /* knots being scanned */ + mp_knot s; /* the starting point for an upcoming scan */ + scaled dx, dy; /* a temporary pointer */ + if (pen_is_elliptical (h)) { + return h; + } else { @<Set |l| to the leftmost knot in polygon~|h|@>; @<Set |r| to the rightmost knot in polygon~|h|@>; - if ( l!=r ) { - s=mp_link(r); + if (l != r) { + s = mp_next_knot (r); @<Find any knots on the path from |l| to |r| above the |l|-|r| line and move them past~|r|@>; @<Find any knots on the path from |s| to |l| below the |l|-|r| line and @@ -9131,220 +8972,286 @@ pointer mp_convex_hull (MP mp,pointer h) { /* Make a polygonal pen convex */ @<Sort the path from |l| to |r| by increasing $x$@>; @<Sort the path from |r| to |l| by decreasing $x$@>; } - if ( l!=mp_link(l) ) { + if (l != mp_next_knot (l)) { @<Do a Gramm scan and remove vertices where there is no left turn@>; } return l; } } + @ All comparisons are done primarily on $x$ and secondarily on $y$. @<Set |l| to the leftmost knot in polygon~|h|@>= -l=h; -p=mp_link(h); -while ( p!=h ) { - if ( mp_x_coord(p)<=mp_x_coord(l) ) - if ( (mp_x_coord(p)<mp_x_coord(l)) || (mp_y_coord(p)<mp_y_coord(l)) ) - l=p; - p=mp_link(p); +l = h; +p = mp_next_knot (h); +while (p != h) { + if (mp_x_coord (p) <= mp_x_coord (l)) + if ((mp_x_coord (p) < mp_x_coord (l)) || (mp_y_coord (p) < mp_y_coord (l))) + l = p; + p = mp_next_knot (p); } + @ @<Set |r| to the rightmost knot in polygon~|h|@>= -r=h; -p=mp_link(h); -while ( p!=h ) { - if ( mp_x_coord(p)>=mp_x_coord(r) ) - if ( (mp_x_coord(p)>mp_x_coord(r)) || (mp_y_coord(p)>mp_y_coord(r)) ) - r=p; - p=mp_link(p); +r = h; +p = mp_next_knot (h); +while (p != h) { + if (mp_x_coord (p) >= mp_x_coord (r)) + if ((mp_x_coord (p) > mp_x_coord (r)) || (mp_y_coord (p) > mp_y_coord (r))) + r = p; + p = mp_next_knot (p); } + @ @<Find any knots on the path from |l| to |r| above the |l|-|r| line...@>= -dx=mp_x_coord(r)-mp_x_coord(l); -dy=mp_y_coord(r)-mp_y_coord(l); -p=mp_link(l); -while ( p!=r ) { - q=mp_link(p); - if ( mp_ab_vs_cd(mp, dx,mp_y_coord(p)-mp_y_coord(l),dy,mp_x_coord(p)-mp_x_coord(l))>0 ) - mp_move_knot(mp, p, r); - p=q; +dx = mp_x_coord (r) - mp_x_coord (l); +dy = mp_y_coord (r) - mp_y_coord (l); +p = mp_next_knot (l); +while (p != r) { +q = mp_next_knot (p); +if (mp_ab_vs_cd + (mp, dx, mp_y_coord (p) - mp_y_coord (l), dy, + mp_x_coord (p) - mp_x_coord (l)) > 0) + mp_move_knot (mp, p, r); +p = q; } + @ The |move_knot| procedure removes |p| from a doubly linked list and inserts it after |q|. @ @<Declarations@>= -static void mp_move_knot (MP mp,pointer p, pointer q) ; +static void mp_move_knot (MP mp, mp_knot p, mp_knot q); @ @c -void mp_move_knot (MP mp,pointer p, pointer q) { - mp_link(knil(p))=mp_link(p); - knil(mp_link(p))=knil(p); - knil(p)=q; - mp_link(p)=mp_link(q); - mp_link(q)=p; - knil(mp_link(p))=p; +void mp_move_knot (MP mp, mp_knot p, mp_knot q) { + (void) mp; + mp_next_knot (mp_prev_knot (p)) = mp_next_knot (p); + mp_prev_knot (mp_next_knot (p)) = mp_prev_knot (p); + mp_prev_knot (p) = q; + mp_next_knot (p) = mp_next_knot (q); + mp_next_knot (q) = p; + mp_prev_knot (mp_next_knot (p)) = p; } + @ @<Find any knots on the path from |s| to |l| below the |l|-|r| line...@>= -p=s; -while ( p!=l ) { - q=mp_link(p); - if ( mp_ab_vs_cd(mp, dx,mp_y_coord(p)-mp_y_coord(l),dy,mp_x_coord(p)-mp_x_coord(l))<0 ) - mp_move_knot(mp, p,l); - p=q; +p = s; +while (p != l) { + q = mp_next_knot (p); + if (mp_ab_vs_cd + (mp, dx, mp_y_coord (p) - mp_y_coord (l), dy, + mp_x_coord (p) - mp_x_coord (l)) < 0) + mp_move_knot (mp, p, l); + p = q; } + @ The list is likely to be in order already so we just do linear insertions. Secondary comparisons on $y$ ensure that the sort is consistent with the choice of |l| and |r|. @<Sort the path from |l| to |r| by increasing $x$@>= -p=mp_link(l); -while ( p!=r ) { - q=knil(p); - while ( mp_x_coord(q)>mp_x_coord(p) ) q=knil(q); - while ( mp_x_coord(q)==mp_x_coord(p) ) { - if ( mp_y_coord(q)>mp_y_coord(p) ) q=knil(q); else break; +p = mp_next_knot (l); +while (p != r) { + q = mp_prev_knot (p); + while (mp_x_coord (q) > mp_x_coord (p)) + q = mp_prev_knot (q); + while (mp_x_coord (q) == mp_x_coord (p)) { + if (mp_y_coord (q) > mp_y_coord (p)) + q = mp_prev_knot (q); + else + break; + } + if (q == mp_prev_knot (p)) { + p = mp_next_knot (p); + } else { + p = mp_next_knot (p); + mp_move_knot (mp, mp_prev_knot (p), q); } - if ( q==knil(p) ) p=mp_link(p); - else { p=mp_link(p); mp_move_knot(mp, knil(p),q); }; } + @ @<Sort the path from |r| to |l| by decreasing $x$@>= -p=mp_link(r); -while ( p!=l ){ - q=knil(p); - while ( mp_x_coord(q)<mp_x_coord(p) ) q=knil(q); - while ( mp_x_coord(q)==mp_x_coord(p) ) { - if ( mp_y_coord(q)<mp_y_coord(p) ) q=knil(q); else break; +p = mp_next_knot (r); +while (p != l) { + q = mp_prev_knot (p); + while (mp_x_coord (q) < mp_x_coord (p)) + q = mp_prev_knot (q); + while (mp_x_coord (q) == mp_x_coord (p)) { + if (mp_y_coord (q) < mp_y_coord (p)) + q = mp_prev_knot (q); + else + break; + } + if (q == mp_prev_knot (p)) { + p = mp_next_knot (p); + } else { + p = mp_next_knot (p); + mp_move_knot (mp, mp_prev_knot (p), q); } - if ( q==knil(p) ) p=mp_link(p); - else { p=mp_link(p); mp_move_knot(mp, knil(p),q); }; } + @ The condition involving |ab_vs_cd| tests if there is not a left turn at knot |q|. There usually will be a left turn so we streamline the case where the |then| clause is not executed. @<Do a Gramm scan and remove vertices where there...@>= -{ -p=l; q=mp_link(l); -while (1) { - dx=mp_x_coord(q)-mp_x_coord(p); - dy=mp_y_coord(q)-mp_y_coord(p); - p=q; q=mp_link(q); - if ( p==l ) break; - if ( p!=r ) - if ( mp_ab_vs_cd(mp, dx,mp_y_coord(q)-mp_y_coord(p),dy,mp_x_coord(q)-mp_x_coord(p))<=0 ) { - @<Remove knot |p| and back up |p| and |q| but don't go past |l|@>; - } +{ + p = l; + q = mp_next_knot (l); + while (1) { + dx = mp_x_coord (q) - mp_x_coord (p); + dy = mp_y_coord (q) - mp_y_coord (p); + p = q; + q = mp_next_knot (q); + if (p == l) + break; + if (p != r) + if (mp_ab_vs_cd + (mp, dx, mp_y_coord (q) - mp_y_coord (p), dy, + mp_x_coord (q) - mp_x_coord (p)) <= 0) { + @<Remove knot |p| and back up |p| and |q| but don't go past |l|@>; + } } } + @ @<Remove knot |p| and back up |p| and |q| but don't go past |l|@>= -{ -s=knil(p); -mp_free_node(mp, p,knot_node_size); -mp_link(s)=q; knil(q)=s; -if ( s==l ) p=s; -else { p=knil(s); q=s; }; +{ + s = mp_prev_knot (p); + mp_xfree (p); + mp_next_knot (s) = q; + mp_prev_knot (q) = s; + if (s == l) { + p = s; + } else { + p = mp_prev_knot (s); + q = s; + } } + @ The |find_offset| procedure sets global variables |(cur_x,cur_y)| to the offset associated with the given direction |(x,y)|. If two different offsets apply, it chooses one of them. -@c -static void mp_find_offset (MP mp,scaled x, scaled y, pointer h) { - pointer p,q; /* consecutive knots */ - scaled wx,wy,hx,hy; +@c +static void mp_find_offset (MP mp, scaled x, scaled y, mp_knot h) { + mp_knot p, q; /* consecutive knots */ + scaled wx, wy, hx, hy; /* the transformation matrix for an elliptical pen */ - fraction xx,yy; /* untransformed offset for an elliptical pen */ - fraction d; /* a temporary register */ - if ( pen_is_elliptical(h) ) { + fraction xx, yy; /* untransformed offset for an elliptical pen */ + fraction d; /* a temporary register */ + if (pen_is_elliptical (h)) { @<Find the offset for |(x,y)| on the elliptical pen~|h|@> - } else { - q=h; - do { - p=q; q=mp_link(q); - } while (!(mp_ab_vs_cd(mp, mp_x_coord(q)-mp_x_coord(p),y, mp_y_coord(q)-mp_y_coord(p),x)>=0)); - do { - p=q; q=mp_link(q); - } while (!(mp_ab_vs_cd(mp, mp_x_coord(q)-mp_x_coord(p),y, mp_y_coord(q)-mp_y_coord(p),x)<=0)); - mp->cur_x=mp_x_coord(p); - mp->cur_y=mp_y_coord(p); + } else { + q = h; + do { + p = q; + q = mp_next_knot (q); + } while (! + (mp_ab_vs_cd + (mp, mp_x_coord (q) - mp_x_coord (p), y, + mp_y_coord (q) - mp_y_coord (p), x) >= 0)); + do { + p = q; + q = mp_next_knot (q); + } while (! + (mp_ab_vs_cd + (mp, mp_x_coord (q) - mp_x_coord (p), y, + mp_y_coord (q) - mp_y_coord (p), x) <= 0)); + mp->cur_x = mp_x_coord (p); + mp->cur_y = mp_y_coord (p); } } + @ @<Glob...@>= scaled cur_x; -scaled cur_y; /* all-purpose return value registers */ +scaled cur_y; /* all-purpose return value registers */ @ @<Find the offset for |(x,y)| on the elliptical pen~|h|@>= -if ( (x==0) && (y==0) ) { - mp->cur_x=mp_x_coord(h); mp->cur_y=mp_y_coord(h); -} else { +if ((x == 0) && (y == 0)) { + mp->cur_x = mp_x_coord (h); + mp->cur_y = mp_y_coord (h); +} else { @<Find the non-constant part of the transformation for |h|@>; - while ( (abs(x)<fraction_half) && (abs(y)<fraction_half) ){ - x+=x; y+=y; + while ((abs (x) < fraction_half) && (abs (y) < fraction_half)) { + x += x; + y += y; }; @<Make |(xx,yy)| the offset on the untransformed \&{pencircle} for the untransformed version of |(x,y)|@>; - mp->cur_x=mp_x_coord(h)+mp_take_fraction(mp, xx,wx)+mp_take_fraction(mp, yy,hx); - mp->cur_y=mp_y_coord(h)+mp_take_fraction(mp, xx,wy)+mp_take_fraction(mp, yy,hy); + mp->cur_x = + mp_x_coord (h) + mp_take_fraction (mp, xx, wx) + mp_take_fraction (mp, yy, + hx); + mp->cur_y = + mp_y_coord (h) + mp_take_fraction (mp, xx, wy) + mp_take_fraction (mp, yy, + hy); } + @ @<Find the non-constant part of the transformation for |h|@>= -wx=mp_left_x(h)-mp_x_coord(h); -wy=mp_left_y(h)-mp_y_coord(h); -hx=mp_right_x(h)-mp_x_coord(h); -hy=mp_right_y(h)-mp_y_coord(h) +wx = mp_left_x (h) - mp_x_coord (h); +wy = mp_left_y (h) - mp_y_coord (h); +hx = mp_right_x (h) - mp_x_coord (h); +hy = mp_right_y (h) - mp_y_coord (h) + @ @<Make |(xx,yy)| the offset on the untransformed \&{pencircle} for the...@>= -yy=-(mp_take_fraction(mp, x,hy)+mp_take_fraction(mp, y,-hx)); -xx=mp_take_fraction(mp, x,-wy)+mp_take_fraction(mp, y,wx); -d=mp_pyth_add(mp, xx,yy); -if ( d>0 ) { - xx=half(mp_make_fraction(mp, xx,d)); - yy=half(mp_make_fraction(mp, yy,d)); +yy = -(mp_take_fraction (mp, x, hy) + mp_take_fraction (mp, y, -hx)); +xx = mp_take_fraction (mp, x, -wy) + mp_take_fraction (mp, y, wx); +d = mp_pyth_add (mp, xx, yy); +if (d > 0) { +xx = half (mp_make_fraction (mp, xx, d)); +yy = half (mp_make_fraction (mp, yy, d)); } @ Finding the bounding box of a pen is easy except if the pen is elliptical. But we can handle that case by just calling |find_offset| twice. The answer is stored in the global variables |minx|, |maxx|, |miny|, and |maxy|. -@c -static void mp_pen_bbox (MP mp,pointer h) { - pointer p; /* for scanning the knot list */ - if ( pen_is_elliptical(h) ) { +@c +static void mp_pen_bbox (MP mp, mp_knot h) { + mp_knot p; /* for scanning the knot list */ + if (pen_is_elliptical (h)) { @<Find the bounding box of an elliptical pen@>; - } else { - mp_minx=mp_x_coord(h); mp_maxx=mp_minx; - mp_miny=mp_y_coord(h); mp_maxy=mp_miny; - p=mp_link(h); - while ( p!=h ) { - if ( mp_x_coord(p)<mp_minx ) mp_minx=mp_x_coord(p); - if ( mp_y_coord(p)<mp_miny ) mp_miny=mp_y_coord(p); - if ( mp_x_coord(p)>mp_maxx ) mp_maxx=mp_x_coord(p); - if ( mp_y_coord(p)>mp_maxy ) mp_maxy=mp_y_coord(p); - p=mp_link(p); + } else { + mp_minx = mp_x_coord (h); + mp_maxx = mp_minx; + mp_miny = mp_y_coord (h); + mp_maxy = mp_miny; + p = mp_next_knot (h); + while (p != h) { + if (mp_x_coord (p) < mp_minx) + mp_minx = mp_x_coord (p); + if (mp_y_coord (p) < mp_miny) + mp_miny = mp_y_coord (p); + if (mp_x_coord (p) > mp_maxx) + mp_maxx = mp_x_coord (p); + if (mp_y_coord (p) > mp_maxy) + mp_maxy = mp_y_coord (p); + p = mp_next_knot (p); } } } + @ @<Find the bounding box of an elliptical pen@>= -{ -mp_find_offset(mp, 0,fraction_one,h); -mp_maxx=mp->cur_x; -mp_minx=2*mp_x_coord(h)-mp->cur_x; -mp_find_offset(mp, -fraction_one,0,h); -mp_maxy=mp->cur_y; -mp_miny=2*mp_y_coord(h)-mp->cur_y; +{ + mp_find_offset (mp, 0, fraction_one, h); + mp_maxx = mp->cur_x; + mp_minx = 2 * mp_x_coord (h) - mp->cur_x; + mp_find_offset (mp, -fraction_one, 0, h); + mp_maxy = mp->cur_y; + mp_miny = 2 * mp_y_coord (h) - mp->cur_y; } -@* \[21] Edge structures. + +@* Edge structures. Now we come to \MP's internal scheme for representing pictures. The representation is very different from \MF's edge structures because \MP\ pictures contain \ps\ graphics objects instead of pixel @@ -9372,107 +9279,148 @@ pointer to a cyclic path and the value to use for \ps' \&{currentrgbcolor} parameter. If a pen is used for filling |pen_p|, |ljoin_val| and |miterlim_val| give the relevant information. -@d mp_path_p(A) mp_link((A)+1) - /* a pointer to the path that needs filling */ -@d mp_pen_p(A) mp_info((A)+1) - /* a pointer to the pen to fill or stroke with */ -@d mp_color_model(A) mp_type((A)+2) /* the color model */ -@d obj_red_loc(A) ((A)+3) /* the first of three locations for the color */ -@d obj_cyan_loc obj_red_loc /* the first of four locations for the color */ -@d obj_grey_loc obj_red_loc /* the location for the color */ -@d red_val(A) mp->mem[(A)+3].sc - /* the red component of the color in the range $0\ldots1$ */ +@d mp_path_p(A) (A)->path_p_ /* a pointer to the path that needs filling */ +@d mp_pen_p(A) (A)->pen_p_ /* a pointer to the pen to fill or stroke with */ +@d mp_color_model(A) ((mp_fill_node)(A))->color_model_ /* the color model */ +@d red_val(A) ((mp_fill_node)(A))->red_val_ /* the red component of the color in the range $0\ldots1$ */ @d cyan_val red_val @d grey_val red_val -@d green_val(A) mp->mem[(A)+4].sc - /* the green component of the color in the range $0\ldots1$ */ +@d green_val(A) ((mp_fill_node)(A))->green_val_ /* the green component of the color in the range $0\ldots1$ */ @d magenta_val green_val -@d blue_val(A) mp->mem[(A)+5].sc - /* the blue component of the color in the range $0\ldots1$ */ +@d blue_val(A) ((mp_fill_node)(A))->blue_val_ /* the blue component of the color in the range $0\ldots1$ */ @d yellow_val blue_val -@d black_val(A) mp->mem[(A)+6].sc - /* the blue component of the color in the range $0\ldots1$ */ -@d ljoin_val(A) mp_name_type((A)) /* the value of \&{linejoin} */ +@d black_val(A) ((mp_fill_node)(A))->black_val_ /* the black component of the color in the range $0\ldots1$ */ +@d ljoin_val(A) ((mp_fill_node)(A))->ljoin_val_ /* the value of \&{linejoin} */ @:mp_linejoin_}{\&{linejoin} primitive@> -@d miterlim_val(A) mp->mem[(A)+7].sc /* the value of \&{miterlimit} */ +@d miterlim_val(A) ((mp_fill_node)(A))->miterlim_val_ /* the value of \&{miterlimit} */ @:mp_miterlimit_}{\&{miterlimit} primitive@> -@d obj_color_part(A) mp->mem[(A)+3-red_part].sc - /* interpret an object pointer that has been offset by |red_part..blue_part| */ -@d mp_pre_script(A) mp->mem[(A)+8].hh.lh -@d mp_post_script(A) mp->mem[(A)+8].hh.rh -@d fill_node_size 9 +@d mp_pre_script(A) ((mp_fill_node)(A))->pre_script_ +@d mp_post_script(A) ((mp_fill_node)(A))->post_script_ + +@(mpmp.h@>= +typedef struct mp_fill_node_data { + NODE_BODY; + halfword color_model_; + scaled red_val_; + scaled green_val_; + scaled blue_val_; + scaled black_val_; + str_number pre_script_; + str_number post_script_; + mp_knot path_p_; + mp_knot pen_p_; + quarterword ljoin_val_; + halfword miterlim_val_; +} mp_fill_node_data; +typedef struct mp_fill_node_data *mp_fill_node; @ @<Graphical object codes@>= -mp_fill_code=1, - -@ @c -static pointer mp_new_fill_node (MP mp,pointer p) { - /* make a fill node for cyclic path |p| and color black */ - pointer t; /* the new node */ - t=mp_get_node(mp, fill_node_size); - mp_type(t)=mp_fill_code; - mp_path_p(t)=p; - mp_pen_p(t)=null; /* |null| means don't use a pen */ - red_val(t)=0; - green_val(t)=0; - blue_val(t)=0; - black_val(t)=0; - mp_color_model(t)=mp_uninitialized_model; - mp_pre_script(t)=null; - mp_post_script(t)=null; +mp_fill_code = 1, + +@ Make a fill node for cyclic path |p| and color black. + +@d fill_node_size sizeof(struct mp_fill_node_data) + +@c +static mp_node mp_new_fill_node (MP mp, mp_knot p) { + mp_fill_node t = xmalloc (1, fill_node_size); + add_var_used (fill_node_size); + memset (t, 0, fill_node_size); + mp_type (t) = mp_fill_node_type; + mp_path_p (t) = p; + mp_pen_p (t) = NULL; /* |NULL| means don't use a pen */ + red_val (t) = 0; + green_val (t) = 0; + blue_val (t) = 0; + black_val (t) = 0; + mp_color_model (t) = mp_uninitialized_model; + mp_pre_script (t) = NULL; + mp_post_script (t) = NULL; @<Set the |ljoin_val| and |miterlim_val| fields in object |t|@>; - return t; + return (mp_node) t; } + @ @<Set the |ljoin_val| and |miterlim_val| fields in object |t|@>= -if ( mp->internal[mp_linejoin]>unity ) ljoin_val(t)=2; -else if ( mp->internal[mp_linejoin]>0 ) ljoin_val(t)=1; -else ljoin_val(t)=0; -if ( mp->internal[mp_miterlimit]<unity ) - miterlim_val(t)=unity; +if (internal_value (mp_linejoin) > unity) + ljoin_val (t) = 2; +else if (internal_value (mp_linejoin) > 0) + ljoin_val (t) = 1; else - miterlim_val(t)=mp->internal[mp_miterlimit] + ljoin_val (t) = 0; +if (internal_value (mp_miterlimit) < unity) + miterlim_val (t) = unity; +else + miterlim_val (t) = internal_value (mp_miterlimit) + @ A stroked path is represented by an eight-word node that is like a filled contour node except that it contains the current \&{linecap} value, a scale -factor for the dash pattern, and a pointer that is non-null if the stroke +factor for the dash pattern, and a pointer that is non-NULL if the stroke is to be dashed. The purpose of the scale factor is to allow a picture to be transformed without touching the picture that |dash_p| points to. -@d mp_dash_p(A) mp_link((A)+9) - /* a pointer to the edge structure that gives the dash pattern */ -@d lcap_val(A) mp_type((A)+9) - /* the value of \&{linecap} */ +@d mp_dash_p(A) ((mp_stroked_node)(A))->dash_p_ /* a pointer to the edge structure that gives the dash pattern */ +@d lcap_val(A) ((mp_stroked_node)(A))->lcap_val_ /* the value of \&{linecap} */ @:mp_linecap_}{\&{linecap} primitive@> -@d dash_scale(A) mp->mem[(A)+10].sc /* dash lengths are scaled by this factor */ -@d stroked_node_size 11 +@d dash_scale(A) ((mp_stroked_node)(A))->dash_scale_ /* dash lengths are scaled by this factor */ + +@(mpmp.h@>= +typedef struct mp_stroked_node_data { + NODE_BODY; + halfword color_model_; + scaled red_val_; + scaled green_val_; + scaled blue_val_; + scaled black_val_; + str_number pre_script_; + str_number post_script_; + mp_knot path_p_; + mp_knot pen_p_; + quarterword ljoin_val_; + halfword miterlim_val_; + quarterword lcap_val_; + mp_node dash_p_; + scaled dash_scale_; +} mp_stroked_node_data; +typedef struct mp_stroked_node_data *mp_stroked_node; + @ @<Graphical object codes@>= -mp_stroked_code=2, - -@ @c -static pointer mp_new_stroked_node (MP mp,pointer p) { - /* make a stroked node for path |p| with |mp_pen_p(p)| temporarily |null| */ - pointer t; /* the new node */ - t=mp_get_node(mp, stroked_node_size); - mp_type(t)=mp_stroked_code; - mp_path_p(t)=p; mp_pen_p(t)=null; - mp_dash_p(t)=null; - dash_scale(t)=unity; - red_val(t)=0; - green_val(t)=0; - blue_val(t)=0; - black_val(t)=0; - mp_color_model(t)=mp_uninitialized_model; - mp_pre_script(t)=null; - mp_post_script(t)=null; +mp_stroked_code = 2, + +@ Make a stroked node for path |p| with |mp_pen_p(p)| temporarily |NULL|. + +@d stroked_node_size sizeof(struct mp_stroked_node_data) + +@c +static mp_node mp_new_stroked_node (MP mp, mp_knot p) { + mp_stroked_node t = (mp_stroked_node) xmalloc (1, stroked_node_size); + add_var_used (stroked_node_size); + memset (t, 0, stroked_node_size); + mp_type (t) = mp_stroked_node_type; + mp_path_p (t) = p; + mp_pen_p (t) = NULL; + mp_dash_p (t) = NULL; + dash_scale (t) = unity; + red_val (t) = 0; + green_val (t) = 0; + blue_val (t) = 0; + black_val (t) = 0; + mp_color_model (t) = mp_uninitialized_model; + mp_pre_script (t) = NULL; + mp_post_script (t) = NULL; @<Set the |ljoin_val| and |miterlim_val| fields in object |t|@>; - if ( mp->internal[mp_linecap]>unity ) lcap_val(t)=2; - else if ( mp->internal[mp_linecap]>0 ) lcap_val(t)=1; - else lcap_val(t)=0; - return t; + if (internal_value (mp_linecap) > unity) + lcap_val (t) = 2; + else if (internal_value (mp_linecap) > 0) + lcap_val (t) = 1; + else + lcap_val (t) = 0; + return (mp_node) t; } + @ When a dashed line is computed in a transformed coordinate system, the dash lengths get scaled like the pen shape and we need to compensate for this. Since there is no unique scale factor for an arbitrary transformation, we use the @@ -9483,33 +9431,49 @@ needed because |square_rt| scales its result by $2^8$ while we need $2^{14}$ to counteract the effect of |take_fraction|. @ @c -scaled mp_sqrt_det (MP mp,scaled a, scaled b, scaled c, scaled d) { - scaled maxabs; /* $max(|a|,|b|,|c|,|d|)$ */ - unsigned s; /* amount by which the result of |square_rt| needs to be scaled */ +scaled mp_sqrt_det (MP mp, scaled a, scaled b, scaled c, scaled d) { + scaled maxabs; /* $max(|a|,|b|,|c|,|d|)$ */ + unsigned s; /* amount by which the result of |square_rt| needs to be scaled */ @<Initialize |maxabs|@>; - s=64; - while ( (maxabs<fraction_one) && (s>1) ){ - a+=a; b+=b; c+=c; d+=d; - maxabs+=maxabs; s=(unsigned)(halfp(s)); - } - return (scaled)(s*(unsigned)mp_square_rt(mp, abs(mp_take_fraction(mp, a,d)-mp_take_fraction(mp, b,c)))); + s = 64; + while ((maxabs < fraction_one) && (s > 1)) { + a += a; + b += b; + c += c; + d += d; + maxabs += maxabs; + s = (unsigned) (halfp (s)); + } + return (scaled) (s * + (unsigned) mp_square_rt (mp, + abs (mp_take_fraction (mp, a, d) - + mp_take_fraction (mp, b, c)))); } @# -static scaled mp_get_pen_scale (MP mp,pointer p) { - return mp_sqrt_det(mp, - mp_left_x(p)-mp_x_coord(p), mp_right_x(p)-mp_x_coord(p), - mp_left_y(p)-mp_y_coord(p), mp_right_y(p)-mp_y_coord(p)); +static scaled mp_get_pen_scale (MP mp, mp_knot p) { + if (p == NULL) + return 0; + return mp_sqrt_det (mp, + mp_left_x (p) - mp_x_coord (p), + mp_right_x (p) - mp_x_coord (p), + mp_left_y (p) - mp_y_coord (p), + mp_right_y (p) - mp_y_coord (p)); } + @ @<Declarations@>= -static scaled mp_sqrt_det (MP mp,scaled a, scaled b, scaled c, scaled d) ; +static scaled mp_sqrt_det (MP mp, scaled a, scaled b, scaled c, scaled d); @ @<Initialize |maxabs|@>= -maxabs=abs(a); -if ( abs(b)>maxabs ) maxabs=abs(b); -if ( abs(c)>maxabs ) maxabs=abs(c); -if ( abs(d)>maxabs ) maxabs=abs(d) +maxabs = abs (a); +if (abs (b) > maxabs) + maxabs = abs (b); +if (abs (c) > maxabs) + maxabs = abs (c); +if (abs (d) > maxabs) + maxabs = abs (d) + @ When a picture contains text, this is represented by a fourteen-word node where the color information and |type| and |link| fields are augmented by @@ -9522,105 +9486,165 @@ words give a transformation to be applied to the text. The |new_text_node| function initializes everything to default values so that the text comes out black with its reference point at the origin. -@d mp_text_p(A) mp_link((A)+1) /* a string pointer for the text to display */ -@d mp_font_n(A) mp_info((A)+1) /* the font number */ -@d width_val(A) mp->mem[(A)+7].sc /* unscaled width of the text */ -@d height_val(A) mp->mem[(A)+9].sc /* unscaled height of the text */ -@d depth_val(A) mp->mem[(A)+10].sc /* unscaled depth of the text */ -@d text_tx_loc(A) ((A)+11) - /* the first of six locations for transformation parameters */ -@d tx_val(A) mp->mem[(A)+11].sc /* $x$ shift amount */ -@d ty_val(A) mp->mem[(A)+12].sc /* $y$ shift amount */ -@d txx_val(A) mp->mem[(A)+13].sc /* |txx| transformation parameter */ -@d txy_val(A) mp->mem[(A)+14].sc /* |txy| transformation parameter */ -@d tyx_val(A) mp->mem[(A)+15].sc /* |tyx| transformation parameter */ -@d tyy_val(A) mp->mem[(A)+16].sc /* |tyy| transformation parameter */ -@d text_trans_part(A) mp->mem[(A)+11-x_part].sc - /* interpret a text node pointer that has been offset by |x_part..yy_part| */ -@d text_node_size 17 +@d mp_text_p(A) ((mp_text_node)(A))->text_p_ /* a string pointer for the text to display */ +@d mp_font_n(A) ((mp_text_node)(A))->font_n_ /* the font number */ +@d width_val(A) ((mp_text_node)(A))->width_val_ /* unscaled width of the text */ +@d height_val(A) ((mp_text_node)(A))->height_val_ /* unscaled height of the text */ +@d depth_val(A) ((mp_text_node)(A))->depth_val_ /* unscaled depth of the text */ +@d tx_val(A) ((mp_text_node)(A))->tx_val_ /* $x$ shift amount */ +@d ty_val(A) ((mp_text_node)(A))->ty_val_ /* $y$ shift amount */ +@d txx_val(A) ((mp_text_node)(A))->txx_val_ /* |txx| transformation parameter */ +@d txy_val(A) ((mp_text_node)(A))->txy_val_ /* |txy| transformation parameter */ +@d tyx_val(A) ((mp_text_node)(A))->tyx_val_ /* |tyx| transformation parameter */ +@d tyy_val(A) ((mp_text_node)(A))->tyy_val_ /* |tyy| transformation parameter */ + +@(mpmp.h@>= +typedef struct mp_text_node_data { + NODE_BODY; + halfword color_model_; + scaled red_val_; + scaled green_val_; + scaled blue_val_; + scaled black_val_; + str_number pre_script_; + str_number post_script_; + str_number text_p_; + halfword font_n_; + scaled width_val_; + scaled height_val_; + scaled depth_val_; + scaled tx_val_; + scaled ty_val_; + scaled txx_val_; + scaled txy_val_; + scaled tyx_val_; + scaled tyy_val_; +} mp_text_node_data; +typedef struct mp_text_node_data *mp_text_node; @ @<Graphical object codes@>= -mp_text_code=3, +mp_text_code = 3, -@ @c -static pointer mp_new_text_node (MP mp,char *f,str_number s) { - /* make a text node for font |f| and text string |s| */ - pointer t; /* the new node */ - t=mp_get_node(mp, text_node_size); - mp_type(t)=mp_text_code; - mp_text_p(t)=s; - mp_font_n(t)=(halfword)mp_find_font(mp, f); /* this identifies the font */ - red_val(t)=0; - green_val(t)=0; - blue_val(t)=0; - black_val(t)=0; - mp_color_model(t)=mp_uninitialized_model; - mp_pre_script(t)=null; - mp_post_script(t)=null; - tx_val(t)=0; ty_val(t)=0; - txx_val(t)=unity; txy_val(t)=0; - tyx_val(t)=0; tyy_val(t)=unity; - mp_set_text_box(mp, t); /* this finds the bounding box */ - return t; +@ Make a text node for font |f| and text string |s|. + +@d text_node_size sizeof(struct mp_text_node_data) + +@c +static mp_node mp_new_text_node (MP mp, char *f, str_number s) { + mp_text_node t = (mp_text_node) xmalloc (1, text_node_size); + add_var_used (text_node_size); + memset (t, 0, text_node_size); + mp_type (t) = mp_text_node_type; + mp_text_p (t) = s; + mp_font_n (t) = (halfword) mp_find_font (mp, f); /* this identifies the font */ + red_val (t) = 0; + green_val (t) = 0; + blue_val (t) = 0; + black_val (t) = 0; + mp_color_model (t) = mp_uninitialized_model; + mp_pre_script (t) = NULL; + mp_post_script (t) = NULL; + tx_val (t) = 0; + ty_val (t) = 0; + txx_val (t) = unity; + txy_val (t) = 0; + tyx_val (t) = 0; + tyy_val (t) = unity; + mp_set_text_box (mp, (mp_node) t); /* this finds the bounding box */ + return (mp_node) t; } + @ The last two types of graphical objects that can occur in an edge structure are clipping paths and \&{setbounds} paths. These are slightly more difficult @:set_bounds_}{\&{setbounds} primitive@> to implement because we must keep track of exactly what is being clipped or bounded when pictures get merged together. For this reason, each clipping or \&{setbounds} operation is represented by a pair of nodes: first comes a -two-word node whose |path_p| gives the relevant path, then there is the list -of objects to clip or bound followed by a two-word node whose second word is -unused. - -Using at least two words for each graphical object node allows them all to be -allocated and deallocated similarly with a global array |gr_object_size| to -give the size in words for each object type. - -@d start_clip_size 2 -@d start_bounds_size 2 -@d stop_clip_size 2 /* the second word is not used here */ -@d stop_bounds_size 2 /* the second word is not used here */ -@# -@d stop_type(A) ((A)+2) - /* matching |type| for |start_clip_code| or |start_bounds_code| */ -@d has_color(A) (mp_type((A))<mp_start_clip_code) +node whose |path_p| gives the relevant path, then there is the list +of objects to clip or bound followed by a closing node. + +@d has_color(A) (mp_type((A))<mp_start_clip_node_type) /* does a graphical object have color fields? */ -@d has_pen(A) (mp_type((A))<mp_text_code) +@d has_pen(A) (mp_type((A))<mp_text_node_type) /* does a graphical object have a |mp_pen_p| field? */ -@d is_start_or_stop(A) (mp_type((A))>=mp_start_clip_code) -@d is_stop(A) (mp_type((A))>=mp_stop_clip_code) +@d is_start_or_stop(A) (mp_type((A))>=mp_start_clip_node_type) +@d is_stop(A) (mp_type((A))>=mp_stop_clip_node_type) + +@(mpmp.h@>= +typedef struct mp_start_clip_node_data { + NODE_BODY; + mp_knot path_p_; +} mp_start_clip_node_data; +typedef struct mp_start_clip_node_data *mp_start_clip_node; +typedef struct mp_start_bounds_node_data { + NODE_BODY; + mp_knot path_p_; +} mp_start_bounds_node_data; +typedef struct mp_start_bounds_node_data *mp_start_bounds_node; +typedef struct mp_stop_clip_node_data { + NODE_BODY; +} mp_stop_clip_node_data; +typedef struct mp_stop_clip_node_data *mp_stop_clip_node; +typedef struct mp_stop_bounds_node_data { + NODE_BODY; +} mp_stop_bounds_node_data; +typedef struct mp_stop_bounds_node_data *mp_stop_bounds_node; + @ @<Graphical object codes@>= -mp_start_clip_code=4, /* |type| of a node that starts clipping */ -mp_start_bounds_code=5, /* |type| of a node that gives a \&{setbounds} path */ -mp_stop_clip_code=6, /* |type| of a node that stops clipping */ -mp_stop_bounds_code=7, /* |type| of a node that stops \&{setbounds} */ +mp_start_clip_code = 4, /* |type| of a node that starts clipping */ + mp_start_bounds_code = 5, /* |type| of a node that gives a \&{setbounds} path */ + mp_stop_clip_code = 6, /* |type| of a node that stops clipping */ + mp_stop_bounds_code = 7, /* |type| of a node that stops \&{setbounds} */ + -@ @c -static pointer mp_new_bounds_node (MP mp,pointer p, quarterword c) { - /* make a node of type |c| where |p| is the clipping or \&{setbounds} path */ - pointer t; /* the new node */ - t=mp_get_node(mp, mp->gr_object_size[c]); - mp_type(t)=c; - mp_path_p(t)=p; - return t; -} +@ -@ We need an array to keep track of the sizes of graphical objects. +@d start_clip_size sizeof(struct mp_start_clip_node_data) +@d stop_clip_size sizeof(struct mp_stop_clip_node_data) +@d start_bounds_size sizeof(struct mp_start_bounds_node_data) +@d stop_bounds_size sizeof(struct mp_stop_bounds_node_data) -@<Glob...@>= -quarterword gr_object_size[mp_stop_bounds_code+1]; +@c +static mp_node mp_new_bounds_node (MP mp, mp_knot p, quarterword c) { + /* make a node of type |c| where |p| is the clipping or \&{setbounds} path */ + if (c == mp_start_clip_node_type) { + mp_start_clip_node t; /* the new node */ + t = (mp_start_clip_node) xmalloc (1, start_clip_size); + add_var_used (start_clip_size); + t->path_p_ = p; + mp_type (t) = c; + t->link = NULL; + return (mp_node) t; + } else if (c == mp_start_bounds_node_type) { + mp_start_bounds_node t; /* the new node */ + t = (mp_start_bounds_node) xmalloc (1, start_bounds_size); + add_var_used (start_bounds_size); + t->path_p_ = p; + mp_type (t) = c; + t->link = NULL; + return (mp_node) t; + } else if (c == mp_stop_clip_node_type) { + mp_stop_clip_node t; /* the new node */ + t = (mp_stop_clip_node) xmalloc (1, stop_clip_size); + add_var_used (stop_clip_size); + mp_type (t) = c; + t->link = NULL; + return (mp_node) t; + } else if (c == mp_stop_bounds_node_type) { + mp_stop_bounds_node t; /* the new node */ + t = (mp_stop_bounds_node) xmalloc (1, stop_bounds_size); + add_var_used (stop_bounds_size); + mp_type (t) = c; + t->link = NULL; + return (mp_node) t; + } else { + assert (0); + } + return NULL; +} -@ @<Set init...@>= -mp->gr_object_size[mp_fill_code]=fill_node_size; -mp->gr_object_size[mp_stroked_code]=stroked_node_size; -mp->gr_object_size[mp_text_code]=text_node_size; -mp->gr_object_size[mp_start_clip_code]=start_clip_size; -mp->gr_object_size[mp_stop_clip_code]=stop_clip_size; -mp->gr_object_size[mp_start_bounds_code]=start_bounds_size; -mp->gr_object_size[mp_stop_bounds_code]=stop_bounds_size; @ All the essential information in an edge structure is encoded as a linked list of graphical objects as we have just seen, but it is helpful to add some @@ -9638,12 +9662,41 @@ dashes). Since the $y$~coordinate of the dash pattern is needed to determine the period of repetition, this needs to be stored in the edge header along with a pointer to the list of dash nodes. -@d start_x(A) mp->mem[(A)+1].sc /* the starting $x$~coordinate in a dash node */ -@d stop_x(A) mp->mem[(A)+2].sc /* the ending $x$~coordinate in a dash node */ -@d dash_node_size 3 -@d dash_list mp_link - /* in an edge header this points to the first dash node */ -@d dash_y(A) mp->mem[(A)+1].sc /* $y$ value for the dash list in an edge header */ +The |dash_info| is explained below. + +@d dash_list(A) ((mp_dash_node)(A))->link /* in an edge header this points to the first dash node */ +@d start_x(A) ((mp_dash_node)(A))->start_x_ /* the starting $x$~coordinate in a dash node */ +@d stop_x(A) ((mp_dash_node)(A))->stop_x_ /* the ending $x$~coordinate in a dash node */ +@d dash_y(A) ((mp_dash_node)(A))->dash_y_ /* $y$ value for the dash list in an edge header */ + +@(mpmp.h@>= +typedef struct mp_dash_node_data { + NODE_BODY; + scaled start_x_; + scaled stop_x_; + scaled dash_y_; + mp_node dash_info_; +} mp_dash_node_data; +typedef struct mp_dash_node_data *mp_dash_node; + +@ @<Initialize table entries@>= +mp->null_dash = mp_get_dash_node (mp); + +@ @<Free table entries@>= +mp_free_node (mp, mp->null_dash, dash_node_size); + +@ +@d dash_node_size sizeof(struct mp_dash_node_data) + +@c +static mp_node mp_get_dash_node (MP mp) { + mp_dash_node p = (mp_dash_node) xmalloc (1, dash_node_size); + add_var_used (dash_node_size); + memset (p, 0, dash_node_size); + mp_type (p) = mp_dash_node_type; + return (mp_node) p; +} + @ It is also convenient for an edge header to contain the bounding box information needed by the \&{llcorner} and \&{urcorner} operators @@ -9656,131 +9709,182 @@ the additional text. When the bounding box has not been computed, the |bblast| pointer points to a dummy link at the head of the graphical object list while the |minx_val| -and |miny_val| fields contain |el_gordo| and the |maxx_val| and |maxy_val| -fields contain |-el_gordo|. +and |miny_val| fields contain |EL_GORDO| and the |maxx_val| and |maxy_val| +fields contain |-EL_GORDO|. Since the bounding box of pictures containing objects of type -|mp_start_bounds_code| depends on the value of \&{truecorners}, the bounding box +|mp_start_bounds_node| depends on the value of \&{truecorners}, the bounding box @:mp_true_corners_}{\&{truecorners} primitive@> data might not be valid for all values of this parameter. Hence, the |bbtype| field is needed to keep track of this. -@d minx_val(A) mp->mem[(A)+2].sc -@d miny_val(A) mp->mem[(A)+3].sc -@d maxx_val(A) mp->mem[(A)+4].sc -@d maxy_val(A) mp->mem[(A)+5].sc -@d bblast(A) mp_link((A)+6) /* last item considered in bounding box computation */ -@d bbtype(A) mp_info((A)+6) /* tells how bounding box data depends on \&{truecorners} */ -@d dummy_loc(A) ((A)+7) /* where the object list begins in an edge header */ -@d no_bounds 0 - /* |bbtype| value when bounding box data is valid for all \&{truecorners} values */ -@d bounds_set 1 - /* |bbtype| value when bounding box data is for \&{truecorners}${}\le 0$ */ -@d bounds_unset 2 - /* |bbtype| value when bounding box data is for \&{truecorners}${}>0$ */ - -@c -static void mp_init_bbox (MP mp,pointer h) { +@d minx_val(A) ((mp_edge_header_node)(A))->minx_val_ +@d miny_val(A) ((mp_edge_header_node)(A))->miny_val_ +@d maxx_val(A) ((mp_edge_header_node)(A))->maxx_val_ +@d maxy_val(A) ((mp_edge_header_node)(A))->maxy_val_ +@d bblast(A) ((mp_edge_header_node)(A))->bblast_ /* last item considered in bounding box computation */ +@d bbtype(A) ((mp_edge_header_node)(A))->bbtype_ /* tells how bounding box data depends on \&{truecorners} */ +@d dummy_loc(A) ((mp_edge_header_node)(A))->dummy_ /* where the object list begins in an edge header */ + +@(mpmp.h@>= +typedef struct mp_edge_header_node_data { + NODE_BODY; + scaled start_x_; + scaled stop_x_; + scaled dash_y_; + mp_node dash_info_; + scaled minx_val_; + scaled miny_val_; + scaled maxx_val_; + scaled maxy_val_; + mp_node bblast_; + halfword bbtype_; + mp_node dummy_; + mp_node obj_tail_; /* explained below */ + halfword ref_count_; /* explained below */ +} mp_edge_header_node_data; +typedef struct mp_edge_header_node_data *mp_edge_header_node; + +@ +@d no_bounds 0 /* |bbtype| value when bounding box data is valid for all \&{truecorners} values */ +@d bounds_set 1 /* |bbtype| value when bounding box data is for \&{truecorners}${}\le 0$ */ +@d bounds_unset 2 /* |bbtype| value when bounding box data is for \&{truecorners}${}>0$ */ +@c +static void mp_init_bbox (MP mp, mp_node h) { /* Initialize the bounding box information in edge structure |h| */ - bblast(h)=dummy_loc(h); - bbtype(h)=no_bounds; - minx_val(h)=el_gordo; - miny_val(h)=el_gordo; - maxx_val(h)=-el_gordo; - maxy_val(h)=-el_gordo; + (void) mp; + bblast (h) = dummy_loc (h); + bbtype (h) = no_bounds; + minx_val (h) = EL_GORDO; + miny_val (h) = EL_GORDO; + maxx_val (h) = -EL_GORDO; + maxy_val (h) = -EL_GORDO; } + @ The only other entries in an edge header are a reference count in the first word and a pointer to the tail of the object list in the last word. -@d obj_tail(A) mp_info((A)+7) /* points to the last entry in the object list */ -@d edge_header_size 8 +@d obj_tail(A) ((mp_edge_header_node)(A))->obj_tail_ /* points to the last entry in the object list */ +@d edge_ref_count(A) ((mp_edge_header_node)(A))->ref_count_ + +@d edge_header_size sizeof(struct mp_edge_header_node_data) -@c -static void mp_init_edges (MP mp,pointer h) { - /* initialize an edge header to null values */ - dash_list(h)=null_dash; - obj_tail(h)=dummy_loc(h); - mp_link(dummy_loc(h))=null; - ref_count(h)=null; - mp_init_bbox(mp, h); +@c +static mp_node mp_get_edge_header_node (MP mp) { + mp_edge_header_node p = (mp_edge_header_node) xmalloc (1, edge_header_size); + add_var_used (edge_header_size); + memset (p, 0, edge_header_size); + mp_type (p) = mp_edge_header_node_type; + p->dummy_ = mp_get_token_node (mp); /* or whatever, just a need a link handle */ + return (mp_node) p; } +static void mp_init_edges (MP mp, mp_node h) { + /* initialize an edge header to NULL values */ + dash_list (h) = mp->null_dash; + obj_tail (h) = dummy_loc (h); + mp_link (dummy_loc (h)) = NULL; + edge_ref_count (h) = 0; + mp_init_bbox (mp, h); +} + @ Here is how edge structures are deleted. The process can be recursive because of the need to dereference edge structures that are used as dash patterns. @^recursion@> -@d add_edge_ref(A) incr(ref_count(A)) +@d add_edge_ref(A) incr(edge_ref_count((A))) @d delete_edge_ref(A) { - if ( ref_count((A))==null ) - mp_toss_edges(mp, A); + if ( edge_ref_count((A))==0 ) + mp_toss_edges(mp, (A)); else - decr(ref_count(A)); + decr(edge_ref_count((A))); } @<Declarations@>= -static void mp_flush_dash_list (MP mp,pointer h); -static pointer mp_toss_gr_object (MP mp,pointer p) ; -static void mp_toss_edges (MP mp,pointer h) ; - -@ @c void mp_toss_edges (MP mp,pointer h) { - pointer p,q; /* pointers that scan the list being recycled */ - pointer r; /* an edge structure that object |p| refers to */ - mp_flush_dash_list(mp, h); - q=mp_link(dummy_loc(h)); - while ( (q!=null) ) { - p=q; q=mp_link(q); - r=mp_toss_gr_object(mp, p); - if ( r!=null ) delete_edge_ref(r); - } - mp_free_node(mp, h,edge_header_size); -} -void mp_flush_dash_list (MP mp,pointer h) { - pointer p,q; /* pointers that scan the list being recycled */ - q=dash_list(h); - while ( q!=null_dash ) { - p=q; q=mp_link(q); - mp_free_node(mp, p,dash_node_size); - } - dash_list(h)=null_dash; -} -pointer mp_toss_gr_object (MP mp,pointer p) { +static void mp_flush_dash_list (MP mp, mp_node h); +static mp_node mp_toss_gr_object (MP mp, mp_node p); +static void mp_toss_edges (MP mp, mp_node h); + +@ @c +void mp_toss_edges (MP mp, mp_node h) { + mp_node p, q; /* pointers that scan the list being recycled */ + mp_node r; /* an edge structure that object |p| refers to */ + mp_flush_dash_list (mp, h); + q = mp_link (dummy_loc (h)); + while ((q != NULL)) { + p = q; + q = mp_link (q); + r = mp_toss_gr_object (mp, p); + if (r != NULL) + delete_edge_ref (r); + } + mp_free_node (mp, ((mp_edge_header_node) h)->dummy_, token_node_size); + mp_free_node (mp, h, edge_header_size); +} +void mp_flush_dash_list (MP mp, mp_node h) { + mp_node p, q; /* pointers that scan the list being recycled */ + q = dash_list (h); + while (q != mp->null_dash) { /* todo: NULL check should not be needed */ + p = q; + q = mp_link (q); + mp_free_node (mp, p, dash_node_size); + } + dash_list (h) = mp->null_dash; +} +mp_node mp_toss_gr_object (MP mp, mp_node p) { /* returns an edge structure that needs to be dereferenced */ - pointer e; /* the edge structure to return */ - e=null; - @<Prepare to recycle graphical object |p|@>; - mp_free_node(mp, p,mp->gr_object_size[mp_type(p)]); + mp_node e = NULL; /* the edge structure to return */ + switch (mp_type (p)) { + case mp_fill_node_type: + mp_toss_knot_list (mp, mp_path_p ((mp_fill_node) p)); + if (mp_pen_p ((mp_fill_node) p) != NULL) + mp_toss_knot_list (mp, mp_pen_p ((mp_fill_node) p)); + if (mp_pre_script (p) != NULL) + delete_str_ref (mp_pre_script (p)); + if (mp_post_script (p) != NULL) + delete_str_ref (mp_post_script (p)); + mp_free_node (mp, p, fill_node_size); + break; + case mp_stroked_node_type: + mp_toss_knot_list (mp, mp_path_p ((mp_stroked_node) p)); + if (mp_pen_p ((mp_stroked_node) p) != NULL) + mp_toss_knot_list (mp, mp_pen_p ((mp_stroked_node) p)); + if (mp_pre_script (p) != NULL) + delete_str_ref (mp_pre_script (p)); + if (mp_post_script (p) != NULL) + delete_str_ref (mp_post_script (p)); + e = mp_dash_p (p); + mp_free_node (mp, p, stroked_node_size); + break; + case mp_text_node_type: + delete_str_ref (mp_text_p (p)); + if (mp_pre_script (p) != NULL) + delete_str_ref (mp_pre_script (p)); + if (mp_post_script (p) != NULL) + delete_str_ref (mp_post_script (p)); + mp_free_node (mp, p, text_node_size); + break; + case mp_start_clip_node_type: + mp_toss_knot_list (mp, mp_path_p ((mp_start_clip_node) p)); + mp_free_node (mp, p, start_clip_size); + break; + case mp_start_bounds_node_type: + mp_toss_knot_list (mp, mp_path_p ((mp_start_bounds_node) p)); + mp_free_node (mp, p, start_bounds_size); + break; + case mp_stop_clip_node_type: + mp_free_node (mp, p, stop_clip_size); + break; + case mp_stop_bounds_node_type: + mp_free_node (mp, p, stop_bounds_size); + break; + default: /* there are no other valid cases, but please the compiler */ + return e; + } return e; } -@ @<Prepare to recycle graphical object |p|@>= -switch (mp_type(p)) { -case mp_fill_code: - mp_toss_knot_list(mp, mp_path_p(p)); - if ( mp_pen_p(p)!=null ) mp_toss_knot_list(mp, mp_pen_p(p)); - if ( mp_pre_script(p)!=null ) delete_str_ref(mp_pre_script(p)); - if ( mp_post_script(p)!=null ) delete_str_ref(mp_post_script(p)); - break; -case mp_stroked_code: - mp_toss_knot_list(mp, mp_path_p(p)); - if ( mp_pen_p(p)!=null ) mp_toss_knot_list(mp, mp_pen_p(p)); - if ( mp_pre_script(p)!=null ) delete_str_ref(mp_pre_script(p)); - if ( mp_post_script(p)!=null ) delete_str_ref(mp_post_script(p)); - e=mp_dash_p(p); - break; -case mp_text_code: - delete_str_ref(mp_text_p(p)); - if ( mp_pre_script(p)!=null ) delete_str_ref(mp_pre_script(p)); - if ( mp_post_script(p)!=null ) delete_str_ref(mp_post_script(p)); - break; -case mp_start_clip_code: -case mp_start_bounds_code: - mp_toss_knot_list(mp, mp_path_p(p)); - break; -case mp_stop_clip_code: -case mp_stop_bounds_code: - break; -} /* there are no other cases */ @ If we use |add_edge_ref| to ``copy'' edge structures, the real copying needs to be done before making a significant change to an edge structure. Much of @@ -9788,15 +9892,16 @@ the work is done in a separate routine |copy_objects| that copies a list of graphical objects into a new edge header. @c -static pointer mp_private_edges (MP mp,pointer h) { +static mp_node mp_private_edges (MP mp, mp_node h) { /* make a private copy of the edge structure headed by |h| */ - pointer hh; /* the edge header for the new copy */ - pointer p,pp; /* pointers for copying the dash list */ - if ( ref_count(h)==null ) { + mp_node hh; /* the edge header for the new copy */ + mp_node p, pp; /* pointers for copying the dash list */ + assert (mp_type (h) == mp_edge_header_node_type); + if (edge_ref_count (h) == 0) { return h; - } else { - decr(ref_count(h)); - hh=mp_copy_objects(mp, mp_link(dummy_loc(h)),null); + } else { + decr (edge_ref_count (h)); + hh = mp_copy_objects (mp, mp_link (dummy_loc (h)), NULL); @<Copy the dash list from |h| to |hh|@>; @<Copy the bounding box information from |h| to |hh| and make |bblast(hh)| point into the new object list@>; @@ -9804,142 +9909,196 @@ static pointer mp_private_edges (MP mp,pointer h) { } } + @ Here we use the fact that |dash_list(hh)=mp_link(hh)|. @^data structure assumptions@> @<Copy the dash list from |h| to |hh|@>= -pp=hh; p=dash_list(h); -while ( (p!=null_dash) ) { - mp_link(pp)=mp_get_node(mp, dash_node_size); - pp=mp_link(pp); - start_x(pp)=start_x(p); - stop_x(pp)=stop_x(p); - p=mp_link(p); -} -mp_link(pp)=null_dash; -dash_y(hh)=dash_y(h) - +pp = hh; +p = dash_list (h); +while ((p != mp->null_dash)) { + mp_link (pp) = mp_get_dash_node (mp); + pp = mp_link (pp); + start_x (pp) = start_x (p); + stop_x (pp) = stop_x (p); + p = mp_link (p); +} +mp_link (pp) = mp->null_dash; +dash_y (hh) = dash_y (h) + @ |h| is an edge structure @c -static mp_dash_object *mp_export_dashes (MP mp, pointer q, scaled *w) { +static mp_dash_object *mp_export_dashes (MP mp, mp_stroked_node q, scaled * w) { mp_dash_object *d; - pointer p, h; - scaled scf; /* scale factor */ + mp_node p, h; + scaled scf; /* scale factor */ int *dashes = NULL; int num_dashes = 1; - h = mp_dash_p(q); - if (h==null || dash_list(h)==null_dash) - return NULL; - p = dash_list(h); - scf=mp_get_pen_scale(mp, mp_pen_p(q)); - if (scf==0) { - if (*w==0) scf = dash_scale(q); else return NULL; + h = mp_dash_p (q); + if (h == NULL || dash_list (h) == mp->null_dash) + return NULL; + p = dash_list (h); + scf = mp_get_pen_scale (mp, mp_pen_p (q)); + if (scf == 0) { + if (*w == 0) + scf = dash_scale (q); + else + return NULL; } else { - scf=mp_make_scaled(mp, *w,scf); - scf=mp_take_scaled(mp, scf,dash_scale(q)); + scf = mp_make_scaled (mp, *w, scf); + scf = mp_take_scaled (mp, scf, dash_scale (q)); } *w = scf; - d = xmalloc(1,sizeof(mp_dash_object)); - start_x(null_dash)=start_x(p)+dash_y(h); - while (p != null_dash) { - dashes = xrealloc(dashes, (num_dashes+2), sizeof(scaled)); - dashes[(num_dashes-1)] = - mp_take_scaled(mp,(stop_x(p)-start_x(p)),scf); - dashes[(num_dashes)] = - mp_take_scaled(mp,(start_x(mp_link(p))-stop_x(p)),scf); - dashes[(num_dashes+1)] = -1; /* terminus */ - num_dashes+=2; - p=mp_link(p); - } - d->array = dashes; - d->offset = mp_take_scaled(mp,mp_dash_offset(mp, h),scf); + d = xmalloc (1, sizeof (mp_dash_object)); + add_var_used (sizeof (mp_dash_object)); + start_x (mp->null_dash) = start_x (p) + dash_y (h); + while (p != mp->null_dash) { + dashes = xrealloc (dashes, (num_dashes + 2), sizeof (scaled)); + dashes[(num_dashes - 1)] = + mp_take_scaled (mp, (stop_x (p) - start_x (p)), scf); + dashes[(num_dashes)] = + mp_take_scaled (mp, (start_x (mp_link (p)) - stop_x (p)), scf); + dashes[(num_dashes + 1)] = -1; /* terminus */ + num_dashes += 2; + p = mp_link (p); + } + d->array = dashes; + d->offset = mp_take_scaled (mp, mp_dash_offset (mp, h), scf); return d; } - @ @<Copy the bounding box information from |h| to |hh|...@>= -minx_val(hh)=minx_val(h); -miny_val(hh)=miny_val(h); -maxx_val(hh)=maxx_val(h); -maxy_val(hh)=maxy_val(h); -bbtype(hh)=bbtype(h); -p=dummy_loc(h); pp=dummy_loc(hh); -while ((p!=bblast(h)) ) { - if ( p==null ) mp_confusion(mp, "bblast"); -@:this can't happen bblast}{\quad bblast@> - p=mp_link(p); pp=mp_link(pp); -} -bblast(hh)=pp +minx_val (hh) = minx_val (h); +miny_val (hh) = miny_val (h); +maxx_val (hh) = maxx_val (h); +maxy_val (hh) = maxy_val (h); +bbtype (hh) = bbtype (h); +p = dummy_loc (h); +pp = dummy_loc (hh); +while ((p != bblast (h))) { + if (p == NULL) + mp_confusion (mp, "bblast"); +@:this can't happen bblast}{\quad bblast@>; + p = mp_link (p); + pp = mp_link (pp); +} +bblast (hh) = pp @ Here is the promised routine for copying graphical objects into a new edge structure. It starts copying at object~|p| and stops just before object~|q|. -If |q| is null, it copies the entire sublist headed at |p|. The resulting edge +If |q| is NULL, it copies the entire sublist headed at |p|. The resulting edge structure requires further initialization by |init_bbox|. @<Declarations@>= -static pointer mp_copy_objects (MP mp, pointer p, pointer q); +static mp_node mp_copy_objects (MP mp, mp_node p, mp_node q); @ @c -pointer mp_copy_objects (MP mp, pointer p, pointer q) { - pointer hh; /* the new edge header */ - pointer pp; /* the last newly copied object */ - quarterword k; /* temporary register */ - hh=mp_get_node(mp, edge_header_size); - dash_list(hh)=null_dash; - ref_count(hh)=null; - pp=dummy_loc(hh); - while ( (p!=q) ) { +mp_node mp_copy_objects (MP mp, mp_node p, mp_node q) { + mp_node hh; /* the new edge header */ + mp_node pp; /* the last newly copied object */ + quarterword k = 0; /* temporary register */ + hh = mp_get_edge_header_node (mp); + dash_list (hh) = mp->null_dash; + edge_ref_count (hh) = 0; + pp = dummy_loc (hh); + while (p != q) { @<Make |mp_link(pp)| point to a copy of object |p|, and update |p| and |pp|@>; } - obj_tail(hh)=pp; - mp_link(pp)=null; + obj_tail (hh) = pp; + mp_link (pp) = NULL; return hh; } + @ @<Make |mp_link(pp)| point to a copy of object |p|, and update |p| and |pp|@>= -{ k=mp->gr_object_size[mp_type(p)]; - mp_link(pp)=mp_get_node(mp, k); - pp=mp_link(pp); - while ( (k>0) ) { k--; mp->mem[pp+k]=mp->mem[p+k]; }; +{ + switch (mp_type (p)) { + case mp_start_clip_node_type: + k = start_clip_size; + break; + case mp_start_bounds_node_type: + k = start_bounds_size; + break; + case mp_fill_node_type: + k = fill_node_size; + break; + case mp_stroked_node_type: + k = stroked_node_size; + break; + case mp_text_node_type: + k = text_node_size; + break; + case mp_stop_clip_node_type: + k = stop_clip_size; + break; + case mp_stop_bounds_node_type: + k = stop_bounds_size; + break; + default: /* there are no other valid cases, but please the compiler */ + break; + } + mp_link (pp) = xmalloc (1, (size_t) k); /* |gr_object| */ + add_var_used ((size_t)k); + pp = mp_link (pp); + memcpy (pp, p, (size_t) k); + pp->link = NULL; @<Fix anything in graphical object |pp| that should differ from the corresponding field in |p|@>; - p=mp_link(p); + p = mp_link (p); } + @ @<Fix anything in graphical object |pp| that should differ from the...@>= -switch (mp_type(p)) { -case mp_start_clip_code: -case mp_start_bounds_code: - mp_path_p(pp)=mp_copy_path(mp, mp_path_p(p)); +switch (mp_type (p)) { +case mp_start_clip_node_type: + mp_path_p ((mp_start_clip_node) pp) = + mp_copy_path (mp, mp_path_p ((mp_start_clip_node) p)); + break; +case mp_start_bounds_node_type: + mp_path_p ((mp_start_bounds_node) pp) = + mp_copy_path (mp, mp_path_p ((mp_start_bounds_node) p)); break; -case mp_fill_code: - mp_path_p(pp)=mp_copy_path(mp, mp_path_p(p)); - if ( mp_pre_script(p)!=null ) add_str_ref(mp_pre_script(p)); - if ( mp_post_script(p)!=null ) add_str_ref(mp_post_script(p)); - if ( mp_pen_p(p)!=null ) mp_pen_p(pp)=copy_pen(mp_pen_p(p)); +case mp_fill_node_type: + mp_path_p ((mp_fill_node) pp) = + mp_copy_path (mp, mp_path_p ((mp_fill_node) p)); + if (mp_pre_script (p) != NULL) + add_str_ref (mp_pre_script (p)); + if (mp_post_script (p) != NULL) + add_str_ref (mp_post_script (p)); + if (mp_pen_p ((mp_fill_node) p) != NULL) + mp_pen_p ((mp_fill_node) pp) = copy_pen (mp_pen_p ((mp_fill_node) p)); break; -case mp_stroked_code: - if ( mp_pre_script(p)!=null ) add_str_ref(mp_pre_script(p)); - if ( mp_post_script(p)!=null ) add_str_ref(mp_post_script(p)); - mp_path_p(pp)=mp_copy_path(mp, mp_path_p(p)); - mp_pen_p(pp)=copy_pen(mp_pen_p(p)); - if ( mp_dash_p(p)!=null ) add_edge_ref(mp_dash_p(pp)); +case mp_stroked_node_type: + if (mp_pre_script (p) != NULL) + add_str_ref (mp_pre_script (p)); + if (mp_post_script (p) != NULL) + add_str_ref (mp_post_script (p)); + mp_path_p ((mp_stroked_node) pp) = + mp_copy_path (mp, mp_path_p ((mp_stroked_node) p)); + mp_pen_p ((mp_stroked_node) pp) = copy_pen (mp_pen_p ((mp_stroked_node) p)); + if (mp_dash_p (p) != NULL) + add_edge_ref (mp_dash_p (pp)); break; -case mp_text_code: - if ( mp_pre_script(p)!=null ) add_str_ref(mp_pre_script(p)); - if ( mp_post_script(p)!=null ) add_str_ref(mp_post_script(p)); - add_str_ref(mp_text_p(pp)); +case mp_text_node_type: + if (mp_pre_script (p) != NULL) + add_str_ref (mp_pre_script (p)); + if (mp_post_script (p) != NULL) + add_str_ref (mp_post_script (p)); + add_str_ref (mp_text_p (pp)); break; -case mp_stop_clip_code: -case mp_stop_bounds_code: +case mp_stop_clip_node_type: +case mp_stop_bounds_node_type: break; -} /* there are no other cases */ +default: /* there are no other valid cases, but please the compiler */ + break; +} + @ Here is one way to find an acceptable value for the second argument to -|copy_objects|. Given a non-null graphical object list, |skip_1component| +|copy_objects|. Given a non-NULL graphical object list, |skip_1component| skips past one picture component, where a ``picture component'' is a single graphical object, or a start bounds or start clip object and everything up through the matching stop bounds or stop clip object. The macro version avoids @@ -9952,155 +10111,178 @@ unless |p| points to a stop bounds or stop clip node, in which case it executes else if ( ! is_stop((A)) ) (A)=mp_skip_1component(mp, (A)); else -@c -static pointer mp_skip_1component (MP mp,pointer p) { - integer lev; /* current nesting level */ - lev=0; - do { - if ( is_start_or_stop(p) ) { - if ( is_stop(p) ) decr(lev); else incr(lev); - } - p=mp_link(p); - } while (lev!=0); +@c +static mp_node mp_skip_1component (MP mp, mp_node p) { + integer lev; /* current nesting level */ + lev = 0; + (void) mp; + do { + if (is_start_or_stop (p)) { + if (is_stop (p)) + decr (lev); + else + incr (lev); + } + p = mp_link (p); + } while (lev != 0); return p; } + @ Here is a diagnostic routine for printing an edge structure in symbolic form. @<Declarations@>= -static void mp_print_edges (MP mp,pointer h, const char *s, boolean nuline) ; +static void mp_print_edges (MP mp, mp_node h, const char *s, boolean nuline); @ @c -void mp_print_edges (MP mp,pointer h, const char *s, boolean nuline) { - pointer p; /* a graphical object to be printed */ - pointer hh,pp; /* temporary pointers */ - scaled scf; /* a scale factor for the dash pattern */ - boolean ok_to_dash; /* |false| for polygonal pen strokes */ - mp_print_diagnostic(mp, "Edge structure",s,nuline); - p=dummy_loc(h); - while ( mp_link(p)!=null ) { - p=mp_link(p); - mp_print_ln(mp); - switch (mp_type(p)) { +void mp_print_edges (MP mp, mp_node h, const char *s, boolean nuline) { + mp_node p; /* a graphical object to be printed */ + mp_node hh, pp; /* temporary pointers */ + scaled scf; /* a scale factor for the dash pattern */ + boolean ok_to_dash; /* |false| for polygonal pen strokes */ + mp_print_diagnostic (mp, "Edge structure", s, nuline); + p = dummy_loc (h); + while (mp_link (p) != NULL) { + p = mp_link (p); + mp_print_ln (mp); + switch (mp_type (p)) { @<Cases for printing graphical object node |p|@>; - default: - mp_print(mp, "[unknown object type!]"); - break; + default: + mp_print (mp, "[unknown object type!]"); + break; } } - mp_print_nl(mp, "End edges"); - if ( p!=obj_tail(h) ) mp_print(mp, "?"); -@.End edges?@> - mp_end_diagnostic(mp, true); + mp_print_nl (mp, "End edges"); + if (p != obj_tail (h)) + mp_print (mp, "?"); +@.End edges?@>; + mp_end_diagnostic (mp, true); } + @ @<Cases for printing graphical object node |p|@>= -case mp_fill_code: - mp_print(mp, "Filled contour "); - mp_print_obj_color(mp, p); - mp_print_char(mp, xord(':')); mp_print_ln(mp); - mp_pr_path(mp, mp_path_p(p)); mp_print_ln(mp); - if ( (mp_pen_p(p)!=null) ) { - @<Print join type for graphical object |p|@>; - mp_print(mp, " with pen"); mp_print_ln(mp); - mp_pr_pen(mp, mp_pen_p(p)); - } - break; +case mp_fill_node_type: +mp_print (mp, "Filled contour "); +mp_print_obj_color (mp, p); +mp_print_char (mp, xord (':')); +mp_print_ln (mp); +mp_pr_path (mp, mp_path_p ((mp_fill_node) p)); +mp_print_ln (mp); +if ((mp_pen_p ((mp_fill_node) p) != NULL)) { + @<Print join type for graphical object |p|@>; + mp_print (mp, " with pen"); + mp_print_ln (mp); + mp_pr_pen (mp, mp_pen_p ((mp_fill_node) p)); +} +break; @ @<Print join type for graphical object |p|@>= -switch (ljoin_val(p)) { +switch (ljoin_val (p)) { case 0: - mp_print(mp, "mitered joins limited "); - mp_print_scaled(mp, miterlim_val(p)); + mp_print (mp, "mitered joins limited "); + mp_print_scaled (mp, miterlim_val (p)); break; case 1: - mp_print(mp, "round joins"); + mp_print (mp, "round joins"); break; case 2: - mp_print(mp, "beveled joins"); + mp_print (mp, "beveled joins"); break; -default: - mp_print(mp, "?? joins"); -@.??@> +default: + mp_print (mp, "?? joins"); +@.??@>; break; } + @ For stroked nodes, we need to print |lcap_val(p)| as well. @<Print join and cap types for stroked node |p|@>= -switch (lcap_val(p)) { -case 0:mp_print(mp, "butt"); break; -case 1:mp_print(mp, "round"); break; -case 2:mp_print(mp, "square"); break; -default: mp_print(mp, "??"); break; +switch (lcap_val (p)) { +case 0: + mp_print (mp, "butt"); + break; +case 1: + mp_print (mp, "round"); + break; +case 2: + mp_print (mp, "square"); + break; +default: + mp_print (mp, "??"); + break; @.??@> } -mp_print(mp, " ends, "); +mp_print (mp, " ends, "); @<Print join type for graphical object |p|@> + @ Here is a routine that prints the color of a graphical object if it isn't black (the default color). @<Declarations@>= -static void mp_print_obj_color (MP mp,pointer p) ; +static void mp_print_obj_color (MP mp, mp_node p); @ @c -void mp_print_obj_color (MP mp,pointer p) { - if ( mp_color_model(p)==mp_grey_model ) { - if ( grey_val(p)>0 ) { - mp_print(mp, "greyed "); - mp_print_compact_node(mp, obj_grey_loc(p),1); +void mp_print_obj_color (MP mp, mp_node p) { + if (mp_color_model (p) == mp_grey_model) { + if (grey_val (p) > 0) { + mp_print (mp, "greyed "); + mp_print_char (mp, xord ('(')); + mp_print_scaled (mp, grey_val (p)); + mp_print_char (mp, xord (')')); }; - } else if ( mp_color_model(p)==mp_cmyk_model ) { - if ( (cyan_val(p)>0) || (magenta_val(p)>0) || - (yellow_val(p)>0) || (black_val(p)>0) ) { - mp_print(mp, "processcolored "); - mp_print_compact_node(mp, obj_cyan_loc(p),4); + } else if (mp_color_model (p) == mp_cmyk_model) { + if ((cyan_val (p) > 0) || (magenta_val (p) > 0) || + (yellow_val (p) > 0) || (black_val (p) > 0)) { + mp_print (mp, "processcolored "); + mp_print_char (mp, xord ('(')); + mp_print_scaled (mp, cyan_val (p)); + mp_print_char (mp, xord (',')); + mp_print_scaled (mp, magenta_val (p)); + mp_print_char (mp, xord (',')); + mp_print_scaled (mp, yellow_val (p)); + mp_print_char (mp, xord (',')); + mp_print_scaled (mp, black_val (p)); + mp_print_char (mp, xord (')')); }; - } else if ( mp_color_model(p)==mp_rgb_model ) { - if ( (red_val(p)>0) || (green_val(p)>0) || (blue_val(p)>0) ) { - mp_print(mp, "colored "); - mp_print_compact_node(mp, obj_red_loc(p),3); + } else if (mp_color_model (p) == mp_rgb_model) { + if ((red_val (p) > 0) || (green_val (p) > 0) || (blue_val (p) > 0)) { + mp_print (mp, "colored "); + mp_print_char (mp, xord ('(')); + mp_print_scaled (mp, red_val (p)); + mp_print_char (mp, xord (',')); + mp_print_scaled (mp, green_val (p)); + mp_print_char (mp, xord (',')); + mp_print_scaled (mp, blue_val (p)); + mp_print_char (mp, xord (')')); }; } } -@ We also need a procedure for printing consecutive scaled values as if they -were a known big node. - -@<Declarations@>= -static void mp_print_compact_node (MP mp,pointer p, quarterword k) ; - -@ @c -void mp_print_compact_node (MP mp,pointer p, quarterword k) { - pointer q; /* last location to print */ - q=p+k-1; - mp_print_char(mp, xord('(')); - while ( p<=q ){ - mp_print_scaled(mp, mp->mem[p].sc); - if ( p<q ) mp_print_char(mp, xord(',')); - incr(p); - } - mp_print_char(mp, xord(')')); -} @ @<Cases for printing graphical object node |p|@>= -case mp_stroked_code: - mp_print(mp, "Filled pen stroke "); - mp_print_obj_color(mp, p); - mp_print_char(mp, xord(':')); mp_print_ln(mp); - mp_pr_path(mp, mp_path_p(p)); - if ( mp_dash_p(p)!=null ) { - mp_print_nl(mp, "dashed ("); - @<Finish printing the dash pattern that |p| refers to@>; - } - mp_print_ln(mp); - @<Print join and cap types for stroked node |p|@>; - mp_print(mp, " with pen"); mp_print_ln(mp); - if ( mp_pen_p(p)==null ) mp_print(mp, "???"); /* shouldn't happen */ +case mp_stroked_node_type: +mp_print (mp, "Filled pen stroke "); +mp_print_obj_color (mp, p); +mp_print_char (mp, xord (':')); +mp_print_ln (mp); +mp_pr_path (mp, mp_path_p ((mp_stroked_node) p)); +if (mp_dash_p (p) != NULL) { + mp_print_nl (mp, "dashed ("); + @<Finish printing the dash pattern that |p| refers to@>; +} +mp_print_ln (mp); +@<Print join and cap types for stroked node |p|@>; +mp_print (mp, " with pen"); +mp_print_ln (mp); +if (mp_pen_p ((mp_stroked_node) p) == NULL) { + mp_print (mp, "???"); /* shouldn't happen */ @.???@> - else mp_pr_pen(mp, mp_pen_p(p)); - break; +} else { + mp_pr_pen (mp, mp_pen_p ((mp_stroked_node) p)); +} +break; @ Normally, the |dash_list| field in an edge header is set to |null_dash| when it is not known to define a suitable dash pattern. This is disallowed @@ -10109,72 +10291,99 @@ Note that memory is allocated for |start_x(null_dash)| and we are free to give it any convenient value. @<Finish printing the dash pattern that |p| refers to@>= -ok_to_dash=pen_is_elliptical(mp_pen_p(p)); -if ( ! ok_to_dash ) scf=unity; else scf=dash_scale(p); -hh=mp_dash_p(p); -pp=dash_list(hh); -if ( (pp==null_dash) || (dash_y(hh)<0) ) { - mp_print(mp, " ??"); -} else { start_x(null_dash)=start_x(pp)+dash_y(hh); - while ( pp!=null_dash ) { - mp_print(mp, "on "); - mp_print_scaled(mp, mp_take_scaled(mp, stop_x(pp)-start_x(pp),scf)); - mp_print(mp, " off "); - mp_print_scaled(mp, mp_take_scaled(mp, start_x(mp_link(pp))-stop_x(pp),scf)); - pp = mp_link(pp); - if ( pp!=null_dash ) mp_print_char(mp, xord(' ')); - } - mp_print(mp, ") shifted "); - mp_print_scaled(mp, -mp_take_scaled(mp, mp_dash_offset(mp, hh),scf)); - if ( ! ok_to_dash || (dash_y(hh)==0) ) mp_print(mp, " (this will be ignored)"); +ok_to_dash = pen_is_elliptical (mp_pen_p ((mp_stroked_node) p)); +if (!ok_to_dash) + scf = unity; +else + scf = dash_scale (p); +hh = mp_dash_p (p); +pp = dash_list (hh); +if ((pp == mp->null_dash) || (dash_y (hh) < 0)) { + mp_print (mp, " ??"); +} else { + start_x (mp->null_dash) = start_x (pp) + dash_y (hh); + while (pp != mp->null_dash) { + mp_print (mp, "on "); + mp_print_scaled (mp, mp_take_scaled (mp, stop_x (pp) - start_x (pp), scf)); + mp_print (mp, " off "); + mp_print_scaled (mp, + mp_take_scaled (mp, start_x (mp_link (pp)) - stop_x (pp), + scf)); + pp = mp_link (pp); + if (pp != mp->null_dash) + mp_print_char (mp, xord (' ')); + } + mp_print (mp, ") shifted "); + mp_print_scaled (mp, -mp_take_scaled (mp, mp_dash_offset (mp, hh), scf)); + if (!ok_to_dash || (dash_y (hh) == 0)) + mp_print (mp, " (this will be ignored)"); } + @ @<Declarations@>= -static scaled mp_dash_offset (MP mp,pointer h) ; +static scaled mp_dash_offset (MP mp, mp_node h); @ @c -scaled mp_dash_offset (MP mp,pointer h) { - scaled x; /* the answer */ - if (dash_list(h)==null_dash || dash_y(h)<0) mp_confusion(mp, "dash0"); -@:this can't happen dash0}{\quad dash0@> - if ( dash_y(h)==0 ) { - x=0; - } else { - x=-(start_x(dash_list(h)) % dash_y(h)); - if ( x<0 ) x=x+dash_y(h); +scaled mp_dash_offset (MP mp, mp_node h) { + scaled x; /* the answer */ + if (dash_list (h) == mp->null_dash || dash_y (h) < 0) + mp_confusion (mp, "dash0"); +@:this can't happen dash0}{\quad dash0@>; + if (dash_y (h) == 0) { + x = 0; + } else { + x = -(start_x (dash_list (h)) % dash_y (h)); + if (x < 0) + x = x + dash_y (h); } return x; } + @ @<Cases for printing graphical object node |p|@>= -case mp_text_code: - mp_print_char(mp, xord('"')); mp_print_str(mp,mp_text_p(p)); - mp_print(mp, "\" infont \""); mp_print(mp, mp->font_name[mp_font_n(p)]); - mp_print_char(mp, xord('"')); mp_print_ln(mp); - mp_print_obj_color(mp, p); - mp_print(mp, "transformed "); - mp_print_compact_node(mp, text_tx_loc(p),6); - break; +case mp_text_node_type: +mp_print_char (mp, xord ('"')); +mp_print_str (mp, mp_text_p (p)); +mp_print (mp, "\" infont \""); +mp_print (mp, mp->font_name[mp_font_n (p)]); +mp_print_char (mp, xord ('"')); +mp_print_ln (mp); +mp_print_obj_color (mp, p); +mp_print (mp, "transformed "); +mp_print_char (mp, xord ('(')); +mp_print_scaled (mp, tx_val (p)); +mp_print_char (mp, xord (',')); +mp_print_scaled (mp, ty_val (p)); +mp_print_char (mp, xord (',')); +mp_print_scaled (mp, txx_val (p)); +mp_print_char (mp, xord (',')); +mp_print_scaled (mp, txy_val (p)); +mp_print_char (mp, xord (',')); +mp_print_scaled (mp, tyx_val (p)); +mp_print_char (mp, xord (',')); +mp_print_scaled (mp, tyy_val (p)); +mp_print_char (mp, xord (')')); +break; @ @<Cases for printing graphical object node |p|@>= -case mp_start_clip_code: - mp_print(mp, "clipping path:"); - mp_print_ln(mp); - mp_pr_path(mp, mp_path_p(p)); - break; -case mp_stop_clip_code: - mp_print(mp, "stop clipping"); - break; +case mp_start_clip_node_type: +mp_print (mp, "clipping path:"); +mp_print_ln (mp); +mp_pr_path (mp, mp_path_p ((mp_start_clip_node) p)); +break; +case mp_stop_clip_node_type: +mp_print (mp, "stop clipping"); +break; @ @<Cases for printing graphical object node |p|@>= -case mp_start_bounds_code: - mp_print(mp, "setbounds path:"); - mp_print_ln(mp); - mp_pr_path(mp, mp_path_p(p)); - break; -case mp_stop_bounds_code: - mp_print(mp, "end of setbounds"); - break; +case mp_start_bounds_node_type: +mp_print (mp, "setbounds path:"); +mp_print_ln (mp); +mp_pr_path (mp, mp_path_p ((mp_start_bounds_node) p)); +break; +case mp_stop_bounds_node_type: +mp_print (mp, "end of setbounds"); +break; @ To initialize the |dash_list| field in an edge header~|h|, we need a subroutine that scans an edge structure and tries to interpret it as a dash @@ -10186,211 +10395,232 @@ be no retracing. If the resulting paths cover a range of $x$~coordinates of length $\Delta x$, we set |dash_y(h)| to the length of the dash pattern by finding the maximum of $\Delta x$ and the absolute value of~$y_0$. -@c -static pointer mp_make_dashes (MP mp,pointer h) { /* returns |h| or |null| */ - pointer p; /* this scans the stroked nodes in the object list */ - pointer p0; /* if not |null| this points to the first stroked node */ - pointer pp,qq,rr; /* pointers into |mp_path_p(p)| */ - pointer d,dd; /* pointers used to create the dash list */ +@c +static mp_node mp_make_dashes (MP mp, mp_node h) { /* returns |h| or |NULL| */ + mp_node p; /* this scans the stroked nodes in the object list */ + mp_node p0; /* if not |NULL| this points to the first stroked node */ + mp_knot pp, qq, rr; /* pointers into |mp_path_p(p)| */ + mp_node d, dd; /* pointers used to create the dash list */ scaled y0; @<Other local variables in |make_dashes|@>; - y0=0; /* the initial $y$ coordinate */ - if ( dash_list(h)!=null_dash ) - return h; - p0=null; - p=mp_link(dummy_loc(h)); - while ( p!=null ) { - if ( mp_type(p)!=mp_stroked_code ) { + y0 = 0; /* the initial $y$ coordinate */ + if (dash_list (h) != mp->null_dash) + return h; + p0 = NULL; + p = mp_link (dummy_loc (h)); + while (p != NULL) { + if (mp_type (p) != mp_stroked_node_type) { @<Compain that the edge structure contains a node of the wrong type and |goto not_found|@>; } - pp=mp_path_p(p); - if ( p0==null ){ p0=p; y0=mp_y_coord(pp); }; + pp = mp_path_p ((mp_stroked_node) p); + if (p0 == NULL) { + p0 = p; + y0 = mp_y_coord (pp); + } @<Make |d| point to a new dash node created from stroke |p| and path |pp| or |goto not_found| if there is an error@>; @<Insert |d| into the dash list and |goto not_found| if there is an error@>; - p=mp_link(p); + p = mp_link (p); } - if ( dash_list(h)==null_dash ) - goto NOT_FOUND; /* No error message */ + if (dash_list (h) == mp->null_dash) + goto NOT_FOUND; /* No error message */ @<Scan |dash_list(h)| and deal with any dashes that are themselves dashed@>; @<Set |dash_y(h)| and merge the first and last dashes if necessary@>; return h; -NOT_FOUND: - @<Flush the dash list, recycle |h| and return |null|@>; +NOT_FOUND: + @<Flush the dash list, recycle |h| and return |NULL|@>; } + @ @<Compain that the edge structure contains a node of the wrong type...@>= -{ -print_err("Picture is too complicated to use as a dash pattern"); -help3("When you say `dashed p', picture p should not contain any", - "text, filled regions, or clipping paths. This time it did", - "so I'll just make it a solid line instead."); -mp_put_get_error(mp); -goto NOT_FOUND; +{ + print_err ("Picture is too complicated to use as a dash pattern"); + help3 ("When you say `dashed p', picture p should not contain any", + "text, filled regions, or clipping paths. This time it did", + "so I'll just make it a solid line instead."); + mp_put_get_error (mp); + goto NOT_FOUND; } + @ A similar error occurs when monotonicity fails. @<Declarations@>= -static void mp_x_retrace_error (MP mp) ; +static void mp_x_retrace_error (MP mp); @ @c -void mp_x_retrace_error (MP mp) { -print_err("Picture is too complicated to use as a dash pattern"); -help3("When you say `dashed p', every path in p should be monotone", - "in x and there must be no overlapping. This failed", - "so I'll just make it a solid line instead."); -mp_put_get_error(mp); +void mp_x_retrace_error (MP mp) { + print_err ("Picture is too complicated to use as a dash pattern"); + help3 ("When you say `dashed p', every path in p should be monotone", + "in x and there must be no overlapping. This failed", + "so I'll just make it a solid line instead."); + mp_put_get_error (mp); } -@ We stash |p| in |mp_info(d)| if |mp_dash_p(p)<>0| so that subsequent processing can + +@ We stash |p| in |dash_info(d)| if |mp_dash_p(p)<>0| so that subsequent processing can handle the case where the pen stroke |p| is itself dashed. +@d dash_info(A) ((mp_dash_node)(A))->dash_info_ /* in an edge header this points to the first dash node */ + @<Make |d| point to a new dash node created from stroke |p| and path...@>= @<Make sure |p| and |p0| are the same color and |goto not_found| if there is an error@>; -rr=pp; -if ( mp_link(pp)!=pp ) { - do { - qq=rr; rr=mp_link(rr); +rr = pp; +if (mp_next_knot (pp) != pp) { + do { + qq = rr; + rr = mp_next_knot (rr); @<Check for retracing between knots |qq| and |rr| and |goto not_found| if there is a problem@>; - } while (mp_right_type(rr)!=mp_endpoint); + } while (mp_right_type (rr) != mp_endpoint); } -d=mp_get_node(mp, dash_node_size); -if ( mp_dash_p(p)==0 ) mp_info(d)=0; else mp_info(d)=p; -if ( mp_x_coord(pp)<mp_x_coord(rr) ) { - start_x(d)=mp_x_coord(pp); - stop_x(d)=mp_x_coord(rr); -} else { - start_x(d)=mp_x_coord(rr); - stop_x(d)=mp_x_coord(pp); +d = mp_get_dash_node (mp); +if (mp_dash_p (p) == NULL) + dash_info (d) = NULL; +else + dash_info (d) = p; +if (mp_x_coord (pp) < mp_x_coord (rr)) { + start_x (d) = mp_x_coord (pp); + stop_x (d) = mp_x_coord (rr); +} else { + start_x (d) = mp_x_coord (rr); + stop_x (d) = mp_x_coord (pp); } + @ We also need to check for the case where the segment from |qq| to |rr| is monotone in $x$ but is reversed relative to the path from |pp| to |qq|. @<Check for retracing between knots |qq| and |rr| and |goto not_found|...@>= -x0=mp_x_coord(qq); -x1=mp_right_x(qq); -x2=mp_left_x(rr); -x3=mp_x_coord(rr); -if ( (x0>x1) || (x1>x2) || (x2>x3) ) { - if ( (x0<x1) || (x1<x2) || (x2<x3) ) { - if ( mp_ab_vs_cd(mp, x2-x1,x2-x1,x1-x0,x3-x2)>0 ) { - mp_x_retrace_error(mp); goto NOT_FOUND; +x0 = mp_x_coord (qq); +x1 = mp_right_x (qq); +x2 = mp_left_x (rr); +x3 = mp_x_coord (rr); +if ((x0 > x1) || (x1 > x2) || (x2 > x3)) { + if ((x0 < x1) || (x1 < x2) || (x2 < x3)) { + if (mp_ab_vs_cd (mp, x2 - x1, x2 - x1, x1 - x0, x3 - x2) > 0) { + mp_x_retrace_error (mp); + goto NOT_FOUND; } } } -if ( (mp_x_coord(pp)>x0) || (x0>x3) ) { - if ( (mp_x_coord(pp)<x0) || (x0<x3) ) { - mp_x_retrace_error(mp); goto NOT_FOUND; +if ((mp_x_coord (pp) > x0) || (x0 > x3)) { + if ((mp_x_coord (pp) < x0) || (x0 < x3)) { + mp_x_retrace_error (mp); + goto NOT_FOUND; } } @ @<Other local variables in |make_dashes|@>= - scaled x0,x1,x2,x3; /* $x$ coordinates of the segment from |qq| to |rr| */ +scaled x0, x1, x2, x3; /* $x$ coordinates of the segment from |qq| to |rr| */ @ @<Make sure |p| and |p0| are the same color and |goto not_found|...@>= -if ( (red_val(p)!=red_val(p0)) || (black_val(p)!=black_val(p0)) || - (green_val(p)!=green_val(p0)) || (blue_val(p)!=blue_val(p0)) ) { - print_err("Picture is too complicated to use as a dash pattern"); - help3("When you say `dashed p', everything in picture p should", - "be the same color. I can\'t handle your color changes", - "so I'll just make it a solid line instead."); - mp_put_get_error(mp); +if ((red_val (p) != red_val (p0)) || (black_val (p) != black_val (p0)) || + (green_val (p) != green_val (p0)) || (blue_val (p) != blue_val (p0))) { + print_err ("Picture is too complicated to use as a dash pattern"); + help3 ("When you say `dashed p', everything in picture p should", + "be the same color. I can\'t handle your color changes", + "so I'll just make it a solid line instead."); + mp_put_get_error (mp); goto NOT_FOUND; } @ @<Insert |d| into the dash list and |goto not_found| if there is an error@>= -start_x(null_dash)=stop_x(d); -dd=h; /* this makes |mp_link(dd)=dash_list(h)| */ -while ( start_x(mp_link(dd))<stop_x(d) ) - dd=mp_link(dd); -if ( dd!=h ) { - if ( (stop_x(dd)>start_x(d)) ) - { mp_x_retrace_error(mp); goto NOT_FOUND; }; +start_x (mp->null_dash) = stop_x (d); +dd = h; /* this makes |mp_link(dd)=dash_list(h)| */ +while (start_x (mp_link (dd)) < stop_x (d)) + dd = mp_link (dd); +if (dd != h) { + if ((stop_x (dd) > start_x (d))) { + mp_x_retrace_error (mp); + goto NOT_FOUND; + }; } -mp_link(d)=mp_link(dd); -mp_link(dd)=d +mp_link (d) = mp_link (dd); +mp_link (dd) = d @ @<Set |dash_y(h)| and merge the first and last dashes if necessary@>= -d=dash_list(h); -while ( (mp_link(d)!=null_dash) ) - d=mp_link(d); -dd=dash_list(h); -dash_y(h)=stop_x(d)-start_x(dd); -if ( abs(y0)>dash_y(h) ) { - dash_y(h)=abs(y0); -} else if ( d!=dd ) { - dash_list(h)=mp_link(dd); - stop_x(d)=stop_x(dd)+dash_y(h); - mp_free_node(mp, dd,dash_node_size); -} - -@ We get here when the argument is a null picture or when there is an error. +d = dash_list (h); +while ((mp_link (d) != mp->null_dash)) + d = mp_link (d); +dd = dash_list (h); +dash_y (h) = stop_x (d) - start_x (dd); +if (abs (y0) > dash_y (h)) { +dash_y (h) = abs (y0); +} else if (d != dd) { + dash_list (h) = mp_link (dd); + stop_x (d) = stop_x (dd) + dash_y (h); + mp_free_node (mp, dd, dash_node_size); +} + +@ We get here when the argument is a NULL picture or when there is an error. Recovering from an error involves making |dash_list(h)| empty to indicate that |h| is not known to be a valid dash pattern. We also dereference |h| since it is not being used for the return value. -@<Flush the dash list, recycle |h| and return |null|@>= -mp_flush_dash_list(mp, h); -delete_edge_ref(h); -return null +@<Flush the dash list, recycle |h| and return |NULL|@>= +mp_flush_dash_list (mp, h); +delete_edge_ref (h); +return NULL @ Having carefully saved the dashed stroked nodes in the corresponding dash nodes, we must be prepared to break up these dashes into smaller dashes. @<Scan |dash_list(h)| and deal with any dashes that are themselves dashed@>= -d=h; /* now |mp_link(d)=dash_list(h)| */ -while ( mp_link(d)!=null_dash ) { - ds=mp_info(mp_link(d)); - if ( ds==null ) { - d=mp_link(d); +d = h; /* now |mp_link(d)=dash_list(h)| */ +while (mp_link (d) != mp->null_dash) { + ds = dash_info (mp_link (d)); + if (ds == NULL) { + d = mp_link (d); } else { - hh=mp_dash_p(ds); - hsf=dash_scale(ds); - if ( (hh==null) ) mp_confusion(mp, "dash1"); -@:this can't happen dash0}{\quad dash1@> - if ( dash_y(hh)==0 ) { - d=mp_link(d); - } else { - if ( dash_list(hh)==null ) mp_confusion(mp, "dash1"); -@:this can't happen dash0}{\quad dash1@> + hh = mp_dash_p (ds); + hsf = dash_scale (ds); + if ((hh == NULL)) + mp_confusion (mp, "dash1"); +@:this can't happen dash0}{\quad dash1@>; + if (dash_y (hh) == 0) { + d = mp_link (d); + } else { + if (dash_list (hh) == NULL) + mp_confusion (mp, "dash1"); +@:this can't happen dash0}{\quad dash1@>; @<Replace |mp_link(d)| by a dashed version as determined by edge header |hh| and scale factor |ds|@>; } } } + @ @<Other local variables in |make_dashes|@>= -pointer dln; /* |mp_link(d)| */ -pointer hh; /* an edge header that tells how to break up |dln| */ -scaled hsf; /* the dash pattern from |hh| gets scaled by this */ -pointer ds; /* the stroked node from which |hh| and |hsf| are derived */ -scaled xoff; /* added to $x$ values in |dash_list(hh)| to match |dln| */ +mp_node dln; /* |mp_link(d)| */ +mp_node hh; /* an edge header that tells how to break up |dln| */ +scaled hsf; /* the dash pattern from |hh| gets scaled by this */ +mp_node ds; /* the stroked node from which |hh| and |hsf| are derived */ +scaled xoff; /* added to $x$ values in |dash_list(hh)| to match |dln| */ @ @<Replace |mp_link(d)| by a dashed version as determined by edge header...@>= -dln=mp_link(d); -dd=dash_list(hh); -xoff=start_x(dln)-mp_take_scaled(mp, hsf,start_x(dd))- - mp_take_scaled(mp, hsf,mp_dash_offset(mp, hh)); -start_x(null_dash)=mp_take_scaled(mp, hsf,start_x(dd)) - +mp_take_scaled(mp, hsf,dash_y(hh)); -stop_x(null_dash)=start_x(null_dash); +dln = mp_link (d); +dd = dash_list (hh); +xoff = start_x (dln) - mp_take_scaled (mp, hsf, start_x (dd)) - +mp_take_scaled (mp, hsf, mp_dash_offset (mp, hh)); +start_x (mp->null_dash) = mp_take_scaled (mp, hsf, start_x (dd)) + + mp_take_scaled (mp, hsf, dash_y (hh)); +stop_x (mp->null_dash) = start_x (mp->null_dash); @<Advance |dd| until finding the first dash that overlaps |dln| when offset by |xoff|@>; -while ( start_x(dln)<=stop_x(dln) ) { - @<If |dd| has `fallen off the end', back up to the beginning and fix |xoff|@>; - @<Insert a dash between |d| and |dln| for the overlap with the offset version +while (start_x (dln) <= stop_x (dln)) { +@<If |dd| has `fallen off the end', back up to the beginning and fix |xoff|@>; +@<Insert a dash between |d| and |dln| for the overlap with the offset version of |dd|@>; - dd=mp_link(dd); - start_x(dln)=xoff+mp_take_scaled(mp, hsf,start_x(dd)); +dd = mp_link (dd); +start_x (dln) = xoff + mp_take_scaled (mp, hsf, start_x (dd)); } -mp_link(d)=mp_link(dln); -mp_free_node(mp, dln,dash_node_size) +mp_link (d) = mp_link (dln); +mp_free_node (mp, dln, dash_node_size) + @ The name of this module is a bit of a lie because we just find the first |dd| where |take_scaled (hsf, stop_x(dd))| is large enough to make an @@ -10398,32 +10628,33 @@ overlap possible. It could be that the unoffset version of dash |dln| falls in the gap between |dd| and its predecessor. @<Advance |dd| until finding the first dash that overlaps |dln| when...@>= -while ( xoff+mp_take_scaled(mp, hsf,stop_x(dd))<start_x(dln) ) { - dd=mp_link(dd); +while (xoff + mp_take_scaled (mp, hsf, stop_x (dd)) < start_x (dln)) { + dd = mp_link (dd); } + @ @<If |dd| has `fallen off the end', back up to the beginning and fix...@>= -if ( dd==null_dash ) { - dd=dash_list(hh); - xoff=xoff+mp_take_scaled(mp, hsf,dash_y(hh)); +if (dd == mp->null_dash) { + dd = dash_list (hh); + xoff = xoff + mp_take_scaled (mp, hsf, dash_y (hh)); } @ At this point we already know that |start_x(dln)<=xoff+take_scaled(hsf,stop_x(dd))|. @<Insert a dash between |d| and |dln| for the overlap with the offset...@>= -if ( (xoff+mp_take_scaled(mp, hsf,start_x(dd)))<=stop_x(dln) ) { - mp_link(d)=mp_get_node(mp, dash_node_size); - d=mp_link(d); - mp_link(d)=dln; - if ( start_x(dln)>(xoff+mp_take_scaled(mp, hsf,start_x(dd)))) - start_x(d)=start_x(dln); - else - start_x(d)=xoff+mp_take_scaled(mp, hsf,start_x(dd)); - if ( stop_x(dln)<(xoff+mp_take_scaled(mp, hsf,stop_x(dd)))) - stop_x(d)=stop_x(dln); - else - stop_x(d)=xoff+mp_take_scaled(mp, hsf,stop_x(dd)); +if ((xoff + mp_take_scaled (mp, hsf, start_x (dd))) <= stop_x (dln)) { + mp_link (d) = mp_get_dash_node (mp); + d = mp_link (d); + mp_link (d) = dln; + if (start_x (dln) > (xoff + mp_take_scaled (mp, hsf, start_x (dd)))) + start_x (d) = start_x (dln); + else + start_x (d) = xoff + mp_take_scaled (mp, hsf, start_x (dd)); + if (stop_x (dln) < (xoff + mp_take_scaled (mp, hsf, stop_x (dd)))) + stop_x (d) = stop_x (dln); + else + stop_x (d) = xoff + mp_take_scaled (mp, hsf, stop_x (dd)); } @ The next major task is to update the bounding box information in an edge @@ -10432,90 +10663,108 @@ header's bounding box to accommodate the box computed by |path_bbox| or |pen_bbox|. (This is stored in global variables |minx|, |miny|, |maxx|, and |maxy|.) -@c static void mp_adjust_bbox (MP mp,pointer h) { - if ( mp_minx<minx_val(h) ) minx_val(h)=mp_minx; - if ( mp_miny<miny_val(h) ) miny_val(h)=mp_miny; - if ( mp_maxx>maxx_val(h) ) maxx_val(h)=mp_maxx; - if ( mp_maxy>maxy_val(h) ) maxy_val(h)=mp_maxy; +@c +static void mp_adjust_bbox (MP mp, mp_node h) { + if (mp_minx < minx_val (h)) + minx_val (h) = mp_minx; + if (mp_miny < miny_val (h)) + miny_val (h) = mp_miny; + if (mp_maxx > maxx_val (h)) + maxx_val (h) = mp_maxx; + if (mp_maxy > maxy_val (h)) + maxy_val (h) = mp_maxy; } + @ Here is a special routine for updating the bounding box information in edge header~|h| to account for the squared-off ends of a non-cyclic path~|p| that is to be stroked with the pen~|pp|. -@c static void mp_box_ends (MP mp, pointer p, pointer pp, pointer h) { - pointer q; /* a knot node adjacent to knot |p| */ - fraction dx,dy; /* a unit vector in the direction out of the path at~|p| */ - scaled d; /* a factor for adjusting the length of |(dx,dy)| */ - scaled z; /* a coordinate being tested against the bounding box */ - scaled xx,yy; /* the extreme pen vertex in the |(dx,dy)| direction */ - integer i; /* a loop counter */ - if ( mp_right_type(p)!=mp_endpoint ) { - q=mp_link(p); - while (1) { +@c +static void mp_box_ends (MP mp, mp_knot p, mp_knot pp, mp_node h) { + mp_knot q; /* a knot node adjacent to knot |p| */ + fraction dx, dy; /* a unit vector in the direction out of the path at~|p| */ + scaled d; /* a factor for adjusting the length of |(dx,dy)| */ + scaled z; /* a coordinate being tested against the bounding box */ + scaled xx, yy; /* the extreme pen vertex in the |(dx,dy)| direction */ + integer i; /* a loop counter */ + if (mp_right_type (p) != mp_endpoint) { + q = mp_next_knot (p); + while (1) { @<Make |(dx,dy)| the final direction for the path segment from |q| to~|p|; set~|d|@>; - d=mp_pyth_add(mp, dx,dy); - if ( d>0 ) { - @<Normalize the direction |(dx,dy)| and find the pen offset |(xx,yy)|@>; - for (i=1;i<= 2;i++) { - @<Use |(dx,dy)| to generate a vertex of the square end cap and + d = mp_pyth_add (mp, dx, dy); + if (d > 0) { + @<Normalize the direction |(dx,dy)| and find the pen offset |(xx,yy)|@>; + for (i = 1; i <= 2; i++) { + @<Use |(dx,dy)| to generate a vertex of the square end cap and update the bounding box to accommodate it@>; - dx=-dx; dy=-dy; + dx = -dx; + dy = -dy; } } - if ( mp_right_type(p)==mp_endpoint ) { - return; + if (mp_right_type (p) == mp_endpoint) { + return; } else { @<Advance |p| to the end of the path and make |q| the previous knot@>; - } + } } } } + @ @<Make |(dx,dy)| the final direction for the path segment from...@>= -if ( q==mp_link(p) ) { - dx=mp_x_coord(p)-mp_right_x(p); - dy=mp_y_coord(p)-mp_right_y(p); - if ( (dx==0)&&(dy==0) ) { - dx=mp_x_coord(p)-mp_left_x(q); - dy=mp_y_coord(p)-mp_left_y(q); +if (q == mp_next_knot (p)) { + dx = mp_x_coord (p) - mp_right_x (p); + dy = mp_y_coord (p) - mp_right_y (p); + if ((dx == 0) && (dy == 0)) { + dx = mp_x_coord (p) - mp_left_x (q); + dy = mp_y_coord (p) - mp_left_y (q); } -} else { - dx=mp_x_coord(p)-mp_left_x(p); - dy=mp_y_coord(p)-mp_left_y(p); - if ( (dx==0)&&(dy==0) ) { - dx=mp_x_coord(p)-mp_right_x(q); - dy=mp_y_coord(p)-mp_right_y(q); +} else { + dx = mp_x_coord (p) - mp_left_x (p); + dy = mp_y_coord (p) - mp_left_y (p); + if ((dx == 0) && (dy == 0)) { + dx = mp_x_coord (p) - mp_right_x (q); + dy = mp_y_coord (p) - mp_right_y (q); } } -dx=mp_x_coord(p)-mp_x_coord(q); -dy=mp_y_coord(p)-mp_y_coord(q) +dx = mp_x_coord (p) - mp_x_coord (q); +dy = mp_y_coord (p) - mp_y_coord (q) + @ @<Normalize the direction |(dx,dy)| and find the pen offset |(xx,yy)|@>= -dx=mp_make_fraction(mp, dx,d); -dy=mp_make_fraction(mp, dy,d); -mp_find_offset(mp, -dy,dx,pp); -xx=mp->cur_x; yy=mp->cur_y +dx = mp_make_fraction (mp, dx, d); +dy = mp_make_fraction (mp, dy, d); +mp_find_offset (mp, -dy, dx, pp); +xx = mp->cur_x; +yy = mp->cur_y @ @<Use |(dx,dy)| to generate a vertex of the square end cap and...@>= -mp_find_offset(mp, dx,dy,pp); -d=mp_take_fraction(mp, xx-mp->cur_x,dx)+mp_take_fraction(mp, yy-mp->cur_y,dy); -if ( ((d<0)&&(i==1)) || ((d>0)&&(i==2))) - mp_confusion(mp, "box_ends"); -@:this can't happen box ends}{\quad\\{box\_ends}@> -z=mp_x_coord(p)+mp->cur_x+mp_take_fraction(mp, d,dx); -if ( z<minx_val(h) ) minx_val(h)=z; -if ( z>maxx_val(h) ) maxx_val(h)=z; -z=mp_y_coord(p)+mp->cur_y+mp_take_fraction(mp, d,dy); -if ( z<miny_val(h) ) miny_val(h)=z; -if ( z>maxy_val(h) ) maxy_val(h)=z +mp_find_offset (mp, dx, dy, pp); +d = +mp_take_fraction (mp, xx - mp->cur_x, dx) + mp_take_fraction (mp, + yy - mp->cur_y, + dy); +if (((d < 0) && (i == 1)) || ((d > 0) && (i == 2))) + mp_confusion (mp, "box_ends"); +@:this can't happen box ends}{\quad\\{box\_ends}@>; +z = mp_x_coord (p) + mp->cur_x + mp_take_fraction (mp, d, dx); +if (z < minx_val (h)) + minx_val (h) = z; +if (z > maxx_val (h)) + maxx_val (h) = z; +z = mp_y_coord (p) + mp->cur_y + mp_take_fraction (mp, d, dy); +if (z < miny_val (h)) + miny_val (h) = z; +if (z > maxy_val (h)) + maxy_val (h) = z @ @<Advance |p| to the end of the path and make |q| the previous knot@>= -do { - q=p; - p=mp_link(p); -} while (mp_right_type(p)!=mp_endpoint) +do { + q = p; + p = mp_next_knot (p); +} while (mp_right_type (p) != mp_endpoint) @ The major difficulty in finding the bounding box of an edge structure is the effect of clipping paths. We treat them conservatively by only clipping to the @@ -10525,86 +10774,102 @@ requires recursive calls to |set_bbox| in order to find the bounding box of the objects to be clipped. Such calls are distinguished by the fact that the boolean parameter |top_level| is false. -@c -void mp_set_bbox (MP mp,pointer h, boolean top_level) { - pointer p; /* a graphical object being considered */ - scaled sminx,sminy,smaxx,smaxy; +@c +void mp_set_bbox (MP mp, mp_node h, boolean top_level) { + mp_node p; /* a graphical object being considered */ + scaled sminx, sminy, smaxx, smaxy; /* for saving the bounding box during recursive calls */ - scaled x0,x1,y0,y1; /* temporary registers */ - integer lev; /* nesting level for |mp_start_bounds_code| nodes */ + scaled x0, x1, y0, y1; /* temporary registers */ + integer lev; /* nesting level for |mp_start_bounds_node| nodes */ @<Wipe out any existing bounding box information if |bbtype(h)| is incompatible with |internal[mp_true_corners]|@>; - while ( mp_link(bblast(h))!=null ) { - p=mp_link(bblast(h)); - bblast(h)=p; - switch (mp_type(p)) { - case mp_stop_clip_code: - if ( top_level ) mp_confusion(mp, "bbox"); else return; -@:this can't happen bbox}{\quad bbox@> + while (mp_link (bblast (h)) != NULL) { + p = mp_link (bblast (h)); + bblast (h) = p; + switch (mp_type (p)) { + case mp_stop_clip_node_type: + if (top_level) + mp_confusion (mp, "bbox"); + else + return; +@:this can't happen bbox}{\quad bbox@>; break; - @<Other cases for updating the bounding box based on the type of object |p|@>; - } /* all cases are enumerated above */ + @<Other cases for updating the bounding box based on the type of object |p|@>; + default: /* there are no other valid cases, but please the compiler */ + break; + } } - if ( ! top_level ) mp_confusion(mp, "bbox"); + if (!top_level) + mp_confusion (mp, "bbox"); } + @ @<Declarations@>= -static void mp_set_bbox (MP mp,pointer h, boolean top_level); +static void mp_set_bbox (MP mp, mp_node h, boolean top_level); @ @<Wipe out any existing bounding box information if |bbtype(h)| is...@>= -switch (bbtype(h)) { -case no_bounds: +switch (bbtype (h)) { +case no_bounds: break; -case bounds_set: - if ( mp->internal[mp_true_corners]>0 ) mp_init_bbox(mp, h); +case bounds_set: + if (internal_value (mp_true_corners) > 0) + mp_init_bbox (mp, h); break; -case bounds_unset: - if ( mp->internal[mp_true_corners]<=0 ) mp_init_bbox(mp, h); +case bounds_unset: + if (internal_value (mp_true_corners) <= 0) + mp_init_bbox (mp, h); break; -} /* there are no other cases */ +} /* there are no other cases */ + @ @<Other cases for updating the bounding box...@>= -case mp_fill_code: - mp_path_bbox(mp, mp_path_p(p)); - if ( mp_pen_p(p)!=null ) { - x0=mp_minx; y0=mp_miny; - x1=mp_maxx; y1=mp_maxy; - mp_pen_bbox(mp, mp_pen_p(p)); - mp_minx=mp_minx+x0; - mp_miny=mp_miny+y0; - mp_maxx=mp_maxx+x1; - mp_maxy=mp_maxy+y1; - } - mp_adjust_bbox(mp, h); - break; +case mp_fill_node_type: +mp_path_bbox (mp, mp_path_p ((mp_fill_node) p)); +if (mp_pen_p ((mp_fill_node) p) != NULL) { + x0 = mp_minx; + y0 = mp_miny; + x1 = mp_maxx; + y1 = mp_maxy; + mp_pen_bbox (mp, mp_pen_p ((mp_fill_node) p)); + mp_minx = mp_minx + x0; + mp_miny = mp_miny + y0; + mp_maxx = mp_maxx + x1; + mp_maxy = mp_maxy + y1; +} +mp_adjust_bbox (mp, h); +break; @ @<Other cases for updating the bounding box...@>= -case mp_start_bounds_code: - if ( mp->internal[mp_true_corners]>0 ) { - bbtype(h)=bounds_unset; - } else { - bbtype(h)=bounds_set; - mp_path_bbox(mp, mp_path_p(p)); - mp_adjust_bbox(mp, h); - @<Scan to the matching |mp_stop_bounds_code| node and update |p| and +case mp_start_bounds_node_type: +if (internal_value (mp_true_corners) > 0) { + bbtype (h) = bounds_unset; +} else { + bbtype (h) = bounds_set; + mp_path_bbox (mp, mp_path_p ((mp_start_bounds_node) p)); + mp_adjust_bbox (mp, h); + @<Scan to the matching |mp_stop_bounds_node| node and update |p| and |bblast(h)|@>; - } - break; -case mp_stop_bounds_code: - if ( mp->internal[mp_true_corners]<=0 ) mp_confusion(mp, "bbox2"); -@:this can't happen bbox2}{\quad bbox2@> - break; +} +break; +case mp_stop_bounds_node_type: +if (internal_value (mp_true_corners) <= 0) + mp_confusion (mp, "bbox2"); +@:this can't happen bbox2}{\quad bbox2@>; +break; -@ @<Scan to the matching |mp_stop_bounds_code| node and update |p| and...@>= -lev=1; -while ( lev!=0 ) { - if ( mp_link(p)==null ) mp_confusion(mp, "bbox2"); -@:this can't happen bbox2}{\quad bbox2@> - p=mp_link(p); - if ( mp_type(p)==mp_start_bounds_code ) incr(lev); - else if ( mp_type(p)==mp_stop_bounds_code ) decr(lev); +@ @<Scan to the matching |mp_stop_bounds_node| node and update |p| and...@>= +lev = 1; +while (lev != 0) { + if (mp_link (p) == NULL) + mp_confusion (mp, "bbox2"); +@:this can't happen bbox2}{\quad bbox2@>; + p = mp_link (p); + if (mp_type (p) == mp_start_bounds_node_type) + incr (lev); + else if (mp_type (p) == mp_stop_bounds_node_type) + decr (lev); } -bblast(h)=p +bblast (h) = p @ It saves a lot of grief here to be slightly conservative and not account for omitted parts of dashed lines. We also don't worry about the material omitted @@ -10612,78 +10877,109 @@ when using butt end caps. The basic computation is for round end caps and |box_ends| augments it for square end caps. @<Other cases for updating the bounding box...@>= -case mp_stroked_code: - mp_path_bbox(mp, mp_path_p(p)); - x0=mp_minx; y0=mp_miny; - x1=mp_maxx; y1=mp_maxy; - mp_pen_bbox(mp, mp_pen_p(p)); - mp_minx=mp_minx+x0; - mp_miny=mp_miny+y0; - mp_maxx=mp_maxx+x1; - mp_maxy=mp_maxy+y1; - mp_adjust_bbox(mp, h); - if ( (mp_left_type(mp_path_p(p))==mp_endpoint)&&(lcap_val(p)==2) ) - mp_box_ends(mp, mp_path_p(p), mp_pen_p(p), h); - break; +case mp_stroked_node_type: +mp_path_bbox (mp, mp_path_p ((mp_stroked_node) p)); +x0 = mp_minx; +y0 = mp_miny; +x1 = mp_maxx; +y1 = mp_maxy; +mp_pen_bbox (mp, mp_pen_p ((mp_stroked_node) p)); +mp_minx = mp_minx + x0; +mp_miny = mp_miny + y0; +mp_maxx = mp_maxx + x1; +mp_maxy = mp_maxy + y1; +mp_adjust_bbox (mp, h); +if ((mp_left_type (mp_path_p ((mp_stroked_node) p)) == mp_endpoint) + && (lcap_val (p) == 2)) + mp_box_ends (mp, mp_path_p ((mp_stroked_node) p), + mp_pen_p ((mp_stroked_node) p), h); +break; @ The height width and depth information stored in a text node determines a rectangle that needs to be transformed according to the transformation parameters stored in the text node. @<Other cases for updating the bounding box...@>= -case mp_text_code: - x1=mp_take_scaled(mp, txx_val(p),width_val(p)); - y0=mp_take_scaled(mp, txy_val(p),-depth_val(p)); - y1=mp_take_scaled(mp, txy_val(p),height_val(p)); - mp_minx=tx_val(p); - mp_maxx=mp_minx; - if ( y0<y1 ) { mp_minx=mp_minx+y0; mp_maxx=mp_maxx+y1; } - else { mp_minx=mp_minx+y1; mp_maxx=mp_maxx+y0; } - if ( x1<0 ) mp_minx=mp_minx+x1; else mp_maxx=mp_maxx+x1; - x1=mp_take_scaled(mp, tyx_val(p),width_val(p)); - y0=mp_take_scaled(mp, tyy_val(p),-depth_val(p)); - y1=mp_take_scaled(mp, tyy_val(p),height_val(p)); - mp_miny=ty_val(p); - mp_maxy=mp_miny; - if ( y0<y1 ) { mp_miny=mp_miny+y0; mp_maxy=mp_maxy+y1; } - else { mp_miny=mp_miny+y1; mp_maxy=mp_maxy+y0; } - if ( x1<0 ) mp_miny=mp_miny+x1; else mp_maxy=mp_maxy+x1; - mp_adjust_bbox(mp, h); - break; +case mp_text_node_type: +x1 = mp_take_scaled (mp, txx_val (p), width_val (p)); +y0 = mp_take_scaled (mp, txy_val (p), -depth_val (p)); +y1 = mp_take_scaled (mp, txy_val (p), height_val (p)); +mp_minx = tx_val (p); +mp_maxx = mp_minx; +if (y0 < y1) { + mp_minx = mp_minx + y0; + mp_maxx = mp_maxx + y1; +} else { + mp_minx = mp_minx + y1; + mp_maxx = mp_maxx + y0; +} +if (x1 < 0) + mp_minx = mp_minx + x1; +else + mp_maxx = mp_maxx + x1; +x1 = mp_take_scaled (mp, tyx_val (p), width_val (p)); +y0 = mp_take_scaled (mp, tyy_val (p), -depth_val (p)); +y1 = mp_take_scaled (mp, tyy_val (p), height_val (p)); +mp_miny = ty_val (p); +mp_maxy = mp_miny; +if (y0 < y1) { + mp_miny = mp_miny + y0; + mp_maxy = mp_maxy + y1; +} else { + mp_miny = mp_miny + y1; + mp_maxy = mp_maxy + y0; +} +if (x1 < 0) + mp_miny = mp_miny + x1; +else + mp_maxy = mp_maxy + x1; +mp_adjust_bbox (mp, h); +break; @ This case involves a recursive call that advances |bblast(h)| to the node of -type |mp_stop_clip_code| that matches |p|. +type |mp_stop_clip_node| that matches |p|. @<Other cases for updating the bounding box...@>= -case mp_start_clip_code: - mp_path_bbox(mp, mp_path_p(p)); - x0=mp_minx; y0=mp_miny; - x1=mp_maxx; y1=mp_maxy; - sminx=minx_val(h); sminy=miny_val(h); - smaxx=maxx_val(h); smaxy=maxy_val(h); - @<Reinitialize the bounding box in header |h| and call |set_bbox| recursively +case mp_start_clip_node_type: +mp_path_bbox (mp, mp_path_p ((mp_start_clip_node) p)); +x0 = mp_minx; +y0 = mp_miny; +x1 = mp_maxx; +y1 = mp_maxy; +sminx = minx_val (h); +sminy = miny_val (h); +smaxx = maxx_val (h); +smaxy = maxy_val (h); +@<Reinitialize the bounding box in header |h| and call |set_bbox| recursively starting at |mp_link(p)|@>; - @<Clip the bounding box in |h| to the rectangle given by |x0|, |x1|, +@<Clip the bounding box in |h| to the rectangle given by |x0|, |x1|, |y0|, |y1|@>; - mp_minx=sminx; mp_miny=sminy; - mp_maxx=smaxx; mp_maxy=smaxy; - mp_adjust_bbox(mp, h); - break; +mp_minx = sminx; +mp_miny = sminy; +mp_maxx = smaxx; +mp_maxy = smaxy; +mp_adjust_bbox (mp, h); +break; @ @<Reinitialize the bounding box in header |h| and call |set_bbox|...@>= -minx_val(h)=el_gordo; -miny_val(h)=el_gordo; -maxx_val(h)=-el_gordo; -maxy_val(h)=-el_gordo; -mp_set_bbox(mp, h,false) +minx_val (h) = EL_GORDO; +miny_val (h) = EL_GORDO; +maxx_val (h) = -EL_GORDO; +maxy_val (h) = -EL_GORDO; +mp_set_bbox (mp, h, false) + @ @<Clip the bounding box in |h| to the rectangle given by |x0|, |x1|,...@>= -if ( minx_val(h)<x0 ) minx_val(h)=x0; -if ( miny_val(h)<y0 ) miny_val(h)=y0; -if ( maxx_val(h)>x1 ) maxx_val(h)=x1; -if ( maxy_val(h)>y1 ) maxy_val(h)=y1 - -@* \[22] Finding an envelope. +if (minx_val (h) < x0) + minx_val (h) = x0; +if (miny_val (h) < y0) + miny_val (h) = y0; +if (maxx_val (h) > x1) + maxx_val (h) = x1; +if (maxy_val (h) > y1) + maxy_val (h) = y1 + +@* Finding an envelope. When \MP\ has a path and a polygonal pen, it needs to express the desired shape in terms of things \ps\ can understand. The present task is to compute a new path that describes the region to be filled. It is convenient to @@ -10709,66 +11005,72 @@ Since an envelope spec only determines relative changes in pen offsets, @d zero_off 16384 /* added to offset changes to make them positive */ @<Glob...@>= -integer spec_offset; /* number of pen edges between |h| and the initial offset */ +integer spec_offset; /* number of pen edges between |h| and the initial offset */ @ @c -static pointer mp_offset_prep (MP mp,pointer c, pointer h) { - halfword n; /* the number of vertices in the pen polygon */ - pointer c0,p,q,q0,r,w, ww; /* for list manipulation */ - integer k_needed; /* amount to be added to |mp_info(p)| when it is computed */ - pointer w0; /* a pointer to pen offset to use just before |p| */ - scaled dxin,dyin; /* the direction into knot |p| */ - integer turn_amt; /* change in pen offsets for the current cubic */ +static mp_knot mp_offset_prep (MP mp, mp_knot c, mp_knot h) { + halfword n; /* the number of vertices in the pen polygon */ + mp_knot c0, p, q, q0, r, w, ww; /* for list manipulation */ + integer k_needed; /* amount to be added to |mp_info(p)| when it is computed */ + mp_knot w0; /* a pointer to pen offset to use just before |p| */ + scaled dxin, dyin; /* the direction into knot |p| */ + integer turn_amt; /* change in pen offsets for the current cubic */ @<Other local variables for |offset_prep|@>; - dx0=0; dy0=0; + dx0 = 0; + dy0 = 0; @<Initialize the pen size~|n|@>; @<Initialize the incoming direction and pen offset at |c|@>; - p=c; c0=c; k_needed=0; - do { - q=mp_link(p); + p = c; + c0 = c; + k_needed = 0; + do { + q = mp_next_knot (p); @<Split the cubic between |p| and |q|, if necessary, into cubics associated with single offsets, after which |q| should point to the end of the final such cubic@>; NOT_FOUND: @<Advance |p| to node |q|, removing any ``dead'' cubics that might have been introduced by the splitting process@>; - } while (q!=c); - @<Fix the offset change in |mp_info(c)| and set |c| to the return value of + } while (q != c); + @<Fix the offset change in |mp_knot_info(c)| and set |c| to the return value of |offset_prep|@>; return c; } + @ We shall want to keep track of where certain knots on the cyclic path wind up in the envelope spec. It doesn't suffice just to keep pointers to knot nodes because some nodes are deleted while removing dead cubics. Thus |offset_prep| updates the following pointers @<Glob...@>= -pointer spec_p1; -pointer spec_p2; /* pointers to distinguished knots */ +mp_knot spec_p1; +mp_knot spec_p2; /* pointers to distinguished knots */ @ @<Set init...@>= -mp->spec_p1=null; mp->spec_p2=null; +mp->spec_p1 = NULL; +mp->spec_p2 = NULL; @ @<Initialize the pen size~|n|@>= -n=0; p=h; -do { - incr(n); - p=mp_link(p); -} while (p!=h) +n = 0; +p = h; +do { + incr (n); + p = mp_next_knot (p); +} while (p != h) @ Since the true incoming direction isn't known yet, we just pick a direction consistent with the pen offset~|h|. If this is wrong, it can be corrected later. @<Initialize the incoming direction and pen offset at |c|@>= -dxin=mp_x_coord(mp_link(h))-mp_x_coord(knil(h)); -dyin=mp_y_coord(mp_link(h))-mp_y_coord(knil(h)); -if ( (dxin==0)&&(dyin==0) ) { - dxin=mp_y_coord(knil(h))-mp_y_coord(h); - dyin=mp_x_coord(h)-mp_x_coord(knil(h)); +dxin = mp_x_coord (mp_next_knot (h)) - mp_x_coord (mp_prev_knot (h)); +dyin = mp_y_coord (mp_next_knot (h)) - mp_y_coord (mp_prev_knot (h)); +if ((dxin == 0) && (dyin == 0)) { +dxin = mp_y_coord (mp_prev_knot (h)) - mp_y_coord (h); +dyin = mp_x_coord (h) - mp_x_coord (mp_prev_knot (h)); } -w0=h +w0 = h @ We must be careful not to remove the only cubic in a cycle. @@ -10781,76 +11083,94 @@ the testcase reported by Bogus\l{}aw Jackowski in tracker id 267, case 52c on Sarovar.) @<Advance |p| to node |q|, removing any ``dead'' cubics...@>= -q0=q; -do { - r=mp_link(p); - if ( mp_x_coord(p)==mp_right_x(p) && mp_y_coord(p)==mp_right_y(p) && - mp_x_coord(p)==mp_left_x(r) && mp_y_coord(p)==mp_left_y(r) && - mp_x_coord(p)==mp_x_coord(r) && mp_y_coord(p)==mp_y_coord(r) && - r!=p ) { - @<Remove the cubic following |p| and update the data structures +q0 = q; +do { + r = mp_next_knot (p); + if (mp_x_coord (p) == mp_right_x (p) && mp_y_coord (p) == mp_right_y (p) && + mp_x_coord (p) == mp_left_x (r) && mp_y_coord (p) == mp_left_y (r) && + mp_x_coord (p) == mp_x_coord (r) && mp_y_coord (p) == mp_y_coord (r) && + r != p) { + @<Remove the cubic following |p| and update the data structures to merge |r| into |p|@>; } - p=r; -} while (p!=q); + p = r; +} while (p != q); /* Check if we removed too much */ -if ((q!=q0)&&(q!=c||c==c0)) - q = mp_link(q) +if ((q != q0) && (q != c || c == c0)) + q = mp_next_knot (q) + @ @<Remove the cubic following |p| and update the data structures...@>= -{ k_needed=mp_info(p)-zero_off; - if ( r==q ) { - q=p; - } else { - mp_info(p)=k_needed+mp_info(r); - k_needed=0; - }; - if ( r==c ) { mp_info(p)=mp_info(c); c=p; }; - if ( r==mp->spec_p1 ) mp->spec_p1=p; - if ( r==mp->spec_p2 ) mp->spec_p2=p; - r=p; mp_remove_cubic(mp, p); +{ + k_needed = mp_knot_info (p) - zero_off; + if (r == q) { + q = p; + } else { + mp_knot_info (p) = k_needed + mp_knot_info (r); + k_needed = 0; + } + if (r == c) { + mp_knot_info (p) = mp_knot_info (c); + c = p; + } + if (r == mp->spec_p1) + mp->spec_p1 = p; + if (r == mp->spec_p2) + mp->spec_p2 = p; + r = p; + mp_remove_cubic (mp, p); } + @ Not setting the |info| field of the newly created knot allows the splitting routine to work for paths. @<Declarations@>= -static void mp_split_cubic (MP mp,pointer p, fraction t) ; +static void mp_split_cubic (MP mp, mp_knot p, fraction t); @ @c -void mp_split_cubic (MP mp,pointer p, fraction t) { /* splits the cubic after |p| */ - scaled v; /* an intermediate value */ - pointer q,r; /* for list manipulation */ - q=mp_link(p); r=mp_get_node(mp, knot_node_size); mp_link(p)=r; mp_link(r)=q; - mp_originator(r)=mp_program_code; - mp_left_type(r)=mp_explicit; mp_right_type(r)=mp_explicit; - v=t_of_the_way(mp_right_x(p),mp_left_x(q)); - mp_right_x(p)=t_of_the_way(mp_x_coord(p),mp_right_x(p)); - mp_left_x(q)=t_of_the_way(mp_left_x(q),mp_x_coord(q)); - mp_left_x(r)=t_of_the_way(mp_right_x(p),v); - mp_right_x(r)=t_of_the_way(v,mp_left_x(q)); - mp_x_coord(r)=t_of_the_way(mp_left_x(r),mp_right_x(r)); - v=t_of_the_way(mp_right_y(p),mp_left_y(q)); - mp_right_y(p)=t_of_the_way(mp_y_coord(p),mp_right_y(p)); - mp_left_y(q)=t_of_the_way(mp_left_y(q),mp_y_coord(q)); - mp_left_y(r)=t_of_the_way(mp_right_y(p),v); - mp_right_y(r)=t_of_the_way(v,mp_left_y(q)); - mp_y_coord(r)=t_of_the_way(mp_left_y(r),mp_right_y(r)); -} - -@ This does not set |mp_info(p)| or |mp_right_type(p)|. +void mp_split_cubic (MP mp, mp_knot p, fraction t) { /* splits the cubic after |p| */ + scaled v; /* an intermediate value */ + mp_knot q, r; /* for list manipulation */ + q = mp_next_knot (p); + r = mp_new_knot (mp); + mp_next_knot (p) = r; + mp_next_knot (r) = q; + mp_originator (r) = mp_program_code; + mp_left_type (r) = mp_explicit; + mp_right_type (r) = mp_explicit; + v = t_of_the_way (mp_right_x (p), mp_left_x (q)); + mp_right_x (p) = t_of_the_way (mp_x_coord (p), mp_right_x (p)); + mp_left_x (q) = t_of_the_way (mp_left_x (q), mp_x_coord (q)); + mp_left_x (r) = t_of_the_way (mp_right_x (p), v); + mp_right_x (r) = t_of_the_way (v, mp_left_x (q)); + mp_x_coord (r) = t_of_the_way (mp_left_x (r), mp_right_x (r)); + v = t_of_the_way (mp_right_y (p), mp_left_y (q)); + mp_right_y (p) = t_of_the_way (mp_y_coord (p), mp_right_y (p)); + mp_left_y (q) = t_of_the_way (mp_left_y (q), mp_y_coord (q)); + mp_left_y (r) = t_of_the_way (mp_right_y (p), v); + mp_right_y (r) = t_of_the_way (v, mp_left_y (q)); + mp_y_coord (r) = t_of_the_way (mp_left_y (r), mp_right_y (r)); +} + + +@ This does not set |mp_knot_info(p)| or |mp_right_type(p)|. @<Declarations@>= -static void mp_remove_cubic (MP mp,pointer p) ; +static void mp_remove_cubic (MP mp, mp_knot p); @ @c -void mp_remove_cubic (MP mp,pointer p) { /* removes the dead cubic following~|p| */ - pointer q; /* the node that disappears */ - q=mp_link(p); mp_link(p)=mp_link(q); - mp_right_x(p)=mp_right_x(q); mp_right_y(p)=mp_right_y(q); - mp_free_node(mp, q,knot_node_size); +void mp_remove_cubic (MP mp, mp_knot p) { /* removes the dead cubic following~|p| */ + mp_knot q; /* the node that disappears */ + (void) mp; + q = mp_next_knot (p); + mp_next_knot (p) = mp_next_knot (q); + mp_right_x (p) = mp_right_x (q); + mp_right_y (p) = mp_right_y (q); + mp_xfree (q); } + @ Let $d\prec d'$ mean that the counter-clockwise angle from $d$ to~$d'$ is strictly between zero and $180^\circ$. Then we can define $d\preceq d'$ to mean that the angle could be zero or $180^\circ$. If $w_k=(u_k,v_k)$ is the @@ -10865,30 +11185,39 @@ We may have to split a cubic into many pieces before each piece corresponds to a unique offset. @<Split the cubic between |p| and |q|, if necessary, into cubics...@>= -mp_info(p)=zero_off+k_needed; -k_needed=0; +mp_knot_info (p) = zero_off + k_needed; +k_needed = 0; @<Prepare for derivative computations; |goto not_found| if the current cubic is dead@>; @<Find the initial direction |(dx,dy)|@>; -@<Update |mp_info(p)| and find the offset $w_k$ such that +@<Update |mp_knot_info(p)| and find the offset $w_k$ such that $d_{k-1}\preceq(\\{dx},\\{dy})\prec d_k$; also advance |w0| for the direction change at |p|@>; @<Find the final direction |(dxin,dyin)|@>; @<Decide on the net change in pen offsets and set |turn_amt|@>; @<Complete the offset splitting process@>; -w0=mp_pen_walk(mp, w0,turn_amt) +w0 = mp_pen_walk (mp, w0, turn_amt) + @ @<Declarations@>= -static pointer mp_pen_walk (MP mp,pointer w, integer k) ; +static mp_knot mp_pen_walk (MP mp, mp_knot w, integer k); @ @c -pointer mp_pen_walk (MP mp,pointer w, integer k) { +mp_knot mp_pen_walk (MP mp, mp_knot w, integer k) { /* walk |k| steps around a pen from |w| */ - while ( k>0 ) { w=mp_link(w); decr(k); }; - while ( k<0 ) { w=knil(w); incr(k); }; + (void) mp; + while (k > 0) { + w = mp_next_knot (w); + decr (k); + } + while (k < 0) { + w = mp_prev_knot (w); + incr (k); + } return w; } + @ The direction of a cubic $B(z_0,z_1,z_2,z_3;t)=\bigl(x(t),y(t)\bigr)$ can be calculated from the quadratic polynomials ${1\over3}x'(t)=B(x_1-x_0,x_2-x_1,x_3-x_2;t)$ and @@ -10903,34 +11232,46 @@ $X_1=2^l(x_2-x_1)$, and $X_2=2^l(x_3-x_2)$; similarly |y0|, |y1|, and~|y2| represent $Y_0=2^l(y_1-y_0)$, $Y_1=2^l(y_2-y_1)$, and $Y_2=2^l(y_3-y_2)$. @<Other local variables for |offset_prep|@>= -integer x0,x1,x2,y0,y1,y2; /* representatives of derivatives */ -integer t0,t1,t2; /* coefficients of polynomial for slope testing */ -integer du,dv,dx,dy; /* for directions of the pen and the curve */ -integer dx0,dy0; /* initial direction for the first cubic in the curve */ -integer max_coef; /* used while scaling */ -integer x0a,x1a,x2a,y0a,y1a,y2a; /* intermediate values */ -fraction t; /* where the derivative passes through zero */ -fraction s; /* a temporary value */ +integer x0, x1, x2, y0, y1, y2; /* representatives of derivatives */ +integer t0, t1, t2; /* coefficients of polynomial for slope testing */ +integer du, dv, dx, dy; /* for directions of the pen and the curve */ +integer dx0, dy0; /* initial direction for the first cubic in the curve */ +integer max_coef; /* used while scaling */ +integer x0a, x1a, x2a, y0a, y1a, y2a; /* intermediate values */ +fraction t; /* where the derivative passes through zero */ +fraction s; /* a temporary value */ @ @<Prepare for derivative computations...@>= -x0=mp_right_x(p)-mp_x_coord(p); -x2=mp_x_coord(q)-mp_left_x(q); -x1=mp_left_x(q)-mp_right_x(p); -y0=mp_right_y(p)-mp_y_coord(p); y2=mp_y_coord(q)-mp_left_y(q); -y1=mp_left_y(q)-mp_right_y(p); -max_coef=abs(x0); -if ( abs(x1)>max_coef ) max_coef=abs(x1); -if ( abs(x2)>max_coef ) max_coef=abs(x2); -if ( abs(y0)>max_coef ) max_coef=abs(y0); -if ( abs(y1)>max_coef ) max_coef=abs(y1); -if ( abs(y2)>max_coef ) max_coef=abs(y2); -if ( max_coef==0 ) goto NOT_FOUND; -while ( max_coef<fraction_half ) { - double(max_coef); - double(x0); double(x1); double(x2); - double(y0); double(y1); double(y2); +x0 = mp_right_x (p) - mp_x_coord (p); +x2 = mp_x_coord (q) - mp_left_x (q); +x1 = mp_left_x (q) - mp_right_x (p); +y0 = mp_right_y (p) - mp_y_coord (p); +y2 = mp_y_coord (q) - mp_left_y (q); +y1 = mp_left_y (q) - mp_right_y (p); +max_coef = abs (x0); +if (abs (x1) > max_coef) + max_coef = abs (x1); +if (abs (x2) > max_coef) + max_coef = abs (x2); +if (abs (y0) > max_coef) + max_coef = abs (y0); +if (abs (y1) > max_coef) + max_coef = abs (y1); +if (abs (y2) > max_coef) + max_coef = abs (y2); +if (max_coef == 0) + goto NOT_FOUND; +while (max_coef < fraction_half) { +double (max_coef); +double (x0); +double (x1); +double (x2); +double (y0); +double (y1); +double (y2); } + @ Let us first solve a special case of the problem: Suppose we know an index~$k$ such that either (i)~$d(t)\succeq d_{k-1}$ for all~$t$ and $d(0)\prec d_k$, or (ii)~$d(t)\preceq d_k$ for all~$t$ and @@ -10956,88 +11297,116 @@ be set properly. The |turn_amt| parameter gives the absolute value of the overall net change in pen offsets. @<Declarations@>= -static void mp_fin_offset_prep (MP mp,pointer p, pointer w, integer - x0,integer x1, integer x2, integer y0, integer y1, integer y2, - integer rise, integer turn_amt) ; +static void mp_fin_offset_prep (MP mp, mp_knot p, mp_knot w, integer + x0, integer x1, integer x2, integer y0, + integer y1, integer y2, integer rise, + integer turn_amt); @ @c -void mp_fin_offset_prep (MP mp,pointer p, pointer w, integer - x0,integer x1, integer x2, integer y0, integer y1, integer y2, - integer rise, integer turn_amt) { - pointer ww; /* for list manipulation */ - scaled du,dv; /* for slope calculation */ - integer t0,t1,t2; /* test coefficients */ - fraction t; /* place where the derivative passes a critical slope */ - fraction s; /* slope or reciprocal slope */ - integer v; /* intermediate value for updating |x0..y2| */ - pointer q; /* original |mp_link(p)| */ - q=mp_link(p); - while (1) { - if ( rise>0 ) ww=mp_link(w); /* a pointer to $w\k$ */ - else ww=knil(w); /* a pointer to $w_{k-1}$ */ +void mp_fin_offset_prep (MP mp, mp_knot p, mp_knot w, integer + x0, integer x1, integer x2, integer y0, integer y1, + integer y2, integer rise, integer turn_amt) { + mp_knot ww; /* for list manipulation */ + scaled du, dv; /* for slope calculation */ + integer t0, t1, t2; /* test coefficients */ + fraction t; /* place where the derivative passes a critical slope */ + fraction s; /* slope or reciprocal slope */ + integer v; /* intermediate value for updating |x0..y2| */ + mp_knot q; /* original |mp_next_knot(p)| */ + q = mp_next_knot (p); + while (1) { + if (rise > 0) + ww = mp_next_knot (w); /* a pointer to $w\k$ */ + else + ww = mp_prev_knot (w); /* a pointer to $w_{k-1}$ */ @<Compute test coefficients |(t0,t1,t2)| for $d(t)$ versus $d_k$ or $d_{k-1}$@>; - t=mp_crossing_point(mp, t0,t1,t2); - if ( t>=fraction_one ) { - if ( turn_amt>0 ) t=fraction_one; else return; + t = mp_crossing_point (mp, t0, t1, t2); + if (t >= fraction_one) { + if (turn_amt > 0) + t = fraction_one; + else + return; } @<Split the cubic at $t$, and split off another cubic if the derivative crosses back@>; - w=ww; + w = ww; } } + @ We want $B(\\{t0},\\{t1},\\{t2};t)$ to be the dot product of $d(t)$ with a $-90^\circ$ rotation of the vector from |w| to |ww|. This makes the resulting function cross from positive to negative when $d_{k-1}\preceq d(t)\preceq d_k$ begins to fail. @<Compute test coefficients |(t0,t1,t2)| for $d(t)$ versus...@>= -du=mp_x_coord(ww)-mp_x_coord(w); dv=mp_y_coord(ww)-mp_y_coord(w); -if ( abs(du)>=abs(dv) ) { - s=mp_make_fraction(mp, dv,du); - t0=mp_take_fraction(mp, x0,s)-y0; - t1=mp_take_fraction(mp, x1,s)-y1; - t2=mp_take_fraction(mp, x2,s)-y2; - if ( du<0 ) { negate(t0); negate(t1); negate(t2); } -} else { - s=mp_make_fraction(mp, du,dv); - t0=x0-mp_take_fraction(mp, y0,s); - t1=x1-mp_take_fraction(mp, y1,s); - t2=x2-mp_take_fraction(mp, y2,s); - if ( dv<0 ) { negate(t0); negate(t1); negate(t2); } -} -if ( t0<0 ) t0=0 /* should be positive without rounding error */ +du = mp_x_coord (ww) - mp_x_coord (w); +dv = mp_y_coord (ww) - mp_y_coord (w); +if (abs (du) >= abs (dv)) { +s = mp_make_fraction (mp, dv, du); +t0 = mp_take_fraction (mp, x0, s) - y0; +t1 = mp_take_fraction (mp, x1, s) - y1; +t2 = mp_take_fraction (mp, x2, s) - y2; +if (du < 0) { + negate (t0); + negate (t1); + negate (t2); +} +} else { + s = mp_make_fraction (mp, du, dv); + t0 = x0 - mp_take_fraction (mp, y0, s); + t1 = x1 - mp_take_fraction (mp, y1, s); + t2 = x2 - mp_take_fraction (mp, y2, s); + if (dv < 0) { + negate (t0); + negate (t1); + negate (t2); + } +} +if (t0 < 0) + t0 = 0 /* should be positive without rounding error */ + @ The curve has crossed $d_k$ or $d_{k-1}$; its initial segment satisfies $(*)$, and it might cross again and return towards $s_{k-1}$ or $s_k$, respectively, yielding another solution of $(*)$. @<Split the cubic at $t$, and split off another...@>= -{ -mp_split_cubic(mp, p,t); p=mp_link(p); mp_info(p)=zero_off+rise; -decr(turn_amt); -v=t_of_the_way(x0,x1); x1=t_of_the_way(x1,x2); -x0=t_of_the_way(v,x1); -v=t_of_the_way(y0,y1); y1=t_of_the_way(y1,y2); -y0=t_of_the_way(v,y1); -if ( turn_amt<0 ) { - t1=t_of_the_way(t1,t2); - if ( t1>0 ) t1=0; /* without rounding error, |t1| would be |<=0| */ - t=mp_crossing_point(mp, 0,-t1,-t2); - if ( t>fraction_one ) t=fraction_one; - incr(turn_amt); - if ( (t==fraction_one)&&(mp_link(p)!=q) ) { - mp_info(mp_link(p))=mp_info(mp_link(p))-rise; - } else { - mp_split_cubic(mp, p,t); mp_info(mp_link(p))=zero_off-rise; - v=t_of_the_way(x1,x2); x1=t_of_the_way(x0,x1); - x2=t_of_the_way(x1,v); - v=t_of_the_way(y1,y2); y1=t_of_the_way(y0,y1); - y2=t_of_the_way(y1,v); +{ + mp_split_cubic (mp, p, t); + p = mp_next_knot (p); + mp_knot_info (p) = zero_off + rise; + decr (turn_amt); + v = t_of_the_way (x0, x1); + x1 = t_of_the_way (x1, x2); + x0 = t_of_the_way (v, x1); + v = t_of_the_way (y0, y1); + y1 = t_of_the_way (y1, y2); + y0 = t_of_the_way (v, y1); + if (turn_amt < 0) { + t1 = t_of_the_way (t1, t2); + if (t1 > 0) + t1 = 0; /* without rounding error, |t1| would be |<=0| */ + t = mp_crossing_point (mp, 0, -t1, -t2); + if (t > fraction_one) + t = fraction_one; + incr (turn_amt); + if ((t == fraction_one) && (mp_next_knot (p) != q)) { + mp_knot_info (mp_next_knot (p)) = mp_knot_info (mp_next_knot (p)) - rise; + } else { + mp_split_cubic (mp, p, t); + mp_knot_info (mp_next_knot (p)) = zero_off - rise; + v = t_of_the_way (x1, x2); + x1 = t_of_the_way (x0, x1); + x2 = t_of_the_way (x1, v); + v = t_of_the_way (y1, y2); + y1 = t_of_the_way (y0, y1); + y2 = t_of_the_way (y1, v); + } } } -} + @ Now we must consider the general problem of |offset_prep|, when nothing is known about a given cubic. We start by finding its @@ -11049,21 +11418,30 @@ the true initial direction for the given cubic, even if it is almost degenerate. @<Find the initial direction |(dx,dy)|@>= -dx=x0; dy=y0; -if ( dx==0 && dy==0 ) { - dx=x1; dy=y1; - if ( dx==0 && dy==0 ) { - dx=x2; dy=y2; +dx = x0; +dy = y0; +if (dx == 0 && dy == 0) { + dx = x1; + dy = y1; + if (dx == 0 && dy == 0) { + dx = x2; + dy = y2; } } -if ( p==c ) { dx0=dx; dy0=dy; } +if (p == c) { + dx0 = dx; + dy0 = dy; +} @ @<Find the final direction |(dxin,dyin)|@>= -dxin=x2; dyin=y2; -if ( dxin==0 && dyin==0 ) { - dxin=x1; dyin=y1; - if ( dxin==0 && dyin==0 ) { - dxin=x0; dyin=y0; +dxin = x2; +dyin = y2; +if (dxin == 0 && dyin == 0) { + dxin = x1; + dyin = y1; + if (dxin == 0 && dyin == 0) { + dxin = x0; + dyin = y0; } } @@ -11074,11 +11452,12 @@ counter-clockwise in order to make \&{doublepath} envelopes come out @:double_path_}{\&{doublepath} primitive@> right.) This code depends on |w0| being the offset for |(dxin,dyin)|. -@<Update |mp_info(p)| and find the offset $w_k$ such that...@>= -turn_amt=mp_get_turn_amt(mp,w0,dx,dy,(mp_ab_vs_cd(mp, dy,dxin,dx,dyin)>=0)); -w=mp_pen_walk(mp, w0, turn_amt); -w0=w; -mp_info(p)=mp_info(p)+turn_amt +@<Update |mp_knot_info(p)| and find the offset $w_k$ such that...@>= +turn_amt = +mp_get_turn_amt (mp, w0, dx, dy, (mp_ab_vs_cd (mp, dy, dxin, dx, dyin) >= 0)); +w = mp_pen_walk (mp, w0, turn_amt); +w0 = w; +mp_knot_info (p) = mp_knot_info (p) + turn_amt @ Decide how many pen offsets to go away from |w| in order to find the offset for |(dx,dy)|, going counterclockwise if |ccw| is |true|. This assumes that @@ -11090,94 +11469,119 @@ to |(dx,dy)|. In this case, we must be careful to stop after crossing the first such edge in order to avoid an infinite loop. @<Declarations@>= -static integer mp_get_turn_amt (MP mp,pointer w, scaled dx, - scaled dy, boolean ccw); +static integer mp_get_turn_amt (MP mp, mp_knot w, scaled dx, + scaled dy, boolean ccw); @ @c -integer mp_get_turn_amt (MP mp,pointer w, scaled dx, - scaled dy, boolean ccw) { - pointer ww; /* a neighbor of knot~|w| */ - integer s; /* turn amount so far */ - integer t; /* |ab_vs_cd| result */ - s=0; - if ( ccw ) { - ww=mp_link(w); - do { - t=mp_ab_vs_cd(mp, dy,(mp_x_coord(ww)-mp_x_coord(w)), - dx,(mp_y_coord(ww)-mp_y_coord(w))); - if ( t<0 ) break; - incr(s); - w=ww; ww=mp_link(ww); - } while (t>0); - } else { - ww=knil(w); - while ( mp_ab_vs_cd(mp, dy,(mp_x_coord(w)-mp_x_coord(ww)), - dx,(mp_y_coord(w)-mp_y_coord(ww))) < 0) { - decr(s); - w=ww; ww=knil(ww); +integer mp_get_turn_amt (MP mp, mp_knot w, scaled dx, scaled dy, boolean ccw) { + mp_knot ww; /* a neighbor of knot~|w| */ + integer s; /* turn amount so far */ + integer t; /* |ab_vs_cd| result */ + s = 0; + (void) mp; + if (ccw) { + ww = mp_next_knot (w); + do { + t = mp_ab_vs_cd (mp, dy, (mp_x_coord (ww) - mp_x_coord (w)), + dx, (mp_y_coord (ww) - mp_y_coord (w))); + if (t < 0) + break; + incr (s); + w = ww; + ww = mp_next_knot (ww); + } while (t > 0); + } else { + ww = mp_prev_knot (w); + while (mp_ab_vs_cd (mp, dy, (mp_x_coord (w) - mp_x_coord (ww)), + dx, (mp_y_coord (w) - mp_y_coord (ww))) < 0) { + decr (s); + w = ww; + ww = mp_prev_knot (ww); } } return s; } + @ When we're all done, the final offset is |w0| and the final curve direction is |(dxin,dyin)|. With this knowledge of the incoming direction at |c|, we can correct |mp_info(c)| which was erroneously based on an incoming offset of~|h|. -@d fix_by(A) mp_info(c)=mp_info(c)+(A) +@d fix_by(A) mp_knot_info(c)=mp_knot_info(c)+(A) -@<Fix the offset change in |mp_info(c)| and set |c| to the return value of...@>= -mp->spec_offset=mp_info(c)-zero_off; -if ( mp_link(c)==c ) { - mp_info(c)=zero_off+n; -} else { - fix_by(k_needed); - while ( w0!=h ) { fix_by(1); w0=mp_link(w0); }; - while ( mp_info(c)<=zero_off-n ) fix_by(n); - while ( mp_info(c)>zero_off ) fix_by(-n); - if ( (mp_info(c)!=zero_off)&&(mp_ab_vs_cd(mp, dy0,dxin,dx0,dyin)>=0) ) fix_by(n); +@<Fix the offset change in |mp_knot_info(c)| and set |c| to the return value of...@>= +mp->spec_offset = mp_knot_info (c) - zero_off; +if (mp_next_knot (c) == c) { +mp_knot_info (c) = zero_off + n; +} else { + fix_by (k_needed); + while (w0 != h) { + fix_by (1); + w0 = mp_next_knot (w0); + } + while (mp_knot_info (c) <= zero_off - n) + fix_by (n); + while (mp_knot_info (c) > zero_off) + fix_by (-n); + if ((mp_knot_info (c) != zero_off) + && (mp_ab_vs_cd (mp, dy0, dxin, dx0, dyin) >= 0)) + fix_by (n); } + @ Finally we want to reduce the general problem to situations that |fin_offset_prep| can handle. We split the cubic into at most three parts with respect to $d_{k-1}$, and apply |fin_offset_prep| to each part. @<Complete the offset splitting process@>= -ww=knil(w); +ww = mp_prev_knot (w); @<Compute test coeff...@>; @<Find the first |t| where $d(t)$ crosses $d_{k-1}$ or set |t:=fraction_one+1|@>; -if ( t>fraction_one ) { - mp_fin_offset_prep(mp, p,w,x0,x1,x2,y0,y1,y2,1,turn_amt); +if (t > fraction_one) { + mp_fin_offset_prep (mp, p, w, x0, x1, x2, y0, y1, y2, 1, turn_amt); } else { - mp_split_cubic(mp, p,t); r=mp_link(p); - x1a=t_of_the_way(x0,x1); x1=t_of_the_way(x1,x2); - x2a=t_of_the_way(x1a,x1); - y1a=t_of_the_way(y0,y1); y1=t_of_the_way(y1,y2); - y2a=t_of_the_way(y1a,y1); - mp_fin_offset_prep(mp, p,w,x0,x1a,x2a,y0,y1a,y2a,1,0); x0=x2a; y0=y2a; - mp_info(r)=zero_off-1; - if ( turn_amt>=0 ) { - t1=t_of_the_way(t1,t2); - if ( t1>0 ) t1=0; - t=mp_crossing_point(mp, 0,-t1,-t2); - if ( t>fraction_one ) t=fraction_one; + mp_split_cubic (mp, p, t); + r = mp_next_knot (p); + x1a = t_of_the_way (x0, x1); + x1 = t_of_the_way (x1, x2); + x2a = t_of_the_way (x1a, x1); + y1a = t_of_the_way (y0, y1); + y1 = t_of_the_way (y1, y2); + y2a = t_of_the_way (y1a, y1); + mp_fin_offset_prep (mp, p, w, x0, x1a, x2a, y0, y1a, y2a, 1, 0); + x0 = x2a; + y0 = y2a; + mp_knot_info (r) = zero_off - 1; + if (turn_amt >= 0) { + t1 = t_of_the_way (t1, t2); + if (t1 > 0) + t1 = 0; + t = mp_crossing_point (mp, 0, -t1, -t2); + if (t > fraction_one) + t = fraction_one; @<Split off another rising cubic for |fin_offset_prep|@>; - mp_fin_offset_prep(mp, r,ww,x0,x1,x2,y0,y1,y2,-1,0); + mp_fin_offset_prep (mp, r, ww, x0, x1, x2, y0, y1, y2, -1, 0); } else { - mp_fin_offset_prep(mp, r,ww,x0,x1,x2,y0,y1,y2,-1,(-1-turn_amt)); + mp_fin_offset_prep (mp, r, ww, x0, x1, x2, y0, y1, y2, -1, (-1 - turn_amt)); } } + @ @<Split off another rising cubic for |fin_offset_prep|@>= -mp_split_cubic(mp, r,t); mp_info(mp_link(r))=zero_off+1; -x1a=t_of_the_way(x1,x2); x1=t_of_the_way(x0,x1); -x0a=t_of_the_way(x1,x1a); -y1a=t_of_the_way(y1,y2); y1=t_of_the_way(y0,y1); -y0a=t_of_the_way(y1,y1a); -mp_fin_offset_prep(mp, mp_link(r),w,x0a,x1a,x2,y0a,y1a,y2,1,turn_amt); -x2=x0a; y2=y0a +mp_split_cubic (mp, r, t); +mp_knot_info (mp_next_knot (r)) = zero_off + 1; +x1a = t_of_the_way (x1, x2); +x1 = t_of_the_way (x0, x1); +x0a = t_of_the_way (x1, x1a); +y1a = t_of_the_way (y1, y2); +y1 = t_of_the_way (y0, y1); +y0a = t_of_the_way (y1, y1a); +mp_fin_offset_prep (mp, mp_next_knot (r), w, x0a, x1a, x2, y0a, y1a, y2, 1, + turn_amt); +x2 = x0a; +y2 = y0a @ At this point, the direction of the incoming pen edge is |(-du,-dv)|. When the component of $d(t)$ perpendicular to |(-du,-dv)| crosses zero, we @@ -11189,27 +11593,28 @@ answer. If |t2<0|, there is one crossing and it is antiparallel only if crossing and the first crossing cannot be antiparallel. @<Find the first |t| where $d(t)$ crosses $d_{k-1}$ or set...@>= -t=mp_crossing_point(mp, t0,t1,t2); -if ( turn_amt>=0 ) { - if ( t2<0 ) { - t=fraction_one+1; - } else { - u0=t_of_the_way(x0,x1); - u1=t_of_the_way(x1,x2); - ss=mp_take_fraction(mp, -du,t_of_the_way(u0,u1)); - v0=t_of_the_way(y0,y1); - v1=t_of_the_way(y1,y2); - ss=ss+mp_take_fraction(mp, -dv,t_of_the_way(v0,v1)); - if ( ss<0 ) t=fraction_one+1; - } -} else if ( t>fraction_one ) { - t=fraction_one; +t = mp_crossing_point (mp, t0, t1, t2); +if (turn_amt >= 0) { + if (t2 < 0) { + t = fraction_one + 1; + } else { + u0 = t_of_the_way (x0, x1); + u1 = t_of_the_way (x1, x2); + ss = mp_take_fraction (mp, -du, t_of_the_way (u0, u1)); + v0 = t_of_the_way (y0, y1); + v1 = t_of_the_way (y1, y2); + ss = ss + mp_take_fraction (mp, -dv, t_of_the_way (v0, v1)); + if (ss < 0) + t = fraction_one + 1; + } +} else if (t > fraction_one) { + t = fraction_one; } @ @<Other local variables for |offset_prep|@>= -integer u0,u1,v0,v1; /* intermediate values for $d(t)$ calculation */ +integer u0, u1, v0, v1; /* intermediate values for $d(t)$ calculation */ integer ss = 0; /* the part of the dot product computed so far */ -int d_sign; /* sign of overall change in direction for this cubic */ +int d_sign; /* sign of overall change in direction for this cubic */ @ If the cubic almost has a cusp, it is a numerically ill-conditioned problem to decide which way it loops around but that's OK as long we're @@ -11217,21 +11622,28 @@ consistent. To make \&{doublepath} envelopes work properly, reversing the path should always change the sign of |turn_amt|. @<Decide on the net change in pen offsets and set |turn_amt|@>= -d_sign=mp_ab_vs_cd(mp, dx,dyin, dxin,dy); -if ( d_sign==0 ) { +d_sign = mp_ab_vs_cd (mp, dx, dyin, dxin, dy); +if (d_sign == 0) { @<Check rotation direction based on node position@> } -if ( d_sign==0 ) { - if ( dx==0 ) { - if ( dy>0 ) d_sign=1; else d_sign=-1; +if (d_sign == 0) { + if (dx == 0) { + if (dy > 0) + d_sign = 1; + else + d_sign = -1; } else { - if ( dx>0 ) d_sign=1; else d_sign=-1; + if (dx > 0) + d_sign = 1; + else + d_sign = -1; } } @<Make |ss| negative if and only if the total change in direction is more than $180^\circ$@>; -turn_amt=mp_get_turn_amt(mp, w, dxin, dyin, (d_sign>0)); -if ( ss<0 ) turn_amt=turn_amt-d_sign*n +turn_amt = mp_get_turn_amt (mp, w, dxin, dyin, (d_sign > 0)); +if (ss < 0) + turn_amt = turn_amt - d_sign * n @ We check rotation direction by looking at the vector connecting the current node with the next. If its angle with incoming and outgoing tangents has the @@ -11239,10 +11651,10 @@ same sign, we pick this as |d_sign|, since it means we have a flex, not a cusp. Otherwise we proceed to the cusp code. @<Check rotation direction based on node position@>= -u0=mp_x_coord(q)-mp_x_coord(p); -u1=mp_y_coord(q)-mp_y_coord(p); -d_sign = half(mp_ab_vs_cd(mp, dx, u1, u0, dy)+ - mp_ab_vs_cd(mp, u0, dyin, dxin, u1)); +u0 = mp_x_coord (q) - mp_x_coord (p); +u1 = mp_y_coord (q) - mp_y_coord (p); +d_sign = half (mp_ab_vs_cd (mp, dx, u1, u0, dy) + + mp_ab_vs_cd (mp, u0, dyin, dxin, u1)); @ In order to be invariant under path reversal, the result of this computation should not change when |x0|, |y0|, $\ldots$ are all negated and |(x0,y0)| is @@ -11251,73 +11663,84 @@ then swapped with |(x2,y2)|. We make use of the identities |t_of_the_way(-a,-b)=-(t_of_the_way(a,b))|. @<Make |ss| negative if and only if the total change in direction is...@>= -t0=half(mp_take_fraction(mp, x0,y2))-half(mp_take_fraction(mp, x2,y0)); -t1=half(mp_take_fraction(mp, x1,(y0+y2)))-half(mp_take_fraction(mp, y1,(x0+x2))); -if ( t0==0 ) t0=d_sign; /* path reversal always negates |d_sign| */ -if ( t0>0 ) { - t=mp_crossing_point(mp, t0,t1,-t0); - u0=t_of_the_way(x0,x1); - u1=t_of_the_way(x1,x2); - v0=t_of_the_way(y0,y1); - v1=t_of_the_way(y1,y2); -} else { - t=mp_crossing_point(mp, -t0,t1,t0); - u0=t_of_the_way(x2,x1); - u1=t_of_the_way(x1,x0); - v0=t_of_the_way(y2,y1); - v1=t_of_the_way(y1,y0); -} -ss=mp_take_fraction(mp, (x0+x2),t_of_the_way(u0,u1))+ - mp_take_fraction(mp, (y0+y2),t_of_the_way(v0,v1)) +t0 = +half (mp_take_fraction (mp, x0, y2)) - half (mp_take_fraction (mp, x2, y0)); +t1 = +half (mp_take_fraction (mp, x1, (y0 + y2))) - +half (mp_take_fraction (mp, y1, (x0 + x2))); +if (t0 == 0) + t0 = d_sign; /* path reversal always negates |d_sign| */ +if (t0 > 0) { +t = mp_crossing_point (mp, t0, t1, -t0); +u0 = t_of_the_way (x0, x1); +u1 = t_of_the_way (x1, x2); +v0 = t_of_the_way (y0, y1); +v1 = t_of_the_way (y1, y2); +} else { + t = mp_crossing_point (mp, -t0, t1, t0); + u0 = t_of_the_way (x2, x1); + u1 = t_of_the_way (x1, x0); + v0 = t_of_the_way (y2, y1); + v1 = t_of_the_way (y1, y0); +} +ss = mp_take_fraction (mp, (x0 + x2), t_of_the_way (u0, u1)) + +mp_take_fraction (mp, (y0 + y2), t_of_the_way (v0, v1)) + @ Here's a routine that prints an envelope spec in symbolic form. It assumes that the |cur_pen| has not been walked around to the first offset. -@c -static void mp_print_spec (MP mp,pointer cur_spec, pointer cur_pen, const char *s) { - pointer p,q; /* list traversal */ - pointer w; /* the current pen offset */ - mp_print_diagnostic(mp, "Envelope spec",s,true); - p=cur_spec; w=mp_pen_walk(mp, cur_pen,mp->spec_offset); - mp_print_ln(mp); - mp_print_two(mp, mp_x_coord(cur_spec),mp_y_coord(cur_spec)); - mp_print(mp, " % beginning with offset "); - mp_print_two(mp, mp_x_coord(w),mp_y_coord(w)); - do { - while (1) { - q=mp_link(p); +@c +static void mp_print_spec (MP mp, mp_knot cur_spec, mp_knot cur_pen, + const char *s) { + mp_knot p, q; /* list traversal */ + mp_knot w; /* the current pen offset */ + mp_print_diagnostic (mp, "Envelope spec", s, true); + p = cur_spec; + w = mp_pen_walk (mp, cur_pen, mp->spec_offset); + mp_print_ln (mp); + mp_print_two (mp, mp_x_coord (cur_spec), mp_y_coord (cur_spec)); + mp_print (mp, " % beginning with offset "); + mp_print_two (mp, mp_x_coord (w), mp_y_coord (w)); + do { + while (1) { + q = mp_next_knot (p); @<Print the cubic between |p| and |q|@>; - p=q; - if ((p==cur_spec) || (mp_info(p)!=zero_off)) - break; + p = q; + if ((p == cur_spec) || (mp_knot_info (p) != zero_off)) + break; } - if ( mp_info(p)!=zero_off ) { - @<Update |w| as indicated by |mp_info(p)| and print an explanation@>; + if (mp_knot_info (p) != zero_off) { + @<Update |w| as indicated by |mp_knot_info(p)| and print an explanation@>; } - } while (p!=cur_spec); - mp_print_nl(mp, " & cycle"); - mp_end_diagnostic(mp, true); + } while (p != cur_spec); + mp_print_nl (mp, " & cycle"); + mp_end_diagnostic (mp, true); } -@ @<Update |w| as indicated by |mp_info(p)| and print an explanation@>= -{ - w=mp_pen_walk(mp, w, (mp_info(p)-zero_off)); - mp_print(mp, " % "); - if ( mp_info(p)>zero_off ) mp_print(mp, "counter"); - mp_print(mp, "clockwise to offset "); - mp_print_two(mp, mp_x_coord(w),mp_y_coord(w)); + +@ @<Update |w| as indicated by |mp_knot_info(p)| and print an explanation@>= +{ + w = mp_pen_walk (mp, w, (mp_knot_info (p) - zero_off)); + mp_print (mp, " % "); + if (mp_knot_info (p) > zero_off) + mp_print (mp, "counter"); + mp_print (mp, "clockwise to offset "); + mp_print_two (mp, mp_x_coord (w), mp_y_coord (w)); } + @ @<Print the cubic between |p| and |q|@>= -{ - mp_print_nl(mp, " ..controls "); - mp_print_two(mp, mp_right_x(p),mp_right_y(p)); - mp_print(mp, " and "); - mp_print_two(mp, mp_left_x(q),mp_left_y(q)); - mp_print_nl(mp, " .."); - mp_print_two(mp, mp_x_coord(q),mp_y_coord(q)); +{ + mp_print_nl (mp, " ..controls "); + mp_print_two (mp, mp_right_x (p), mp_right_y (p)); + mp_print (mp, " and "); + mp_print_two (mp, mp_left_x (q), mp_left_y (q)); + mp_print_nl (mp, " .."); + mp_print_two (mp, mp_x_coord (q), mp_y_coord (q)); } + @ Once we have an envelope spec, the remaining task to construct the actual envelope by offsetting each cubic as determined by the |info| fields in the knots. First we use |offset_prep| to convert the |c| into an envelope @@ -11337,49 +11760,58 @@ has retrograde lines. In such cases we use a simple connect-the-endpoints approach that is achieved by setting |join_type:=2|. @c -static pointer mp_make_envelope (MP mp,pointer c, pointer h, quarterword ljoin, - quarterword lcap, scaled miterlim) { - pointer p,q,r,q0; /* for manipulating the path */ - int join_type=0; /* codes |0..3| for mitered, round, beveled, or square */ - pointer w,w0; /* the pen knot for the current offset */ - scaled qx,qy; /* unshifted coordinates of |q| */ - halfword k,k0; /* controls pen edge insertion */ +static mp_knot mp_make_envelope (MP mp, mp_knot c, mp_knot h, quarterword ljoin, + quarterword lcap, scaled miterlim) { + mp_knot p, q, r, q0; /* for manipulating the path */ + mp_knot w, w0; /* the pen knot for the current offset */ + scaled qx, qy; /* unshifted coordinates of |q| */ + halfword k, k0; /* controls pen edge insertion */ + int join_type = 0; /* codes |0..3| for mitered, round, beveled, or square */ @<Other local variables for |make_envelope|@>; - dxin=0; dyin=0; dxout=0; dyout=0; - mp->spec_p1=null; mp->spec_p2=null; + dxin = 0; + dyin = 0; + dxout = 0; + dyout = 0; + mp->spec_p1 = NULL; + mp->spec_p2 = NULL; @<If endpoint, double the path |c|, and set |spec_p1| and |spec_p2|@>; @<Use |offset_prep| to compute the envelope spec then walk |h| around to the initial offset@>; - w=h; - p=c; - do { - q=mp_link(p); q0=q; - qx=mp_x_coord(q); qy=mp_y_coord(q); - k=mp_info(q); - k0=k; w0=w; - if ( k!=zero_off ) { + w = h; + p = c; + do { + q = mp_next_knot (p); + q0 = q; + qx = mp_x_coord (q); + qy = mp_y_coord (q); + k = mp_knot_info (q); + k0 = k; + w0 = w; + if (k != zero_off) { @<Set |join_type| to indicate how to handle offset changes at~|q|@>; } @<Add offset |w| to the cubic from |p| to |q|@>; - while ( k!=zero_off ) { + while (k != zero_off) { @<Step |w| and move |k| one step closer to |zero_off|@>; - if ( (join_type==1)||(k==zero_off) ) - q=mp_insert_knot(mp, q,qx+mp_x_coord(w),qy+mp_y_coord(w)); + if ((join_type == 1) || (k == zero_off)) + q = mp_insert_knot (mp, q, qx + mp_x_coord (w), qy + mp_y_coord (w)); }; - if ( q!=mp_link(p) ) { + if (q != mp_next_knot (p)) { @<Set |p=mp_link(p)| and add knots between |p| and |q| as required by |join_type|@>; } - p=q; - } while (q0!=c); + p = q; + } while (q0 != c); return c; } + @ @<Use |offset_prep| to compute the envelope spec then walk |h| around to...@>= -c=mp_offset_prep(mp, c,h); -if ( mp->internal[mp_tracing_specs]>0 ) - mp_print_spec(mp, c,h,""); -h=mp_pen_walk(mp, h,mp->spec_offset) +c = mp_offset_prep (mp, c, h); +if (internal_value (mp_tracing_specs) > 0) + mp_print_spec (mp, c, h, ""); +h = mp_pen_walk (mp, h, mp->spec_offset) + @ Mitered and squared-off joins depend on path directions that are difficult to compute for degenerate cubics. The envelope spec computed by |offset_prep| can @@ -11388,239 +11820,274 @@ degenerate cubic. Setting |join_type:=2| in this case makes the computed envelope degenerate as well. @<Set |join_type| to indicate how to handle offset changes at~|q|@>= -if ( k<zero_off ) { - join_type=2; +if (k < zero_off) { + join_type = 2; } else { - if ( (q!=mp->spec_p1)&&(q!=mp->spec_p2) ) join_type=ljoin; - else if ( lcap==2 ) join_type=3; - else join_type=2-lcap; - if ( (join_type==0)||(join_type==3) ) { + if ((q != mp->spec_p1) && (q != mp->spec_p2)) + join_type = ljoin; + else if (lcap == 2) + join_type = 3; + else + join_type = 2 - lcap; + if ((join_type == 0) || (join_type == 3)) { @<Set the incoming and outgoing directions at |q|; in case of degeneracy set |join_type:=2|@>; - if ( join_type==0 ) { + if (join_type == 0) { @<If |miterlim| is less than the secant of half the angle at |q| then set |join_type:=2|@>; } } } + @ @<If |miterlim| is less than the secant of half the angle at |q|...@>= -{ - tmp=mp_take_fraction(mp, miterlim,fraction_half+ - half(mp_take_fraction(mp, dxin,dxout)+mp_take_fraction(mp, dyin,dyout))); - if ( tmp<unity ) - if ( mp_take_scaled(mp, miterlim,tmp)<unity ) join_type=2; +{ + tmp = mp_take_fraction (mp, miterlim, fraction_half + + half (mp_take_fraction (mp, dxin, dxout) + + mp_take_fraction (mp, dyin, dyout))); + if (tmp < unity) + if (mp_take_scaled (mp, miterlim, tmp) < unity) + join_type = 2; } + @ @<Other local variables for |make_envelope|@>= -fraction dxin,dyin,dxout,dyout; /* directions at |q| when square or mitered */ -scaled tmp; /* a temporary value */ +fraction dxin, dyin, dxout, dyout; /* directions at |q| when square or mitered */ +scaled tmp; /* a temporary value */ @ The coordinates of |p| have already been shifted unless |p| is the first knot in which case they get shifted at the very end. @<Add offset |w| to the cubic from |p| to |q|@>= -mp_right_x(p)=mp_right_x(p)+mp_x_coord(w); -mp_right_y(p)=mp_right_y(p)+mp_y_coord(w); -mp_left_x(q)=mp_left_x(q)+mp_x_coord(w); -mp_left_y(q)=mp_left_y(q)+mp_y_coord(w); -mp_x_coord(q)=mp_x_coord(q)+mp_x_coord(w); -mp_y_coord(q)=mp_y_coord(q)+mp_y_coord(w); -mp_left_type(q)=mp_explicit; -mp_right_type(q)=mp_explicit +mp_right_x (p) = mp_right_x (p) + mp_x_coord (w); +mp_right_y (p) = mp_right_y (p) + mp_y_coord (w); +mp_left_x (q) = mp_left_x (q) + mp_x_coord (w); +mp_left_y (q) = mp_left_y (q) + mp_y_coord (w); +mp_x_coord (q) = mp_x_coord (q) + mp_x_coord (w); +mp_y_coord (q) = mp_y_coord (q) + mp_y_coord (w); +mp_left_type (q) = mp_explicit; +mp_right_type (q) = mp_explicit @ @<Step |w| and move |k| one step closer to |zero_off|@>= -if ( k>zero_off ){ w=mp_link(w); decr(k); } -else { w=knil(w); incr(k); } +if (k > zero_off) { + w = mp_next_knot (w); + decr (k); +} else { + w = mp_prev_knot (w); + incr (k); +} + @ The cubic from |q| to the new knot at |(x,y)| becomes a line segment and the |mp_right_x| and |mp_right_y| fields of |r| are set from |q|. This is done in case the cubic containing these control points is ``yet to be examined.'' @<Declarations@>= -static pointer mp_insert_knot (MP mp,pointer q, scaled x, scaled y); +static mp_knot mp_insert_knot (MP mp, mp_knot q, scaled x, scaled y); @ @c -pointer mp_insert_knot (MP mp,pointer q, scaled x, scaled y) { +mp_knot mp_insert_knot (MP mp, mp_knot q, scaled x, scaled y) { /* returns the inserted knot */ - pointer r; /* the new knot */ - r=mp_get_node(mp, knot_node_size); - mp_link(r)=mp_link(q); mp_link(q)=r; - mp_right_x(r)=mp_right_x(q); - mp_right_y(r)=mp_right_y(q); - mp_x_coord(r)=x; - mp_y_coord(r)=y; - mp_right_x(q)=mp_x_coord(q); - mp_right_y(q)=mp_y_coord(q); - mp_left_x(r)=mp_x_coord(r); - mp_left_y(r)=mp_y_coord(r); - mp_left_type(r)=mp_explicit; - mp_right_type(r)=mp_explicit; - mp_originator(r)=mp_program_code; + mp_knot r; /* the new knot */ + r = mp_new_knot (mp); + mp_next_knot (r) = mp_next_knot (q); + mp_next_knot (q) = r; + mp_right_x (r) = mp_right_x (q); + mp_right_y (r) = mp_right_y (q); + mp_x_coord (r) = x; + mp_y_coord (r) = y; + mp_right_x (q) = mp_x_coord (q); + mp_right_y (q) = mp_y_coord (q); + mp_left_x (r) = mp_x_coord (r); + mp_left_y (r) = mp_y_coord (r); + mp_left_type (r) = mp_explicit; + mp_right_type (r) = mp_explicit; + mp_originator (r) = mp_program_code; return r; } + @ After setting |p:=mp_link(p)|, either |join_type=1| or |q=mp_link(p)|. @<Set |p=mp_link(p)| and add knots between |p| and |q| as...@>= -{ - p=mp_link(p); - if ( (join_type==0)||(join_type==3) ) { - if ( join_type==0 ) { +{ + p = mp_next_knot (p); + if ((join_type == 0) || (join_type == 3)) { + if (join_type == 0) { @<Insert a new knot |r| between |p| and |q| as required for a mitered join@> } else { @<Make |r| the last of two knots inserted between |p| and |q| to form a squared join@>; } - if ( r!=null ) { - mp_right_x(r)=mp_x_coord(r); - mp_right_y(r)=mp_y_coord(r); + if (r != NULL) { + mp_right_x (r) = mp_x_coord (r); + mp_right_y (r) = mp_y_coord (r); } } } + @ For very small angles, adding a knot is unnecessary and would cause numerical -problems, so we just set |r:=null| in that case. +problems, so we just set |r:=NULL| in that case. @<Insert a new knot |r| between |p| and |q| as required for a mitered join@>= -{ - det=mp_take_fraction(mp, dyout,dxin)-mp_take_fraction(mp, dxout,dyin); - if ( abs(det)<26844 ) { - r=null; /* sine $<10^{-4}$ */ - } else { - tmp=mp_take_fraction(mp, mp_x_coord(q)-mp_x_coord(p),dyout)- - mp_take_fraction(mp, mp_y_coord(q)-mp_y_coord(p),dxout); - tmp=mp_make_fraction(mp, tmp,det); - r=mp_insert_knot(mp, p,mp_x_coord(p)+mp_take_fraction(mp, tmp,dxin), - mp_y_coord(p)+mp_take_fraction(mp, tmp,dyin)); +{ + det = mp_take_fraction (mp, dyout, dxin) - mp_take_fraction (mp, dxout, dyin); + if (abs (det) < 26844) { + r = NULL; /* sine $<10^{-4}$ */ + } else { + tmp = mp_take_fraction (mp, mp_x_coord (q) - mp_x_coord (p), dyout) - + mp_take_fraction (mp, mp_y_coord (q) - mp_y_coord (p), dxout); + tmp = mp_make_fraction (mp, tmp, det); + r = + mp_insert_knot (mp, p, mp_x_coord (p) + mp_take_fraction (mp, tmp, dxin), + mp_y_coord (p) + mp_take_fraction (mp, tmp, dyin)); } } + @ @<Other local variables for |make_envelope|@>= -fraction det; /* a determinant used for mitered join calculations */ +fraction det; /* a determinant used for mitered join calculations */ @ @<Make |r| the last of two knots inserted between |p| and |q| to form a...@>= -{ - ht_x=mp_y_coord(w)-mp_y_coord(w0); - ht_y=mp_x_coord(w0)-mp_x_coord(w); - while ( (abs(ht_x)<fraction_half)&&(abs(ht_y)<fraction_half) ) { - ht_x+=ht_x; ht_y+=ht_y; +{ + ht_x = mp_y_coord (w) - mp_y_coord (w0); + ht_y = mp_x_coord (w0) - mp_x_coord (w); + while ((abs (ht_x) < fraction_half) && (abs (ht_y) < fraction_half)) { + ht_x += ht_x; + ht_y += ht_y; } @<Scan the pen polygon between |w0| and |w| and make |max_ht| the range dot product with |(ht_x,ht_y)|@>; - tmp=mp_make_fraction(mp, max_ht,mp_take_fraction(mp, dxin,ht_x)+ - mp_take_fraction(mp, dyin,ht_y)); - r=mp_insert_knot(mp, p,mp_x_coord(p)+mp_take_fraction(mp, tmp,dxin), - mp_y_coord(p)+mp_take_fraction(mp, tmp,dyin)); - tmp=mp_make_fraction(mp, max_ht,mp_take_fraction(mp, dxout,ht_x)+ - mp_take_fraction(mp, dyout,ht_y)); - r=mp_insert_knot(mp, r,mp_x_coord(q)+mp_take_fraction(mp, tmp,dxout), - mp_y_coord(q)+mp_take_fraction(mp, tmp,dyout)); + tmp = mp_make_fraction (mp, max_ht, mp_take_fraction (mp, dxin, ht_x) + + mp_take_fraction (mp, dyin, ht_y)); + r = mp_insert_knot (mp, p, mp_x_coord (p) + mp_take_fraction (mp, tmp, dxin), + mp_y_coord (p) + mp_take_fraction (mp, tmp, dyin)); + tmp = mp_make_fraction (mp, max_ht, mp_take_fraction (mp, dxout, ht_x) + + mp_take_fraction (mp, dyout, ht_y)); + r = mp_insert_knot (mp, r, mp_x_coord (q) + mp_take_fraction (mp, tmp, dxout), + mp_y_coord (q) + mp_take_fraction (mp, tmp, dyout)); } + @ @<Other local variables for |make_envelope|@>= -fraction ht_x,ht_y; /* perpendicular to the segment from |p| to |q| */ -scaled max_ht; /* maximum height of the pen polygon above the |w0|-|w| line */ -halfword kk; /* keeps track of the pen vertices being scanned */ -pointer ww; /* the pen vertex being tested */ +fraction ht_x, ht_y; /* perpendicular to the segment from |p| to |q| */ +scaled max_ht; /* maximum height of the pen polygon above the |w0|-|w| line */ +halfword kk; /* keeps track of the pen vertices being scanned */ +mp_knot ww; /* the pen vertex being tested */ @ The dot product of the vector from |w0| to |ww| with |(ht_x,ht_y)| ranges from zero to |max_ht|. @<Scan the pen polygon between |w0| and |w| and make |max_ht| the range...@>= -max_ht=0; -kk=zero_off; -ww=w; -while (1) { +max_ht = 0; +kk = zero_off; +ww = w; +while (1) { @<Step |ww| and move |kk| one step closer to |k0|@>; - if ( kk==k0 ) break; - tmp=mp_take_fraction(mp, (mp_x_coord(ww)-mp_x_coord(w0)),ht_x)+ - mp_take_fraction(mp, (mp_y_coord(ww)-mp_y_coord(w0)),ht_y); - if ( tmp>max_ht ) max_ht=tmp; + if (kk == k0) + break; + tmp = mp_take_fraction (mp, (mp_x_coord (ww) - mp_x_coord (w0)), ht_x) + + mp_take_fraction (mp, (mp_y_coord (ww) - mp_y_coord (w0)), ht_y); + if (tmp > max_ht) + max_ht = tmp; } @ @<Step |ww| and move |kk| one step closer to |k0|@>= -if ( kk>k0 ) { ww=mp_link(ww); decr(kk); } -else { ww=knil(ww); incr(kk); } +if (kk > k0) { + ww = mp_next_knot (ww); + decr (kk); +} else { + ww = mp_prev_knot (ww); + incr (kk); +} + @ @<If endpoint, double the path |c|, and set |spec_p1| and |spec_p2|@>= -if ( mp_left_type(c)==mp_endpoint ) { - mp->spec_p1=mp_htap_ypoc(mp, c); - mp->spec_p2=mp->path_tail; - mp_originator(mp->spec_p1)=mp_program_code; - mp_link(mp->spec_p2)=mp_link(mp->spec_p1); - mp_link(mp->spec_p1)=c; - mp_remove_cubic(mp, mp->spec_p1); - c=mp->spec_p1; - if ( c!=mp_link(c) ) { - mp_originator(mp->spec_p2)=mp_program_code; - mp_remove_cubic(mp, mp->spec_p2); +if (mp_left_type (c) == mp_endpoint) { + mp->spec_p1 = mp_htap_ypoc (mp, c); + mp->spec_p2 = mp->path_tail; + mp_originator (mp->spec_p1) = mp_program_code; + mp_next_knot (mp->spec_p2) = mp_next_knot (mp->spec_p1); + mp_next_knot (mp->spec_p1) = c; + mp_remove_cubic (mp, mp->spec_p1); + c = mp->spec_p1; + if (c != mp_next_knot (c)) { + mp_originator (mp->spec_p2) = mp_program_code; + mp_remove_cubic (mp, mp->spec_p2); } else { @<Make |c| look like a cycle of length one@>; } } @ @<Make |c| look like a cycle of length one@>= -{ - mp_left_type(c)=mp_explicit; mp_right_type(c)=mp_explicit; - mp_left_x(c)=mp_x_coord(c); mp_left_y(c)=mp_y_coord(c); - mp_right_x(c)=mp_x_coord(c); mp_right_y(c)=mp_y_coord(c); +{ + mp_left_type (c) = mp_explicit; + mp_right_type (c) = mp_explicit; + mp_left_x (c) = mp_x_coord (c); + mp_left_y (c) = mp_y_coord (c); + mp_right_x (c) = mp_x_coord (c); + mp_right_y (c) = mp_y_coord (c); } + @ In degenerate situations we might have to look at the knot preceding~|q|. That knot is |p| but if |p<>c|, its coordinates have already been offset by |w|. @<Set the incoming and outgoing directions at |q|; in case of...@>= -dxin=mp_x_coord(q)-mp_left_x(q); -dyin=mp_y_coord(q)-mp_left_y(q); -if ( (dxin==0)&&(dyin==0) ) { - dxin=mp_x_coord(q)-mp_right_x(p); - dyin=mp_y_coord(q)-mp_right_y(p); - if ( (dxin==0)&&(dyin==0) ) { - dxin=mp_x_coord(q)-mp_x_coord(p); - dyin=mp_y_coord(q)-mp_y_coord(p); - if ( p!=c ) { /* the coordinates of |p| have been offset by |w| */ - dxin=dxin+mp_x_coord(w); - dyin=dyin+mp_y_coord(w); - } - } -} -tmp=mp_pyth_add(mp, dxin,dyin); -if ( tmp==0 ) { - join_type=2; -} else { - dxin=mp_make_fraction(mp, dxin,tmp); - dyin=mp_make_fraction(mp, dyin,tmp); +dxin = mp_x_coord (q) - mp_left_x (q); +dyin = mp_y_coord (q) - mp_left_y (q); +if ((dxin == 0) && (dyin == 0)) { +dxin = mp_x_coord (q) - mp_right_x (p); +dyin = mp_y_coord (q) - mp_right_y (p); +if ((dxin == 0) && (dyin == 0)) { + dxin = mp_x_coord (q) - mp_x_coord (p); + dyin = mp_y_coord (q) - mp_y_coord (p); + if (p != c) { /* the coordinates of |p| have been offset by |w| */ + dxin = dxin + mp_x_coord (w); + dyin = dyin + mp_y_coord (w); + } +} +} +tmp = mp_pyth_add (mp, dxin, dyin); +if (tmp == 0) { + join_type = 2; +} else { + dxin = mp_make_fraction (mp, dxin, tmp); + dyin = mp_make_fraction (mp, dyin, tmp); @<Set the outgoing direction at |q|@>; } + @ If |q=c| then the coordinates of |r| and the control points between |q| and~|r| have already been offset by |h|. @<Set the outgoing direction at |q|@>= -dxout=mp_right_x(q)-mp_x_coord(q); -dyout=mp_right_y(q)-mp_y_coord(q); -if ( (dxout==0)&&(dyout==0) ) { - r=mp_link(q); - dxout=mp_left_x(r)-mp_x_coord(q); - dyout=mp_left_y(r)-mp_y_coord(q); - if ( (dxout==0)&&(dyout==0) ) { - dxout=mp_x_coord(r)-mp_x_coord(q); - dyout=mp_y_coord(r)-mp_y_coord(q); - } -} -if ( q==c ) { - dxout=dxout-mp_x_coord(h); - dyout=dyout-mp_y_coord(h); -} -tmp=mp_pyth_add(mp, dxout,dyout); -if ( tmp==0 ) mp_confusion(mp, "degenerate spec"); -@:this can't happen degerate spec}{\quad degenerate spec@> -dxout=mp_make_fraction(mp, dxout,tmp); -dyout=mp_make_fraction(mp, dyout,tmp) - -@* \[23] Direction and intersection times. +dxout = mp_right_x (q) - mp_x_coord (q); +dyout = mp_right_y (q) - mp_y_coord (q); +if ((dxout == 0) && (dyout == 0)) { +r = mp_next_knot (q); +dxout = mp_left_x (r) - mp_x_coord (q); +dyout = mp_left_y (r) - mp_y_coord (q); +if ((dxout == 0) && (dyout == 0)) { + dxout = mp_x_coord (r) - mp_x_coord (q); + dyout = mp_y_coord (r) - mp_y_coord (q); +} +} +if (q == c) { + dxout = dxout - mp_x_coord (h); + dyout = dyout - mp_y_coord (h); +} +tmp = mp_pyth_add (mp, dxout, dyout); +if (tmp == 0) + mp_confusion (mp, "degenerate spec"); +@:this can't happen degerate spec}{\quad degenerate spec@>; +dxout = mp_make_fraction (mp, dxout, tmp); +dyout = mp_make_fraction (mp, dyout, tmp) + + +@* Direction and intersection times. A path of length $n$ is defined parametrically by functions $x(t)$ and $y(t)$, for |0<=t<=n|; we can regard $t$ as the ``time'' at which the path reaches the point $\bigl(x(t),y(t)\bigr)$. In this section of the program @@ -11642,40 +12109,52 @@ The routine solves this problem in nondegenerate cases by rotating the path and the given direction so that |(x,y)=(1,0)|; i.e., the main task will be to find when a given path first travels ``due east.'' -@c -static scaled mp_find_direction_time (MP mp,scaled x, scaled y, pointer h) { - scaled max; /* $\max\bigl(\vert x\vert,\vert y\vert\bigr)$ */ - pointer p,q; /* for list traversal */ - scaled n; /* the direction time at knot |p| */ - scaled tt; /* the direction time within a cubic */ +@c +static scaled mp_find_direction_time (MP mp, scaled x, scaled y, mp_knot h) { + scaled max; /* $\max\bigl(\vert x\vert,\vert y\vert\bigr)$ */ + mp_knot p, q; /* for list traversal */ + scaled n; /* the direction time at knot |p| */ + scaled tt; /* the direction time within a cubic */ @<Other local variables for |find_direction_time|@>; @<Normalize the given direction for better accuracy; but |return| with zero result if it's zero@>; - n=0; p=h; phi=0; - while (1) { - if ( mp_right_type(p)==mp_endpoint ) break; - q=mp_link(p); + n = 0; + p = h; + phi = 0; + while (1) { + if (mp_right_type (p) == mp_endpoint) + break; + q = mp_next_knot (p); @<Rotate the cubic between |p| and |q|; then |goto found| if the rotated cubic travels due east at some time |tt|; but |break| if an entire cyclic path has been traversed@>; - p=q; n=n+unity; + p = q; + n = n + unity; } return (-unity); -FOUND: - return (n+tt); +FOUND: + return (n + tt); } + @ @<Normalize the given direction for better accuracy...@>= -if ( abs(x)<abs(y) ) { - x=mp_make_fraction(mp, x,abs(y)); - if ( y>0 ) y=fraction_one; else y=-fraction_one; -} else if ( x==0 ) { +if (abs (x) < abs (y)) { + x = mp_make_fraction (mp, x, abs (y)); + if (y > 0) + y = fraction_one; + else + y = -fraction_one; +} else if (x == 0) { return 0; -} else { - y=mp_make_fraction(mp, y,abs(x)); - if ( x>0 ) x=fraction_one; else x=-fraction_one; +} else { + y = mp_make_fraction (mp, y, abs (x)); + if (x > 0) + x = fraction_one; + else + x = -fraction_one; } + @ Since we're interested in the tangent directions, we work with the derivative $${1\over3}B'(x_0,x_1,x_2,x_3;t)= B(x_1-x_0,x_2-x_1,x_3-x_2;t)$$ instead of @@ -11688,50 +12167,75 @@ in which the previous rotated cubic was traveling. (The value of |phi| will be undefined on the first cubic, i.e., when |n=0|.) @<Rotate the cubic between |p| and |q|; then...@>= -tt=0; +tt = 0; @<Set local variables |x1,x2,x3| and |y1,y2,y3| to multiples of the control points of the rotated derivatives@>; -if ( y1==0 ) if ( x1>=0 ) goto FOUND; -if ( n>0 ) { +if (y1 == 0) + if (x1 >= 0) + goto FOUND; +if (n > 0) { @<Exit to |found| if an eastward direction occurs at knot |p|@>; - if ( p==h ) break; - }; -if ( (x3!=0)||(y3!=0) ) phi=mp_n_arg(mp, x3,y3); + if (p == h) + break; +}; +if ((x3 != 0) || (y3 != 0)) + phi = mp_n_arg (mp, x3, y3); @<Exit to |found| if the curve whose derivatives are specified by |x1,x2,x3,y1,y2,y3| travels eastward at some time~|tt|@> + @ @<Other local variables for |find_direction_time|@>= -scaled x1,x2,x3,y1,y2,y3; /* multiples of rotated derivatives */ -angle theta,phi; /* angles of exit and entry at a knot */ -fraction t; /* temp storage */ +scaled x1, x2, x3, y1, y2, y3; /* multiples of rotated derivatives */ +angle theta, phi; /* angles of exit and entry at a knot */ +fraction t; /* temp storage */ @ @<Set local variables |x1,x2,x3| and |y1,y2,y3| to multiples...@>= -x1=mp_right_x(p)-mp_x_coord(p); x2=mp_left_x(q)-mp_right_x(p); -x3=mp_x_coord(q)-mp_left_x(q); -y1=mp_right_y(p)-mp_y_coord(p); y2=mp_left_y(q)-mp_right_y(p); -y3=mp_y_coord(q)-mp_left_y(q); -max=abs(x1); -if ( abs(x2)>max ) max=abs(x2); -if ( abs(x3)>max ) max=abs(x3); -if ( abs(y1)>max ) max=abs(y1); -if ( abs(y2)>max ) max=abs(y2); -if ( abs(y3)>max ) max=abs(y3); -if ( max==0 ) goto FOUND; -while ( max<fraction_half ){ - max+=max; x1+=x1; x2+=x2; x3+=x3; - y1+=y1; y2+=y2; y3+=y3; -} -t=x1; x1=mp_take_fraction(mp, x1,x)+mp_take_fraction(mp, y1,y); -y1=mp_take_fraction(mp, y1,x)-mp_take_fraction(mp, t,y); -t=x2; x2=mp_take_fraction(mp, x2,x)+mp_take_fraction(mp, y2,y); -y2=mp_take_fraction(mp, y2,x)-mp_take_fraction(mp, t,y); -t=x3; x3=mp_take_fraction(mp, x3,x)+mp_take_fraction(mp, y3,y); -y3=mp_take_fraction(mp, y3,x)-mp_take_fraction(mp, t,y) +x1 = mp_right_x (p) - mp_x_coord (p); +x2 = mp_left_x (q) - mp_right_x (p); +x3 = mp_x_coord (q) - mp_left_x (q); +y1 = mp_right_y (p) - mp_y_coord (p); +y2 = mp_left_y (q) - mp_right_y (p); +y3 = mp_y_coord (q) - mp_left_y (q); +max = abs (x1); +if (abs (x2) > max) + max = abs (x2); +if (abs (x3) > max) + max = abs (x3); +if (abs (y1) > max) + max = abs (y1); +if (abs (y2) > max) + max = abs (y2); +if (abs (y3) > max) + max = abs (y3); +if (max == 0) + goto FOUND; +while (max < fraction_half) { +max += max; +x1 += x1; +x2 += x2; +x3 += x3; +y1 += y1; +y2 += y2; +y3 += y3; +} +t = x1; +x1 = mp_take_fraction (mp, x1, x) + mp_take_fraction (mp, y1, y); +y1 = mp_take_fraction (mp, y1, x) - mp_take_fraction (mp, t, y); +t = x2; +x2 = mp_take_fraction (mp, x2, x) + mp_take_fraction (mp, y2, y); +y2 = mp_take_fraction (mp, y2, x) - mp_take_fraction (mp, t, y); +t = x3; +x3 = mp_take_fraction (mp, x3, x) + mp_take_fraction (mp, y3, y); +y3 = mp_take_fraction (mp, y3, x) - mp_take_fraction (mp, t, y) + @ @<Exit to |found| if an eastward direction occurs at knot |p|@>= -theta=mp_n_arg(mp, x1,y1); -if ( theta>=0 ) if ( phi<=0 ) if ( phi>=theta-one_eighty_deg ) goto FOUND; -if ( theta<=0 ) if ( phi>=0 ) if ( phi<=theta+one_eighty_deg ) goto FOUND +theta = mp_n_arg (mp, x1, y1); +if (theta >= 0 && phi <= 0 && phi >= theta - one_eighty_deg) + goto FOUND; +if (theta <= 0 && phi >= 0 && phi <= theta + one_eighty_deg) + goto FOUND; + @ In this step we want to use the |crossing_point| routine to find the roots of the quadratic equation $B(y_1,y_2,y_3;t)=0$. @@ -11744,14 +12248,23 @@ And finally, we need to do special things if $B(y_1,y_2,y_3;t)$ is identically zero. @ @<Exit to |found| if the curve whose derivatives are specified by...@>= -if ( x1<0 ) if ( x2<0 ) if ( x3<0 ) goto DONE; -if ( mp_ab_vs_cd(mp, y1,y3,y2,y2)==0 ) { +if (x1 < 0) + if (x2 < 0) + if (x3 < 0) + goto DONE; +if (mp_ab_vs_cd (mp, y1, y3, y2, y2) == 0) { @<Handle the test for eastward directions when $y_1y_3=y_2^2$; either |goto found| or |goto done|@>; } -if ( y1<=0 ) { - if ( y1<0 ) { y1=-y1; y2=-y2; y3=-y3; } - else if ( y2>0 ){ y2=-y2; y3=-y3; }; +if (y1 <= 0) { + if (y1 < 0) { + y1 = -y1; + y2 = -y2; + y3 = -y3; + } else if (y2 > 0) { + y2 = -y2; + y3 = -y3; + }; } @<Check the places where $B(y_1,y_2,y_3;t)=0$ to see if $B(x_1,x_2,x_3;t)\ge0$@>; @@ -11769,53 +12282,65 @@ do the right thing. @d we_found_it { tt=(t+04000) / 010000; goto FOUND; } @<Check the places where $B(y_1,y_2,y_3;t)=0$...@>= -t=mp_crossing_point(mp, y1,y2,y3); -if ( t>fraction_one ) goto DONE; -y2=t_of_the_way(y2,y3); -x1=t_of_the_way(x1,x2); -x2=t_of_the_way(x2,x3); -x1=t_of_the_way(x1,x2); -if ( x1>=0 ) we_found_it; -if ( y2>0 ) y2=0; -tt=t; t=mp_crossing_point(mp, 0,-y2,-y3); -if ( t>fraction_one ) goto DONE; -x1=t_of_the_way(x1,x2); -x2=t_of_the_way(x2,x3); -if ( t_of_the_way(x1,x2)>=0 ) { - t=t_of_the_way(tt,fraction_one); we_found_it; +t = mp_crossing_point (mp, y1, y2, y3); +if (t > fraction_one) + goto DONE; +y2 = t_of_the_way (y2, y3); +x1 = t_of_the_way (x1, x2); +x2 = t_of_the_way (x2, x3); +x1 = t_of_the_way (x1, x2); +if (x1 >= 0) + we_found_it; +if (y2 > 0) + y2 = 0; +tt = t; +t = mp_crossing_point (mp, 0, -y2, -y3); +if (t > fraction_one) + goto DONE; +x1 = t_of_the_way (x1, x2); +x2 = t_of_the_way (x2, x3); +if (t_of_the_way (x1, x2) >= 0) { + t = t_of_the_way (tt, fraction_one); + we_found_it; } @ @<Handle the test for eastward directions when $y_1y_3=y_2^2$; either |goto found| or |goto done|@>= -{ - if ( mp_ab_vs_cd(mp, y1,y2,0,0)<0 ) { - t=mp_make_fraction(mp, y1,y1-y2); - x1=t_of_the_way(x1,x2); - x2=t_of_the_way(x2,x3); - if ( t_of_the_way(x1,x2)>=0 ) we_found_it; - } else if ( y3==0 ) { - if ( y1==0 ) { +{ + if (mp_ab_vs_cd (mp, y1, y2, 0, 0) < 0) { + t = mp_make_fraction (mp, y1, y1 - y2); + x1 = t_of_the_way (x1, x2); + x2 = t_of_the_way (x2, x3); + if (t_of_the_way (x1, x2) >= 0) + we_found_it; + } else if (y3 == 0) { + if (y1 == 0) { @<Exit to |found| if the derivative $B(x_1,x_2,x_3;t)$ becomes |>=0|@>; - } else if ( x3>=0 ) { - tt=unity; goto FOUND; + } else if (x3 >= 0) { + tt = unity; + goto FOUND; } } goto DONE; } + @ At this point we know that the derivative of |y(t)| is identically zero, and that |x1<0|; but either |x2>=0| or |x3>=0|, so there's some hope of traveling east. @<Exit to |found| if the derivative $B(x_1,x_2,x_3;t)$ becomes |>=0|...@>= -{ - t=mp_crossing_point(mp, -x1,-x2,-x3); - if ( t<=fraction_one ) we_found_it; - if ( mp_ab_vs_cd(mp, x1,x3,x2,x2)<=0 ) { - t=mp_make_fraction(mp, x1,x1-x2); we_found_it; +{ + t = mp_crossing_point (mp, -x1, -x2, -x3); + if (t <= fraction_one) + we_found_it; + if (mp_ab_vs_cd (mp, x1, x3, x2, x2) <= 0) { + t = mp_make_fraction (mp, x1, x1 - x2); + we_found_it; } } + @ The intersection of two cubics can be found by an interesting variant of the general bisection scheme described in the introduction to |crossing_point|.\ @@ -11894,7 +12419,7 @@ It is convenient to do this double-take by letting `3' in the preceding paragraph be a parameter, which is first 0, then 3. @<Glob...@>= -unsigned int tol_step; /* either 0 or 3, usually */ +unsigned int tol_step; /* either 0 or 3, usually */ @ We shall use an explicit stack to implement the recursive bisection method described above. The |bisect_stack| array will contain numerous 5-word @@ -11965,13 +12490,14 @@ integer *bisect_stack; integer bisect_ptr; @ @<Allocate or initialize ...@>= -mp->bisect_stack = xmalloc((bistack_size+1),sizeof(integer)); +mp->bisect_stack = xmalloc ((bistack_size + 1), sizeof (integer)); @ @<Dealloc variables@>= -xfree(mp->bisect_stack); +xfree (mp->bisect_stack); @ @<Check the ``constant''...@>= -if ( int_packets+17*int_increment>bistack_size ) mp->bad=19; +if (int_packets + 17 * int_increment > bistack_size) + mp->bad = 19; @ Computation of the min and max is a tedious but fairly fast sequence of instructions; exactly four comparisons are made in each branch. @@ -12016,193 +12542,268 @@ of fruitless computation would be possible). @d max_patience 5000 @<Glob...@>= -integer cur_t;integer cur_tt; /* controls and results of |cubic_intersection| */ -integer time_to_go; /* this many backtracks before giving up */ -integer max_t; /* maximum of $2^{l+1}$ so far achieved */ +integer cur_t; +integer cur_tt; /* controls and results of |cubic_intersection| */ +integer time_to_go; /* this many backtracks before giving up */ +integer max_t; /* maximum of $2^{l+1}$ so far achieved */ @ The given cubics $B(w_0,w_1,w_2,w_3;t)$ and $B(z_0,z_1,z_2,z_3;t)$ are specified in adjacent knot nodes |(p,mp_link(p))| and |(pp,mp_link(pp))|, respectively. -@c -static void mp_cubic_intersection (MP mp,pointer p, pointer pp) { - pointer q,qq; /* |mp_link(p)|, |mp_link(pp)| */ - mp->time_to_go=max_patience; mp->max_t=2; +@c +static void mp_cubic_intersection (MP mp, mp_knot p, mp_knot pp) { + mp_knot q, qq; /* |mp_link(p)|, |mp_link(pp)| */ + mp->time_to_go = max_patience; + mp->max_t = 2; @<Initialize for intersections at level zero@>; CONTINUE: - while (1) { - if ( mp->delx-mp->tol<=stack_max(x_packet(mp->xy))-stack_min(u_packet(mp->uv))) - if ( mp->delx+mp->tol>=stack_min(x_packet(mp->xy))-stack_max(u_packet(mp->uv))) - if ( mp->dely-mp->tol<=stack_max(y_packet(mp->xy))-stack_min(v_packet(mp->uv))) - if ( mp->dely+mp->tol>=stack_min(y_packet(mp->xy))-stack_max(v_packet(mp->uv))) - { - if ( mp->cur_t>=mp->max_t ){ - if ( mp->max_t==two ) { /* we've done 17 bisections */ - mp->cur_t=halfp(mp->cur_t+1); - mp->cur_tt=halfp(mp->cur_tt+1); - return; - } - mp->max_t+=mp->max_t; mp->appr_t=mp->cur_t; mp->appr_tt=mp->cur_tt; - } - @<Subdivide for a new level of intersection@>; - goto CONTINUE; - } - if ( mp->time_to_go>0 ) { - decr(mp->time_to_go); - } else { - while ( mp->appr_t<unity ) { - mp->appr_t+=mp->appr_t; mp->appr_tt+=mp->appr_tt; + while (1) { + if (mp->delx - mp->tol <= + stack_max (x_packet (mp->xy)) - stack_min (u_packet (mp->uv))) + if (mp->delx + mp->tol >= + stack_min (x_packet (mp->xy)) - stack_max (u_packet (mp->uv))) + if (mp->dely - mp->tol <= + stack_max (y_packet (mp->xy)) - stack_min (v_packet (mp->uv))) + if (mp->dely + mp->tol >= + stack_min (y_packet (mp->xy)) - stack_max (v_packet (mp->uv))) { + if (mp->cur_t >= mp->max_t) { + if (mp->max_t == two) { /* we've done 17 bisections */ + mp->cur_t = halfp (mp->cur_t + 1); + mp->cur_tt = halfp (mp->cur_tt + 1); + return; + } + mp->max_t += mp->max_t; + mp->appr_t = mp->cur_t; + mp->appr_tt = mp->cur_tt; + } + @<Subdivide for a new level of intersection@>; + goto CONTINUE; + } + if (mp->time_to_go > 0) { + decr (mp->time_to_go); + } else { + while (mp->appr_t < unity) { + mp->appr_t += mp->appr_t; + mp->appr_tt += mp->appr_tt; } - mp->cur_t=mp->appr_t; mp->cur_tt=mp->appr_tt; return; + mp->cur_t = mp->appr_t; + mp->cur_tt = mp->appr_tt; + return; } @<Advance to the next pair |(cur_t,cur_tt)|@>; } } + @ The following variables are global, although they are used only by |cubic_intersection|, because it is necessary on some machines to split |cubic_intersection| up into two procedures. @<Glob...@>= -integer delx;integer dely; /* the components of $\Delta=2^l(w_0-z_0)$ */ -integer tol; /* bound on the uncertainty in the overlap test */ +integer delx; +integer dely; /* the components of $\Delta=2^l(w_0-z_0)$ */ +integer tol; /* bound on the uncertainty in the overlap test */ integer uv; -integer xy; /* pointers to the current packets of interest */ -integer three_l; /* |tol_step| times the bisection level */ -integer appr_t;integer appr_tt; /* best approximations known to the answers */ +integer xy; /* pointers to the current packets of interest */ +integer three_l; /* |tol_step| times the bisection level */ +integer appr_t; +integer appr_tt; /* best approximations known to the answers */ @ We shall assume that the coordinates are sufficiently non-extreme that integer overflow will not occur. @^overflow in arithmetic@> @<Initialize for intersections at level zero@>= -q=mp_link(p); qq=mp_link(pp); mp->bisect_ptr=int_packets; -u1r=mp_right_x(p)-mp_x_coord(p); u2r=mp_left_x(q)-mp_right_x(p); -u3r=mp_x_coord(q)-mp_left_x(q); set_min_max(ur_packet); -v1r=mp_right_y(p)-mp_y_coord(p); v2r=mp_left_y(q)-mp_right_y(p); -v3r=mp_y_coord(q)-mp_left_y(q); set_min_max(vr_packet); -x1r=mp_right_x(pp)-mp_x_coord(pp); x2r=mp_left_x(qq)-mp_right_x(pp); -x3r=mp_x_coord(qq)-mp_left_x(qq); set_min_max(xr_packet); -y1r=mp_right_y(pp)-mp_y_coord(pp); y2r=mp_left_y(qq)-mp_right_y(pp); -y3r=mp_y_coord(qq)-mp_left_y(qq); set_min_max(yr_packet); -mp->delx=mp_x_coord(p)-mp_x_coord(pp); mp->dely=mp_y_coord(p)-mp_y_coord(pp); -mp->tol=0; mp->uv=r_packets; mp->xy=r_packets; -mp->three_l=0; mp->cur_t=1; mp->cur_tt=1 +q = mp_next_knot (p); +qq = mp_next_knot (pp); +mp->bisect_ptr = int_packets; +u1r = mp_right_x (p) - mp_x_coord (p); +u2r = mp_left_x (q) - mp_right_x (p); +u3r = mp_x_coord (q) - mp_left_x (q); +set_min_max (ur_packet); +v1r = mp_right_y (p) - mp_y_coord (p); +v2r = mp_left_y (q) - mp_right_y (p); +v3r = mp_y_coord (q) - mp_left_y (q); +set_min_max (vr_packet); +x1r = mp_right_x (pp) - mp_x_coord (pp); +x2r = mp_left_x (qq) - mp_right_x (pp); +x3r = mp_x_coord (qq) - mp_left_x (qq); +set_min_max (xr_packet); +y1r = mp_right_y (pp) - mp_y_coord (pp); +y2r = mp_left_y (qq) - mp_right_y (pp); +y3r = mp_y_coord (qq) - mp_left_y (qq); +set_min_max (yr_packet); +mp->delx = mp_x_coord (p) - mp_x_coord (pp); +mp->dely = mp_y_coord (p) - mp_y_coord (pp); +mp->tol = 0; +mp->uv = r_packets; +mp->xy = r_packets; +mp->three_l = 0; +mp->cur_t = 1; +mp->cur_tt = 1 @ @<Subdivide for a new level of intersection@>= -stack_dx=mp->delx; stack_dy=mp->dely; stack_tol=mp->tol; -stack_uv=mp->uv; stack_xy=mp->xy; -mp->bisect_ptr=mp->bisect_ptr+int_increment; -mp->cur_t+=mp->cur_t; mp->cur_tt+=mp->cur_tt; -u1l=stack_1(u_packet(mp->uv)); u3r=stack_3(u_packet(mp->uv)); -u2l=half(u1l+stack_2(u_packet(mp->uv))); -u2r=half(u3r+stack_2(u_packet(mp->uv))); -u3l=half(u2l+u2r); u1r=u3l; -set_min_max(ul_packet); set_min_max(ur_packet); -v1l=stack_1(v_packet(mp->uv)); v3r=stack_3(v_packet(mp->uv)); -v2l=half(v1l+stack_2(v_packet(mp->uv))); -v2r=half(v3r+stack_2(v_packet(mp->uv))); -v3l=half(v2l+v2r); v1r=v3l; -set_min_max(vl_packet); set_min_max(vr_packet); -x1l=stack_1(x_packet(mp->xy)); x3r=stack_3(x_packet(mp->xy)); -x2l=half(x1l+stack_2(x_packet(mp->xy))); -x2r=half(x3r+stack_2(x_packet(mp->xy))); -x3l=half(x2l+x2r); x1r=x3l; -set_min_max(xl_packet); set_min_max(xr_packet); -y1l=stack_1(y_packet(mp->xy)); y3r=stack_3(y_packet(mp->xy)); -y2l=half(y1l+stack_2(y_packet(mp->xy))); -y2r=half(y3r+stack_2(y_packet(mp->xy))); -y3l=half(y2l+y2r); y1r=y3l; -set_min_max(yl_packet); set_min_max(yr_packet); -mp->uv=l_packets; mp->xy=l_packets; -mp->delx += mp->delx; mp->dely+=mp->dely; -mp->tol = mp->tol- mp->three_l+(integer)mp->tol_step; -mp->tol += mp->tol; mp->three_l=mp->three_l+(integer)mp->tol_step +stack_dx = mp->delx; +stack_dy = mp->dely; +stack_tol = mp->tol; +stack_uv = mp->uv; +stack_xy = mp->xy; +mp->bisect_ptr = mp->bisect_ptr + int_increment; +mp->cur_t += mp->cur_t; +mp->cur_tt += mp->cur_tt; +u1l = stack_1 (u_packet (mp->uv)); +u3r = stack_3 (u_packet (mp->uv)); +u2l = half (u1l + stack_2 (u_packet (mp->uv))); +u2r = half (u3r + stack_2 (u_packet (mp->uv))); +u3l = half (u2l + u2r); +u1r = u3l; +set_min_max (ul_packet); +set_min_max (ur_packet); +v1l = stack_1 (v_packet (mp->uv)); +v3r = stack_3 (v_packet (mp->uv)); +v2l = half (v1l + stack_2 (v_packet (mp->uv))); +v2r = half (v3r + stack_2 (v_packet (mp->uv))); +v3l = half (v2l + v2r); +v1r = v3l; +set_min_max (vl_packet); +set_min_max (vr_packet); +x1l = stack_1 (x_packet (mp->xy)); +x3r = stack_3 (x_packet (mp->xy)); +x2l = half (x1l + stack_2 (x_packet (mp->xy))); +x2r = half (x3r + stack_2 (x_packet (mp->xy))); +x3l = half (x2l + x2r); +x1r = x3l; +set_min_max (xl_packet); +set_min_max (xr_packet); +y1l = stack_1 (y_packet (mp->xy)); +y3r = stack_3 (y_packet (mp->xy)); +y2l = half (y1l + stack_2 (y_packet (mp->xy))); +y2r = half (y3r + stack_2 (y_packet (mp->xy))); +y3l = half (y2l + y2r); +y1r = y3l; +set_min_max (yl_packet); +set_min_max (yr_packet); +mp->uv = l_packets; +mp->xy = l_packets; +mp->delx += mp->delx; +mp->dely += mp->dely; +mp->tol = mp->tol - mp->three_l + (integer) mp->tol_step; +mp->tol += mp->tol; +mp->three_l = mp->three_l + (integer) mp->tol_step @ @<Advance to the next pair |(cur_t,cur_tt)|@>= -NOT_FOUND: -if ( odd(mp->cur_tt) ) { - if ( odd(mp->cur_t) ) { - @<Descend to the previous level and |goto not_found|@>; - } else { - incr(mp->cur_t); - mp->delx=mp->delx+stack_1(u_packet(mp->uv))+stack_2(u_packet(mp->uv)) - +stack_3(u_packet(mp->uv)); - mp->dely=mp->dely+stack_1(v_packet(mp->uv))+stack_2(v_packet(mp->uv)) - +stack_3(v_packet(mp->uv)); - mp->uv=mp->uv+int_packets; /* switch from |l_packets| to |r_packets| */ - decr(mp->cur_tt); mp->xy=mp->xy-int_packets; - /* switch from |r_packets| to |l_packets| */ - mp->delx=mp->delx+stack_1(x_packet(mp->xy))+stack_2(x_packet(mp->xy)) - +stack_3(x_packet(mp->xy)); - mp->dely=mp->dely+stack_1(y_packet(mp->xy))+stack_2(y_packet(mp->xy)) - +stack_3(y_packet(mp->xy)); - } -} else { - incr(mp->cur_tt); mp->tol=mp->tol+mp->three_l; - mp->delx=mp->delx-stack_1(x_packet(mp->xy))-stack_2(x_packet(mp->xy)) - -stack_3(x_packet(mp->xy)); - mp->dely=mp->dely-stack_1(y_packet(mp->xy))-stack_2(y_packet(mp->xy)) - -stack_3(y_packet(mp->xy)); - mp->xy=mp->xy+int_packets; /* switch from |l_packets| to |r_packets| */ +NOT_FOUND: +if (odd (mp->cur_tt)) { + if (odd (mp->cur_t)) { + @<Descend to the previous level and |goto not_found|@>; + } else { + incr (mp->cur_t); + mp->delx = + mp->delx + stack_1 (u_packet (mp->uv)) + stack_2 (u_packet (mp->uv)) + + stack_3 (u_packet (mp->uv)); + mp->dely = + mp->dely + stack_1 (v_packet (mp->uv)) + stack_2 (v_packet (mp->uv)) + + stack_3 (v_packet (mp->uv)); + mp->uv = mp->uv + int_packets; /* switch from |l_packets| to |r_packets| */ + decr (mp->cur_tt); + mp->xy = mp->xy - int_packets; + /* switch from |r_packets| to |l_packets| */ + mp->delx = + mp->delx + stack_1 (x_packet (mp->xy)) + stack_2 (x_packet (mp->xy)) + + stack_3 (x_packet (mp->xy)); + mp->dely = + mp->dely + stack_1 (y_packet (mp->xy)) + stack_2 (y_packet (mp->xy)) + + stack_3 (y_packet (mp->xy)); + } +} else { + incr (mp->cur_tt); + mp->tol = mp->tol + mp->three_l; + mp->delx = + mp->delx - stack_1 (x_packet (mp->xy)) - stack_2 (x_packet (mp->xy)) + - stack_3 (x_packet (mp->xy)); + mp->dely = + mp->dely - stack_1 (y_packet (mp->xy)) - stack_2 (y_packet (mp->xy)) + - stack_3 (y_packet (mp->xy)); + mp->xy = mp->xy + int_packets; /* switch from |l_packets| to |r_packets| */ } + @ @<Descend to the previous level...@>= -{ - mp->cur_t=halfp(mp->cur_t); mp->cur_tt=halfp(mp->cur_tt); - if ( mp->cur_t==0 ) return; - mp->bisect_ptr -= int_increment; - mp->three_l -= (integer)mp->tol_step; - mp->delx=stack_dx; mp->dely=stack_dy; mp->tol=stack_tol; - mp->uv=stack_uv; mp->xy=stack_xy; +{ + mp->cur_t = halfp (mp->cur_t); + mp->cur_tt = halfp (mp->cur_tt); + if (mp->cur_t == 0) + return; + mp->bisect_ptr -= int_increment; + mp->three_l -= (integer) mp->tol_step; + mp->delx = stack_dx; + mp->dely = stack_dy; + mp->tol = stack_tol; + mp->uv = stack_uv; + mp->xy = stack_xy; goto NOT_FOUND; } + @ The |path_intersection| procedure is much simpler. It invokes |cubic_intersection| in lexicographic order until finding a pair of cubics that intersect. The final intersection times are placed in |cur_t| and~|cur_tt|. -@c -static void mp_path_intersection (MP mp,pointer h, pointer hh) { - pointer p,pp; /* link registers that traverse the given paths */ - integer n,nn; /* integer parts of intersection times, minus |unity| */ +@c +static void mp_path_intersection (MP mp, mp_knot h, mp_knot hh) { + mp_knot p, pp; /* link registers that traverse the given paths */ + integer n, nn; /* integer parts of intersection times, minus |unity| */ @<Change one-point paths into dead cycles@>; - mp->tol_step=0; - do { - n=-unity; p=h; - do { - if ( mp_right_type(p)!=mp_endpoint ) { - nn=-unity; pp=hh; - do { - if ( mp_right_type(pp)!=mp_endpoint ) { - mp_cubic_intersection(mp, p,pp); - if ( mp->cur_t>0 ) { - mp->cur_t=mp->cur_t+n; mp->cur_tt=mp->cur_tt+nn; + mp->tol_step = 0; + do { + n = -unity; + p = h; + do { + if (mp_right_type (p) != mp_endpoint) { + nn = -unity; + pp = hh; + do { + if (mp_right_type (pp) != mp_endpoint) { + mp_cubic_intersection (mp, p, pp); + if (mp->cur_t > 0) { + mp->cur_t = mp->cur_t + n; + mp->cur_tt = mp->cur_tt + nn; return; } } - nn=nn+unity; pp=mp_link(pp); - } while (pp!=hh); + nn = nn + unity; + pp = mp_next_knot (pp); + } while (pp != hh); } - n=n+unity; p=mp_link(p); - } while (p!=h); - mp->tol_step=mp->tol_step+3; - } while (mp->tol_step<=3); - mp->cur_t=-unity; mp->cur_tt=-unity; + n = n + unity; + p = mp_next_knot (p); + } while (p != h); + mp->tol_step = mp->tol_step + 3; + } while (mp->tol_step <= 3); + mp->cur_t = -unity; + mp->cur_tt = -unity; } -@ @<Change one-point paths...@>= -if ( mp_right_type(h)==mp_endpoint ) { - mp_right_x(h)=mp_x_coord(h); mp_left_x(h)=mp_x_coord(h); - mp_right_y(h)=mp_y_coord(h); mp_left_y(h)=mp_y_coord(h); mp_right_type(h)=mp_explicit; -} -if ( mp_right_type(hh)==mp_endpoint ) { - mp_right_x(hh)=mp_x_coord(hh); mp_left_x(hh)=mp_x_coord(hh); - mp_right_y(hh)=mp_y_coord(hh); mp_left_y(hh)=mp_y_coord(hh); mp_right_type(hh)=mp_explicit; -} -@* \[24] Dynamic linear equations. +@ @<Change one-point paths...@>= +if (mp_right_type (h) == mp_endpoint) { + mp_right_x (h) = mp_x_coord (h); + mp_left_x (h) = mp_x_coord (h); + mp_right_y (h) = mp_y_coord (h); + mp_left_y (h) = mp_y_coord (h); + mp_right_type (h) = mp_explicit; +} +if (mp_right_type (hh) == mp_endpoint) { + mp_right_x (hh) = mp_x_coord (hh); + mp_left_x (hh) = mp_x_coord (hh); + mp_right_y (hh) = mp_y_coord (hh); + mp_left_y (hh) = mp_y_coord (hh); + mp_right_type (hh) = mp_explicit; +} + +@* Dynamic linear equations. \MP\ users define variables implicitly by stating equations that should be satisfied; the computer is supposed to be smart enough to solve those equations. And indeed, the computer tries valiantly to do so, by distinguishing five @@ -12219,10 +12820,10 @@ as a |scaled| number plus a sum of independent variables with |fraction| coefficients. \smallskip\hang -|type(p)=mp_independent| means that |value(p)=64s+m|, where |s>0| is a ``serial +|type(p)=mp_independent| means that |value(p)=s|, where |s>0| is a ``serial number'' reflecting the time this variable was first used in an equation; -also |0<=m<64|, and each dependent variable -that refers to this one is actually referring to the future value of +and there is an extra field |indep_scale(p)=m|, with |0<=m<64|, each dependent +variable that refers to this one is actually referring to the future value of this variable times~$2^m$. (Usually |m=0|, but higher degrees of scaling are sometimes needed to keep the coefficients in dependency lists from getting too large. The value of~|m| will always be even.) @@ -12245,34 +12846,31 @@ and start again). A backward step may, however, take place: Sometimes a |dependent| variable becomes |mp_independent| again, when one of the independent variables it depends on is reverting to |undefined|. +@d indep_scale(A) ((mp_value_node)(A))->data.scale +@d set_indep_scale(A,B) ((mp_value_node)(A))->data.scale=(B) -The next patch detects overflow of independent-variable serial -numbers. Diagnosed and patched by Thorsten Dahlheimer. - -@d s_scale 64 /* the serial numbers are multiplied by this factor */ @d new_indep(A) /* create a new independent variable */ - { if ( mp->serial_no>el_gordo-s_scale ) + { if ( mp->serial_no>=max_integer ) mp_fatal_error(mp, "variable instance identifiers exhausted"); - mp_type((A))=mp_independent; mp->serial_no=mp->serial_no+s_scale; - value((A))=mp->serial_no; + mp_type((A))=mp_independent; mp->serial_no=mp->serial_no+1; + set_indep_scale((A),0); + set_value((A),mp->serial_no); } @<Glob...@>= -integer serial_no; /* the most recent serial number, times |s_scale| */ - -@ @<Make variable |q+s| newly independent@>=new_indep(q+s) +integer serial_no; /* the most recent serial number */ @ But how are dependency lists represented? It's simple: The linear combination $\alpha_1v_1+\cdots+\alpha_kv_k+\beta$ appears in |k+1| value nodes. If -|q=dep_list(p)| points to this list, and if |k>0|, then |value(q)= -@t$\alpha_1$@>| (which is a |fraction|); |mp_info(q)| points to the location +|q=dep_list(p)| points to this list, and if |k>0|, then |dep_value(q)= +@t$\alpha_1$@>| (which is a |fraction|); |dep_info(q)| points to the location of $\alpha_1$; and |mp_link(p)| points to the dependency list $\alpha_2v_2+\cdots+\alpha_kv_k+\beta$. On the other hand if |k=0|, -then |value(q)=@t$\beta$@>| (which is |scaled|) and |mp_info(q)=null|. +then |dep_value(q)=@t$\beta$@>| (which is |scaled|) and |dep_info(q)=NULL|. The independent variables $v_1$, \dots,~$v_k$ have been sorted so that they appear in decreasing order of their |value| fields (i.e., of their serial numbers). \ (It is convenient to use decreasing order, -since |value(null)=0|. If the independent variables were not sorted by +since |value(NULL)=0|. If the independent variables were not sorted by serial number but by some other criterion, such as their location in |mem|, the equation-solving mechanism would be too system-dependent, because the ordering can affect the computed results.) @@ -12289,15 +12887,73 @@ points to its dependency list. If the final link of that dependency list occurs in location~|q|, then |mp_link(q)| points to the next dependent variable (say~|r|); and we have |prev_dep(r)=q|, etc. -@d dep_list(A) mp_link(value_loc((A))) - /* half of the |value| field in a |dependent| variable */ -@d prev_dep(A) mp_info(value_loc((A))) - /* the other half; makes a doubly linked list */ -@d dep_node_size 2 /* the number of words per dependency node */ +Dependency nodes sometimes mutate into value nodes and vice versa, so their +structures have to match. + +@d dep_value(A) ((mp_value_node)(A))->data.val /* half of the |value| field in a |dependent| variable */ +@d set_dep_value(A,B) do { + ((mp_value_node)(A))->data.val=(B); /* half of the |value| field in a |dependent| variable */ + FUNCTION_TRACE4("set_dep_value(%p,%d) on %d\n",A,B,__LINE__); + set_dep_list((A), NULL); + set_prev_dep((A), NULL); + } while (0) +@d dep_info(A) get_dep_info(mp,(A)) +@d set_dep_info(A,B) do { + mp_value_node d = (mp_value_node)(B); + FUNCTION_TRACE4("set_dep_info(%p,%p) on %d\n",A,d,__LINE__); + ((mp_value_node)(A))->parent_ = (mp_node)d; +} while (0) +@d dep_list(A) ((mp_value_node)(A))->attr_head_ /* half of the |value| field in a |dependent| variable */ +@d set_dep_list(A,B) do { + mp_value_node d = (mp_value_node)(B); + FUNCTION_TRACE4("set_dep_list(%p,%p) on %d\n",A,d,__LINE__); + dep_list((A)) = (mp_node)d; +} while (0) +@d prev_dep(A) ((mp_value_node)(A))->subscr_head_ /* the other half; makes a doubly linked list */ +@d set_prev_dep(A,B) do { + mp_value_node d = (mp_value_node)(B); + FUNCTION_TRACE4("set_prev_dep(%p,%p) on %d\n",A,d,__LINE__); + prev_dep((A)) = (mp_node)d; +} while (0) + +@c +static mp_node get_dep_info (MP mp, mp_value_node p) { + mp_node d; + d = p->parent_; /* half of the |value| field in a |dependent| variable */ + FUNCTION_TRACE3 ("%p = dep_info(%p)\n", d, p); + return d; +} + -@<Initialize table entries...@>= mp->serial_no=0; -mp_link(dep_head)=dep_head; prev_dep(dep_head)=dep_head; -mp_info(dep_head)=null; dep_list(dep_head)=null; +@ @<Declarations...@>= +static mp_node get_dep_info (MP mp, mp_value_node p); + +@ + +@c +static mp_value_node mp_get_dep_node (MP mp) { + mp_value_node p = (mp_value_node) mp_get_value_node (mp); + mp_type (p) = mp_dep_node_type; + return p; +} +static void mp_free_dep_node (MP mp, mp_value_node p) { + mp_free_node (mp, (mp_node) p, value_node_size); +} + + +@ @<Declarations...@>= +static void mp_free_dep_node (MP mp, mp_value_node p); + +@ @<Initialize table entries@>= +mp->serial_no = 0; +mp->dep_head = mp_get_dep_node (mp); +set_mp_link (mp->dep_head, (mp_node) mp->dep_head); +set_prev_dep (mp->dep_head, (mp_node) mp->dep_head); +set_dep_info (mp->dep_head, NULL); +set_dep_list (mp->dep_head, NULL); + +@ @<Free table entries@>= +mp_free_dep_node (mp, mp->dep_head); @ Actually the description above contains a little white lie. There's another kind of variable called |mp_proto_dependent|, which is @@ -12311,51 +12967,69 @@ The second parameter should be either |dependent| or |mp_proto_dependent|, to indicate the scaling of the coefficients. @<Declarations@>= -static void mp_print_dependency (MP mp,pointer p, quarterword t); +static void mp_print_dependency (MP mp, mp_value_node p, quarterword t); @ @c -void mp_print_dependency (MP mp,pointer p, quarterword t) { - integer v; /* a coefficient */ - pointer pp,q; /* for list manipulation */ - pp=p; - while (true) { - v=abs(value(p)); q=mp_info(p); - if ( q==null ) { /* the constant term */ - if ( (v!=0)||(p==pp) ) { - if ( value(p)>0 ) if ( p!=pp ) mp_print_char(mp, xord('+')); - mp_print_scaled(mp, value(p)); +void mp_print_dependency (MP mp, mp_value_node p, quarterword t) { + integer v; /* a coefficient */ + mp_value_node pp; /* for list manipulation */ + mp_node q; + pp = p; + while (true) { + v = abs (dep_value (p)); + q = dep_info (p); + if (q == NULL) { /* the constant term */ + if ((v != 0) || (p == pp)) { + if (dep_value (p) > 0) + if (p != pp) + mp_print_char (mp, xord ('+')); + mp_print_scaled (mp, dep_value (p)); } return; } @<Print the coefficient, unless it's $\pm1.0$@>; - if ( mp_type(q)!=mp_independent ) mp_confusion(mp, "dep"); -@:this can't happen dep}{\quad dep@> - mp_print_variable_name(mp, q); v=value(q) % s_scale; - while ( v>0 ) { mp_print(mp, "*4"); v=v-2; } - p=mp_link(p); + if (mp_type (q) != mp_independent) + mp_confusion (mp, "dep"); +@:this can't happen dep}{\quad dep@>; + mp_print_variable_name (mp, q); + v = indep_scale(q); + while (v > 0) { + mp_print (mp, "*4"); + v = v - 2; + } + p = (mp_value_node) mp_link (p); } } + @ @<Print the coefficient, unless it's $\pm1.0$@>= -if ( value(p)<0 ) mp_print_char(mp, xord('-')); -else if ( p!=pp ) mp_print_char(mp, xord('+')); -if ( t==mp_dependent ) v=mp_round_fraction(mp, v); -if ( v!=unity ) mp_print_scaled(mp, v) +if (dep_value (p) < 0) + mp_print_char (mp, xord ('-')); +else if (p != pp) + mp_print_char (mp, xord ('+')); +if (t == mp_dependent) + v = mp_round_fraction (mp, v); +if (v != unity) + mp_print_scaled (mp, v) + @ The maximum absolute value of a coefficient in a given dependency list is returned by the following simple function. -@c -static fraction mp_max_coef (MP mp,pointer p) { - fraction x; /* the maximum so far */ - x=0; - while ( mp_info(p)!=null ) { - if ( abs(value(p))>x ) x=abs(value(p)); - p=mp_link(p); +@c +static fraction mp_max_coef (MP mp, mp_value_node p) { + fraction x; /* the maximum so far */ + (void) mp; + x = 0; + while (dep_info (p) != NULL) { + if (abs (dep_value (p)) > x) + x = abs (dep_value (p)); + p = (mp_value_node) mp_link (p); } return x; } + @ One of the main operations needed on dependency lists is to add a multiple of one list to the other; we call this |p_plus_fq|, where |p| and~|q| point to dependency lists and |f| is a fraction. @@ -12381,12 +13055,13 @@ the dependency list that they produce. @d independent_needing_fix 0 @<Glob...@>= -boolean fix_needed; /* does at least one |independent| variable need scaling? */ -boolean watch_coefs; /* should we scale coefficients that exceed |coef_bound|? */ -pointer dep_final; /* location of the constant term and final link */ +boolean fix_needed; /* does at least one |independent| variable need scaling? */ +boolean watch_coefs; /* should we scale coefficients that exceed |coef_bound|? */ +mp_value_node dep_final; /* location of the constant term and final link */ @ @<Set init...@>= -mp->fix_needed=false; mp->watch_coefs=true; +mp->fix_needed = false; +mp->watch_coefs = true; @ The |p_plus_fq| procedure has a fourth parameter, |t|, that should be set to |mp_proto_dependent| if |p| is a proto-dependency list. In this @@ -12412,121 +13087,182 @@ proto-dependencies. @d half_scaled_threshold 4 /* half of |scaled_threshold| */ @<Declarations@>= -static pointer mp_p_plus_fq ( MP mp, pointer p, integer f, - pointer q, quarterword t, quarterword tt) ; +static mp_value_node mp_p_plus_fq (MP mp, mp_value_node p, integer f, + mp_value_node q, mp_variable_type t, + mp_variable_type tt); @ @c -pointer mp_p_plus_fq ( MP mp, pointer p, integer f, - pointer q, quarterword t, quarterword tt) { - pointer pp,qq; /* |mp_info(p)| and |mp_info(q)|, respectively */ - pointer r,s; /* for list manipulation */ - integer threshold; /* defines a neighborhood of zero */ - integer v; /* temporary register */ - if ( t==mp_dependent ) threshold=fraction_threshold; - else threshold=scaled_threshold; - r=temp_head; pp=mp_info(p); qq=mp_info(q); +static mp_value_node mp_p_plus_fq (MP mp, mp_value_node p, integer f, + mp_value_node q, mp_variable_type t, + mp_variable_type tt) { + mp_node pp, qq; /* |dep_info(p)| and |dep_info(q)|, respectively */ + mp_value_node r, s; /* for list manipulation */ + integer threshold; /* defines a neighborhood of zero */ + integer v, vv; /* temporary registers */ + if (t == mp_dependent) + threshold = fraction_threshold; + else + threshold = scaled_threshold; + r = (mp_value_node) mp->temp_head; + pp = dep_info (p); + qq = dep_info (q); while (1) { - if ( pp==qq ) { - if ( pp==null ) { - break; + if (pp == qq) { + if (pp == NULL) { + break; } else { @<Contribute a term from |p|, plus |f| times the corresponding term from |q|@> } - } else if ( value(pp)<value(qq) ) { - @<Contribute a term from |q|, multiplied by~|f|@> - } else { - mp_link(r)=p; r=p; p=mp_link(p); pp=mp_info(p); + } else { + v = (pp == NULL ? 0 : value (pp)); + vv = (qq == NULL ? 0 : value (qq)); + if (v < vv) { + @<Contribute a term from |q|, multiplied by~|f|@> + } else { + set_mp_link (r, (mp_node) p); + r = p; + p = (mp_value_node) mp_link (p); + pp = dep_info (p); + } } } - if ( t==mp_dependent ) - value(p)=mp_slow_add(mp, value(p),mp_take_fraction(mp, value(q),f)); - else - value(p)=mp_slow_add(mp, value(p),mp_take_scaled(mp, value(q),f)); - mp_link(r)=p; mp->dep_final=p; - return mp_link(temp_head); + if (t == mp_dependent) + set_dep_value (p, + mp_slow_add (mp, dep_value (p), + mp_take_fraction (mp, dep_value (q), f))); + else + set_dep_value (p, + mp_slow_add (mp, dep_value (p), + mp_take_scaled (mp, dep_value (q), f))); + set_mp_link (r, (mp_node) p); + mp->dep_final = p; + return (mp_value_node) mp_link (mp->temp_head); } + @ @<Contribute a term from |p|, plus |f|...@>= -{ - if ( tt==mp_dependent ) v=value(p)+mp_take_fraction(mp, f,value(q)); - else v=value(p)+mp_take_scaled(mp, f,value(q)); - value(p)=v; s=p; p=mp_link(p); - if ( abs(v)<threshold ) { - mp_free_node(mp, s,dep_node_size); +{ + if (tt == mp_dependent) + v = dep_value (p) + mp_take_fraction (mp, f, dep_value (q)); + else + v = dep_value (p) + mp_take_scaled (mp, f, dep_value (q)); + set_dep_value (p, v); + s = p; + p = (mp_value_node) mp_link (p); + if (abs (v) < threshold) { + mp_free_dep_node (mp, s); } else { - if ( (abs(v)>=coef_bound) && mp->watch_coefs ) { - mp_type(qq)=independent_needing_fix; mp->fix_needed=true; + if ((abs (v) >= coef_bound) && mp->watch_coefs) { + mp_type (qq) = independent_needing_fix; + mp->fix_needed = true; } - mp_link(r)=s; r=s; - }; - pp=mp_info(p); q=mp_link(q); qq=mp_info(q); + set_mp_link (r, (mp_node) s); + r = s; + } + pp = dep_info (p); + q = (mp_value_node) mp_link (q); + qq = dep_info (q); } + @ @<Contribute a term from |q|, multiplied by~|f|@>= -{ - if ( tt==mp_dependent ) v=mp_take_fraction(mp, f,value(q)); - else v=mp_take_scaled(mp, f,value(q)); - if ( abs(v)>halfp(threshold) ) { - s=mp_get_node(mp, dep_node_size); mp_info(s)=qq; value(s)=v; - if ( (abs(v)>=coef_bound) && mp->watch_coefs ) { - mp_type(qq)=independent_needing_fix; mp->fix_needed=true; +{ + if (tt == mp_dependent) + v = mp_take_fraction (mp, f, dep_value (q)); + else + v = mp_take_scaled (mp, f, dep_value (q)); + if (abs (v) > halfp (threshold)) { + s = mp_get_dep_node (mp); + set_dep_info (s, qq); + set_dep_value (s, v); + if ((abs (v) >= coef_bound) && mp->watch_coefs) { + mp_type (qq) = independent_needing_fix; + mp->fix_needed = true; } - mp_link(r)=s; r=s; + set_mp_link (r, (mp_node) s); + r = s; } - q=mp_link(q); qq=mp_info(q); + q = (mp_value_node) mp_link (q); + qq = dep_info (q); } + @ It is convenient to have another subroutine for the special case of |p_plus_fq| when |f=1.0|. In this routine lists |p| and |q| are both of the same type~|t| (either |dependent| or |mp_proto_dependent|). -@c -static pointer mp_p_plus_q (MP mp,pointer p, pointer q, quarterword t) { - pointer pp,qq; /* |mp_info(p)| and |mp_info(q)|, respectively */ - pointer r,s; /* for list manipulation */ - integer threshold; /* defines a neighborhood of zero */ - integer v; /* temporary register */ - if ( t==mp_dependent ) threshold=fraction_threshold; - else threshold=scaled_threshold; - r=temp_head; pp=mp_info(p); qq=mp_info(q); +@c +static mp_value_node mp_p_plus_q (MP mp, mp_value_node p, mp_value_node q, + mp_variable_type t) { + mp_node pp, qq; /* |dep_info(p)| and |dep_info(q)|, respectively */ + mp_value_node s; /* for list manipulation */ + mp_value_node r; /* for list manipulation */ + integer threshold; /* defines a neighborhood of zero */ + integer v, vv; /* temporary register */ + if (t == mp_dependent) + threshold = fraction_threshold; + else + threshold = scaled_threshold; + r = (mp_value_node) mp->temp_head; + pp = dep_info (p); + qq = dep_info (q); while (1) { - if ( pp==qq ) { - if ( pp==null ) { + if (pp == qq) { + if (pp == NULL) { break; } else { @<Contribute a term from |p|, plus the corresponding term from |q|@> } - } else { - if ( value(pp)<value(qq) ) { - s=mp_get_node(mp, dep_node_size); mp_info(s)=qq; value(s)=value(q); - q=mp_link(q); qq=mp_info(q); mp_link(r)=s; r=s; - } else { - mp_link(r)=p; r=p; p=mp_link(p); pp=mp_info(p); + } else { + v = (pp == NULL ? 0 : value (pp)); + vv = (qq == NULL ? 0 : value (qq)); + if (v < vv) { + s = mp_get_dep_node (mp); + set_dep_info (s, qq); + set_dep_value (s, dep_value (q)); + q = (mp_value_node) mp_link (q); + qq = dep_info (q); + set_mp_link (r, (mp_node) s); + r = s; + } else { + set_mp_link (r, (mp_node) p); + r = p; + p = (mp_value_node) mp_link (p); + pp = dep_info (p); } } } - value(p)=mp_slow_add(mp, value(p),value(q)); - mp_link(r)=p; mp->dep_final=p; - return mp_link(temp_head); + set_dep_value (p, mp_slow_add (mp, dep_value (p), dep_value (q))); + set_mp_link (r, (mp_node) p); + mp->dep_final = p; + return (mp_value_node) mp_link (mp->temp_head); } + @ @<Contribute a term from |p|, plus the...@>= -{ - v=value(p)+value(q); - value(p)=v; s=p; p=mp_link(p); pp=mp_info(p); - if ( abs(v)<threshold ) { - mp_free_node(mp, s,dep_node_size); - } else { - if ( (abs(v)>=coef_bound ) && mp->watch_coefs ) { - mp_type(qq)=independent_needing_fix; mp->fix_needed=true; +{ + v = dep_value (p) + dep_value (q); + set_dep_value (p, v); + s = p; + p = (mp_value_node) mp_link (p); + pp = dep_info (p); + if (abs (v) < threshold) { + mp_free_dep_node (mp, s); + } else { + if ((abs (v) >= coef_bound) && mp->watch_coefs) { + mp_type (qq) = independent_needing_fix; + mp->fix_needed = true; } - mp_link(r)=s; r=s; + set_mp_link (r, (mp_node) s); + r = s; } - q=mp_link(q); qq=mp_info(q); + q = (mp_value_node) mp_link (q); + qq = dep_info (q); } + @ A somewhat simpler routine will multiply a dependency list by a given constant~|v|. The constant is either a |fraction| less than |fraction_one|, or it is |scaled|. In the latter case we might be forced to @@ -12535,73 +13271,105 @@ Parameters |t0| and |t1| are the list types before and after; they should agree unless |t0=mp_dependent| and |t1=mp_proto_dependent| and |v_is_scaled=true|. -@c -static pointer mp_p_times_v (MP mp,pointer p, integer v, quarterword t0, - quarterword t1, boolean v_is_scaled) { - pointer r,s; /* for list manipulation */ - integer w; /* tentative coefficient */ +@c +static mp_value_node mp_p_times_v (MP mp, mp_value_node p, integer v, + quarterword t0, quarterword t1, + boolean v_is_scaled) { + mp_value_node r, s; /* for list manipulation */ + integer w; /* tentative coefficient */ integer threshold; boolean scaling_down; - if ( t0!=t1 ) scaling_down=true; else scaling_down=(!v_is_scaled); - if ( t1==mp_dependent ) threshold=half_fraction_threshold; - else threshold=half_scaled_threshold; - r=temp_head; - while ( mp_info(p)!=null ) { - if ( scaling_down ) w=mp_take_fraction(mp, v,value(p)); - else w=mp_take_scaled(mp, v,value(p)); - if ( abs(w)<=threshold ) { - s=mp_link(p); mp_free_node(mp, p,dep_node_size); p=s; + if (t0 != t1) + scaling_down = true; + else + scaling_down = (!v_is_scaled); + if (t1 == mp_dependent) + threshold = half_fraction_threshold; + else + threshold = half_scaled_threshold; + r = (mp_value_node) mp->temp_head; + while (dep_info (p) != NULL) { + if (scaling_down) + w = mp_take_fraction (mp, v, dep_value (p)); + else + w = mp_take_scaled (mp, v, dep_value (p)); + if (abs (w) <= threshold) { + s = (mp_value_node) mp_link (p); + mp_free_dep_node (mp, p); + p = s; } else { - if ( abs(w)>=coef_bound ) { - mp->fix_needed=true; mp_type(mp_info(p))=independent_needing_fix; + if (abs (w) >= coef_bound) { + mp->fix_needed = true; + mp_type (dep_info (p)) = independent_needing_fix; } - mp_link(r)=p; r=p; value(p)=w; p=mp_link(p); + set_mp_link (r, (mp_node) p); + r = p; + set_dep_value (p, w); + p = (mp_value_node) mp_link (p); } } - mp_link(r)=p; - if ( v_is_scaled ) value(p)=mp_take_scaled(mp, value(p),v); - else value(p)=mp_take_fraction(mp, value(p),v); - return mp_link(temp_head); + set_mp_link (r, (mp_node) p); + if (v_is_scaled) + set_dep_value (p, mp_take_scaled (mp, dep_value (p), v)); + else + set_dep_value (p, mp_take_fraction (mp, dep_value (p), v)); + return (mp_value_node) mp_link (mp->temp_head); } + @ Similarly, we sometimes need to divide a dependency list by a given |scaled| constant. @<Declarations@>= -static pointer mp_p_over_v (MP mp,pointer p, scaled v, quarterword - t0, quarterword t1) ; +static mp_value_node mp_p_over_v (MP mp, mp_value_node p, scaled v, quarterword + t0, quarterword t1); @ @c -pointer mp_p_over_v (MP mp,pointer p, scaled v, quarterword - t0, quarterword t1) { - pointer r,s; /* for list manipulation */ - integer w; /* tentative coefficient */ +mp_value_node mp_p_over_v (MP mp, mp_value_node p, scaled v, quarterword + t0, quarterword t1) { + mp_value_node r, s; /* for list manipulation */ + integer w; /* tentative coefficient */ integer threshold; boolean scaling_down; - if ( t0!=t1 ) scaling_down=true; else scaling_down=false; - if ( t1==mp_dependent ) threshold=half_fraction_threshold; - else threshold=half_scaled_threshold; - r=temp_head; - while ( mp_info( p)!=null ) { - if ( scaling_down ) { - if ( abs(v)<02000000 ) w=mp_make_scaled(mp, value(p),v*010000); - else w=mp_make_scaled(mp, mp_round_fraction(mp, value(p)),v); + if (t0 != t1) + scaling_down = true; + else + scaling_down = false; + if (t1 == mp_dependent) + threshold = half_fraction_threshold; + else + threshold = half_scaled_threshold; + r = (mp_value_node) mp->temp_head; + while (dep_info (p) != NULL) { + if (scaling_down) { + if (abs (v) < 02000000) + w = mp_make_scaled (mp, dep_value (p), v * 010000); + else + w = mp_make_scaled (mp, mp_round_fraction (mp, dep_value (p)), v); } else { - w=mp_make_scaled(mp, value(p),v); + w = mp_make_scaled (mp, dep_value (p), v); } - if ( abs(w)<=threshold ) { - s=mp_link(p); mp_free_node(mp, p,dep_node_size); p=s; - } else { - if ( abs(w)>=coef_bound ) { - mp->fix_needed=true; mp_type(mp_info(p))=independent_needing_fix; + if (abs (w) <= threshold) { + s = (mp_value_node) mp_link (p); + mp_free_dep_node (mp, p); + p = s; + } else { + if (abs (w) >= coef_bound) { + mp->fix_needed = true; + mp_type (dep_info (p)) = independent_needing_fix; } - mp_link(r)=p; r=p; value(p)=w; p=mp_link(p); + set_mp_link (r, (mp_node) p); + r = p; + set_dep_value (p, w); + p = (mp_value_node) mp_link (p); } } - mp_link(r)=p; value(p)=mp_make_scaled(mp, value(p),v); - return mp_link(temp_head); + set_mp_link (r, (mp_node) p); + set_dep_value (p, mp_make_scaled (mp, dep_value (p), v)); + return (mp_value_node) mp_link (mp->temp_head); } + @ Here's another utility routine for dependency lists. When an independent variable becomes dependent, we want to remove it from all existing dependencies. The |p_with_x_becoming_q| function computes the @@ -12613,67 +13381,91 @@ final link are inherited from~|p|; and the fourth parameter tells whether or not |p| is |mp_proto_dependent|. However, the global variable |dep_final| is not altered if |x| does not occur in list~|p|. -@c -static pointer mp_p_with_x_becoming_q (MP mp,pointer p, - pointer x, pointer q, quarterword t) { - pointer r,s; /* for list manipulation */ - integer v; /* coefficient of |x| */ - integer sx; /* serial number of |x| */ - s=p; r=temp_head; sx=value(x); - while ( value(mp_info(s))>sx ) { r=s; s=mp_link(s); }; - if ( mp_info(s)!=x ) { +@c +static mp_value_node mp_p_with_x_becoming_q (MP mp, mp_value_node p, + mp_node x, mp_node q, + quarterword t) { + mp_value_node r, s; /* for list manipulation */ + integer v; /* coefficient of |x| */ + integer sx; /* serial number of |x| */ + s = p; + r = (mp_value_node) mp->temp_head; + sx = value (x); + while (dep_info (s) != NULL && value (dep_info (s)) > sx) { + r = s; + s = (mp_value_node) mp_link (s); + } + if (dep_info (s) != x) { return p; - } else { - mp_link(temp_head)=p; mp_link(r)=mp_link(s); v=value(s); - mp_free_node(mp, s,dep_node_size); - return mp_p_plus_fq(mp, mp_link(temp_head),v,q,t,mp_dependent); + } else { + set_mp_link (mp->temp_head, (mp_node) p); + set_mp_link (r, mp_link (s)); + v = dep_value (s); + mp_free_dep_node (mp, s); + return mp_p_plus_fq (mp, (mp_value_node) mp_link (mp->temp_head), v, + (mp_value_node) q, t, mp_dependent); } } + @ Here's a simple procedure that reports an error when a variable has just received a known value that's out of the required range. @<Declarations@>= -static void mp_val_too_big (MP mp,scaled x) ; +static void mp_val_too_big (MP mp, scaled x); -@ @c void mp_val_too_big (MP mp,scaled x) { - if ( mp->internal[mp_warning_check]>0 ) { - print_err("Value is too large ("); mp_print_scaled(mp, x); mp_print_char(mp, xord(')')); -@.Value is too large@> - help4("The equation I just processed has given some variable", - "a value of 4096 or more. Continue and I'll try to cope", - "with that big value; but it might be dangerous.", - "(Set warningcheck:=0 to suppress this message.)"); - mp_error(mp); +@ @c +void mp_val_too_big (MP mp, scaled x) { + if (internal_value (mp_warning_check) > 0) { + print_err ("Value is too large ("); + mp_print_scaled (mp, x); + mp_print_char (mp, xord (')')); +@.Value is too large@>; + help4 ("The equation I just processed has given some variable", + "a value of 4096 or more. Continue and I'll try to cope", + "with that big value; but it might be dangerous.", + "(Set warningcheck:=0 to suppress this message.)"); + mp_error (mp); } } + @ When a dependent variable becomes known, the following routine removes its dependency list. Here |p| points to the variable, and |q| points to the dependency list (which is one node long). @<Declarations@>= -static void mp_make_known (MP mp,pointer p, pointer q) ; +static void mp_make_known (MP mp, mp_value_node p, mp_value_node q); -@ @c void mp_make_known (MP mp,pointer p, pointer q) { - int t; /* the previous type */ - prev_dep(mp_link(q))=prev_dep(p); - mp_link(prev_dep(p))=mp_link(q); t=mp_type(p); - mp_type(p)=mp_known; value(p)=value(q); mp_free_node(mp, q,dep_node_size); - if ( abs(value(p))>=fraction_one ) mp_val_too_big(mp, value(p)); - if (( mp->internal[mp_tracing_equations]>0) && mp_interesting(mp, p) ) { - mp_begin_diagnostic(mp); mp_print_nl(mp, "#### "); -@:]]]\#\#\#\#_}{\.{\#\#\#\#}@> - mp_print_variable_name(mp, p); - mp_print_char(mp, xord('=')); mp_print_scaled(mp, value(p)); - mp_end_diagnostic(mp, false); - } - if (( mp->cur_exp==p ) && mp->cur_type==t ) { - mp->cur_type=mp_known; mp->cur_exp=value(p); - mp_free_node(mp, p,value_node_size); +@ @c +void mp_make_known (MP mp, mp_value_node p, mp_value_node q) { + mp_variable_type t; /* the previous type */ + set_prev_dep (mp_link (q), prev_dep (p)); + set_mp_link (prev_dep (p), mp_link (q)); + t = mp_type (p); + mp_type (p) = mp_known; + set_value (p, dep_value (q)); + mp_free_dep_node (mp, q); + if (abs (value (p)) >= fraction_one) + mp_val_too_big (mp, value (p)); + if ((internal_value (mp_tracing_equations) > 0) + && mp_interesting (mp, (mp_node) p)) { + mp_begin_diagnostic (mp); + mp_print_nl (mp, "#### "); +@:]]]\#\#\#\#_}{\.{\#\#\#\#}@>; + mp_print_variable_name (mp, (mp_node) p); + mp_print_char (mp, xord ('=')); + mp_print_scaled (mp, value (p)); + mp_end_diagnostic (mp, false); + } + if (cur_exp_node () == (mp_node) p && mp->cur_exp.type == t) { + mp->cur_exp.type = mp_known; + set_cur_exp_value (value (p)); + mp_free_node (mp, (mp_node) p, value_node_size); } } + @ The |fix_dependencies| routine is called into action when |fix_needed| has been triggered. The program keeps a list~|s| of independent variables whose coefficients must be divided by~4. @@ -12684,68 +13476,93 @@ to zero, so that a variable will become known more or less by default. @<Declarations@>= static void mp_fix_dependencies (MP mp); -@ @c +@ @c static void mp_fix_dependencies (MP mp) { - pointer p,q,r,s,t; /* list manipulation registers */ - pointer x; /* an independent variable */ - r=mp_link(dep_head); s=null; - while ( r!=dep_head ){ - t=r; + mp_value_node p, q, r, s, t; /* list manipulation registers */ + mp_node x; /* an independent variable */ + r = (mp_value_node) mp_link (mp->dep_head); + s = NULL; + while (r != mp->dep_head) { + t = r; @<Run through the dependency list for variable |t|, fixing all nodes, and ending with final link~|q|@>; - r=mp_link(q); - if ( q==dep_list(t) ) mp_make_known(mp, t,q); + r = (mp_value_node) mp_link (q); + if (q == (mp_value_node) dep_list (t)) + mp_make_known (mp, t, q); } - while ( s!=null ) { - p=mp_link(s); x=mp_info(s); free_avail(s); s=p; - mp_type(x)=mp_independent; value(x)=value(x)+2; + while (s != NULL) { + p = (mp_value_node) mp_link (s); + x = dep_info (s); + mp_free_dep_node (mp, s); + s = p; + mp_type (x) = mp_independent; + set_indep_scale (x, indep_scale (x) + 2); } - mp->fix_needed=false; + mp->fix_needed = false; } + @ @d independent_being_fixed 1 /* this variable already appears in |s| */ @<Run through the dependency list for variable |t|...@>= -r=value_loc(t); /* |mp_link(r)=dep_list(t)| */ -while (1) { - q=mp_link(r); x=mp_info(q); - if ( x==null ) break; - if ( mp_type(x)<=independent_being_fixed ) { - if ( mp_type(x)<independent_being_fixed ) { - p=mp_get_avail(mp); mp_link(p)=s; s=p; - mp_info(s)=x; mp_type(x)=independent_being_fixed; +set_mp_link (r, dep_list (t)); /* start off one item before the actual |dep_list| */ +while (1) { + q = (mp_value_node) mp_link (r); + x = dep_info (q); + if (x == NULL) + break; + if (mp_type (x) <= independent_being_fixed) { + if (mp_type (x) < independent_being_fixed) { + p = mp_get_dep_node (mp); + set_mp_link (p, (mp_node) s); + s = p; + set_dep_info (s, x); + mp_type (x) = independent_being_fixed; } - value(q)=value(q) / 4; - if ( value(q)==0 ) { - mp_link(r)=mp_link(q); mp_free_node(mp, q,dep_node_size); q=r; + set_dep_value (q, dep_value (q) / 4); + if (dep_value (q) == 0) { + set_mp_link (r, mp_link (q)); + mp_free_dep_node (mp, q); + q = r; } } - r=q; + r = q; } -@ The |new_dep| routine installs a dependency list~|p| into the value node~|q|, -linking it into the list of all known dependencies. We assume that -|dep_final| points to the final node of list~|p|. +@ The |new_dep| routine installs a dependency list~|p| based on the value node~|q|, +linking it into the list of all known dependencies. It replaces |q| with the new +dependency node. We assume that |dep_final| points to the final node of list~|p|. -@c -static void mp_new_dep (MP mp,pointer q, pointer p) { - pointer r; /* what used to be the first dependency */ - dep_list(q)=p; prev_dep(q)=dep_head; - r=mp_link(dep_head); mp_link(mp->dep_final)=r; prev_dep(r)=mp->dep_final; - mp_link(dep_head)=q; +@c +static void mp_new_dep (MP mp, mp_node q, mp_variable_type newtype, + mp_value_node p) { + mp_node r; /* what used to be the first dependency */ + FUNCTION_TRACE4 ("mp_new_dep(%p,%d,%p)\n", q, newtype, p); + mp_type (q) = newtype; + set_dep_list (q, (mp_node) p); + set_prev_dep (q, (mp_node) mp->dep_head); + r = mp_link (mp->dep_head); + set_mp_link (mp->dep_final, r); + set_prev_dep (r, (mp_node) mp->dep_final); + set_mp_link (mp->dep_head, q); } + @ Here is one of the ways a dependency list gets started. The |const_dependency| routine produces a list that has nothing but a constant term. -@c static pointer mp_const_dependency (MP mp, scaled v) { - mp->dep_final=mp_get_node(mp, dep_node_size); - value(mp->dep_final)=v; mp_info(mp->dep_final)=null; +@c +static mp_value_node mp_const_dependency (MP mp, scaled v) { + mp->dep_final = mp_get_dep_node (mp); + set_dep_value (mp->dep_final, v); + set_dep_info (mp->dep_final, NULL); + FUNCTION_TRACE3 ("%p = mp_const_dependency(%d)\n", mp->dep_final, v); return mp->dep_final; } + @ And here's a more interesting way to start a dependency list from scratch: The parameter to |single_dependency| is the location of an independent variable~|x|, and the result is the simple dependency list @@ -12757,36 +13574,48 @@ often that we can't refer to it with a nonzero coefficient, recognized by testing that the returned list pointer is equal to |dep_final|. -@c -static pointer mp_single_dependency (MP mp,pointer p) { - pointer q; /* the new dependency list */ - integer m; /* the number of doublings */ - m=value(p) % s_scale; - if ( m>28 ) { - return mp_const_dependency(mp, 0); - } else { - q=mp_get_node(mp, dep_node_size); - value(q)=(integer)two_to_the(28-m); mp_info(q)=p; - mp_link(q)=mp_const_dependency(mp, 0); - return q; +@d two_to_the(A) (1<<(unsigned)(A)) + +@c +static mp_value_node mp_single_dependency (MP mp, mp_node p) { + mp_value_node q, rr; /* the new dependency list */ + integer m; /* the number of doublings */ + m = indep_scale (p); + if (m > 28) { + q = mp_const_dependency (mp, 0); + } else { + q = mp_get_dep_node (mp); + set_dep_value (q, (integer) two_to_the (28 - m)); + set_dep_info (q, p); + rr = mp_const_dependency (mp, 0); + set_mp_link (q, (mp_node) rr); } + FUNCTION_TRACE3 ("%p = mp_single_dependency(%p)\n", q, p); + return q; } + @ We sometimes need to make an exact copy of a dependency list. -@c -static pointer mp_copy_dep_list (MP mp,pointer p) { - pointer q; /* the new dependency list */ - q=mp_get_node(mp, dep_node_size); mp->dep_final=q; - while (1) { - mp_info(mp->dep_final)=mp_info(p); value(mp->dep_final)=value(p); - if ( mp_info(mp->dep_final)==null ) break; - mp_link(mp->dep_final)=mp_get_node(mp, dep_node_size); - mp->dep_final=mp_link(mp->dep_final); p=mp_link(p); +@c +static mp_value_node mp_copy_dep_list (MP mp, mp_value_node p) { + mp_value_node q; /* the new dependency list */ + FUNCTION_TRACE2 ("mp_copy_dep_list(%p)\n", p); + q = mp_get_dep_node (mp); + mp->dep_final = q; + while (1) { + set_dep_info (mp->dep_final, dep_info (p)); + set_dep_value (mp->dep_final, dep_value (p)); + if (dep_info (mp->dep_final) == NULL) + break; + set_mp_link (mp->dep_final, (mp_node) mp_get_dep_node (mp)); + mp->dep_final = (mp_value_node) mp_link (mp->dep_final); + p = (mp_value_node) mp_link (p); } return q; } + @ But how do variables normally become known? Ah, now we get to the heart of the equation-solving mechanism. The |linear_eq| procedure is given a |dependent| or |mp_proto_dependent| list,~|p|, in which at least one independent variable @@ -12797,139 +13626,159 @@ thereby possibly making other dependent variables known. The given list |p| is, of course, totally destroyed by all this processing. -@c -static void mp_linear_eq (MP mp, pointer p, quarterword t) { - pointer q,r,s; /* for link manipulation */ - pointer x; /* the variable that loses its independence */ - integer n; /* the number of times |x| had been halved */ - integer v; /* the coefficient of |x| in list |p| */ - pointer prev_r; /* lags one step behind |r| */ - pointer final_node; /* the constant term of the new dependency list */ - integer w; /* a tentative coefficient */ - @<Find a node |q| in list |p| whose coefficient |v| is largest@>; - x=mp_info(q); n=value(x) % s_scale; +@c +static void mp_linear_eq (MP mp, mp_value_node p, quarterword t) { + mp_value_node q, r; /* for link manipulation */ + mp_value_node s; + mp_node x; /* the variable that loses its independence */ + integer n; /* the number of times |x| had been halved */ + integer v; /* the coefficient of |x| in list |p| */ + mp_value_node prev_r; /* lags one step behind |r| */ + mp_value_node final_node; /* the constant term of the new dependency list */ + integer w; /* a tentative coefficient */ + FUNCTION_TRACE3 ("mp_linear_eq(%p,%d)\n", p, t); + @<Find a node |q| in list |p| whose coefficient |v| is largest@>; + x = dep_info (q); + n = indep_scale (x); @<Divide list |p| by |-v|, removing node |q|@>; - if ( mp->internal[mp_tracing_equations]>0 ) { + if (internal_value (mp_tracing_equations) > 0) { @<Display the new dependency@>; } @<Simplify all existing dependencies by substituting for |x|@>; @<Change variable |x| from |independent| to |dependent| or |known|@>; - if ( mp->fix_needed ) mp_fix_dependencies(mp); + if (mp->fix_needed) + mp_fix_dependencies (mp); } + @ @<Find a node |q| in list |p| whose coefficient |v| is largest@>= -q=p; r=mp_link(p); v=value(q); -while ( mp_info(r)!=null ) { - if ( abs(value(r))>abs(v) ) { q=r; v=value(r); }; - r=mp_link(r); +q = p; +r = (mp_value_node) mp_link (p); +v = dep_value (q); +while (dep_info (r) != NULL) { + if (abs (dep_value (r)) > abs (v)) { + q = r; + v = dep_value (r); + } + r = (mp_value_node) mp_link (r); } + @ Here we want to change the coefficients from |scaled| to |fraction|, except in the constant term. In the common case of a trivial equation like `\.{x=3.14}', we will have |v=-fraction_one|, |q=p|, and |t=mp_dependent|. @<Divide list |p| by |-v|, removing node |q|@>= -s=temp_head; mp_link(s)=p; r=p; -do { - if ( r==q ) { - mp_link(s)=mp_link(r); mp_free_node(mp, r,dep_node_size); - } else { - w=mp_make_fraction(mp, value(r),v); - if ( abs(w)<=half_fraction_threshold ) { - mp_link(s)=mp_link(r); mp_free_node(mp, r,dep_node_size); - } else { - value(r)=-w; s=r; +s = (mp_value_node) mp->temp_head; +set_mp_link (s, (mp_node) p); +r = p; +do { + if (r == q) { + set_mp_link (s, mp_link (r)); + mp_free_dep_node (mp, r); + } else { + w = mp_make_fraction (mp, dep_value (r), v); + if (abs (w) <= half_fraction_threshold) { + set_mp_link (s, mp_link (r)); + mp_free_dep_node (mp, r); + } else { + set_dep_value (r, -w); + s = r; } } - r=mp_link(s); -} while (mp_info(r)!=null); -if ( t==mp_proto_dependent ) { - value(r)=-mp_make_scaled(mp, value(r),v); -} else if ( v!=-fraction_one ) { - value(r)=-mp_make_fraction(mp, value(r),v); + r = (mp_value_node) mp_link (s); +} while (dep_info (r) != NULL); +if (t == mp_proto_dependent) { + set_dep_value (r, (-mp_make_scaled (mp, dep_value (r), v))); +} else if (v != -fraction_one) { + set_dep_value (r, (-mp_make_fraction (mp, dep_value (r), v))); } -final_node=r; p=mp_link(temp_head) +final_node = r; +p = (mp_value_node) mp_link (mp->temp_head) + @ @<Display the new dependency@>= -if ( mp_interesting(mp, x) ) { - mp_begin_diagnostic(mp); mp_print_nl(mp, "## "); - mp_print_variable_name(mp, x); -@:]]]\#\#_}{\.{\#\#}@> - w=n; - while ( w>0 ) { mp_print(mp, "*4"); w=w-2; }; - mp_print_char(mp, xord('=')); mp_print_dependency(mp, p,mp_dependent); - mp_end_diagnostic(mp, false); +if (mp_interesting (mp, (mp_node) x)) { + mp_begin_diagnostic (mp); + mp_print_nl (mp, "## "); + mp_print_variable_name (mp, (mp_node) x); +@:]]]\#\#_}{\.{\#\#}@>; + w = n; + while (w > 0) { + mp_print (mp, "*4"); + w = w - 2; + } + mp_print_char (mp, xord ('=')); + mp_print_dependency (mp, p, mp_dependent); + mp_end_diagnostic (mp, false); } @ @<Simplify all existing dependencies by substituting for |x|@>= -prev_r=dep_head; r=mp_link(dep_head); -while ( r!=dep_head ) { - s=dep_list(r); q=mp_p_with_x_becoming_q(mp, s,x,p,mp_type(r)); - if ( mp_info(q)==null ) { - mp_make_known(mp, r,q); - } else { - dep_list(r)=q; - do { q=mp_link(q); } while (mp_info(q)!=null); - prev_r=q; +prev_r = (mp_value_node) mp->dep_head; +r = (mp_value_node) mp_link (mp->dep_head); +while (r != mp->dep_head) { + s = (mp_value_node) dep_list (r); + q = mp_p_with_x_becoming_q (mp, s, x, (mp_node) p, mp_type (r)); + if (dep_info (q) == NULL) { + mp_make_known (mp, r, q); + } else { + set_dep_list (r, (mp_node) q); + do { + q = (mp_value_node) mp_link (q); + } while (dep_info (q) != NULL); + prev_r = q; } - r=mp_link(prev_r); + r = (mp_value_node) mp_link (prev_r); } + @ @<Change variable |x| from |independent| to |dependent| or |known|@>= -if ( n>0 ) @<Divide list |p| by $2^n$@>; -if ( mp_info(p)==null ) { - mp_type(x)=mp_known; - value(x)=value(p); - if ( abs(value(x))>=fraction_one ) mp_val_too_big(mp, value(x)); - mp_free_node(mp, p,dep_node_size); - if ( mp->cur_exp==x ) if ( mp->cur_type==mp_independent ) { - mp->cur_exp=value(x); mp->cur_type=mp_known; - mp_free_node(mp, x,value_node_size); +if (n > 0) + @<Divide list |p| by $2^n$@>; +if (dep_info (p) == NULL) { + mp_type (x) = mp_known; + set_value (x, dep_value (p)); + if (abs (value (x)) >= fraction_one) + mp_val_too_big (mp, value (x)); + mp_free_dep_node (mp, p); + if (cur_exp_node () == x && mp->cur_exp.type == mp_independent) { + set_cur_exp_value (value (x)); + mp->cur_exp.type = mp_known; + mp_free_node (mp, x, value_node_size); + } +} else { + mp->dep_final = final_node; + mp_new_dep (mp, x, mp_dependent, p); + if (cur_exp_node () == x && mp->cur_exp.type == mp_independent) { + mp->cur_exp.type = mp_dependent; } -} else { - mp_type(x)=mp_dependent; mp->dep_final=final_node; mp_new_dep(mp, x,p); - if ( mp->cur_exp==x ) if ( mp->cur_type==mp_independent ) mp->cur_type=mp_dependent; } + @ @<Divide list |p| by $2^n$@>= -{ - s=temp_head; mp_link(temp_head)=p; r=p; - do { - if ( n>30 ) w=0; - else w=value(r) / two_to_the(n); - if ( (abs(w)<=half_fraction_threshold)&&(mp_info(r)!=null) ) { - mp_link(s)=mp_link(r); - mp_free_node(mp, r,dep_node_size); - } else { - value(r)=w; s=r; +{ + s = (mp_value_node) mp->temp_head; + set_mp_link (mp->temp_head, (mp_node) p); + r = p; + do { + if (n > 30) + w = 0; + else + w = dep_value (r) / two_to_the (n); + if ((abs (w) <= half_fraction_threshold) && (dep_info (r) != NULL)) { + set_mp_link (s, mp_link (r)); + mp_free_dep_node (mp, r); + } else { + set_dep_value (r, w); + s = r; } - r=mp_link(s); - } while (mp_info(s)!=null); - p=mp_link(temp_head); + r = (mp_value_node) mp_link (s); + } while (dep_info (s) != NULL); + p = (mp_value_node) mp_link (mp->temp_head); } -@ The |check_mem| procedure, which is used only when \MP\ is being -debugged, makes sure that the current dependency lists are well formed. - -@<Check the list of linear dependencies@>= -q=dep_head; p=mp_link(q); -while ( p!=dep_head ) { - if ( prev_dep(p)!=q ) { - mp_print_nl(mp, "Bad PREVDEP at "); mp_print_int(mp, p); -@.Bad PREVDEP...@> - } - p=dep_list(p); - while (1) { - r=mp_info(p); q=p; p=mp_link(q); - if ( r==null ) break; - if ( value(mp_info(p))>=value(r) ) { - mp_print_nl(mp, "Out of order at "); mp_print_int(mp, p); -@.Out of order...@> - } - } -} -@* \[25] Dynamic nonlinear equations. +@* Dynamic nonlinear equations. Variables of numeric type are maintained by the general scheme of independent, dependent, and known values that we have just studied; and the components of pair and transform variables are handled in the @@ -12943,7 +13792,7 @@ either equal to each other, or they're not. Before a boolean variable has received a value, its type is |mp_unknown_boolean|; similarly, there are variables whose type is |mp_unknown_string|, |mp_unknown_pen|, |mp_unknown_path|, and |mp_unknown_picture|. In such cases the value is either -|null| (which means that no other variables are equivalent to this one), or +|NULL| (which means that no other variables are equivalent to this one), or it points to another variable of the same undefined type. The pointers in the latter case form a cycle of nodes, which we shall call a ``ring.'' Rings of undefined variables may include capsules, which arise as @@ -12958,33 +13807,41 @@ lots of memory space. @ The following procedure is called when a capsule node is being added to a ring (e.g., when an unknown variable is mentioned in an expression). -@c -static pointer mp_new_ring_entry (MP mp,pointer p) { - pointer q; /* the new capsule node */ - q=mp_get_node(mp, value_node_size); mp_name_type(q)=mp_capsule; - mp_type(q)=mp_type(p); - if ( value(p)==null ) value(q)=p; else value(q)=value(p); - value(p)=q; +@c +static mp_node mp_new_ring_entry (MP mp, mp_node p) { + mp_node q; /* the new capsule node */ + q = mp_get_value_node (mp); + mp_name_type (q) = mp_capsule; + mp_type (q) = mp_type (p); + if (value_node (p) == NULL) + set_value_node (q, p); + else + set_value_node (q, value_node (p)); + set_value_node (p, q); return q; } + @ Conversely, we might delete a capsule or a variable before it becomes known. The following procedure simply detaches a quantity from its ring, without recycling the storage. @<Declarations@>= -static void mp_ring_delete (MP mp,pointer p); +static void mp_ring_delete (MP mp, mp_node p); @ @c -void mp_ring_delete (MP mp,pointer p) { - pointer q; - q=value(p); - if ( q!=null ) if ( q!=p ){ - while ( value(q)!=p ) q=value(q); - value(q)=value(p); +void mp_ring_delete (MP mp, mp_node p) { + mp_node q; + (void) mp; + q = value_node (p); + if (q != NULL && q != p) { + while (value_node (q) != p) + q = value_node (q); + set_value_node (q, value_node (p)); } } + @ Eventually there might be an equation that assigns values to all of the variables in a ring. The |nonlinear_eq| subroutine does the necessary propagation of values. @@ -12992,52 +13849,76 @@ propagation of values. If the parameter |flush_p| is |true|, node |p| itself needn't receive a value, it will soon be recycled. -@c -static void mp_nonlinear_eq (MP mp,integer v, pointer p, boolean flush_p) { - quarterword t; /* the type of ring |p| */ - pointer q,r; /* link manipulation registers */ - t=(quarterword)(mp_type(p)-unknown_tag); q=value(p); - if ( flush_p ) mp_type(p)=mp_vacuous; else p=q; - do { - r=value(q); mp_type(q)=t; +@c +static void mp_nonlinear_eq (MP mp, mp_value v, mp_node p, boolean flush_p) { + mp_variable_type t; /* the type of ring |p| */ + mp_node q, r; /* link manipulation registers */ + t = (mp_type (p) - unknown_tag); + q = value_node (p); + if (flush_p) + mp_type (p) = mp_vacuous; + else + p = q; + do { + r = value_node (q); + mp_type (q) = t; switch (t) { - case mp_boolean_type: value(q)=v; break; - case mp_string_type: value(q)=v; add_str_ref(v); break; - case mp_pen_type: value(q)=copy_pen(v); break; - case mp_path_type: value(q)=mp_copy_path(mp, v); break; - case mp_picture_type: value(q)=v; add_edge_ref(v); break; - } /* there ain't no more cases */ - q=r; - } while (q!=p); + case mp_boolean_type: + set_value (q, v.data.val); + break; + case mp_string_type: + set_str_value (q, v.data.str); + add_str_ref (v.data.str); + break; + case mp_pen_type: + set_knot_value (q, copy_pen (v.data.p)); + break; + case mp_path_type: + set_knot_value (q, mp_copy_path (mp, v.data.p)); + break; + case mp_picture_type: + set_value_node (q, v.data.node); + add_edge_ref (v.data.node); + break; + default: + break; + } /* there ain't no more cases */ + q = r; + } while (q != p); } + @ If two members of rings are equated, and if they have the same type, the |ring_merge| procedure is called on to make them equivalent. -@c -static void mp_ring_merge (MP mp,pointer p, pointer q) { - pointer r; /* traverses one list */ - r=value(p); - while ( r!=p ) { - if ( r==q ) { +@c +static void mp_ring_merge (MP mp, mp_node p, mp_node q) { + mp_node r; /* traverses one list */ + r = value_node (p); + while (r != p) { + if (r == q) { @<Exclaim about a redundant equation@>; return; }; - r=value(r); + r = value_node (r); } - r=value(p); value(p)=value(q); value(q)=r; + r = value_node (p); + set_value_node (p, value_node (q)); + set_value_node (q, r); } + @ @<Exclaim about a redundant equation@>= -{ - print_err("Redundant equation"); -@.Redundant equation@> - help2("I already knew that this equation was true.", - "But perhaps no harm has been done; let's continue."); - mp_put_get_error(mp); +{ + print_err ("Redundant equation"); +@.Redundant equation@>; + help2 ("I already knew that this equation was true.", + "But perhaps no harm has been done; let's continue."); + mp_put_get_error (mp); } -@* \[26] Introduction to the syntactic routines. + +@* Introduction to the syntactic routines. Let's pause a moment now and try to look at the Big Picture. The \MP\ program consists of three main parts: syntactic routines, semantic routines, and output routines. The chief purpose of the @@ -13080,9 +13961,13 @@ input mechanism. These stacks record the current state of an implicitly recursive process, but the |get_next| procedure is not recursive. @<Glob...@>= -integer cur_cmd; /* current command set by |get_next| */ -integer cur_mod; /* operand of current command */ -halfword cur_sym; /* hash address of current symbol */ +integer cur_cmd; /* current command set by |get_next| */ +integer cur_mod; /* operand of current command */ +mp_node cur_mod_node; /* operand of current command, if it is a node */ +str_number cur_mod_str; /* operand of current command, if it is a string */ +mp_sym cur_sym; /* the current symbol */ +mp_sym cur_sym2; /* a secondary symbol, needed for use of \&{delimiters} */ +quarterword cur_sym_mod; /* the |name_type| of |param_stack| cases of |cur_sym| */ @ The |print_cmd_mod| routine prints a symbolic interpretation of a command code and its modifier. @@ -13093,29 +13978,35 @@ all of this procedure appears elsewhere in the program, together with the corresponding |primitive| calls. @<Declarations@>= -static void mp_print_cmd_mod (MP mp,integer c, integer m) ; +static void mp_print_cmd_mod (MP mp, integer c, integer m); @ @c -void mp_print_cmd_mod (MP mp,integer c, integer m) { - switch (c) { - @<Cases of |print_cmd_mod| for symbolic printing of primitives@> - default: mp_print(mp, "[unknown command code!]"); break; +void mp_print_cmd_mod (MP mp, integer c, integer m) { + switch (c) { + @<Cases of |print_cmd_mod| for symbolic printing of primitives@> + default: + mp_print (mp, "[unknown command code!]"); + break; } } + @ Here is a procedure that displays a given command in braces, in the user's transcript file. @d show_cur_cmd_mod mp_show_cmd_mod(mp, mp->cur_cmd,mp->cur_mod) -@c -static void mp_show_cmd_mod (MP mp,integer c, integer m) { - mp_begin_diagnostic(mp); mp_print_nl(mp, "{"); - mp_print_cmd_mod(mp, c,m); mp_print_char(mp, xord('}')); - mp_end_diagnostic(mp, false); +@c +static void mp_show_cmd_mod (MP mp, integer c, integer m) { + mp_begin_diagnostic (mp); + mp_print_nl (mp, "{"); + mp_print_cmd_mod (mp, c, m); + mp_print_char (mp, xord ('}')); + mp_end_diagnostic (mp, false); } -@* \[27] Input stacks and states. + +@* Input stacks and states. The state of \MP's input mechanism appears in the input stack, whose entries are records with five fields, called |index|, |start|, |loc|, |limit|, and |name|. The top element of this stack is maintained in a @@ -13125,23 +14016,25 @@ elements of the stack appear in an array. Hence the stack is declared thus: @<Types...@>= typedef struct { char *long_name_field; - halfword start_field, loc_field, limit_field, name_field; + halfword start_field, loc_field, limit_field; + mp_node nstart_field, nloc_field; + str_number name_field; quarterword index_field; } in_state_record; @ @<Glob...@>= in_state_record *input_stack; -integer input_ptr; /* first unused location of |input_stack| */ -integer max_in_stack; /* largest value of |input_ptr| when pushing */ -in_state_record cur_input; /* the ``top'' input state */ +integer input_ptr; /* first unused location of |input_stack| */ +integer max_in_stack; /* largest value of |input_ptr| when pushing */ +in_state_record cur_input; /* the ``top'' input state */ int stack_size; /* maximum number of simultaneous input sources */ @ @<Allocate or initialize ...@>= -mp->stack_size = 300; -mp->input_stack = xmalloc((mp->stack_size+1),sizeof(in_state_record)); +mp->stack_size = 16; +mp->input_stack = xmalloc ((mp->stack_size + 1), sizeof (in_state_record)); @ @<Dealloc variables@>= -xfree(mp->input_stack); +xfree (mp->input_stack); @ We've already defined the special variable |loc==cur_input.loc_field| in our discussion of basic input-output routines. The other components of @@ -13176,9 +14069,9 @@ The |name| variable is a string number that designates the name of the current file, if we are reading an ordinary text file. Special codes |is_term..max_spec_src| indicate other sources of input text. -@d is_term 0 /* |name| value when reading from the terminal for normal input */ -@d is_read 1 /* |name| value when executing a \&{readstring} or \&{readfrom} */ -@d is_scantok 2 /* |name| value when reading text generated by \&{scantokens} */ +@d is_term (str_number)0 /* |name| value when reading from the terminal for normal input */ +@d is_read (str_number)1 /* |name| value when executing a \&{readstring} or \&{readfrom} */ +@d is_scantok (str_number)2 /* |name| value when reading text generated by \&{scantokens} */ @d max_spec_src is_scantok @ Additional information about the current line is available via the @@ -13230,54 +14123,67 @@ by analogy with |line_stack|. @d in_ext mp->inext_stack[iindex] /* a string used to construct \.{MPX} file names */ @d in_name mp->iname_stack[iindex] /* a string used to construct \.{MPX} file names */ @d in_area mp->iarea_stack[iindex] /* another string for naming \.{MPX} files */ -@d absent 1 /* |name_field| value for unused |mpx_in_stack| entries */ +@d absent (str_number)1 /* |name_field| value for unused |mpx_in_stack| entries */ @d mpx_reading (mp->mpx_name[iindex]>absent) /* when reading a file, is it an \.{MPX} file? */ @d mpx_finished 0 /* |name_field| value when the corresponding \.{MPX} file is finished */ @<Glob...@>= -integer in_open; /* the number of lines in the buffer, less one */ -integer in_open_max; /* highest value of |in_open| ever seen */ -unsigned int open_parens; /* the number of open text files */ -void * *input_file ; -integer *line_stack ; /* the line number for each file */ -char * *inext_stack; /* used for naming \.{MPX} files */ -char * *iname_stack; /* used for naming \.{MPX} files */ -char * *iarea_stack; /* used for naming \.{MPX} files */ -halfword*mpx_name ; +integer in_open; /* the number of lines in the buffer, less one */ +integer in_open_max; /* highest value of |in_open| ever seen */ +unsigned int open_parens; /* the number of open text files */ +void **input_file; +integer *line_stack; /* the line number for each file */ +char **inext_stack; /* used for naming \.{MPX} files */ +char **iname_stack; /* used for naming \.{MPX} files */ +char **iarea_stack; /* used for naming \.{MPX} files */ +str_number *mpx_name; -@ @<Allocate or ...@>= -mp->input_file = xmalloc((mp->max_in_open+1),sizeof(void *)); -mp->line_stack = xmalloc((mp->max_in_open+1),sizeof(integer)); -mp->inext_stack = xmalloc((mp->max_in_open+1),sizeof(char *)); -mp->iname_stack = xmalloc((mp->max_in_open+1),sizeof(char *)); -mp->iarea_stack = xmalloc((mp->max_in_open+1),sizeof(char *)); -mp->mpx_name = xmalloc((mp->max_in_open+1),sizeof(halfword)); -{ +@ @<Declarations@>= +static void mp_reallocate_input_stack (MP mp, int newsize); + +@ @c +static void mp_reallocate_input_stack (MP mp, int newsize) { int k; - for (k=0;k<=mp->max_in_open;k++) { - mp->inext_stack[k] =NULL; - mp->iname_stack[k] =NULL; - mp->iarea_stack[k] =NULL; - } -} + int n = newsize +1; + XREALLOC (mp->input_file, n, void *); + XREALLOC (mp->line_stack, n, integer); + XREALLOC (mp->inext_stack, n, char *); + XREALLOC (mp->iname_stack, n, char *); + XREALLOC (mp->iarea_stack, n, char *); + XREALLOC (mp->mpx_name, n, str_number); + for (k = mp->max_in_open; k <= n; k++) { + mp->input_file[k] = NULL; + mp->line_stack[k] = 0; + mp->inext_stack[k] = NULL; + mp->iname_stack[k] = NULL; + mp->iarea_stack[k] = NULL; + mp->mpx_name[k] = NULL; + } + mp->max_in_open = newsize; +} + + +@ This has to be more than |file_bottom|, so: +@<Allocate or ...@>= +mp_reallocate_input_stack (mp, file_bottom+4); @ @<Dealloc variables@>= { int l; - for (l=0;l<=mp->max_in_open;l++) { - xfree(mp->inext_stack[l]); - xfree(mp->iname_stack[l]); - xfree(mp->iarea_stack[l]); + for (l = 0; l <= mp->max_in_open; l++) { + xfree (mp->inext_stack[l]); + xfree (mp->iname_stack[l]); + xfree (mp->iarea_stack[l]); } } -xfree(mp->input_file); -xfree(mp->line_stack); -xfree(mp->inext_stack); -xfree(mp->iname_stack); -xfree(mp->iarea_stack); -xfree(mp->mpx_name); +xfree (mp->input_file); +xfree (mp->line_stack); +xfree (mp->inext_stack); +xfree (mp->iname_stack); +xfree (mp->iarea_stack); +xfree (mp->mpx_name); @ However, all this discussion about input state really applies only to the @@ -13286,8 +14192,8 @@ namely when we are currently getting input from a token list. In this case |iindex>max_in_open|, and the conventions about the other state variables are different: -\yskip\hang|loc| is a pointer to the current node in the token list, i.e., -the node that will be read next. If |loc=null|, the token list has been +\yskip\hang|nloc| is a pointer to the current node in the token list, i.e., +the node that will be read next. If |nloc=NULL|, the token list has been fully read. \yskip\hang|start| points to the first node of the token list; this node @@ -13300,7 +14206,7 @@ is being scanned. \yskip\hang|name| points to the |eqtb| address of the control sequence being expanded, if the current token list is a macro not defined by -\&{vardef}. Macros defined by \&{vardef} have |name=null|; their name +\&{vardef}. Macros defined by \&{vardef} have |name=NULL|; their name can be deduced by looking at their first two parameters. \yskip\hang|param_start|, which takes the place of |limit|, tells where @@ -13332,31 +14238,45 @@ The token list begins with a reference count if and only if |token_type= macro|. @^reference counts@> +@d nloc mp->cur_input.nloc_field /* location of next node node */ +@d nstart mp->cur_input.nstart_field /* location of next node node */ + @d token_type iindex /* type of current token list */ -@d token_state (iindex>(int)mp->max_in_open) /* are we scanning a token list? */ -@d file_state (iindex<=(int)mp->max_in_open) /* are we scanning a file line? */ +@d token_state (iindex<=macro) /* are we scanning a token list? */ +@d file_state (iindex>macro) /* are we scanning a file line? */ @d param_start limit /* base of macro parameters in |param_stack| */ -@d forever_text (mp->max_in_open+1) /* |token_type| code for loop texts */ -@d loop_text (mp->max_in_open+2) /* |token_type| code for loop texts */ -@d parameter (mp->max_in_open+3) /* |token_type| code for parameter texts */ -@d backed_up (mp->max_in_open+4) /* |token_type| code for texts to be reread */ -@d inserted (mp->max_in_open+5) /* |token_type| code for inserted texts */ -@d macro (mp->max_in_open+6) /* |token_type| code for macro replacement texts */ +@d forever_text 0 /* |token_type| code for loop texts */ +@d loop_text 1 /* |token_type| code for loop texts */ +@d parameter 2 /* |token_type| code for parameter texts */ +@d backed_up 3 /* |token_type| code for texts to be reread */ +@d inserted 4 /* |token_type| code for inserted texts */ +@d macro 5 /* |token_type| code for macro replacement texts */ +@d file_bottom 6 /* lowest file code */ @ The |param_stack| is an auxiliary array used to hold pointers to the token lists for parameters at the current level and subsidiary levels of input. -This stack grows at a different rate from the others. +This stack grows at a different rate from the others, and is dynamically reallocated +when needed. @<Glob...@>= -pointer *param_stack; /* token list pointers for parameters */ -integer param_ptr; /* first unused entry in |param_stack| */ -integer max_param_stack; /* largest value of |param_ptr| */ +mp_node *param_stack; /* token list pointers for parameters */ +integer param_ptr; /* first unused entry in |param_stack| */ +integer max_param_stack; /* largest value of |param_ptr| */ @ @<Allocate or initialize ...@>= -mp->param_stack = xmalloc((mp->param_size+1),sizeof(pointer)); +mp->param_stack = xmalloc ((mp->param_size + 1), sizeof (mp_node)); + +@ @c +static void mp_check_param_size (MP mp, int k) { + while (k >= mp->param_size) { + XREALLOC (mp->param_stack, (k + k / 4), mp_node); + mp->param_size = k + k / 4; + } +} + @ @<Dealloc variables@>= -xfree(mp->param_stack); +xfree (mp->param_stack); @ Notice that the |line| isn't valid when |token_state| is true because it depends on |iindex|. If we really need to know the line number for the @@ -13366,24 +14286,25 @@ compute it as well. @^system dependencies@> @<Declarations@>= -static integer mp_true_line (MP mp) ; +static integer mp_true_line (MP mp); @ @c integer mp_true_line (MP mp) { - int k; /* an index into the input stack */ - if ( file_state && (name>max_spec_src) ) { + int k; /* an index into the input stack */ + if (file_state && (name > max_spec_src)) { return line; - } else { - k=mp->input_ptr; - while ((k>0) && - ((mp->input_stack[(k-1)].index_field>mp->max_in_open)|| - (mp->input_stack[(k-1)].name_field<=max_spec_src))) { - decr(k); + } else { + k = mp->input_ptr; + while ((k > 0) && + ((mp->input_stack[(k - 1)].index_field < file_bottom) || + (mp->input_stack[(k - 1)].name_field <= max_spec_src))) { + decr (k); } - return (k>0 ? mp->line_stack[(k-1)] : 0 ); + return (k > 0 ? mp->line_stack[(k - 1) + file_bottom] : 0); } } + @ Thus, the ``current input state'' can be very complicated indeed; there can be many levels and each level can arise in a variety of ways. The |show_context| procedure, which is used by \MP's error-reporting routine to @@ -13393,7 +14314,7 @@ The global variable |file_ptr| contains the lowest level that was displayed by this procedure. @<Glob...@>= -integer file_ptr; /* shallowest level shown by |show_context| */ +integer file_ptr; /* shallowest level shown by |show_context| */ @ The status at each level is indicated by printing two lines, where the first line indicates what was read so far and the second line shows what remains @@ -13402,35 +14323,39 @@ contains at most |half_error_line| characters, and the second contains at most |error_line|. Non-current input levels whose |token_type| is `|backed_up|' are shown only if they have not been fully read. -@c void mp_show_context (MP mp) { /* prints where the scanner is */ +@c +void mp_show_context (MP mp) { /* prints where the scanner is */ unsigned old_setting; /* saved |selector| setting */ - @<Local variables for formatting calculations@> - mp->file_ptr=mp->input_ptr; mp->input_stack[mp->file_ptr]=mp->cur_input; + @<Local variables for formatting calculations@>; + mp->file_ptr = mp->input_ptr; + mp->input_stack[mp->file_ptr] = mp->cur_input; /* store current state */ - while (1) { - mp->cur_input=mp->input_stack[mp->file_ptr]; /* enter into the context */ + while (1) { + mp->cur_input = mp->input_stack[mp->file_ptr]; /* enter into the context */ @<Display the current context@>; - if ( file_state ) - if ( (name>max_spec_src) || (mp->file_ptr==0) ) break; - decr(mp->file_ptr); + if (file_state) + if ((name > max_spec_src) || (mp->file_ptr == 0)) + break; + decr (mp->file_ptr); } - mp->cur_input=mp->input_stack[mp->input_ptr]; /* restore original state */ + mp->cur_input = mp->input_stack[mp->input_ptr]; /* restore original state */ } + @ @<Display the current context@>= -if ( (mp->file_ptr==mp->input_ptr) || file_state || - (token_type!=backed_up) || (loc!=null) ) { - /* we omit backed-up token lists that have already been read */ - mp->tally=0; /* get ready to count characters */ - old_setting=mp->selector; - if ( file_state ) { +if ((mp->file_ptr == mp->input_ptr) || file_state || + (token_type != backed_up) || (nloc != NULL)) { + /* we omit backed-up token lists that have already been read */ + mp->tally = 0; /* get ready to count characters */ + old_setting = mp->selector; + if (file_state) { @<Print location of current line@>; @<Pseudoprint the line@>; - } else { + } else { @<Print type of token list@>; @<Pseudoprint the token list@>; } - mp->selector=old_setting; /* stop pseudoprinting */ + mp->selector = old_setting; /* stop pseudoprinting */ @<Print two lines using the tricky pseudoprinted information@>; } @@ -13440,45 +14365,54 @@ For example, on some systems it is best to print both a page and line number. @^system dependencies@> @<Print location of current line@>= -if ( name>max_spec_src ) { - mp_print_nl(mp, "l."); mp_print_int(mp, mp_true_line(mp)); -} else if ( terminal_input ) { - if ( mp->file_ptr==0 ) mp_print_nl(mp, "<*>"); - else mp_print_nl(mp, "<insert>"); -} else if ( name==is_scantok ) { - mp_print_nl(mp, "<scantokens>"); +if (name > max_spec_src) { + mp_print_nl (mp, "l."); + mp_print_int (mp, mp_true_line (mp)); +} else if (terminal_input) { + if (mp->file_ptr == 0) + mp_print_nl (mp, "<*>"); + else + mp_print_nl (mp, "<insert>"); +} else if (name == is_scantok) { + mp_print_nl (mp, "<scantokens>"); } else { - mp_print_nl(mp, "<read>"); + mp_print_nl (mp, "<read>"); } -mp_print_char(mp, xord(' ')) +mp_print_char (mp, xord (' ')) + @ Can't use case statement here because the |token_type| is not a constant expression. @<Print type of token list@>= { - if(token_type==forever_text) { - mp_print_nl(mp, "<forever> "); - } else if (token_type==loop_text) { + if (token_type == forever_text) { + mp_print_nl (mp, "<forever> "); + } else if (token_type == loop_text) { @<Print the current loop value@>; - } else if (token_type==parameter) { - mp_print_nl(mp, "<argument> "); - } else if (token_type==backed_up) { - if ( loc==null ) mp_print_nl(mp, "<recently read> "); - else mp_print_nl(mp, "<to be read again> "); - } else if (token_type==inserted) { - mp_print_nl(mp, "<inserted text> "); - } else if (token_type==macro) { - mp_print_ln(mp); - if ( name!=null ) mp_print_text(name); - else @<Print the name of a \&{vardef}'d macro@>; - mp_print(mp, "->"); + } else if (token_type == parameter) { + mp_print_nl (mp, "<argument> "); + } else if (token_type == backed_up) { + if (nloc == NULL) + mp_print_nl (mp, "<recently read> "); + else + mp_print_nl (mp, "<to be read again> "); + } else if (token_type == inserted) { + mp_print_nl (mp, "<inserted text> "); + } else if (token_type == macro) { + mp_print_ln (mp); + if (name != NULL) + mp_print_str (mp, name); + else + @<Print the name of a \&{vardef}'d macro@>; + mp_print (mp, "->"); } else { - mp_print_nl(mp, "?");/* this should never happen */ + mp_print_nl (mp, "?"); /* this should never happen */ @.?\relax@> } } + @ The parameter that corresponds to a loop text is either a token list (in the case of \&{forsuffixes}) or a ``capsule'' (in the case of \&{for}). We'll discuss capsules later; for now, all we need to know is that @@ -13486,31 +14420,41 @@ the |link| field in a capsule parameter is |void| and that |print_exp(p,0)| displays the value of capsule~|p| in abbreviated form. @<Print the current loop value@>= -{ mp_print_nl(mp, "<for("); p=mp->param_stack[param_start]; - if ( p!=null ) { - if ( mp_link(p)==mp_void ) mp_print_exp(mp, p,0); /* we're in a \&{for} loop */ - else mp_show_token_list(mp, p,null,20,mp->tally); +{ + mp_node pp; + mp_print_nl (mp, "<for("); + pp = mp->param_stack[param_start]; + if (pp != NULL) { + if (mp_link (pp) == MP_VOID) + mp_print_exp (mp, pp, 0); /* we're in a \&{for} loop */ + else + mp_show_token_list (mp, pp, NULL, 20, mp->tally); } - mp_print(mp, ")> "); + mp_print (mp, ")> "); } + @ The first two parameters of a macro defined by \&{vardef} will be token lists representing the macro's prefix and ``at point.'' By putting these together, we get the macro's full name. @<Print the name of a \&{vardef}'d macro@>= -{ p=mp->param_stack[param_start]; - if ( p==null ) { - mp_show_token_list(mp, mp->param_stack[param_start+1],null,20,mp->tally); - } else { - q=p; - while ( mp_link(q)!=null ) q=mp_link(q); - mp_link(q)=mp->param_stack[param_start+1]; - mp_show_token_list(mp, p,null,20,mp->tally); - mp_link(q)=null; +{ + mp_node pp = mp->param_stack[param_start]; + if (pp == NULL) { + mp_show_token_list (mp, mp->param_stack[param_start + 1], NULL, 20, + mp->tally); + } else { + mp_node qq = pp; + while (mp_link (qq) != NULL) + qq = mp_link (qq); + mp_link (qq) = mp->param_stack[param_start + 1]; + mp_show_token_list (mp, pp, NULL, 20, mp->tally); + mp_link (qq) = NULL; } } + @ Now it is necessary to explain a little trick. We don't want to store a long string that corresponds to a token list, because that string might take up lots of memory; and we are printing during a time when an error message is @@ -13542,12 +14486,12 @@ unless |n+m>error_line|; in the latter case, further cropping is done. This is easier to program than to explain. @<Local variables for formatting...@>= -int i; /* index into |buffer| */ -integer l; /* length of descriptive information on line 1 */ -integer m; /* context information gathered for line 2 */ -int n; /* length of line 1 */ -integer p; /* starting or ending place in |trick_buf| */ -integer q; /* temporary index */ +int i; /* index into |buffer| */ +integer l; /* length of descriptive information on line 1 */ +integer m; /* context information gathered for line 2 */ +int n; /* length of line 1 */ +integer p; /* starting or ending place in |trick_buf| */ +integer q; /* temporary index */ @ The following code tells the print routines to gather the desired information. @@ -13565,29 +14509,38 @@ the desired information. @ And the following code uses the information after it has been gathered. @<Print two lines using the tricky pseudoprinted information@>= -if ( mp->trick_count==1000000 ) set_trick_count; +if (mp->trick_count == 1000000) + set_trick_count; /* |set_trick_count| must be performed */ -if ( mp->tally<mp->trick_count ) m=mp->tally-mp->first_count; -else m=mp->trick_count-mp->first_count; /* context on line 2 */ -if ( l+mp->first_count<=mp->half_error_line ) { - p=0; n=l+mp->first_count; -} else { - mp_print(mp, "..."); p=l+mp->first_count-mp->half_error_line+3; - n=mp->half_error_line; +if (mp->tally < mp->trick_count) + m = mp->tally - mp->first_count; +else + m = mp->trick_count - mp->first_count; /* context on line 2 */ +if (l + mp->first_count <= mp->half_error_line) { + p = 0; + n = l + mp->first_count; +} else { + mp_print (mp, "..."); + p = l + mp->first_count - mp->half_error_line + 3; + n = mp->half_error_line; } -for (q=p;q<=mp->first_count-1;q++) { - mp_print_char(mp, mp->trick_buf[q % mp->error_line]); +for (q = p; q <= mp->first_count - 1; q++) { + mp_print_char (mp, mp->trick_buf[q % mp->error_line]); } -mp_print_ln(mp); -for (q=1;q<=n;q++) { - mp_print_char(mp, xord(' ')); /* print |n| spaces to begin line~2 */ +mp_print_ln (mp); +for (q = 1; q <= n; q++) { + mp_print_char (mp, xord (' ')); /* print |n| spaces to begin line~2 */ } -if ( m+n<=mp->error_line ) p=mp->first_count+m; -else p=mp->first_count+(mp->error_line-n-3); -for (q=mp->first_count;q<=p-1;q++) { - mp_print_char(mp, mp->trick_buf[q % mp->error_line]); +if (m + n <= mp->error_line) + p = mp->first_count + m; +else + p = mp->first_count + (mp->error_line - n - 3); +for (q = mp->first_count; q <= p - 1; q++) { + mp_print_char (mp, mp->trick_buf[q % mp->error_line]); } -if ( m+n>mp->error_line ) mp_print(mp, "...") +if (m + n > mp->error_line) + mp_print (mp, "...") + @ But the trick is distracting us from our current goal, which is to understand the input state. So let's concentrate on the data structures that @@ -13595,24 +14548,29 @@ are being pseudoprinted as we finish up the |show_context| procedure. @<Pseudoprint the line@>= begin_pseudoprint; -if ( limit>0 ) { - for (i=start;i<=limit-1;i++) { - if ( i==loc ) set_trick_count; - mp_print_str(mp, mp->buffer[i]); +if (limit > 0) { + for (i = start; i <= limit - 1; i++) { + if (i == loc) + set_trick_count; + mp_print_char (mp, mp->buffer[i]); } } @ @<Pseudoprint the token list@>= begin_pseudoprint; -if ( token_type!=macro ) mp_show_token_list(mp, start,loc,100000,0); -else mp_show_macro(mp, start,loc,100000) +if (token_type != macro) + mp_show_token_list (mp, nstart, nloc, 100000, 0); +else + mp_show_macro (mp, nstart, nloc, 100000) + @ Here is the missing piece of |show_token_list| that is activated when the token beginning line~2 is about to be shown: -@<Do magic computation@>=set_trick_count +@<Do magic computation@>= +set_trick_count -@* \[28] Maintaining the input stacks. +@* Maintaining the input stacks. The following subroutines change the input status in commonly needed ways. First comes |push_input|, which stores the current state and creates a @@ -13643,218 +14601,280 @@ set |name|, reset~|loc|, and increase the macro's reference count. @d back_list(A) mp_begin_token_list(mp, (A), (quarterword)backed_up) /* backs up a simple token list */ -@c -static void mp_begin_token_list (MP mp,pointer p, quarterword t) { - push_input; start=p; token_type=t; - param_start=mp->param_ptr; loc=p; +@c +static void mp_begin_token_list (MP mp, mp_node p, quarterword t) { + push_input; + nstart = p; + token_type = t; + param_start = mp->param_ptr; + nloc = p; } + @ When a token list has been fully scanned, the following computations should be done as we leave that level of input. @^inner loop@> -@c -static void mp_end_token_list (MP mp) { /* leave a token-list input level */ - pointer p; /* temporary register */ - if ( token_type>=backed_up ) { /* token list to be deleted */ - if ( token_type<=inserted ) { - mp_flush_token_list(mp, start); goto DONE; +@c +static void mp_end_token_list (MP mp) { /* leave a token-list input level */ + mp_node p; /* temporary register */ + if (token_type >= backed_up) { /* token list to be deleted */ + if (token_type <= inserted) { + mp_flush_token_list (mp, nstart); + goto DONE; } else { - mp_delete_mac_ref(mp, start); /* update reference count */ + mp_delete_mac_ref (mp, nstart); /* update reference count */ } } - while ( mp->param_ptr>param_start ) { /* parameters must be flushed */ - decr(mp->param_ptr); - p=mp->param_stack[mp->param_ptr]; - if ( p!=null ) { - if ( mp_link(p)==mp_void ) { /* it's an \&{expr} parameter */ - mp_recycle_value(mp, p); mp_free_node(mp, p,value_node_size); + while (mp->param_ptr > param_start) { /* parameters must be flushed */ + decr (mp->param_ptr); + p = mp->param_stack[mp->param_ptr]; + if (p != NULL) { + if (mp_link (p) == MP_VOID) { /* it's an \&{expr} parameter */ + mp_recycle_value (mp, p); + mp_free_node (mp, p, value_node_size); } else { - mp_flush_token_list(mp, p); /* it's a \&{suffix} or \&{text} parameter */ + mp_flush_token_list (mp, p); /* it's a \&{suffix} or \&{text} parameter */ } } } -DONE: - pop_input; check_interrupt; +DONE: + pop_input; + check_interrupt; } + @ The contents of |cur_cmd,cur_mod,cur_sym| are placed into an equivalent token by the |cur_tok| routine. @^inner loop@> -@c @<Declare the procedure called |make_exp_copy|@> -static pointer mp_cur_tok (MP mp) { - pointer p; /* a new token node */ - quarterword save_type; /* |cur_type| to be restored */ - integer save_exp; /* |cur_exp| to be restored */ - if ( mp->cur_sym==0 ) { - if ( mp->cur_cmd==capsule_token ) { - save_type=mp->cur_type; save_exp=mp->cur_exp; - mp_make_exp_copy(mp, mp->cur_mod); p=mp_stash_cur_exp(mp); mp_link(p)=null; - mp->cur_type=save_type; mp->cur_exp=save_exp; - } else { - p=mp_get_node(mp, token_node_size); - value(p)=mp->cur_mod; mp_name_type(p)=mp_token; - if ( mp->cur_cmd==numeric_token ) mp_type(p)=mp_known; - else mp_type(p)=mp_string_type; +@c +@<Declare the procedure called |make_exp_copy|@>; +static mp_node mp_cur_tok (MP mp) { + mp_node p; /* a new token node */ + if (mp->cur_sym == NULL && mp->cur_sym_mod == 0) { + if (mp->cur_cmd == capsule_token) { + mp_value save_exp = mp->cur_exp; /* |cur_exp| to be restored */ + mp_make_exp_copy (mp, mp->cur_mod_node); + p = mp_stash_cur_exp (mp); + mp_link (p) = NULL; + mp->cur_exp = save_exp; + } else { + p = mp_get_token_node (mp); + mp_name_type (p) = mp_token; + if (mp->cur_cmd == numeric_token) { + set_value (p, mp->cur_mod); + mp_type (p) = mp_known; + } else { + set_str_value (p, mp->cur_mod_str); + mp_type (p) = mp_string_type; + } } - } else { - fast_get_avail(p); mp_info(p)=mp->cur_sym; + } else { + p = mp_get_symbolic_node (mp); + set_mp_sym_sym (p, mp->cur_sym); + mp_name_type (p) = mp->cur_sym_mod; } return p; } + @ Sometimes \MP\ has read too far and wants to ``unscan'' what it has seen. The |back_input| procedure takes care of this by putting the token just scanned back into the input stream, ready to be read again. If |cur_sym<>0|, the values of |cur_cmd| and |cur_mod| are irrelevant. -@<Declarations@>= +@<Declarations@>= static void mp_back_input (MP mp); -@ @c void mp_back_input (MP mp) {/* undoes one token of input */ - pointer p; /* a token list of length one */ - p=mp_cur_tok(mp); - while ( token_state &&(loc==null) ) - mp_end_token_list(mp); /* conserve stack space */ - back_list(p); +@ @c +void mp_back_input (MP mp) { /* undoes one token of input */ + mp_node p; /* a token list of length one */ + p = mp_cur_tok (mp); + while (token_state && (nloc == NULL)) + mp_end_token_list (mp); /* conserve stack space */ + back_list (p); } + @ The |back_error| routine is used when we want to restore or replace an offending token just before issuing an error message. We disable interrupts during the call of |back_input| so that the help message won't be lost. -@ @c static void mp_back_error (MP mp) { /* back up one token and call |error| */ - mp->OK_to_interrupt=false; - mp_back_input(mp); - mp->OK_to_interrupt=true; mp_error(mp); +@ @c +static void mp_back_error (MP mp) { /* back up one token and call |error| */ + mp->OK_to_interrupt = false; + mp_back_input (mp); + mp->OK_to_interrupt = true; + mp_error (mp); } -static void mp_ins_error (MP mp) { /* back up one inserted token and call |error| */ - mp->OK_to_interrupt=false; - mp_back_input(mp); token_type=(quarterword)inserted; - mp->OK_to_interrupt=true; mp_error(mp); +static void mp_ins_error (MP mp) { /* back up one inserted token and call |error| */ + mp->OK_to_interrupt = false; + mp_back_input (mp); + token_type = (quarterword) inserted; + mp->OK_to_interrupt = true; + mp_error (mp); } + @ The |begin_file_reading| procedure starts a new level of input for lines of characters to be read from a file, or as an insertion from the terminal. It does not take care of opening the file, nor does it set |loc| or |limit| or |line|. @^system dependencies@> -@c void mp_begin_file_reading (MP mp) { - if ( mp->in_open==mp->max_in_open ) - mp_overflow(mp, "text input levels",mp->max_in_open); -@:MetaPost capacity exceeded text input levels}{\quad text input levels@> - if ( mp->first==mp->buf_size ) - mp_reallocate_buffer(mp,(mp->buf_size+(mp->buf_size/4))); - mp->in_open++; push_input; iindex=(quarterword)mp->in_open; - if (mp->in_open_max<mp->in_open) - mp->in_open_max=mp->in_open; - mp->mpx_name[iindex]=absent; - start=(halfword)mp->first; - name=is_term; /* |terminal_input| is now |true| */ +@c +void mp_begin_file_reading (MP mp) { + if (mp->in_open == (mp->max_in_open-1)) + mp_reallocate_input_stack (mp, (mp->max_in_open + mp->max_in_open / 4)); + if (mp->first == mp->buf_size) + mp_reallocate_buffer (mp, (mp->buf_size + mp->buf_size / 4)); + mp->in_open++; + push_input; + iindex = (quarterword) mp->in_open; + if (mp->in_open_max < mp->in_open) + mp->in_open_max = mp->in_open; + mp->mpx_name[iindex] = absent; + start = (halfword) mp->first; + name = is_term; /* |terminal_input| is now |true| */ } + @ Conversely, the variables must be downdated when such a level of input is finished. Any associated \.{MPX} file must also be closed and popped off the file stack. -@c static void mp_end_file_reading (MP mp) { - if ( mp->in_open>iindex ) { - if ( (mp->mpx_name[mp->in_open]==absent)||(name<=max_spec_src) ) { - mp_confusion(mp, "endinput"); -@:this can't happen endinput}{\quad endinput@> - } else { - (mp->close_file)(mp,mp->input_file[mp->in_open]); /* close an \.{MPX} file */ - delete_str_ref(mp->mpx_name[mp->in_open]); - decr(mp->in_open); +@c +static void mp_end_file_reading (MP mp) { + if (mp->in_open <= file_bottom) { + print_err ("Attempt to close the bottom level file!"); + help3 ("You attempted to close the bottommost file input level.", + "The most likely cause of this error is that your preload", + "file did not end with 'dump' or 'end'. MP will exit now."); + mp_error (mp); + mp->history = mp_fatal_error_stop; + mp_jump_out (mp); + } + if (mp->in_open > iindex) { + if ((mp->mpx_name[mp->in_open] == absent) || (name <= max_spec_src)) { + mp_confusion (mp, "endinput"); +@:this can't happen endinput}{\quad endinput@>; + } else { + (mp->close_file) (mp, mp->input_file[mp->in_open]); /* close an \.{MPX} file */ + delete_str_ref (mp->mpx_name[mp->in_open]); + decr (mp->in_open); } } - mp->first=(size_t)start; - if ( iindex!=mp->in_open ) mp_confusion(mp, "endinput"); - if ( name>max_spec_src ) { - (mp->close_file)(mp,cur_file); - delete_str_ref(name); - xfree(in_ext); - xfree(in_name); - xfree(in_area); + mp->first = (size_t) start; + if (iindex != mp->in_open) + mp_confusion (mp, "endinput"); + if (name > max_spec_src) { + (mp->close_file) (mp, cur_file); + xfree (in_ext); + xfree (in_name); + xfree (in_area); } - pop_input; decr(mp->in_open); + pop_input; + decr (mp->in_open); } + @ Here is a function that tries to resume input from an \.{MPX} file already associated with the current input file. It returns |false| if this doesn't work. -@c static boolean mp_begin_mpx_reading (MP mp) { - if ( mp->in_open!=iindex+1 ) { - return false; - } else { - if ( mp->mpx_name[mp->in_open]<=absent ) mp_confusion(mp, "mpx"); -@:this can't happen mpx}{\quad mpx@> - if ( mp->first==mp->buf_size ) - mp_reallocate_buffer(mp,(mp->buf_size+(mp->buf_size/4))); - push_input; iindex=(quarterword)mp->in_open; - start=(halfword)mp->first; - name=mp->mpx_name[mp->in_open]; add_str_ref(name); +@c +static boolean mp_begin_mpx_reading (MP mp) { + if (mp->in_open != iindex + 1) { + return false; + } else { + if (mp->mpx_name[mp->in_open] <= absent) + mp_confusion (mp, "mpx"); +@:this can't happen mpx}{\quad mpx@>; + if (mp->first == mp->buf_size) + mp_reallocate_buffer (mp, (mp->buf_size + (mp->buf_size / 4))); + push_input; + iindex = (quarterword) mp->in_open; + start = (halfword) mp->first; + name = mp->mpx_name[mp->in_open]; + add_str_ref (name); @<Put an empty line in the input buffer@>; return true; } } + @ This procedure temporarily stops reading an \.{MPX} file. -@c static void mp_end_mpx_reading (MP mp) { - if ( mp->in_open!=iindex ) mp_confusion(mp, "mpx"); -@:this can't happen mpx}{\quad mpx@> - if ( loc<limit ) { +@c +static void mp_end_mpx_reading (MP mp) { + if (mp->in_open != iindex) + mp_confusion (mp, "mpx"); +@:this can't happen mpx}{\quad mpx@>; + if (loc < limit) { @<Complain that we are not at the end of a line in the \.{MPX} file@>; } - mp->first=(size_t)start; + mp->first = (size_t) start; pop_input; } + @ Here we enforce a restriction that simplifies the input stacks considerably. This should not inconvenience the user because \.{MPX} files are generated by an auxiliary program called \.{DVItoMP}. @ @<Complain that we are not at the end of a line in the \.{MPX} file@>= -{ -print_err("`mpxbreak' must be at the end of a line"); -help4("This file contains picture expressions for btex...etex", - "blocks. Such files are normally generated automatically", - "but this one seems to be messed up. I'm going to ignore", - "the rest of this line."); -mp_error(mp); +{ + print_err ("`mpxbreak' must be at the end of a line"); + help4 ("This file contains picture expressions for btex...etex", + "blocks. Such files are normally generated automatically", + "but this one seems to be messed up. I'm going to ignore", + "the rest of this line."); + mp_error (mp); } + @ In order to keep the stack from overflowing during a long sequence of inserted `\.{show}' commands, the following routine removes completed error-inserted lines from memory. -@c void mp_clear_for_error_prompt (MP mp) { - while ( file_state && terminal_input && - (mp->input_ptr>0)&&(loc==limit) ) mp_end_file_reading(mp); - mp_print_ln(mp); clear_terminal; +@c +void mp_clear_for_error_prompt (MP mp) { + while (file_state && terminal_input && (mp->input_ptr > 0) && (loc == limit)) + mp_end_file_reading (mp); + mp_print_ln (mp); + clear_terminal; } + @ To get \MP's whole input mechanism going, we perform the following actions. @<Initialize the input routines@>= -{ mp->input_ptr=0; mp->max_in_stack=0; - mp->in_open=0; mp->open_parens=0; mp->max_buf_stack=0; - mp->param_ptr=0; mp->max_param_stack=0; - mp->first=0; - start=0; iindex=0; line=0; name=is_term; - mp->mpx_name[0]=absent; - mp->force_eof=false; - if ( ! mp_init_terminal(mp) ) mp_jump_out(mp); - limit=(halfword)mp->last; mp->first=mp->last+1; +{ + mp->input_ptr = 0; + mp->max_in_stack = file_bottom; + mp->in_open = file_bottom; + mp->open_parens = 0; + mp->max_buf_stack = 0; + mp->param_ptr = 0; + mp->max_param_stack = 0; + mp->first = 0; + start = 0; + iindex = file_bottom; + line = 0; + name = is_term; + mp->mpx_name[file_bottom] = absent; + mp->force_eof = false; + if (!mp_init_terminal (mp)) + mp_jump_out (mp); + limit = (halfword) mp->last; + mp->first = mp->last + 1; /* |init_terminal| has set |loc| and |last| */ } -@* \[29] Getting the next token. + +@* Getting the next token. The heart of \MP's input mechanism is the |get_next| procedure, which we shall develop in the next few sections of the program. Perhaps we shouldn't actually call it the ``heart,'' however; it really acts as \MP's @@ -13892,11 +14912,13 @@ name of a macro whose replacement text is being scanned. @<Glob...@>= integer scanner_status; /* are we scanning at high speed? */ -integer warning_info; /* if so, what else do we need to know, - in case an error occurs? */ +mp_sym warning_info; /* if so, what else do we need to know, + in case an error occurs? */ +integer warning_info_line; +mp_node warning_info_node; @ @<Initialize the input routines@>= -mp->scanner_status=normal; +mp->scanner_status = normal; @ The following subroutine is called when an `\&{outer}' symbolic token has been scanned or @@ -13905,161 +14927,179 @@ by |cur_sym|, which is zero at the end of a file. @c static boolean mp_check_outer_validity (MP mp) { - pointer p; /* points to inserted token list */ - if ( mp->scanner_status==normal ) { + mp_node p; /* points to inserted token list */ + if (mp->scanner_status == normal) { return true; - } else if ( mp->scanner_status==tex_flushing ) { + } else if (mp->scanner_status == tex_flushing) { @<Check if the file has ended while flushing \TeX\ material and set the result value for |check_outer_validity|@>; - } else { - mp->deletions_allowed=false; + } else { + mp->deletions_allowed = false; @<Back up an outer symbolic token so that it can be reread@>; - if ( mp->scanner_status>skipping ) { + if (mp->scanner_status > skipping) { @<Tell the user what has run away and try to recover@>; - } else { - print_err("Incomplete if; all text was ignored after line "); -@.Incomplete if...@> - mp_print_int(mp, mp->warning_info); - help3("A forbidden `outer' token occurred in skipped text.", - "This kind of error happens when you say `if...' and forget", - "the matching `fi'. I've inserted a `fi'; this might work."); - if ( mp->cur_sym==0 ) - mp->help_line[2]="The file ended while I was skipping conditional text."; - mp->cur_sym=frozen_fi; mp_ins_error(mp); + } else { + print_err ("Incomplete if; all text was ignored after line "); +@.Incomplete if...@>; + mp_print_int (mp, mp->warning_info_line); + help3 ("A forbidden `outer' token occurred in skipped text.", + "This kind of error happens when you say `if...' and forget", + "the matching `fi'. I've inserted a `fi'; this might work."); + if (mp->cur_sym == NULL) + mp->help_line[2] = + "The file ended while I was skipping conditional text."; + mp->cur_sym = mp->frozen_fi; + mp_ins_error (mp); } - mp->deletions_allowed=true; - return false; + mp->deletions_allowed = true; + return false; } } + @ @<Check if the file has ended while flushing \TeX\ material and set...@>= -if ( mp->cur_sym!=0 ) { - return true; -} else { - mp->deletions_allowed=false; - print_err("TeX mode didn't end; all text was ignored after line "); - mp_print_int(mp, mp->warning_info); - help2("The file ended while I was looking for the `etex' to", - "finish this TeX material. I've inserted `etex' now."); - mp->cur_sym = frozen_etex; - mp_ins_error(mp); - mp->deletions_allowed=true; +if (mp->cur_sym != NULL) { + return true; +} else { + mp->deletions_allowed = false; + print_err ("TeX mode didn't end; all text was ignored after line "); + mp_print_int (mp, mp->warning_info_line); + help2 ("The file ended while I was looking for the `etex' to", + "finish this TeX material. I've inserted `etex' now."); + mp->cur_sym = mp->frozen_etex; + mp_ins_error (mp); + mp->deletions_allowed = true; return false; } + @ @<Back up an outer symbolic token so that it can be reread@>= -if ( mp->cur_sym!=0 ) { - p=mp_get_avail(mp); mp_info(p)=mp->cur_sym; - back_list(p); /* prepare to read the symbolic token again */ +if (mp->cur_sym != NULL) { + p = mp_get_symbolic_node (mp); + set_mp_sym_sym (p, mp->cur_sym); + mp_name_type (p) = mp->cur_sym_mod; + back_list (p); /* prepare to read the symbolic token again */ } @ @<Tell the user what has run away...@>= -{ - mp_runaway(mp); /* print the definition-so-far */ - if ( mp->cur_sym==0 ) { - print_err("File ended"); +{ + mp_runaway (mp); /* print the definition-so-far */ + if (mp->cur_sym == NULL) { + print_err ("File ended"); @.File ended while scanning...@> - } else { - print_err("Forbidden token found"); + } else { + print_err ("Forbidden token found"); @.Forbidden token found...@> } - mp_print(mp, " while scanning "); - help4("I suspect you have forgotten an `enddef',", - "causing me to read past where you wanted me to stop.", - "I'll try to recover; but if the error is serious,", - "you'd better type `E' or `X' now and fix your file."); + mp_print (mp, " while scanning "); + help4 ("I suspect you have forgotten an `enddef',", + "causing me to read past where you wanted me to stop.", + "I'll try to recover; but if the error is serious,", + "you'd better type `E' or `X' now and fix your file."); switch (mp->scanner_status) { @<Complete the error message, and set |cur_sym| to a token that might help recover from the error@> - } /* there are no other cases */ - mp_ins_error(mp); + } /* there are no other cases */ + mp_ins_error (mp); } + @ As we consider various kinds of errors, it is also appropriate to change the first line of the help message just given; |help_line[3]| points to the string that might be changed. @<Complete the error message,...@>= -case flushing: - mp_print(mp, "to the end of the statement"); - mp->help_line[3]="A previous error seems to have propagated,"; - mp->cur_sym=frozen_semicolon; - break; -case absorbing: - mp_print(mp, "a text argument"); - mp->help_line[3]="It seems that a right delimiter was left out,"; - if ( mp->warning_info==0 ) { - mp->cur_sym=frozen_end_group; - } else { - mp->cur_sym=frozen_right_delimiter; - equiv(frozen_right_delimiter)=mp->warning_info; - } - break; +case flushing: +mp_print (mp, "to the end of the statement"); +mp->help_line[3] = "A previous error seems to have propagated,"; +mp->cur_sym = mp->frozen_semicolon; +break; +case absorbing: +mp_print (mp, "a text argument"); +mp->help_line[3] = "It seems that a right delimiter was left out,"; +if (mp->warning_info == NULL) { + mp->cur_sym = mp->frozen_end_group; +} else { + mp->cur_sym = mp->frozen_right_delimiter; + /* the next line makes sure that the inserted delimiter will + match the delimiter that already was read. */ + equiv_sym (mp->cur_sym) = mp->warning_info; +} +break; case var_defining: -case op_defining: - mp_print(mp, "the definition of "); - if ( mp->scanner_status==op_defining ) - mp_print_text(mp->warning_info); - else - mp_print_variable_name(mp, mp->warning_info); - mp->cur_sym=frozen_end_def; - break; -case loop_defining: - mp_print(mp, "the text of a "); - mp_print_text(mp->warning_info); - mp_print(mp, " loop"); - mp->help_line[3]="I suspect you have forgotten an `endfor',"; - mp->cur_sym=frozen_end_for; - break; +case op_defining: +mp_print (mp, "the definition of "); +if (mp->scanner_status == op_defining) + mp_print_text (mp->warning_info); +else + mp_print_variable_name (mp, mp->warning_info_node); +mp->cur_sym = mp->frozen_end_def; +break; +case loop_defining: +mp_print (mp, "the text of a "); +mp_print_text (mp->warning_info); +mp_print (mp, " loop"); +mp->help_line[3] = "I suspect you have forgotten an `endfor',"; +mp->cur_sym = mp->frozen_end_for; +break; @ The |runaway| procedure displays the first part of the text that occurred when \MP\ began its special |scanner_status|, if that text has been saved. @<Declarations@>= -static void mp_runaway (MP mp) ; +static void mp_runaway (MP mp); @ @c -void mp_runaway (MP mp) { - if ( mp->scanner_status>flushing ) { - mp_print_nl(mp, "Runaway "); - switch (mp->scanner_status) { - case absorbing: mp_print(mp, "text?"); break; - case var_defining: - case op_defining: mp_print(mp,"definition?"); break; - case loop_defining: mp_print(mp, "loop?"); break; - } /* there are no other cases */ - mp_print_ln(mp); - mp_show_token_list(mp, mp_link(hold_head),null,mp->error_line-10,0); +void mp_runaway (MP mp) { + if (mp->scanner_status > flushing) { + mp_print_nl (mp, "Runaway "); + switch (mp->scanner_status) { + case absorbing: + mp_print (mp, "text?"); + break; + case var_defining: + case op_defining: + mp_print (mp, "definition?"); + break; + case loop_defining: + mp_print (mp, "loop?"); + break; + } /* there are no other cases */ + mp_print_ln (mp); + mp_show_token_list (mp, mp_link (mp->hold_head), NULL, mp->error_line - 10, + 0); } } + @ We need to mention a procedure that may be called by |get_next|. -@<Declarations@>= +@<Declarations@>= static void mp_firm_up_the_line (MP mp); @ And now we're ready to take the plunge into |get_next| itself. Note that the behavior depends on the |scanner_status| because percent signs and double quotes need to be passed over when skipping TeX material. -@c +@c void mp_get_next (MP mp) { /* sets |cur_cmd|, |cur_mod|, |cur_sym| to next token */ @^inner loop@> - /*restart*/ /* go here to get the next input token */ - /*exit*/ /* go here when the next input token has been got */ - /*|common_ending|*/ /* go here to finish getting a symbolic token */ - /*found*/ /* go here when the end of a symbolic token has been found */ - /*switch*/ /* go here to branch on the class of an input character */ - /*|start_numeric_token|,|start_decimal_token|,|fin_numeric_token|,|done|*/ + /*restart *//* go here to get the next input token */ + /*exit *//* go here when the next input token has been got */ + /*|common_ending| *//* go here to finish getting a symbolic token */ + /*found *//* go here when the end of a symbolic token has been found */ + /*switch *//* go here to branch on the class of an input character */ + /*|start_numeric_token|,|start_decimal_token|,|fin_numeric_token|,|done| */ /* go here at crucial stages when scanning a number */ - int k; /* an index into |buffer| */ + int k; /* an index into |buffer| */ ASCII_code c; /* the current character in the buffer */ - int class; /* its class number */ - integer n,f; /* registers for decimal-to-binary conversion */ -RESTART: - mp->cur_sym=0; - if ( file_state ) { + int class; /* its class number */ + integer n, f; /* registers for decimal-to-binary conversion */ +RESTART: + mp->cur_sym = 0; + mp->cur_sym_mod = 0; + if (file_state) { @<Input from external file; |goto restart| if no input found, or |return| if a non-symbolic token is found@>; } else { @@ -14067,21 +15107,25 @@ RESTART: if a parameter needs to be expanded, or |return| if a non-symbolic token is found@>; } -COMMON_ENDING: +COMMON_ENDING: @<Finish getting the symbolic token in |cur_sym|; |goto restart| if it is illegal@>; } + @ When a symbolic token is declared to be `\&{outer}', its command code is increased by |outer_tag|. @^inner loop@> @<Finish getting the symbolic token in |cur_sym|...@>= -mp->cur_cmd=eq_type(mp->cur_sym); mp->cur_mod=equiv(mp->cur_sym); -if ( mp->cur_cmd>=outer_tag ) { - if ( mp_check_outer_validity(mp) ) - mp->cur_cmd=mp->cur_cmd-outer_tag; - else +mp->cur_cmd = eq_type (mp->cur_sym); +mp->cur_mod = equiv (mp->cur_sym); +mp->cur_mod_node = equiv_node (mp->cur_sym); +mp->cur_sym2 = equiv_sym (mp->cur_sym); +if (mp->cur_cmd >= outer_tag) { + if (mp_check_outer_validity (mp)) + mp->cur_cmd = mp->cur_cmd - outer_tag; + else goto RESTART; } @@ -14090,43 +15134,59 @@ to have a special test for end-of-line. @^inner loop@> @<Input from external file;...@>= -{ -SWITCH: - c=mp->buffer[loc]; incr(loc); class=mp->char_class[c]; +{ +SWITCH: + c = mp->buffer[loc]; + incr (loc); + class = mp->char_class[c]; switch (class) { - case digit_class: goto START_NUMERIC_TOKEN; break; - case period_class: - class=mp->char_class[mp->buffer[loc]]; - if ( class>period_class ) { + case digit_class: + goto START_NUMERIC_TOKEN; + break; + case period_class: + class = mp->char_class[mp->buffer[loc]]; + if (class > period_class) { goto SWITCH; - } else if ( class<period_class ) { /* |class=digit_class| */ - n=0; goto START_DECIMAL_TOKEN; + } else if (class < period_class) { /* |class=digit_class| */ + n = 0; + goto START_DECIMAL_TOKEN; } -@:. }{\..\ token@> +@:. }{\..\ token@>; + break; + case space_class: + goto SWITCH; break; - case space_class: goto SWITCH; break; - case percent_class: - if ( mp->scanner_status==tex_flushing ) { - if ( loc<limit ) goto SWITCH; + case percent_class: + if (mp->scanner_status == tex_flushing) { + if (loc < limit) + goto SWITCH; } @<Move to next line of file, or |goto restart| if there is no next line@>; check_interrupt; goto SWITCH; break; - case string_class: - if ( mp->scanner_status==tex_flushing ) goto SWITCH; - else @<Get a string token and |return|@>; + case string_class: + if (mp->scanner_status == tex_flushing) + goto SWITCH; + else + @<Get a string token and |return|@>; break; - case isolated_classes: - k=loc-1; goto FOUND; break; - case invalid_class: - if ( mp->scanner_status==tex_flushing ) goto SWITCH; - else @<Decry the invalid character and |goto restart|@>; + case isolated_classes: + k = loc - 1; + goto FOUND; + break; + case invalid_class: + if (mp->scanner_status == tex_flushing) + goto SWITCH; + else + @<Decry the invalid character and |goto restart|@>; break; - default: break; /* letters, etc. */ + default: + break; /* letters, etc. */ } - k=loc-1; - while ( mp->char_class[mp->buffer[loc]]==class ) incr(loc); + k = loc - 1; + while (mp->char_class[mp->buffer[loc]] == class) + incr (loc); goto FOUND; START_NUMERIC_TOKEN: @<Get the integer part |n| of a numeric token; @@ -14136,205 +15196,227 @@ START_DECIMAL_TOKEN: FIN_NUMERIC_TOKEN: @<Pack the numeric and fraction parts of a numeric token and |return|@>; -FOUND: - mp->cur_sym=mp_id_lookup(mp, k,loc-k); +FOUND: + mp->cur_sym = + mp_id_lookup (mp, (char *) (mp->buffer + k), (size_t) (loc - k), true); } + @ We go to |restart| instead of to |SWITCH|, because we might enter |token_state| after the error has been dealt with (cf.\ |clear_for_error_prompt|). @<Decry the invalid...@>= -{ - print_err("Text line contains an invalid character"); -@.Text line contains...@> - help2("A funny symbol that I can\'t read has just been input.", - "Continue, and I'll forget that it ever happened."); - mp->deletions_allowed=false; mp_error(mp); mp->deletions_allowed=true; +{ + print_err ("Text line contains an invalid character"); +@.Text line contains...@>; + help2 ("A funny symbol that I can\'t read has just been input.", + "Continue, and I'll forget that it ever happened."); + mp->deletions_allowed = false; + mp_error (mp); + mp->deletions_allowed = true; goto RESTART; } + @ @<Get a string token and |return|@>= -{ - if ( mp->buffer[loc]=='"' ) { - mp->cur_mod=null_str; - } else { - k=loc; mp->buffer[limit+1]=xord('"'); - do { - incr(loc); - } while (mp->buffer[loc]!='"'); - if ( loc>limit ) { +{ + if (mp->buffer[loc] == '"') { + mp->cur_mod_str = null_str; + } else { + k = loc; + mp->buffer[limit + 1] = xord ('"'); + do { + incr (loc); + } while (mp->buffer[loc] != '"'); + if (loc > limit) { @<Decry the missing string delimiter and |goto restart|@>; } - if ( loc==k+1 ) { - mp->cur_mod=mp->buffer[k]; - } else { - str_room(loc-k); - do { - append_char(mp->buffer[k]); incr(k); - } while (k!=loc); - mp->cur_mod=mp_make_string(mp); - } - } - incr(loc); mp->cur_cmd=string_token; + str_room ((size_t) (loc - k)); + do { + append_char (mp->buffer[k]); + incr (k); + } while (k != loc); + mp->cur_mod_str = mp_make_string (mp); + } + incr (loc); + mp->cur_cmd = string_token; return; } + @ We go to |restart| after this error message, not to |SWITCH|, because the |clear_for_error_prompt| routine might have reinstated |token_state| after |error| has finished. @<Decry the missing string delimiter and |goto restart|@>= -{ - loc=limit; /* the next character to be read on this line will be |"%"| */ - print_err("Incomplete string token has been flushed"); -@.Incomplete string token...@> - help3("Strings should finish on the same line as they began.", - "I've deleted the partial string; you might want to", - "insert another by typing, e.g., `I\"new string\"'."); - mp->deletions_allowed=false; mp_error(mp); - mp->deletions_allowed=true; +{ + loc = limit; /* the next character to be read on this line will be |"%"| */ + print_err ("Incomplete string token has been flushed"); +@.Incomplete string token...@>; + help3 ("Strings should finish on the same line as they began.", + "I've deleted the partial string; you might want to", + "insert another by typing, e.g., `I\"new string\"'."); + mp->deletions_allowed = false; + mp_error (mp); + mp->deletions_allowed = true; goto RESTART; } + @ @<Get the integer part |n| of a numeric token...@>= -n=c-'0'; -while ( mp->char_class[mp->buffer[loc]]==digit_class ) { - if ( n<32768 ) n=10*n+mp->buffer[loc]-'0'; - incr(loc); -} -if ( mp->buffer[loc]=='.' ) - if ( mp->char_class[mp->buffer[loc+1]]==digit_class ) +n = c - '0'; +while (mp->char_class[mp->buffer[loc]] == digit_class) { + if (n < 32768) + n = 10 * n + mp->buffer[loc] - '0'; + incr (loc); +} +if (mp->buffer[loc] == '.') + if (mp->char_class[mp->buffer[loc + 1]] == digit_class) goto DONE; -f=0; +f = 0; goto FIN_NUMERIC_TOKEN; -DONE: incr(loc) +DONE:incr (loc) + @ @<Get the fraction part |f| of a numeric token@>= -k=0; -do { - if ( k<17 ) { /* digits for |k>=17| cannot affect the result */ - mp->dig[k]=(unsigned char)(mp->buffer[loc]-'0'); incr(k); - } - incr(loc); -} while (mp->char_class[mp->buffer[loc]]==digit_class); -f=mp_round_decimals(mp, (quarterword)k); -if ( f==unity ) { - incr(n); f=0; +k = 0; +do { + incr (k); + incr (loc); +} while (mp->char_class[mp->buffer[loc]] == digit_class); +f = mp_round_decimals (mp, (unsigned char *)(mp->buffer+loc-k), (quarterword) k); +if (f == unity) { + incr (n); + f = 0; } @ @<Pack the numeric and fraction parts of a numeric token and |return|@>= -if ( n<32768 ) { +if (n < 32768) { @<Set |cur_mod:=n*unity+f| and check if it is uncomfortably large@>; -} else if ( mp->scanner_status!=tex_flushing ) { - print_err("Enormous number has been reduced"); -@.Enormous number...@> - help2("I can\'t handle numbers bigger than 32767.99998;", - "so I've changed your constant to that maximum amount."); - mp->deletions_allowed=false; mp_error(mp); mp->deletions_allowed=true; - mp->cur_mod=el_gordo; -} -mp->cur_cmd=numeric_token; return +} else if (mp->scanner_status != tex_flushing) { + print_err ("Enormous number has been reduced"); +@.Enormous number...@>; + help2 ("I can\'t handle numbers bigger than 32767.99998;", + "so I've changed your constant to that maximum amount."); + mp->deletions_allowed = false; + mp_error (mp); + mp->deletions_allowed = true; + mp->cur_mod = EL_GORDO; +} +mp->cur_cmd = numeric_token; +return @ @<Set |cur_mod:=n*unity+f| and check if it is uncomfortably large@>= -{ - mp->cur_mod=n*unity+f; - if ( mp->cur_mod>=fraction_one ) { - if ( (mp->internal[mp_warning_check]>0) && - (mp->scanner_status!=tex_flushing) ) { - print_err("Number is too large ("); - mp_print_scaled(mp, mp->cur_mod); - mp_print_char(mp, xord(')')); - help3("It is at least 4096. Continue and I'll try to cope", - "with that big value; but it might be dangerous.", - "(Set warningcheck:=0 to suppress this message.)"); - mp_error(mp); +{ + mp->cur_mod = n * unity + f; + if (mp->cur_mod >= fraction_one) { + if ((internal_value (mp_warning_check) > 0) && + (mp->scanner_status != tex_flushing)) { + print_err ("Number is too large ("); + mp_print_scaled (mp, mp->cur_mod); + mp_print_char (mp, xord (')')); + help3 ("It is at least 4096. Continue and I'll try to cope", + "with that big value; but it might be dangerous.", + "(Set warningcheck:=0 to suppress this message.)"); + mp_error (mp); } } } + @ Let's consider now what happens when |get_next| is looking at a token list. @^inner loop@> @<Input from token list;...@>= -if ( loc>=mp->hi_mem_min ) { /* one-word token */ - mp->cur_sym=mp_info(loc); loc=mp_link(loc); /* move to next */ - if ( mp->cur_sym>=expr_base ) { - if ( mp->cur_sym>=suffix_base ) { - @<Insert a suffix or text parameter and |goto restart|@>; - } else { - mp->cur_cmd=capsule_token; - mp->cur_mod=mp->param_stack[param_start+mp->cur_sym-(expr_base)]; - mp->cur_sym=0; return; - } +if (nloc != NULL && mp_type (nloc) == mp_symbol_node) { /* symbolic token */ + halfword cur_info = mp_sym_info (nloc); + mp->cur_sym = mp_sym_sym (nloc); + mp->cur_sym_mod = mp_name_type (nloc); + nloc = mp_link (nloc); /* move to next */ + if (mp->cur_sym_mod == mp_expr_sym) { + mp->cur_cmd = capsule_token; + mp->cur_mod_node = mp->param_stack[param_start + cur_info]; + mp->cur_sym_mod = 0; + mp->cur_sym = 0; + return; + } else if (mp->cur_sym_mod == mp_suffix_sym || mp->cur_sym_mod == mp_text_sym) { + mp_begin_token_list (mp, + mp->param_stack[param_start + cur_info], + (quarterword) parameter); + goto RESTART; } -} else if ( loc>null ) { +} else if (nloc != NULL) { @<Get a stored numeric or string or capsule token and |return|@> -} else { /* we are done with this token list */ - mp_end_token_list(mp); goto RESTART; /* resume previous level */ +} else { /* we are done with this token list */ + mp_end_token_list (mp); + goto RESTART; /* resume previous level */ } -@ @<Insert a suffix or text parameter...@>= -{ - if ( mp->cur_sym>=text_base ) mp->cur_sym=mp->cur_sym-mp->param_size; - /* |param_size=text_base-suffix_base| */ - mp_begin_token_list(mp, - mp->param_stack[param_start+mp->cur_sym-(suffix_base)], - (quarterword)parameter); - goto RESTART; -} @ @<Get a stored numeric or string or capsule token...@>= -{ - if ( mp_name_type(loc)==mp_token ) { - mp->cur_mod=value(loc); - if ( mp_type(loc)==mp_known ) { - mp->cur_cmd=numeric_token; - } else { - mp->cur_cmd=string_token; add_str_ref(mp->cur_mod); +{ + if (mp_name_type (nloc) == mp_token) { + if (mp_type (nloc) == mp_known) { + mp->cur_mod = value (nloc); + mp->cur_cmd = numeric_token; + } else { + mp->cur_mod_str = str_value (nloc); + mp->cur_cmd = string_token; + add_str_ref (mp->cur_mod_str); } - } else { - mp->cur_mod=loc; mp->cur_cmd=capsule_token; - }; - loc=mp_link(loc); return; + } else { + mp->cur_mod_node = nloc; + mp->cur_sym2 = NULL; + mp->cur_cmd = capsule_token; + } + nloc = mp_link (nloc); + return; } + @ All of the easy branches of |get_next| have now been taken care of. There is one more branch. @<Move to next line of file, or |goto restart|...@>= -if ( name>max_spec_src) { +if (name > max_spec_src) { @<Read next line of file into |buffer|, or |goto restart| if the file has ended@>; -} else { - if ( mp->input_ptr>0 ) { - /* text was inserted during error recovery or by \&{scantokens} */ - mp_end_file_reading(mp); goto RESTART; /* resume previous level */ - } - if (mp->job_name == NULL && ( mp->selector<log_only || mp->selector>=write_file)) - mp_open_log_file(mp); - if ( mp->interaction>mp_nonstop_mode ) { - if ( limit==start ) /* previous line was empty */ - mp_print_nl(mp, "(Please type a command or say `end')"); -@.Please type...@> - mp_print_ln(mp); mp->first=(size_t)start; - prompt_input("*"); /* input on-line into |buffer| */ -@.*\relax@> - limit=(halfword)mp->last; mp->buffer[limit]=xord('%'); - mp->first=(size_t)(limit+1); loc=start; +} else { + if (mp->input_ptr > 0) { + /* text was inserted during error recovery or by \&{scantokens} */ + mp_end_file_reading (mp); + goto RESTART; /* resume previous level */ + } + if (mp->job_name == NULL + && (mp->selector < log_only || mp->selector >= write_file)) + mp_open_log_file (mp); + if (mp->interaction > mp_nonstop_mode) { + if (limit == start) /* previous line was empty */ + mp_print_nl (mp, "(Please type a command or say `end')"); +@.Please type...@>; + mp_print_ln (mp); + mp->first = (size_t) start; + prompt_input ("*"); /* input on-line into |buffer| */ +@.*\relax@>; + limit = (halfword) mp->last; + mp->buffer[limit] = xord ('%'); + mp->first = (size_t) (limit + 1); + loc = start; } else { - mp_fatal_error(mp, "*** (job aborted, no legal end found)"); -@.job aborted@> + mp_fatal_error (mp, "*** (job aborted, no legal end found)"); +@.job aborted@>; /* nonstop mode, which is intended for overnight batch processing, never waits for on-line input */ } } + @ The global variable |force_eof| is normally |false|; it is set |true| by an \&{endinput} command. @<Glob...@>= -boolean force_eof; /* should the next \&{input} be aborted early? */ +boolean force_eof; /* should the next \&{input} be aborted early? */ @ We must decrement |loc| in order to leave the buffer in a valid state when an error condition causes us to |goto restart| without calling @@ -14342,87 +15424,105 @@ when an error condition causes us to |goto restart| without calling @<Read next line of file into |buffer|, or |goto restart| if the file has ended@>= -{ - incr(line); mp->first=(size_t)start; - if ( ! mp->force_eof ) { - if ( mp_input_ln(mp, cur_file ) ) /* not end of file */ - mp_firm_up_the_line(mp); /* this sets |limit| */ - else - mp->force_eof=true; +{ + incr (line); + mp->first = (size_t) start; + if (!mp->force_eof) { + if (mp_input_ln (mp, cur_file)) /* not end of file */ + mp_firm_up_the_line (mp); /* this sets |limit| */ + else + mp->force_eof = true; }; - if ( mp->force_eof ) { - mp->force_eof=false; - decr(loc); - if ( mpx_reading ) { + if (mp->force_eof) { + mp->force_eof = false; + decr (loc); + if (mpx_reading) { @<Complain that the \.{MPX} file ended unexpectly; then set - |cur_sym:=frozen_mpx_break| and |goto comon_ending|@>; - } else { - mp_print_char(mp, xord(')')); decr(mp->open_parens); - update_terminal; /* show user that file has been read */ - mp_end_file_reading(mp); /* resume previous level */ - if ( mp_check_outer_validity(mp) ) goto RESTART; - else goto RESTART; + |cur_sym:=mp->frozen_mpx_break| and |goto comon_ending|@>; + } else { + mp_print_char (mp, xord (')')); + decr (mp->open_parens); + update_terminal; /* show user that file has been read */ + mp_end_file_reading (mp); /* resume previous level */ + if (mp_check_outer_validity (mp)) + goto RESTART; + else + goto RESTART; } } - mp->buffer[limit]=xord('%'); mp->first=(size_t)(limit+1); loc=start; /* ready to read */ + mp->buffer[limit] = xord ('%'); + mp->first = (size_t) (limit + 1); + loc = start; /* ready to read */ } + @ We should never actually come to the end of an \.{MPX} file because such files should have an \&{mpxbreak} after the translation of the last \&{btex}$\,\ldots\,$\&{etex} block. @<Complain that the \.{MPX} file ended unexpectly; then set...@>= -{ - mp->mpx_name[iindex]=mpx_finished; - print_err("mpx file ended unexpectedly"); - help4("The file had too few picture expressions for btex...etex", - "blocks. Such files are normally generated automatically", - "but this one got messed up. You might want to insert a", - "picture expression now."); - mp->deletions_allowed=false; mp_error(mp); mp->deletions_allowed=true; - mp->cur_sym=frozen_mpx_break; goto COMMON_ENDING; +{ + mp->mpx_name[iindex] = mpx_finished; + print_err ("mpx file ended unexpectedly"); + help4 ("The file had too few picture expressions for btex...etex", + "blocks. Such files are normally generated automatically", + "but this one got messed up. You might want to insert a", + "picture expression now."); + mp->deletions_allowed = false; + mp_error (mp); + mp->deletions_allowed = true; + mp->cur_sym = mp->frozen_mpx_break; + goto COMMON_ENDING; } + @ Sometimes we want to make it look as though we have just read a blank line without really doing so. @<Put an empty line in the input buffer@>= -mp->last=mp->first; limit=(halfword)mp->last; +mp->last = mp->first; +limit = (halfword) mp->last; /* simulate |input_ln| and |firm_up_the_line| */ -mp->buffer[limit]=xord('%'); mp->first=(size_t)(limit+1); loc=start +mp->buffer[limit] = xord ('%'); +mp->first = (size_t) (limit + 1); +loc = start @ If the user has set the |mp_pausing| parameter to some positive value, and if nonstop mode has not been selected, each line of input is displayed on the terminal and the transcript file, followed by `\.{=>}'. -\MP\ waits for a response. If the response is null (i.e., if nothing is +\MP\ waits for a response. If the response is NULL (i.e., if nothing is typed except perhaps a few blank spaces), the original line is accepted as it stands; otherwise the line typed is used instead of the line in the file. -@c void mp_firm_up_the_line (MP mp) { - size_t k; /* an index into |buffer| */ - limit=(halfword)mp->last; - if ((!mp->noninteractive) - && (mp->internal[mp_pausing]>0 ) - && (mp->interaction>mp_nonstop_mode )) { - wake_up_terminal; mp_print_ln(mp); - if ( start<limit ) { - for (k=(size_t)start;k<(size_t)limit;k++) { - mp_print_str(mp, mp->buffer[k]); - } +@c +void mp_firm_up_the_line (MP mp) { + size_t k; /* an index into |buffer| */ + limit = (halfword) mp->last; + if ((!mp->noninteractive) + && (internal_value (mp_pausing) > 0) + && (mp->interaction > mp_nonstop_mode)) { + wake_up_terminal; + mp_print_ln (mp); + if (start < limit) { + for (k = (size_t) start; k < (size_t) limit; k++) { + mp_print_char (mp, mp->buffer[k]); + } } - mp->first=(size_t)limit; prompt_input("=>"); /* wait for user response */ -@.=>@> - if ( mp->last>mp->first ) { - for (k=mp->first;k<mp->last;k++) { /* move line down in buffer */ - mp->buffer[k+(size_t)start-mp->first]=mp->buffer[k]; + mp->first = (size_t) limit; + prompt_input ("=>"); /* wait for user response */ +@.=>@>; + if (mp->last > mp->first) { + for (k = mp->first; k < mp->last; k++) { /* move line down in buffer */ + mp->buffer[k + (size_t) start - mp->first] = mp->buffer[k]; } - limit=(halfword)((size_t)start+mp->last-mp->first); + limit = (halfword) ((size_t) start + mp->last - mp->first); } } } -@* \[30] Dealing with \TeX\ material. + +@* Dealing with \TeX\ material. The \&{btex}$\,\ldots\,$\&{etex} and \&{verbatimtex}$\,\ldots\,$\&{etex} features need to be implemented at a low level in the scanning process so that \MP\ can stay in synch with the a preprocessor that treats @@ -14440,108 +15540,139 @@ $\,\ldots\,$\&{etex} blocks, switching to the \.{MPX} file when it sees @d verbatim_code 1 @ @<Put each...@>= -mp_primitive(mp, "btex",start_tex,btex_code); -@:btex_}{\&{btex} primitive@> -mp_primitive(mp, "verbatimtex",start_tex,verbatim_code); -@:verbatimtex_}{\&{verbatimtex} primitive@> -mp_primitive(mp, "etex",etex_marker,0); mp->eqtb[frozen_etex]=mp->eqtb[mp->cur_sym]; -@:etex_}{\&{etex} primitive@> -mp_primitive(mp, "mpxbreak",mpx_break,0); mp->eqtb[frozen_mpx_break]=mp->eqtb[mp->cur_sym]; +mp_primitive (mp, "btex", start_tex, btex_code); +@:btex_}{\&{btex} primitive@>; +mp_primitive (mp, "verbatimtex", start_tex, verbatim_code); +@:verbatimtex_}{\&{verbatimtex} primitive@>; +mp_primitive (mp, "etex", etex_marker, 0); +mp->frozen_etex = mp_frozen_primitive (mp, "etex", etex_marker, 0); +@:etex_}{\&{etex} primitive@>; +mp_primitive (mp, "mpxbreak", mpx_break, 0); +mp->frozen_mpx_break = mp_frozen_primitive (mp, "mpxbreak", mpx_break, 0); @:mpx_break_}{\&{mpxbreak} primitive@> + @ @<Cases of |print_cmd...@>= -case start_tex: if ( m==btex_code ) mp_print(mp, "btex"); - else mp_print(mp, "verbatimtex"); break; -case etex_marker: mp_print(mp, "etex"); break; -case mpx_break: mp_print(mp, "mpxbreak"); break; +case start_tex: +if (m == btex_code) + mp_print (mp, "btex"); +else + mp_print (mp, "verbatimtex"); +break; +case etex_marker: +mp_print (mp, "etex"); +break; +case mpx_break: +mp_print (mp, "mpxbreak"); +break; @ Actually, |get_t_next| is a macro that avoids procedure overhead except in the unusual case where \&{btex}, \&{verbatimtex}, \&{etex}, or \&{mpxbreak} is encountered. -@d get_t_next {mp_get_next(mp); if ( mp->cur_cmd<=max_pre_command ) mp_t_next(mp); } +@c +static void get_t_next (MP mp) { + mp_get_next (mp); + if (mp->cur_cmd <= max_pre_command) + mp_t_next (mp); +} + -@<Declarations@>= +@ @<Declarations@>= +static void get_t_next (MP mp); +static void mp_t_next (MP mp); static void mp_start_mpx_input (MP mp); -@ @c +@ @c static void mp_t_next (MP mp) { - int old_status; /* saves the |scanner_status| */ - integer old_info; /* saves the |warning_info| */ - while ( mp->cur_cmd<=max_pre_command ) { - if ( mp->cur_cmd==mpx_break ) { - if ( ! file_state || (mp->mpx_name[iindex]==absent) ) { + int old_status; /* saves the |scanner_status| */ + integer old_info; /* saves the |warning_info| */ + while (mp->cur_cmd <= max_pre_command) { + if (mp->cur_cmd == mpx_break) { + if (!file_state || (mp->mpx_name[iindex] == absent)) { @<Complain about a misplaced \&{mpxbreak}@>; - } else { - mp_end_mpx_reading(mp); - goto TEX_FLUSH; + } else { + mp_end_mpx_reading (mp); + goto TEX_FLUSH; } - } else if ( mp->cur_cmd==start_tex ) { - if ( token_state || (name<=max_spec_src) ) { + } else if (mp->cur_cmd == start_tex) { + if (token_state || (name <= max_spec_src)) { @<Complain that we are not reading a file@>; - } else if ( mpx_reading ) { + } else if (mpx_reading) { @<Complain that \.{MPX} files cannot contain \TeX\ material@>; - } else if ( (mp->cur_mod!=verbatim_code)&& - (mp->mpx_name[iindex]!=mpx_finished) ) { - if ( ! mp_begin_mpx_reading(mp) ) mp_start_mpx_input(mp); + } else if ((mp->cur_mod != verbatim_code) && + (mp->mpx_name[iindex] != mpx_finished)) { + if (!mp_begin_mpx_reading (mp)) + mp_start_mpx_input (mp); } else { goto TEX_FLUSH; } } else { - @<Complain about a misplaced \&{etex}@>; + @<Complain about a misplaced \&{etex}@>; } goto COMMON_ENDING; - TEX_FLUSH: + TEX_FLUSH: @<Flush the \TeX\ material@>; - COMMON_ENDING: - mp_get_next(mp); + COMMON_ENDING: + mp_get_next (mp); } } + @ We could be in the middle of an operation such as skipping false conditional text when \TeX\ material is encountered, so we must be careful to save the |scanner_status|. @<Flush the \TeX\ material@>= -old_status=mp->scanner_status; -old_info=mp->warning_info; -mp->scanner_status=tex_flushing; -mp->warning_info=line; -do { mp_get_next(mp); } while (mp->cur_cmd!=etex_marker); -mp->scanner_status=old_status; -mp->warning_info=old_info +old_status = mp->scanner_status; +old_info = mp->warning_info_line; +mp->scanner_status = tex_flushing; +mp->warning_info_line = line; +do { + mp_get_next (mp); +} while (mp->cur_cmd != etex_marker); +mp->scanner_status = old_status; +mp->warning_info_line = old_info @ @<Complain that \.{MPX} files cannot contain \TeX\ material@>= -{ print_err("An mpx file cannot contain btex or verbatimtex blocks"); -help4("This file contains picture expressions for btex...etex", - "blocks. Such files are normally generated automatically", - "but this one seems to be messed up. I'll just keep going", - "and hope for the best."); -mp_error(mp); +{ + print_err ("An mpx file cannot contain btex or verbatimtex blocks"); + help4 ("This file contains picture expressions for btex...etex", + "blocks. Such files are normally generated automatically", + "but this one seems to be messed up. I'll just keep going", + "and hope for the best."); + mp_error (mp); } + @ @<Complain that we are not reading a file@>= -{ print_err("You can only use `btex' or `verbatimtex' in a file"); -help3("I'll have to ignore this preprocessor command because it", - "only works when there is a file to preprocess. You might", - "want to delete everything up to the next `etex`."); -mp_error(mp); +{ + print_err ("You can only use `btex' or `verbatimtex' in a file"); + help3 ("I'll have to ignore this preprocessor command because it", + "only works when there is a file to preprocess. You might", + "want to delete everything up to the next `etex`."); + mp_error (mp); } + @ @<Complain about a misplaced \&{mpxbreak}@>= -{ print_err("Misplaced mpxbreak"); -help2("I'll ignore this preprocessor command because it", - "doesn't belong here"); -mp_error(mp); +{ + print_err ("Misplaced mpxbreak"); + help2 ("I'll ignore this preprocessor command because it", + "doesn't belong here"); + mp_error (mp); } + @ @<Complain about a misplaced \&{etex}@>= -{ print_err("Extra etex will be ignored"); -help1("There is no btex or verbatimtex for this to match"); -mp_error(mp); +{ + print_err ("Extra etex will be ignored"); + help1 ("There is no btex or verbatimtex for this to match"); + mp_error (mp); } -@* \[31] Scanning macro definitions. + +@* Scanning macro definitions. \MP\ has a variety of ways to tuck tokens away into token lists for later use: Macros can be defined with \&{def}, \&{vardef}, \&{primarydef}, etc.; repeatable code can be defined with \&{for}, \&{forever}, \&{forsuffixes}. @@ -14554,55 +15685,62 @@ like \&{enddef} and \&{endfor}. @d var_def 2 /* command modifier for \&{vardef} */ @d end_def 0 /* command modifier for \&{enddef} */ @d start_forever 1 /* command modifier for \&{forever} */ +@d start_for 2 /* command modifier for \&{forever} */ +@d start_forsuffixes 3 /* command modifier for \&{forever} */ @d end_for 0 /* command modifier for \&{endfor} */ @<Put each...@>= -mp_primitive(mp, "def",macro_def,start_def); -@:def_}{\&{def} primitive@> -mp_primitive(mp, "vardef",macro_def,var_def); -@:var_def_}{\&{vardef} primitive@> -mp_primitive(mp, "primarydef",macro_def,secondary_primary_macro); -@:primary_def_}{\&{primarydef} primitive@> -mp_primitive(mp, "secondarydef",macro_def,tertiary_secondary_macro); -@:secondary_def_}{\&{secondarydef} primitive@> -mp_primitive(mp, "tertiarydef",macro_def,expression_tertiary_macro); -@:tertiary_def_}{\&{tertiarydef} primitive@> -mp_primitive(mp, "enddef",macro_def,end_def); mp->eqtb[frozen_end_def]=mp->eqtb[mp->cur_sym]; -@:end_def_}{\&{enddef} primitive@> -@# -mp_primitive(mp, "for",iteration,expr_base); -@:for_}{\&{for} primitive@> -mp_primitive(mp, "forsuffixes",iteration,suffix_base); -@:for_suffixes_}{\&{forsuffixes} primitive@> -mp_primitive(mp, "forever",iteration,start_forever); -@:forever_}{\&{forever} primitive@> -mp_primitive(mp, "endfor",iteration,end_for); mp->eqtb[frozen_end_for]=mp->eqtb[mp->cur_sym]; +mp_primitive (mp, "def", macro_def, start_def); +@:def_}{\&{def} primitive@>; +mp_primitive (mp, "vardef", macro_def, var_def); +@:var_def_}{\&{vardef} primitive@>; +mp_primitive (mp, "primarydef", macro_def, secondary_primary_macro); +@:primary_def_}{\&{primarydef} primitive@>; +mp_primitive (mp, "secondarydef", macro_def, tertiary_secondary_macro); +@:secondary_def_}{\&{secondarydef} primitive@>; +mp_primitive (mp, "tertiarydef", macro_def, expression_tertiary_macro); +@:tertiary_def_}{\&{tertiarydef} primitive@>; +mp_primitive (mp, "enddef", macro_def, end_def); +mp->frozen_end_def = mp_frozen_primitive (mp, "enddef", macro_def, end_def); +@:end_def_}{\&{enddef} primitive@>; +mp_primitive (mp, "for", iteration, start_for); +@:for_}{\&{for} primitive@>; +mp_primitive (mp, "forsuffixes", iteration, start_forsuffixes); +@:for_suffixes_}{\&{forsuffixes} primitive@>; +mp_primitive (mp, "forever", iteration, start_forever); +@:forever_}{\&{forever} primitive@>; +mp_primitive (mp, "endfor", iteration, end_for); +mp->frozen_end_for = mp_frozen_primitive (mp, "endfor", iteration, end_for); @:end_for_}{\&{endfor} primitive@> + @ @<Cases of |print_cmd...@>= case macro_def: - if ( m<=var_def ) { - if ( m==start_def ) mp_print(mp, "def"); - else if ( m<start_def ) mp_print(mp, "enddef"); - else mp_print(mp, "vardef"); - } else if ( m==secondary_primary_macro ) { - mp_print(mp, "primarydef"); - } else if ( m==tertiary_secondary_macro ) { - mp_print(mp, "secondarydef"); - } else { - mp_print(mp, "tertiarydef"); - } - break; -case iteration: - if ( m<=start_forever ) { - if ( m==start_forever ) mp_print(mp, "forever"); - else mp_print(mp, "endfor"); - } else if ( m==expr_base ) { - mp_print(mp, "for"); - } else { - mp_print(mp, "forsuffixes"); - } - break; +if (m <= var_def) { + if (m == start_def) + mp_print (mp, "def"); + else if (m < start_def) + mp_print (mp, "enddef"); + else + mp_print (mp, "vardef"); +} else if (m == secondary_primary_macro) { + mp_print (mp, "primarydef"); +} else if (m == tertiary_secondary_macro) { + mp_print (mp, "secondarydef"); +} else { + mp_print (mp, "tertiarydef"); +} +break; +case iteration: +if (m == start_forever) + mp_print (mp, "forever"); +else if (m == end_for) + mp_print (mp, "endfor"); +else if (m == start_for) + mp_print (mp, "for"); +else + mp_print (mp, "forsuffixes"); +break; @ Different macro-absorbing operations have different syntaxes, but they also have a lot in common. There is a list of special symbols that are to @@ -14615,7 +15753,7 @@ The first parameter to |scan_toks| is the command code that will terminate scanning (either |macro_def| or |iteration|). The second parameter, |subst_list|, points to a (possibly empty) list -of two-word nodes whose |info| and |value| fields specify symbol tokens +of non-symbolic nodes whose |info| and |value| fields specify symbol tokens before and after replacement. The list will be returned to free storage by |scan_toks|. @@ -14625,48 +15763,90 @@ And the final parameter tells how many of the special operations When such parameters are present, they are called \.{(SUFFIX0)}, \.{(SUFFIX1)}, and \.{(SUFFIX2)}. -@c static pointer mp_scan_toks (MP mp,command_code terminator, pointer - subst_list, pointer tail_end, quarterword suffix_count) { - pointer p; /* tail of the token list being built */ - pointer q; /* temporary for link management */ - integer balance; /* left delimiters minus right delimiters */ - p=hold_head; balance=1; mp_link(hold_head)=null; - while (1) { - get_t_next; - if ( mp->cur_sym>0 ) { +@<Types...@>= +typedef struct mp_subst_list_item { + quarterword info_mod; + quarterword value_mod; + mp_sym info; + halfword value_data; + struct mp_subst_list_item *link; +} mp_subst_list_item; + +@ +@c +static mp_node mp_scan_toks (MP mp, command_code terminator, + mp_subst_list_item * subst_list, mp_node tail_end, + quarterword suffix_count) { + mp_node p; /* tail of the token list being built */ + mp_subst_list_item *q = NULL; /* temporary for link management */ + integer balance; /* left delimiters minus right delimiters */ + halfword cur_data; + quarterword cur_data_mod = 0; + p = mp->hold_head; + balance = 1; + mp_link (mp->hold_head) = NULL; + while (1) { + get_t_next (mp); + cur_data = -1; + if (mp->cur_sym != NULL) { @<Substitute for |cur_sym|, if it's on the |subst_list|@>; - if ( mp->cur_cmd==terminator ) { + if (mp->cur_cmd == terminator) { @<Adjust the balance; |break| if it's zero@>; - } else if ( mp->cur_cmd==macro_special ) { - @<Handle quoted symbols, \.{\#\AT!}, \.{\AT!}, or \.{\AT!\#}@>; + } else if (mp->cur_cmd == macro_special) { + /* Handle quoted symbols, \.{\#\AT!}, \.{\AT!}, or \.{\AT!\#} */ + if (mp->cur_mod == quote) { + get_t_next (mp); + } else if (mp->cur_mod <= suffix_count) { + cur_data = mp->cur_mod - 1; + cur_data_mod = mp_suffix_sym; + } } } - mp_link(p)=mp_cur_tok(mp); p=mp_link(p); + if (cur_data != -1) { + mp_node pp = mp_get_symbolic_node (mp); + set_mp_sym_info (pp, cur_data); + mp_name_type (pp) = cur_data_mod; + mp_link (p) = pp; + } else { + mp_link (p) = mp_cur_tok (mp); + } + p = mp_link (p); + } + mp_link (p) = tail_end; + while (subst_list) { + q = subst_list->link; + xfree (subst_list); + subst_list = q; } - mp_link(p)=tail_end; mp_flush_node_list(mp, subst_list); - return mp_link(hold_head); + return mp_link (mp->hold_head); } + @ @<Substitute for |cur_sym|...@>= -{ - q=subst_list; - while ( q!=null ) { - if ( mp_info(q)==mp->cur_sym ) { - mp->cur_sym=value(q); mp->cur_cmd=relax; break; +{ + q = subst_list; + while (q != NULL) { + if (q->info == mp->cur_sym && q->info_mod == mp->cur_sym_mod) { + cur_data = q->value_data; + cur_data_mod = q->value_mod; + mp->cur_cmd = relax; + break; } - q=mp_link(q); + q = q->link; } } + @ @<Adjust the balance; |break| if it's zero@>= -if ( mp->cur_mod>0 ) { - incr(balance); -} else { - decr(balance); - if ( balance==0 ) +if (mp->cur_mod > 0) { + incr (balance); +} else { + decr (balance); + if (balance == 0) break; } + @ Four commands are intended to be used only within macro texts: \&{quote}, \.{\#\AT!}, \.{\AT!}, and \.{\AT!\#}. They are variants of a single command code called |macro_special|. @@ -14677,131 +15857,167 @@ code called |macro_special|. @d macro_suffix 3 /* |macro_special| modifier for \.{\AT!\#} */ @<Put each...@>= -mp_primitive(mp, "quote",macro_special,quote); -@:quote_}{\&{quote} primitive@> -mp_primitive(mp, "#@@",macro_special,macro_prefix); -@:]]]\#\AT!_}{\.{\#\AT!} primitive@> -mp_primitive(mp, "@@",macro_special,macro_at); -@:]]]\AT!_}{\.{\AT!} primitive@> -mp_primitive(mp, "@@#",macro_special,macro_suffix); +mp_primitive (mp, "quote", macro_special, quote); +@:quote_}{\&{quote} primitive@>; +mp_primitive (mp, "#@@", macro_special, macro_prefix); +@:]]]\#\AT!_}{\.{\#\AT!} primitive@>; +mp_primitive (mp, "@@", macro_special, macro_at); +@:]]]\AT!_}{\.{\AT!} primitive@>; +mp_primitive (mp, "@@#", macro_special, macro_suffix); @:]]]\AT!\#_}{\.{\AT!\#} primitive@> + @ @<Cases of |print_cmd...@>= -case macro_special: - switch (m) { - case macro_prefix: mp_print(mp, "#@@"); break; - case macro_at: mp_print_char(mp, xord('@@')); break; - case macro_suffix: mp_print(mp, "@@#"); break; - default: mp_print(mp, "quote"); break; - } +case macro_special: +switch (m) { +case macro_prefix: + mp_print (mp, "#@@"); + break; +case macro_at: + mp_print_char (mp, xord ('@@')); + break; +case macro_suffix: + mp_print (mp, "@@#"); + break; +default: + mp_print (mp, "quote"); break; - -@ @<Handle quoted...@>= -{ - if ( mp->cur_mod==quote ) { get_t_next; } - else if ( mp->cur_mod<=suffix_count ) - mp->cur_sym=suffix_base-1+mp->cur_mod; } +break; @ Here is a routine that's used whenever a token will be redefined. If -the user's token is unredefinable, the `|frozen_inaccessible|' token is +the user's token is unredefinable, the `|mp->frozen_inaccessible|' token is substituted; the latter is redefinable but essentially impossible to use, hence \MP's tables won't get fouled up. -@c static void mp_get_symbol (MP mp) { /* sets |cur_sym| to a safe symbol */ -RESTART: - get_t_next; - if ( (mp->cur_sym==0)||(mp->cur_sym>(integer)frozen_inaccessible) ) { - print_err("Missing symbolic token inserted"); -@.Missing symbolic token...@> - help3("Sorry: You can\'t redefine a number, string, or expr.", - "I've inserted an inaccessible symbol so that your", - "definition will be completed without mixing me up too badly."); - if ( mp->cur_sym>0 ) - mp->help_line[2]="Sorry: You can\'t redefine my error-recovery tokens."; - else if ( mp->cur_cmd==string_token ) - delete_str_ref(mp->cur_mod); - mp->cur_sym=frozen_inaccessible; mp_ins_error(mp); goto RESTART; +@c +static void mp_get_symbol (MP mp) { /* sets |cur_sym| to a safe symbol */ +RESTART: + get_t_next (mp); + if ((mp->cur_sym == NULL) || + mp_is_frozen(mp, mp->cur_sym)) { + print_err ("Missing symbolic token inserted"); +@.Missing symbolic token...@>; + help3 ("Sorry: You can\'t redefine a number, string, or expr.", + "I've inserted an inaccessible symbol so that your", + "definition will be completed without mixing me up too badly."); + if (mp->cur_sym != NULL) + mp->help_line[2] = "Sorry: You can\'t redefine my error-recovery tokens."; + else if (mp->cur_cmd == string_token) + delete_str_ref (mp->cur_mod_str); + mp->cur_sym = mp->frozen_inaccessible; + mp_ins_error (mp); + goto RESTART; } } + @ Before we actually redefine a symbolic token, we need to clear away its former value, if it was a variable. The following stronger version of |get_symbol| does that. -@c static void mp_get_clear_symbol (MP mp) { - mp_get_symbol(mp); mp_clear_symbol(mp, mp->cur_sym,false); +@c +static void mp_get_clear_symbol (MP mp) { + mp_get_symbol (mp); + mp_clear_symbol (mp, mp->cur_sym, false); } + @ Here's another little subroutine; it checks that an equals sign or assignment sign comes along at the proper place in a macro definition. -@c static void mp_check_equals (MP mp) { - if ( mp->cur_cmd!=equals ) if ( mp->cur_cmd!=assignment ) { - mp_missing_err(mp, "="); -@.Missing `='@> - help5("The next thing in this `def' should have been `=',", - "because I've already looked at the definition heading.", - "But don't worry; I'll pretend that an equals sign", - "was present. Everything from here to `enddef'", - "will be the replacement text of this macro."); - mp_back_error(mp); - } +@c +static void mp_check_equals (MP mp) { + if (mp->cur_cmd != equals) + if (mp->cur_cmd != assignment) { + mp_missing_err (mp, "="); +@.Missing `='@>; + help5 ("The next thing in this `def' should have been `=',", + "because I've already looked at the definition heading.", + "But don't worry; I'll pretend that an equals sign", + "was present. Everything from here to `enddef'", + "will be the replacement text of this macro."); + mp_back_error (mp); + } } + @ A \&{primarydef}, \&{secondarydef}, or \&{tertiarydef} is rather easily handled now that we have |scan_toks|. In this case there are -two parameters, which will be \.{EXPR0} and \.{EXPR1} (i.e., -|expr_base| and |expr_base+1|). - -@c static void mp_make_op_def (MP mp) { - command_code m; /* the type of definition */ - pointer p,q,r; /* for list manipulation */ - m=mp->cur_mod; - mp_get_symbol(mp); q=mp_get_node(mp, token_node_size); - mp_info(q)=mp->cur_sym; value(q)=expr_base; - mp_get_clear_symbol(mp); mp->warning_info=mp->cur_sym; - mp_get_symbol(mp); p=mp_get_node(mp, token_node_size); - mp_info(p)=mp->cur_sym; value(p)=expr_base+1; mp_link(p)=q; - get_t_next; mp_check_equals(mp); - mp->scanner_status=op_defining; q=mp_get_avail(mp); ref_count(q)=null; - r=mp_get_avail(mp); mp_link(q)=r; mp_info(r)=general_macro; - mp_link(r)=mp_scan_toks(mp, macro_def,p,null,0); - mp->scanner_status=normal; eq_type(mp->warning_info)=m; - equiv(mp->warning_info)=q; mp_get_x_next(mp); +two parameters, which will be \.{EXPR0} and \.{EXPR1}. + +@c +static void mp_make_op_def (MP mp) { + command_code m; /* the type of definition */ + mp_node q, r; /* for list manipulation */ + mp_subst_list_item *qm = NULL, *qn = NULL; + m = mp->cur_mod; + mp_get_symbol (mp); + qm = xmalloc (1, sizeof (mp_subst_list_item)); + qm->link = NULL; + qm->info = mp->cur_sym; + qm->info_mod = mp->cur_sym_mod; + qm->value_data = 0; + qm->value_mod = mp_expr_sym; + mp_get_clear_symbol (mp); + mp->warning_info = mp->cur_sym; + mp_get_symbol (mp); + qn = xmalloc (1, sizeof (mp_subst_list_item)); + qn->link = qm; + qn->info = mp->cur_sym; + qn->info_mod = mp->cur_sym_mod; + qn->value_data = 1; + qn->value_mod = mp_expr_sym; + get_t_next (mp); + mp_check_equals (mp); + mp->scanner_status = op_defining; + q = mp_get_symbolic_node (mp); + set_mp_sym_info (q, 0); /* |ref_count(q)=NULL;| */ + r = mp_get_symbolic_node (mp); + mp_link (q) = r; + set_mp_sym_info (r, general_macro); + mp_name_type (r) = mp_macro_sym; + mp_link (r) = mp_scan_toks (mp, macro_def, qn, NULL, 0); + mp->scanner_status = normal; + eq_type (mp->warning_info) = m; + equiv_node (mp->warning_info) = q; + mp_get_x_next (mp); } + @ Parameters to macros are introduced by the keywords \&{expr}, \&{suffix}, \&{text}, \&{primary}, \&{secondary}, and \&{tertiary}. @<Put each...@>= -mp_primitive(mp, "expr",param_type,expr_base); -@:expr_}{\&{expr} primitive@> -mp_primitive(mp, "suffix",param_type,suffix_base); -@:suffix_}{\&{suffix} primitive@> -mp_primitive(mp, "text",param_type,text_base); -@:text_}{\&{text} primitive@> -mp_primitive(mp, "primary",param_type,primary_macro); -@:primary_}{\&{primary} primitive@> -mp_primitive(mp, "secondary",param_type,secondary_macro); -@:secondary_}{\&{secondary} primitive@> -mp_primitive(mp, "tertiary",param_type,tertiary_macro); +mp_primitive (mp, "expr", param_type, expr_param); +@:expr_}{\&{expr} primitive@>; +mp_primitive (mp, "suffix", param_type, suffix_param); +@:suffix_}{\&{suffix} primitive@>; +mp_primitive (mp, "text", param_type, text_param); +@:text_}{\&{text} primitive@>; +mp_primitive (mp, "primary", param_type, primary_macro); +@:primary_}{\&{primary} primitive@>; +mp_primitive (mp, "secondary", param_type, secondary_macro); +@:secondary_}{\&{secondary} primitive@>; +mp_primitive (mp, "tertiary", param_type, tertiary_macro); @:tertiary_}{\&{tertiary} primitive@> + @ @<Cases of |print_cmd...@>= case param_type: - if ( m>=expr_base ) { - if ( m==expr_base ) mp_print(mp, "expr"); - else if ( m==suffix_base ) mp_print(mp, "suffix"); - else mp_print(mp, "text"); - } else if ( m<secondary_macro ) { - mp_print(mp, "primary"); - } else if ( m==secondary_macro ) { - mp_print(mp, "secondary"); - } else { - mp_print(mp, "tertiary"); - } - break; +if (m == expr_param) + mp_print (mp, "expr"); +else if (m == suffix_param) + mp_print (mp, "suffix"); +else if (m == text_param) + mp_print (mp, "text"); +else if (m == primary_macro) + mp_print (mp, "primary"); +else if (m == secondary_macro) + mp_print (mp, "secondary"); +else + mp_print (mp, "tertiary"); +break; @ Let's turn next to the more complex processing associated with \&{def} and \&{vardef}. When the following procedure is called, |cur_mod| @@ -14812,131 +16028,201 @@ Note that although the macro scanner allows |def = := enddef| and an error because by the time the second of the two identical tokens is seen, its meaning has already become undefined. -@c +@c static void mp_scan_def (MP mp) { - int m; /* the type of definition */ - int n; /* the number of special suffix parameters */ - int k; /* the total number of parameters */ - int c; /* the kind of macro we're defining */ - pointer r; /* parameter-substitution list */ - pointer q; /* tail of the macro token list */ - pointer p; /* temporary storage */ - halfword base; /* |expr_base|, |suffix_base|, or |text_base| */ - pointer l_delim,r_delim; /* matching delimiters */ - m=mp->cur_mod; c=general_macro; mp_link(hold_head)=null; - q=mp_get_avail(mp); ref_count(q)=null; r=null; + int m; /* the type of definition */ + int n; /* the number of special suffix parameters */ + int k; /* the total number of parameters */ + int c; /* the kind of macro we're defining */ + mp_subst_list_item *r = NULL, *rp = NULL; /* parameter-substitution list */ + mp_node q; /* tail of the macro token list */ + mp_node p; /* temporary storage */ + quarterword sym_type; /* |expr_sym|, |suffix_sym|, or |text_sym| */ + mp_sym l_delim, r_delim; /* matching delimiters */ + m = mp->cur_mod; + c = general_macro; + mp_link (mp->hold_head) = NULL; + q = mp_get_symbolic_node (mp); + set_mp_sym_info (q, 0); /* |ref_count(q)=NULL;| */ + r = NULL; @<Scan the token or variable to be defined; set |n|, |scanner_status|, and |warning_info|@>; - k=n; - if ( mp->cur_cmd==left_delimiter ) { + k = n; + if (mp->cur_cmd == left_delimiter) { @<Absorb delimited parameters, putting them into lists |q| and |r|@>; } - if ( mp->cur_cmd==param_type ) { + if (mp->cur_cmd == param_type) { @<Absorb undelimited parameters, putting them into list |r|@>; } - mp_check_equals(mp); - p=mp_get_avail(mp); mp_info(p)=c; mp_link(q)=p; + mp_check_equals (mp); + p = mp_get_symbolic_node (mp); + set_mp_sym_info (p, c); + mp_name_type (p) = mp_macro_sym; + mp_link (q) = p; @<Attach the replacement text to the tail of node |p|@>; - mp->scanner_status=normal; mp_get_x_next(mp); + mp->scanner_status = normal; + mp_get_x_next (mp); } -@ We don't put `|frozen_end_group|' into the replacement text of + +@ We don't put `|mp->frozen_end_group|' into the replacement text of a \&{vardef}, because the user may want to redefine `\.{endgroup}'. @<Attach the replacement text to the tail of node |p|@>= -if ( m==start_def ) { - mp_link(p)=mp_scan_toks(mp, macro_def,r,null, (quarterword)n); -} else { - q=mp_get_avail(mp); mp_info(q)=mp->bg_loc; mp_link(p)=q; - p=mp_get_avail(mp); mp_info(p)=mp->eg_loc; - mp_link(q)=mp_scan_toks(mp, macro_def,r,p, (quarterword)n); +if (m == start_def) { + mp_link (p) = mp_scan_toks (mp, macro_def, r, NULL, (quarterword) n); +} else { + q = mp_get_symbolic_node (mp); + set_mp_sym_sym (q, mp->bg_loc); + mp_link (p) = q; + p = mp_get_symbolic_node (mp); + set_mp_sym_sym (p, mp->eg_loc); + mp_link (q) = mp_scan_toks (mp, macro_def, r, p, (quarterword) n); } -if ( mp->warning_info==bad_vardef ) - mp_flush_token_list(mp, value(bad_vardef)) +if (mp->warning_info_node == mp->bad_vardef) + mp_flush_token_list (mp, value_node (mp->bad_vardef)) + @ @<Glob...@>= -int bg_loc; -int eg_loc; /* hash addresses of `\.{begingroup}' and `\.{endgroup}' */ +mp_sym bg_loc; +mp_sym eg_loc; /* hash addresses of `\.{begingroup}' and `\.{endgroup}' */ @ @<Scan the token or variable to be defined;...@>= -if ( m==start_def ) { - mp_get_clear_symbol(mp); mp->warning_info=mp->cur_sym; get_t_next; - mp->scanner_status=op_defining; n=0; - eq_type(mp->warning_info)=defined_macro; equiv(mp->warning_info)=q; -} else { - p=mp_scan_declared_variable(mp); - mp_flush_variable(mp, equiv(mp_info(p)),mp_link(p),true); - mp->warning_info=mp_find_variable(mp, p); mp_flush_list(mp, p); - if ( mp->warning_info==null ) @<Change to `\.{a bad variable}'@>; - mp->scanner_status=var_defining; n=2; - if ( mp->cur_cmd==macro_special ) if ( mp->cur_mod==macro_suffix ) {/* \.{\AT!\#} */ - n=3; get_t_next; - } - mp_type(mp->warning_info)=(quarterword)(mp_unsuffixed_macro-2+n); - value(mp->warning_info)=q; -} /* |mp_suffixed_macro=mp_unsuffixed_macro+1| */ +if (m == start_def) { + mp_get_clear_symbol (mp); + mp->warning_info = mp->cur_sym; + get_t_next (mp); + mp->scanner_status = op_defining; + n = 0; + eq_type (mp->warning_info) = defined_macro; + equiv_node (mp->warning_info) = q; +} else { + p = mp_scan_declared_variable (mp); + mp_flush_variable (mp, equiv_node (mp_sym_sym (p)), mp_link (p), true); + mp->warning_info_node = mp_find_variable (mp, p); + mp_flush_node_list (mp, p); + if (mp->warning_info_node == NULL) + @<Change to `\.{a bad variable}'@>; + mp->scanner_status = var_defining; + n = 2; + if (mp->cur_cmd == macro_special && mp->cur_mod == macro_suffix) { /* \.{\AT!\#} */ + n = 3; + get_t_next (mp); + } + mp_type (mp->warning_info_node) = (quarterword) (mp_unsuffixed_macro - 2 + n); + /* |mp_suffixed_macro=mp_unsuffixed_macro+1| */ + set_value_node (mp->warning_info_node, q); +} + @ @<Change to `\.{a bad variable}'@>= -{ - print_err("This variable already starts with a macro"); -@.This variable already...@> - help2("After `vardef a' you can\'t say `vardef a.b'.", - "So I'll have to discard this definition."); - mp_error(mp); mp->warning_info=bad_vardef; +{ + print_err ("This variable already starts with a macro"); +@.This variable already...@>; + help2 ("After `vardef a' you can\'t say `vardef a.b'.", + "So I'll have to discard this definition."); + mp_error (mp); + mp->warning_info_node = mp->bad_vardef; } -@ @<Initialize table entries...@>= -mp_name_type(bad_vardef)=mp_root; mp_link(bad_vardef)=frozen_bad_vardef; -equiv(frozen_bad_vardef)=bad_vardef; eq_type(frozen_bad_vardef)=tag_token; + +@ @<Initialize table entries@>= +mp->bad_vardef = mp_get_value_node (mp); +mp_name_type (mp->bad_vardef) = mp_root; +value_sym (mp->bad_vardef) = mp->frozen_bad_vardef; + +@ @<Free table entries@>= +mp_free_value_node (mp, mp->bad_vardef); + @ @<Absorb delimited parameters, putting them into lists |q| and |r|@>= -do { - l_delim=mp->cur_sym; r_delim=mp->cur_mod; get_t_next; - if ( (mp->cur_cmd==param_type)&&(mp->cur_mod>=expr_base) ) { - base=mp->cur_mod; - } else { - print_err("Missing parameter type; `expr' will be assumed"); -@.Missing parameter type@> - help1("You should've had `expr' or `suffix' or `text' here."); - mp_back_error(mp); base=expr_base; - } - @<Absorb parameter tokens for type |base|@>; - mp_check_delimiter(mp, l_delim,r_delim); - get_t_next; -} while (mp->cur_cmd==left_delimiter) - -@ @<Absorb parameter tokens for type |base|@>= -do { - mp_link(q)=mp_get_avail(mp); q=mp_link(q); mp_info(q)=base+k; - mp_get_symbol(mp); p=mp_get_node(mp, token_node_size); - value(p)=base+k; mp_info(p)=mp->cur_sym; - if ( k==mp->param_size ) mp_overflow(mp, "parameter stack size",mp->param_size); -@:MetaPost capacity exceeded parameter stack size}{\quad parameter stack size@> - incr(k); mp_link(p)=r; r=p; get_t_next; -} while (mp->cur_cmd==comma) +do { + l_delim = mp->cur_sym; + r_delim = mp->cur_sym2; + get_t_next (mp); + if ((mp->cur_cmd == param_type) && (mp->cur_mod == expr_param)) { + sym_type = mp_expr_sym; + } else if ((mp->cur_cmd == param_type) && (mp->cur_mod == suffix_param)) { + sym_type = mp_suffix_sym; + } else if ((mp->cur_cmd == param_type) && (mp->cur_mod == text_param)) { + sym_type = mp_text_sym; + } else { + print_err ("Missing parameter type; `expr' will be assumed"); +@.Missing parameter type@>; + help1 ("You should've had `expr' or `suffix' or `text' here."); + mp_back_error (mp); + sym_type = mp_expr_sym; + } + @<Absorb parameter tokens for type |sym_type|@>; + mp_check_delimiter (mp, l_delim, r_delim); + get_t_next (mp); +} while (mp->cur_cmd == left_delimiter) + +@ @<Absorb parameter tokens for type |sym_type|@>= +do { + mp_link (q) = mp_get_symbolic_node (mp); + q = mp_link (q); + mp_name_type (q) = sym_type; + set_mp_sym_info (q, k); + mp_get_symbol (mp); + rp = xmalloc (1, sizeof (mp_subst_list_item)); + rp->link = NULL; + rp->value_data = k; + rp->value_mod = sym_type; + rp->info = mp->cur_sym; + rp->info_mod = mp->cur_sym_mod; + mp_check_param_size (mp, k); + incr (k); + rp->link = r; + r = rp; + get_t_next (mp); +} while (mp->cur_cmd == comma) @ @<Absorb undelimited parameters, putting them into list |r|@>= -{ - p=mp_get_node(mp, token_node_size); - if ( mp->cur_mod<expr_base ) { - c=mp->cur_mod; value(p)=expr_base+k; - } else { - value(p)=mp->cur_mod+k; - if ( mp->cur_mod==expr_base ) c=expr_macro; - else if ( mp->cur_mod==suffix_base ) c=suffix_macro; - else c=text_macro; - } - if ( k==mp->param_size ) mp_overflow(mp, "parameter stack size",mp->param_size); - incr(k); mp_get_symbol(mp); mp_info(p)=mp->cur_sym; mp_link(p)=r; r=p; get_t_next; - if ( c==expr_macro ) if ( mp->cur_cmd==of_token ) { - c=of_macro; p=mp_get_node(mp, token_node_size); - if ( k==mp->param_size ) mp_overflow(mp, "parameter stack size",mp->param_size); - value(p)=expr_base+k; mp_get_symbol(mp); mp_info(p)=mp->cur_sym; - mp_link(p)=r; r=p; get_t_next; - } -} - -@* \[32] Expanding the next token. +{ + rp = xmalloc (1, sizeof (mp_subst_list_item)); + rp->link = NULL; + rp->value_data = k; + if (mp->cur_mod == expr_param) { + rp->value_mod = mp_expr_sym; + c = expr_macro; + } else if (mp->cur_mod == suffix_param) { + rp->value_mod = mp_suffix_sym; + c = suffix_macro; + } else if (mp->cur_mod == text_param) { + rp->value_mod = mp_text_sym; + c = text_macro; + } else { + c = mp->cur_mod; + rp->value_mod = mp_expr_sym; + } + mp_check_param_size (mp, k); + incr (k); + mp_get_symbol (mp); + rp->info = mp->cur_sym; + rp->info_mod = mp->cur_sym_mod; + rp->link = r; + r = rp; + get_t_next (mp); + if (c == expr_macro) + if (mp->cur_cmd == of_token) { + c = of_macro; + rp = xmalloc (1, sizeof (mp_subst_list_item)); + rp->link = NULL; + mp_check_param_size (mp, k); + rp->value_data = k; + rp->value_mod = mp_expr_sym; + mp_get_symbol (mp); + rp->info = mp->cur_sym; + rp->info_mod = mp->cur_sym_mod; + rp->link = r; + r = rp; + get_t_next (mp); + } +} + + +@* Expanding the next token. Only a few command codes |<min_command| can possibly be returned by |get_t_next|; in increasing order, they are |if_test|, |fi_or_else|, |input|, |iteration|, |repeat_loop|, @@ -14958,7 +16244,7 @@ Therefore it's necessary to declare a whole bunch of |forward| procedures at this point, and to insert some other procedures that will be invoked by |get_x_next|. -@<Declarations@>= +@<Declarations@>= static void mp_scan_primary (MP mp); static void mp_scan_secondary (MP mp); static void mp_scan_tertiary (MP mp); @@ -14980,7 +16266,7 @@ Metapost is used as a library. @<Global...@>= int expand_depth_count; /* current expansion depth */ -int expand_depth; /* current expansion depth */ +int expand_depth; /* current expansion depth */ @ The limit is set at |10000|, which should be enough to allow normal usages of metapost while preventing the most obvious @@ -14989,35 +16275,35 @@ raised if the runtime system allows a larger C stack. @^system dependencies@> @<Set initial...@>= -mp->expand_depth=10000; +mp->expand_depth = 10000; @ Even better would be if the system allows discovery of the amount of space available on the call stack. @^system dependencies@> @c -static void mp_check_expansion_depth (MP mp ){ - if (mp->expand_depth_count>=mp->expand_depth) { - mp_overflow(mp, "expansion depth", mp->expand_depth); +static void mp_check_expansion_depth (MP mp) { + if (mp->expand_depth_count >= mp->expand_depth) { + mp_overflow (mp, "expansion depth", mp->expand_depth); } } + @ An auxiliary subroutine called |expand| is used by |get_x_next| when it has to do exotic expansion commands. -@c +@c static void mp_expand (MP mp) { - pointer p; /* for list manipulation */ - size_t k; /* something that we hope is |<=buf_size| */ - pool_pointer j; /* index into |str_pool| */ + size_t k; /* something that we hope is |<=buf_size| */ + size_t j; /* index into |str_pool| */ mp->expand_depth_count++; - mp_check_expansion_depth(mp); - if ( mp->internal[mp_tracing_commands]>unity ) - if ( mp->cur_cmd!=defined_macro ) + mp_check_expansion_depth (mp); + if (internal_value (mp_tracing_commands) > unity) + if (mp->cur_cmd != defined_macro) show_cur_cmd_mod; - switch (mp->cur_cmd) { + switch (mp->cur_cmd) { case if_test: - mp_conditional(mp); /* this procedure is discussed in Part 36 below */ + mp_conditional (mp); /* this procedure is discussed in Part 36 below */ break; case fi_or_else: @<Terminate the current conditional and skip to \&{fi}@>; @@ -15026,158 +16312,199 @@ static void mp_expand (MP mp) { @<Initiate or terminate input from a file@>; break; case iteration: - if ( mp->cur_mod==end_for ) { + if (mp->cur_mod == end_for) { @<Scold the user for having an extra \&{endfor}@>; } else { - mp_begin_iteration(mp); /* this procedure is discussed in Part 37 below */ + mp_begin_iteration (mp); /* this procedure is discussed in Part 37 below */ } break; - case repeat_loop: + case repeat_loop: @<Repeat a loop@>; break; - case exit_test: + case exit_test: @<Exit a loop if the proper time has come@>; break; - case relax: + case relax: break; - case expand_after: + case expand_after: @<Expand the token after the next token@>; break; - case scan_tokens: + case scan_tokens: @<Put a string into the input buffer@>; break; case defined_macro: - mp_macro_call(mp, mp->cur_mod,null,mp->cur_sym); - break; - }; /* there are no other cases */ + mp_macro_call (mp, mp->cur_mod_node, NULL, mp->cur_sym); + break; + }; /* there are no other cases */ mp->expand_depth_count--; } + @ @<Scold the user...@>= -{ - print_err("Extra `endfor'"); -@.Extra `endfor'@> - help2("I'm not currently working on a for loop,", - "so I had better not try to end anything."); - mp_error(mp); +{ + print_err ("Extra `endfor'"); +@.Extra `endfor'@>; + help2 ("I'm not currently working on a for loop,", + "so I had better not try to end anything."); + mp_error (mp); } + @ The processing of \&{input} involves the |start_input| subroutine, which will be declared later; the processing of \&{endinput} is trivial. @<Put each...@>= -mp_primitive(mp, "input",input,0); -@:input_}{\&{input} primitive@> -mp_primitive(mp, "endinput",input,1); +mp_primitive (mp, "input", input, 0); +@:input_}{\&{input} primitive@>; +mp_primitive (mp, "endinput", input, 1); @:end_input_}{\&{endinput} primitive@> + @ @<Cases of |print_cmd_mod|...@>= -case input: - if ( m==0 ) mp_print(mp, "input"); - else mp_print(mp, "endinput"); - break; +case input: +if (m == 0) + mp_print (mp, "input"); +else + mp_print (mp, "endinput"); +break; @ @<Initiate or terminate input...@>= -if ( mp->cur_mod>0 ) mp->force_eof=true; -else mp_start_input(mp) +if (mp->cur_mod > 0) + mp->force_eof = true; +else + mp_start_input (mp) + @ We'll discuss the complicated parts of loop operations later. For now it suffices to know that there's a global variable called |loop_ptr| -that will be |null| if no loop is in progress. +that will be |NULL| if no loop is in progress. @<Repeat a loop@>= -{ while ( token_state &&(loc==null) ) - mp_end_token_list(mp); /* conserve stack space */ - if ( mp->loop_ptr==null ) { - print_err("Lost loop"); -@.Lost loop@> - help2("I'm confused; after exiting from a loop, I still seem", - "to want to repeat it. I'll try to forget the problem."); - mp_error(mp); +{ + while (token_state && (nloc == NULL)) + mp_end_token_list (mp); /* conserve stack space */ + if (mp->loop_ptr == NULL) { + print_err ("Lost loop"); +@.Lost loop@>; + help2 ("I'm confused; after exiting from a loop, I still seem", + "to want to repeat it. I'll try to forget the problem."); + mp_error (mp); } else { - mp_resume_iteration(mp); /* this procedure is in Part 37 below */ + mp_resume_iteration (mp); /* this procedure is in Part 37 below */ } } + @ @<Exit a loop if the proper time has come@>= -{ mp_get_boolean(mp); - if ( mp->internal[mp_tracing_commands]>unity ) - mp_show_cmd_mod(mp, nullary,mp->cur_exp); - if ( mp->cur_exp==true_code ) { - if ( mp->loop_ptr==null ) { - print_err("No loop is in progress"); -@.No loop is in progress@> - help1("Why say `exitif' when there's nothing to exit from?"); - if ( mp->cur_cmd==semicolon ) mp_error(mp); else mp_back_error(mp); +{ + mp_get_boolean (mp); + if (internal_value (mp_tracing_commands) > unity) + mp_show_cmd_mod (mp, nullary, cur_exp_value ()); + if (cur_exp_value () == true_code) { + if (mp->loop_ptr == NULL) { + print_err ("No loop is in progress"); +@.No loop is in progress@>; + help1 ("Why say `exitif' when there's nothing to exit from?"); + if (mp->cur_cmd == semicolon) + mp_error (mp); + else + mp_back_error (mp); } else { - @<Exit prematurely from an iteration@>; + @<Exit prematurely from an iteration@>; } - } else if ( mp->cur_cmd!=semicolon ) { - mp_missing_err(mp, ";"); -@.Missing `;'@> - help2("After `exitif <boolean exp>' I expect to see a semicolon.", - "I shall pretend that one was there."); mp_back_error(mp); + } else if (mp->cur_cmd != semicolon) { + mp_missing_err (mp, ";"); +@.Missing `;'@>; + help2 ("After `exitif <boolean exp>' I expect to see a semicolon.", + "I shall pretend that one was there."); + mp_back_error (mp); } } + @ Here we use the fact that |forever_text| is the only |token_type| that is less than |loop_text|. @<Exit prematurely...@>= -{ p=null; - do { - if ( file_state ) { - mp_end_file_reading(mp); - } else { - if ( token_type<=loop_text ) p=start; - mp_end_token_list(mp); +{ + mp_node p = NULL; + do { + if (file_state) { + mp_end_file_reading (mp); + } else { + if (token_type <= loop_text) + p = nstart; + mp_end_token_list (mp); } - } while (p==null); - if ( p!=mp_info(mp->loop_ptr) ) mp_fatal_error(mp, "*** (loop confusion)"); -@.loop confusion@> - mp_stop_iteration(mp); /* this procedure is in Part 34 below */ + } while (p == NULL); + if (p != mp->loop_ptr->info) + mp_fatal_error (mp, "*** (loop confusion)"); +@.loop confusion@>; + mp_stop_iteration (mp); /* this procedure is in Part 34 below */ } + @ @<Expand the token after the next token@>= -{ get_t_next; - p=mp_cur_tok(mp); get_t_next; - if ( mp->cur_cmd<min_command ) mp_expand(mp); - else mp_back_input(mp); - back_list(p); +{ + mp_node p; + get_t_next (mp); + p = mp_cur_tok (mp); + get_t_next (mp); + if (mp->cur_cmd < min_command) + mp_expand (mp); + else + mp_back_input (mp); + back_list (p); } + @ @<Put a string into the input buffer@>= -{ mp_get_x_next(mp); mp_scan_primary(mp); - if ( mp->cur_type!=mp_string_type ) { - mp_disp_err(mp, null,"Not a string"); -@.Not a string@> - help2("I'm going to flush this expression, since", - "scantokens should be followed by a known string."); - mp_put_get_flush_error(mp, 0); - } else { - mp_back_input(mp); - if ( length(mp->cur_exp)>0 ) - @<Pretend we're reading a new one-line file@>; +{ + mp_get_x_next (mp); + mp_scan_primary (mp); + if (mp->cur_exp.type != mp_string_type) { + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + mp_disp_err (mp, NULL, "Not a string"); +@.Not a string@>; + help2 ("I'm going to flush this expression, since", + "scantokens should be followed by a known string."); + mp_put_get_flush_error (mp, new_expr); + } else { + mp_back_input (mp); + if (length (cur_exp_str ()) > 0) + @<Pretend we're reading a new one-line file@>; } } + @ @<Pretend we're reading a new one-line file@>= -{ mp_begin_file_reading(mp); name=is_scantok; - k=mp->first+(size_t)length(mp->cur_exp); - if ( k>=mp->max_buf_stack ) { - while ( k>=mp->buf_size ) { - mp_reallocate_buffer(mp,(mp->buf_size+(mp->buf_size/4))); +{ + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + mp_begin_file_reading (mp); + name = is_scantok; + k = mp->first + (size_t) length (cur_exp_str ()); + if (k >= mp->max_buf_stack) { + while (k >= mp->buf_size) { + mp_reallocate_buffer (mp, (mp->buf_size + (mp->buf_size / 4))); } - mp->max_buf_stack=k+1; + mp->max_buf_stack = k + 1; } - j=mp->str_start[mp->cur_exp]; limit=(halfword)k; - while ( mp->first<(size_t)limit ) { - mp->buffer[mp->first]=mp->str_pool[j]; incr(j); incr(mp->first); + j = 0; + limit = (halfword) k; + while (mp->first < (size_t) limit) { + mp->buffer[mp->first] = *(cur_exp_str ()->str + j); + j++; + incr (mp->first); } - mp->buffer[limit]=xord('%'); mp->first=(size_t)(limit+1); loc=start; - mp_flush_cur_exp(mp, 0); + mp->buffer[limit] = xord ('%'); + mp->first = (size_t) (limit + 1); + loc = start; + mp_flush_cur_exp (mp, new_expr); } + @ Here finally is |get_x_next|. The expression scanning routines to be considered later @@ -15189,22 +16516,24 @@ macros are being expanded. @<Declarations@>= static void mp_get_x_next (MP mp); -@ @c void mp_get_x_next (MP mp) { - pointer save_exp; /* a capsule to save |cur_type| and |cur_exp| */ - get_t_next; - if ( mp->cur_cmd<min_command ) { - save_exp=mp_stash_cur_exp(mp); - do { - if ( mp->cur_cmd==defined_macro ) - mp_macro_call(mp, mp->cur_mod,null,mp->cur_sym); - else - mp_expand(mp); - get_t_next; - } while (mp->cur_cmd<min_command); - mp_unstash_cur_exp(mp, save_exp); /* that restores |cur_type| and |cur_exp| */ +@ @c +void mp_get_x_next (MP mp) { + mp_node save_exp; /* a capsule to save |cur_type| and |cur_exp| */ + get_t_next (mp); + if (mp->cur_cmd < min_command) { + save_exp = mp_stash_cur_exp (mp); + do { + if (mp->cur_cmd == defined_macro) + mp_macro_call (mp, mp->cur_mod_node, NULL, mp->cur_sym); + else + mp_expand (mp); + get_t_next (mp); + } while (mp->cur_cmd < min_command); + mp_unstash_cur_exp (mp, save_exp); /* that restores |cur_type| and |cur_exp| */ } } + @ Now let's consider the |macro_call| procedure, which is used to start up all user-defined macros. Since the arguments to a macro might be expressions, |macro_call| is recursive. @@ -15214,12 +16543,12 @@ The first parameter to |macro_call| points to the reference count of the token list that defines the macro. The second parameter contains any arguments that have already been parsed (see below). The third parameter points to the symbolic token that names the macro. If the third parameter -is |null|, the macro was defined by \&{vardef}, so its name can be +is |NULL|, the macro was defined by \&{vardef}, so its name can be reconstructed from the prefix and ``at'' arguments found within the second parameter. -What is this second parameter? It's simply a linked list of one-word items, -whose |info| fields point to the arguments. In other words, if |arg_list=null|, +What is this second parameter? It's simply a linked list of symbolic items, +whose |info| fields point to the arguments. In other words, if |arg_list=NULL|, no arguments have been scanned yet; otherwise |mp_info(arg_list)| points to the first scanned argument, and |mp_link(arg_list)| points to the list of further arguments (if any). @@ -15237,26 +16566,26 @@ the replacement text of the macro is placed at the top of the \MP's input stack, so that |get_t_next| will proceed to read it next. @<Declarations@>= -static void mp_macro_call (MP mp,pointer def_ref, pointer arg_list, - pointer macro_name) ; +static void mp_macro_call (MP mp, mp_node def_ref, mp_node arg_list, + mp_sym macro_name); @ @c -void mp_macro_call (MP mp,pointer def_ref, pointer arg_list, - pointer macro_name) { +void mp_macro_call (MP mp, mp_node def_ref, mp_node arg_list, mp_sym macro_name) { /* invokes a user-defined control sequence */ - pointer r; /* current node in the macro's token list */ - pointer p,q; /* for list manipulation */ - integer n; /* the number of arguments */ - pointer tail = 0; /* tail of the argument list */ - pointer l_delim=0,r_delim=0; /* a delimiter pair */ - r=mp_link(def_ref); add_mac_ref(def_ref); - if ( arg_list==null ) { - n=0; + mp_node r; /* current node in the macro's token list */ + mp_node p, q; /* for list manipulation */ + integer n; /* the number of arguments */ + mp_node tail = 0; /* tail of the argument list */ + mp_sym l_delim = NULL, r_delim = NULL; /* a delimiter pair */ + r = mp_link (def_ref); + add_mac_ref (def_ref); + if (arg_list == NULL) { + n = 0; } else { - @<Determine the number |n| of arguments already supplied, + @<Determine the number |n| of arguments already supplied, and set |tail| to the tail of |arg_list|@>; } - if ( mp->internal[mp_tracing_macros]>0 ) { + if (internal_value (mp_tracing_macros) > 0) { @<Show the text of the macro being expanded, and the existing arguments@>; } @<Scan the remaining arguments, if any; set |r| to the first token @@ -15264,87 +16593,111 @@ void mp_macro_call (MP mp,pointer def_ref, pointer arg_list, @<Feed the arguments and replacement text to the scanner@>; } -@ @<Show the text of the macro...@>= -mp_begin_diagnostic(mp); mp_print_ln(mp); -mp_print_macro_name(mp, arg_list,macro_name); -if ( n==3 ) mp_print(mp, "@@#"); /* indicate a suffixed macro */ -mp_show_macro(mp, def_ref,null,100000); -if ( arg_list!=null ) { - n=0; p=arg_list; - do { - q=mp_info(p); - mp_print_arg(mp, q,n,0); - incr(n); p=mp_link(p); - } while (p!=null); -} -mp_end_diagnostic(mp, false) +@ @<Show the text of the macro...@>= +mp_begin_diagnostic (mp); +mp_print_ln (mp); +mp_print_macro_name (mp, arg_list, macro_name); +if (n == 3) + mp_print (mp, "@@#"); /* indicate a suffixed macro */ +mp_show_macro (mp, def_ref, NULL, 100000); +if (arg_list != NULL) { + n = 0; + p = arg_list; + do { + q = (mp_node)mp_sym_sym (p); + mp_print_arg (mp, q, n, 0, 0); + incr (n); + p = mp_link (p); + } while (p != NULL); +} +mp_end_diagnostic (mp, false) + @ @<Declarations@>= -static void mp_print_macro_name (MP mp,pointer a, pointer n); +static void mp_print_macro_name (MP mp, mp_node a, mp_sym n); @ @c -void mp_print_macro_name (MP mp,pointer a, pointer n) { - pointer p,q; /* they traverse the first part of |a| */ - if ( n!=null ) { - mp_print_text(n); - } else { - p=mp_info(a); - if ( p==null ) { - mp_print_text(mp_info(mp_info(mp_link(a)))); - } else { - q=p; - while ( mp_link(q)!=null ) q=mp_link(q); - mp_link(q)=mp_info(mp_link(a)); - mp_show_token_list(mp, p,null,1000,0); - mp_link(q)=null; +void mp_print_macro_name (MP mp, mp_node a, mp_sym n) { + mp_node p, q; /* they traverse the first part of |a| */ + if (n != NULL) { + mp_print_text (n); + } else { + p = (mp_node)mp_sym_sym (a); + if (p == NULL) { + mp_print_text (mp_sym_sym ((mp_node)mp_sym_sym (mp_link (a)))); + } else { + q = p; + while (mp_link (q) != NULL) + q = mp_link (q); + mp_link (q) = (mp_node)mp_sym_sym (mp_link (a)); + mp_show_token_list (mp, p, NULL, 1000, 0); + mp_link (q) = NULL; } } } + @ @<Declarations@>= -static void mp_print_arg (MP mp,pointer q, integer n, pointer b) ; +static void mp_print_arg (MP mp, mp_node q, integer n, halfword b, + quarterword bb); @ @c -void mp_print_arg (MP mp,pointer q, integer n, pointer b) { - if ( mp_link(q)==mp_void ) mp_print_nl(mp, "(EXPR"); - else if ( (b<text_base)&&(b!=text_macro) ) mp_print_nl(mp, "(SUFFIX"); - else mp_print_nl(mp, "(TEXT"); - mp_print_int(mp, n); mp_print(mp, ")<-"); - if ( mp_link(q)==mp_void ) mp_print_exp(mp, q,1); - else mp_show_token_list(mp, q,null,1000,0); +void mp_print_arg (MP mp, mp_node q, integer n, halfword b, quarterword bb) { + if (q && mp_link (q) == MP_VOID) { + mp_print_nl (mp, "(EXPR"); + } else { + if ((bb < mp_text_sym) && (b != text_macro)) + mp_print_nl (mp, "(SUFFIX"); + else + mp_print_nl (mp, "(TEXT"); + } + mp_print_int (mp, n); + mp_print (mp, ")<-"); + if (q && mp_link (q) == MP_VOID) + mp_print_exp (mp, q, 1); + else + mp_show_token_list (mp, q, NULL, 1000, 0); } + @ @<Determine the number |n| of arguments already supplied...@>= -{ - n=1; tail=arg_list; - while ( mp_link(tail)!=null ) { - incr(n); tail=mp_link(tail); +{ + n = 1; + tail = arg_list; + while (mp_link (tail) != NULL) { + incr (n); + tail = mp_link (tail); } } + @ @<Scan the remaining arguments, if any; set |r|...@>= -mp->cur_cmd=comma+1; /* anything |<>comma| will do */ -while ( mp_info(r)>=expr_base ) { - @<Scan the delimited argument represented by |mp_info(r)|@>; - r=mp_link(r); -} -if ( mp->cur_cmd==comma ) { - print_err("Too many arguments to "); -@.Too many arguments...@> - mp_print_macro_name(mp, arg_list,macro_name); mp_print_char(mp, xord(';')); - mp_print_nl(mp, " Missing `"); mp_print_text(r_delim); -@.Missing `)'...@> - mp_print(mp, "' has been inserted"); - help3("I'm going to assume that the comma I just read was a", - "right delimiter, and then I'll begin expanding the macro.", - "You might want to delete some tokens before continuing."); - mp_error(mp); -} -if ( mp_info(r)!=general_macro ) { +mp->cur_cmd = comma + 1; /* anything |<>comma| will do */ +while (mp_name_type (r) == mp_expr_sym || + mp_name_type (r) == mp_suffix_sym || mp_name_type (r) == mp_text_sym) { + @<Scan the delimited argument represented by |mp_sym_info(r)|@>; + r = mp_link (r); +} +if (mp->cur_cmd == comma) { + print_err ("Too many arguments to "); +@.Too many arguments...@>; + mp_print_macro_name (mp, arg_list, macro_name); + mp_print_char (mp, xord (';')); + mp_print_nl (mp, " Missing `"); + mp_print_text (r_delim); +@.Missing `)'...@>; + mp_print (mp, "' has been inserted"); + help3 ("I'm going to assume that the comma I just read was a", + "right delimiter, and then I'll begin expanding the macro.", + "You might want to delete some tokens before continuing."); + mp_error (mp); +} +if (mp_sym_info (r) != general_macro) { @<Scan undelimited argument(s)@>; } -r=mp_link(r) +r = mp_link (r) + @ At this point, the reader will find it advisable to review the explanation of token list format that was presented earlier, paying special attention to @@ -15355,47 +16708,55 @@ aspects of the following program on faith; we will explain |cur_type| and |cur_exp| later. (Several things in this program depend on each other, and it's necessary to jump into the circle somewhere.) -@<Scan the delimited argument represented by |mp_info(r)|@>= -if ( mp->cur_cmd!=comma ) { - mp_get_x_next(mp); - if ( mp->cur_cmd!=left_delimiter ) { - print_err("Missing argument to "); -@.Missing argument...@> - mp_print_macro_name(mp, arg_list,macro_name); - help3("That macro has more parameters than you thought.", - "I'll continue by pretending that each missing argument", - "is either zero or null."); - if ( mp_info(r)>=suffix_base ) { - mp->cur_exp=null; mp->cur_type=mp_token_list; - } else { - mp->cur_exp=0; mp->cur_type=mp_known; +@<Scan the delimited argument represented by |mp_sym_info(r)|@>= +if (mp->cur_cmd != comma) { + mp_get_x_next (mp); + if (mp->cur_cmd != left_delimiter) { + print_err ("Missing argument to "); +@.Missing argument...@>; + mp_print_macro_name (mp, arg_list, macro_name); + help3 ("That macro has more parameters than you thought.", + "I'll continue by pretending that each missing argument", + "is either zero or null."); + if (mp_name_type (r) == mp_suffix_sym || mp_name_type (r) == mp_text_sym) { + set_cur_exp_value (0); /* todo: this was |null| */ + mp->cur_exp.type = mp_token_list; + } else { + set_cur_exp_value (0); + mp->cur_exp.type = mp_known; } - mp_back_error(mp); mp->cur_cmd=right_delimiter; + mp_back_error (mp); + mp->cur_cmd = right_delimiter; goto FOUND; } - l_delim=mp->cur_sym; r_delim=mp->cur_mod; + l_delim = mp->cur_sym; + r_delim = mp->cur_sym2; } -@<Scan the argument represented by |mp_info(r)|@>; -if ( mp->cur_cmd!=comma ) +@<Scan the argument represented by |mp_sym_info(r)|@>; +if (mp->cur_cmd != comma) @<Check that the proper right delimiter was present@>; -FOUND: +FOUND: @<Append the current expression to |arg_list|@> + @ @<Check that the proper right delim...@>= -if ( (mp->cur_cmd!=right_delimiter)||(mp->cur_mod!=l_delim) ) { - if ( mp_info(mp_link(r))>=expr_base ) { - mp_missing_err(mp, ","); -@.Missing `,'@> - help3("I've finished reading a macro argument and am about to", - "read another; the arguments weren't delimited correctly.", - "You might want to delete some tokens before continuing."); - mp_back_error(mp); mp->cur_cmd=comma; - } else { - mp_missing_err(mp, str(text(r_delim))); -@.Missing `)'@> - help2("I've gotten to the end of the macro parameter list.", - "You might want to delete some tokens before continuing."); - mp_back_error(mp); +if ((mp->cur_cmd != right_delimiter) || (mp->cur_sym2 != l_delim)) { + if (mp_name_type (mp_link (r)) == mp_expr_sym || + mp_name_type (mp_link (r)) == mp_suffix_sym || + mp_name_type (mp_link (r)) == mp_text_sym) { + mp_missing_err (mp, ","); +@.Missing `,'@>; + help3 ("I've finished reading a macro argument and am about to", + "read another; the arguments weren't delimited correctly.", + "You might want to delete some tokens before continuing."); + mp_back_error (mp); + mp->cur_cmd = comma; + } else { + mp_missing_err (mp, mp_str (mp, text (r_delim))); +@.Missing `)'@>; + help2 ("I've gotten to the end of the macro parameter list.", + "You might want to delete some tokens before continuing."); + mp_back_error (mp); } } @@ -15404,173 +16765,225 @@ a token list pointed to by |cur_exp|, in which case we will have |cur_type=token_list|. @<Append the current expression to |arg_list|@>= -{ - p=mp_get_avail(mp); - if ( mp->cur_type==mp_token_list ) mp_info(p)=mp->cur_exp; - else mp_info(p)=mp_stash_cur_exp(mp); - if ( mp->internal[mp_tracing_macros]>0 ) { - mp_begin_diagnostic(mp); mp_print_arg(mp, mp_info(p),n,mp_info(r)); - mp_end_diagnostic(mp, false); - } - if ( arg_list==null ) arg_list=p; - else mp_link(tail)=p; - tail=p; incr(n); +{ + p = mp_get_symbolic_node (mp); + if (mp->cur_exp.type == mp_token_list) + set_mp_sym_sym (p, mp->cur_exp.data.node); + else + set_mp_sym_sym (p, mp_stash_cur_exp (mp)); + if (internal_value (mp_tracing_macros) > 0) { + mp_begin_diagnostic (mp); + mp_print_arg (mp, (mp_node)mp_sym_sym (p), n, mp_sym_info (r), mp_name_type (r)); + mp_end_diagnostic (mp, false); + } + if (arg_list == NULL) + arg_list = p; + else + mp_link (tail) = p; + tail = p; + incr (n); } -@ @<Scan the argument represented by |mp_info(r)|@>= -if ( mp_info(r)>=text_base ) { - mp_scan_text_arg(mp, l_delim,r_delim); -} else { - mp_get_x_next(mp); - if ( mp_info(r)>=suffix_base ) mp_scan_suffix(mp); - else mp_scan_expression(mp); + +@ @<Scan the argument represented by |mp_sym_info(r)|@>= +if (mp_name_type (r) == mp_text_sym) { + mp_scan_text_arg (mp, l_delim, r_delim); +} else { + mp_get_x_next (mp); + if (mp_name_type (r) == mp_suffix_sym) + mp_scan_suffix (mp); + else + mp_scan_expression (mp); } + @ The parameters to |scan_text_arg| are either a pair of delimiters or zero; the latter case is for undelimited text arguments, which end with the first semicolon or \&{endgroup} or \&{end} that is not contained in a group. @<Declarations@>= -static void mp_scan_text_arg (MP mp,pointer l_delim, pointer r_delim) ; +static void mp_scan_text_arg (MP mp, mp_sym l_delim, mp_sym r_delim); @ @c -void mp_scan_text_arg (MP mp,pointer l_delim, pointer r_delim) { - integer balance; /* excess of |l_delim| over |r_delim| */ - pointer p; /* list tail */ - mp->warning_info=l_delim; mp->scanner_status=absorbing; - p=hold_head; balance=1; mp_link(hold_head)=null; - while (1) { - get_t_next; - if ( l_delim==0 ) { +void mp_scan_text_arg (MP mp, mp_sym l_delim, mp_sym r_delim) { + integer balance; /* excess of |l_delim| over |r_delim| */ + mp_node p; /* list tail */ + mp->warning_info = l_delim; + mp->scanner_status = absorbing; + p = mp->hold_head; + balance = 1; + mp_link (mp->hold_head) = NULL; + while (1) { + get_t_next (mp); + if (l_delim == NULL) { @<Adjust the balance for an undelimited argument; |break| if done@>; } else { - @<Adjust the balance for a delimited argument; |break| if done@>; + @<Adjust the balance for a delimited argument; |break| if done@>; } - mp_link(p)=mp_cur_tok(mp); p=mp_link(p); + mp_link (p) = mp_cur_tok (mp); + p = mp_link (p); } - mp->cur_exp=mp_link(hold_head); mp->cur_type=mp_token_list; - mp->scanner_status=normal; + set_cur_exp_node (mp_link (mp->hold_head)); + mp->cur_exp.type = mp_token_list; + mp->scanner_status = normal; } + @ @<Adjust the balance for a delimited argument...@>= -if ( mp->cur_cmd==right_delimiter ) { - if ( mp->cur_mod==l_delim ) { - decr(balance); - if ( balance==0 ) break; +if (mp->cur_cmd == right_delimiter) { + if (mp->cur_sym2 == l_delim) { + decr (balance); + if (balance == 0) + break; } -} else if ( mp->cur_cmd==left_delimiter ) { - if ( mp->cur_mod==r_delim ) incr(balance); +} else if (mp->cur_cmd == left_delimiter) { + if (mp->cur_sym2 == r_delim) + incr (balance); } @ @<Adjust the balance for an undelimited...@>= -if ( end_of_statement ) { /* |cur_cmd=semicolon|, |end_group|, or |stop| */ - if ( balance==1 ) { break; } - else { if ( mp->cur_cmd==end_group ) decr(balance); } -} else if ( mp->cur_cmd==begin_group ) { - incr(balance); +if (end_of_statement) { /* |cur_cmd=semicolon|, |end_group|, or |stop| */ + if (balance == 1) { + break; + } else { + if (mp->cur_cmd == end_group) + decr (balance); + } +} else if (mp->cur_cmd == begin_group) { + incr (balance); } @ @<Scan undelimited argument(s)@>= -{ - if ( mp_info(r)<text_macro ) { - mp_get_x_next(mp); - if ( mp_info(r)!=suffix_macro ) { - if ( (mp->cur_cmd==equals)||(mp->cur_cmd==assignment) ) mp_get_x_next(mp); +{ + if (mp_sym_info (r) < text_macro) { + mp_get_x_next (mp); + if (mp_sym_info (r) != suffix_macro) { + if ((mp->cur_cmd == equals) || (mp->cur_cmd == assignment)) + mp_get_x_next (mp); } } - switch (mp_info(r)) { - case primary_macro:mp_scan_primary(mp); break; - case secondary_macro:mp_scan_secondary(mp); break; - case tertiary_macro:mp_scan_tertiary(mp); break; - case expr_macro:mp_scan_expression(mp); break; + switch (mp_sym_info (r)) { + case primary_macro: + mp_scan_primary (mp); + break; + case secondary_macro: + mp_scan_secondary (mp); + break; + case tertiary_macro: + mp_scan_tertiary (mp); + break; + case expr_macro: + mp_scan_expression (mp); + break; case of_macro: @<Scan an expression followed by `\&{of} $\langle$primary$\rangle$'@>; break; case suffix_macro: @<Scan a suffix with optional delimiters@>; break; - case text_macro:mp_scan_text_arg(mp, 0,0); break; - } /* there are no other cases */ - mp_back_input(mp); + case text_macro: + mp_scan_text_arg (mp, NULL, NULL); + break; + } /* there are no other cases */ + mp_back_input (mp); @<Append the current expression to |arg_list|@>; } + @ @<Scan an expression followed by `\&{of} $\langle$primary$\rangle$'@>= -{ - mp_scan_expression(mp); p=mp_get_avail(mp); mp_info(p)=mp_stash_cur_exp(mp); - if ( mp->internal[mp_tracing_macros]>0 ) { - mp_begin_diagnostic(mp); mp_print_arg(mp, mp_info(p),n,0); - mp_end_diagnostic(mp, false); - } - if ( arg_list==null ) arg_list=p; else mp_link(tail)=p; - tail=p;incr(n); - if ( mp->cur_cmd!=of_token ) { - mp_missing_err(mp, "of"); mp_print(mp, " for "); -@.Missing `of'@> - mp_print_macro_name(mp, arg_list,macro_name); - help1("I've got the first argument; will look now for the other."); - mp_back_error(mp); +{ + mp_scan_expression (mp); + p = mp_get_symbolic_node (mp); + set_mp_sym_sym (p, mp_stash_cur_exp (mp)); + if (internal_value (mp_tracing_macros) > 0) { + mp_begin_diagnostic (mp); + mp_print_arg (mp, (mp_node)mp_sym_sym (p), n, 0, 0); + mp_end_diagnostic (mp, false); + } + if (arg_list == NULL) + arg_list = p; + else + mp_link (tail) = p; + tail = p; + incr (n); + if (mp->cur_cmd != of_token) { + mp_missing_err (mp, "of"); + mp_print (mp, " for "); +@.Missing `of'@>; + mp_print_macro_name (mp, arg_list, macro_name); + help1 ("I've got the first argument; will look now for the other."); + mp_back_error (mp); } - mp_get_x_next(mp); mp_scan_primary(mp); + mp_get_x_next (mp); + mp_scan_primary (mp); } + @ @<Scan a suffix with optional delimiters@>= -{ - if ( mp->cur_cmd!=left_delimiter ) { - l_delim=null; - } else { - l_delim=mp->cur_sym; r_delim=mp->cur_mod; mp_get_x_next(mp); - }; - mp_scan_suffix(mp); - if ( l_delim!=null ) { - if ((mp->cur_cmd!=right_delimiter)||(mp->cur_mod!=l_delim) ) { - mp_missing_err(mp, str(text(r_delim))); -@.Missing `)'@> - help2("I've gotten to the end of the macro parameter list.", - "You might want to delete some tokens before continuing."); - mp_back_error(mp); +{ + if (mp->cur_cmd != left_delimiter) { + l_delim = NULL; + } else { + l_delim = mp->cur_sym; + r_delim = mp->cur_sym2; + mp_get_x_next (mp); + } + mp_scan_suffix (mp); + if (l_delim != NULL) { + if ((mp->cur_cmd != right_delimiter) || (mp->cur_sym2 != l_delim)) { + mp_missing_err (mp, mp_str (mp, text (r_delim))); +@.Missing `)'@>; + help2 ("I've gotten to the end of the macro parameter list.", + "You might want to delete some tokens before continuing."); + mp_back_error (mp); } - mp_get_x_next(mp); + mp_get_x_next (mp); } } + @ Before we put a new token list on the input stack, it is wise to clean off all token lists that have recently been depleted. Then a user macro that ends with a call to itself will not require unbounded stack space. @<Feed the arguments and replacement text to the scanner@>= -while ( token_state &&(loc==null) ) mp_end_token_list(mp); /* conserve stack space */ -if ( mp->param_ptr+n>mp->max_param_stack ) { - mp->max_param_stack=mp->param_ptr+n; - if ( mp->max_param_stack>mp->param_size ) - mp_overflow(mp, "parameter stack size",mp->param_size); +while (token_state && (nloc == NULL)) + mp_end_token_list (mp); /* conserve stack space */ +if (mp->param_ptr + n > mp->max_param_stack) { + mp->max_param_stack = mp->param_ptr + n; + mp_check_param_size (mp, mp->max_param_stack); @:MetaPost capacity exceeded parameter stack size}{\quad parameter stack size@> } -mp_begin_token_list(mp, def_ref, (quarterword)macro); name=macro_name; loc=r; -if ( n>0 ) { - p=arg_list; - do { - mp->param_stack[mp->param_ptr]=mp_info(p); incr(mp->param_ptr); p=mp_link(p); - } while (p!=null); - mp_flush_list(mp, arg_list); +mp_begin_token_list (mp, def_ref, (quarterword) macro); +if (macro_name) + name = text (macro_name); +nloc = r; +if (n > 0) { + p = arg_list; + do { + mp->param_stack[mp->param_ptr] = (mp_node)mp_sym_sym (p); + incr (mp->param_ptr); + p = mp_link (p); + } while (p != NULL); + mp_flush_node_list (mp, arg_list); } @ It's sometimes necessary to put a single argument onto |param_stack|. The |stack_argument| subroutine does this. -@c -static void mp_stack_argument (MP mp,pointer p) { - if ( mp->param_ptr==mp->max_param_stack ) { - incr(mp->max_param_stack); - if ( mp->max_param_stack>mp->param_size ) - mp_overflow(mp, "parameter stack size",mp->param_size); -@:MetaPost capacity exceeded parameter stack size}{\quad parameter stack size@> +@c +static void mp_stack_argument (MP mp, mp_node p) { + if (mp->param_ptr == mp->max_param_stack) { + incr (mp->max_param_stack); + mp_check_param_size (mp, mp->max_param_stack); } - mp->param_stack[mp->param_ptr]=p; incr(mp->param_ptr); + mp->param_stack[mp->param_ptr] = p; + incr (mp->param_ptr); } -@* \[33] Conditional processing. + +@* Conditional processing. Let's consider now the way \&{if} commands are handled. Conditions can be inside conditions, and this nesting has a stack @@ -15582,48 +16995,80 @@ the largest code of a |fi_or_else| command that is syntactically legal; and |if_line| is the line number at which the current conditional began. If no conditions are currently in progress, the condition stack has the -special state |cond_ptr=null|, |if_limit=normal|, |cur_if=0|, |if_line=0|. -Otherwise |cond_ptr| points to a two-word node; the |type|, |name_type|, and +special state |cond_ptr=NULL|, |if_limit=normal|, |cur_if=0|, |if_line=0|. +Otherwise |cond_ptr| points to a non-symbolic node; the |type|, |name_type|, and |link| fields of the first word contain |if_limit|, |cur_if|, and |cond_ptr| at the next level, and the second word contains the corresponding |if_line|. -@d if_node_size 2 /* number of words in stack entry for conditionals */ -@d if_line_field(A) mp->mem[(A)+1].cint +@d if_line_field(A) ((mp_if_node)(A))->if_line_field_ @d if_code 1 /* code for \&{if} being evaluated */ @d fi_code 2 /* code for \&{fi} */ @d else_code 3 /* code for \&{else} */ @d else_if_code 4 /* code for \&{elseif} */ -@<Glob...@>= -pointer cond_ptr; /* top of the condition stack */ -integer if_limit; /* upper bound on |fi_or_else| codes */ -quarterword cur_if; /* type of conditional being worked on */ -integer if_line; /* line where that conditional began */ +@(mpmp.h@>= +typedef struct mp_if_node_data { + NODE_BODY; + int if_line_field_; +} mp_if_node_data; +typedef struct mp_if_node_data *mp_if_node; + +@ +@d if_node_size sizeof(struct mp_if_node_data) /* number of words in stack entry for conditionals */ + +@c +static mp_node mp_get_if_node (MP mp) { + mp_if_node p = (mp_if_node) xmalloc (1, if_node_size); + add_var_used (if_node_size); + memset (p, 0, if_node_size); + mp_type (p) = mp_if_node_type; + return (mp_node) p; +} + + +@ @<Glob...@>= +mp_node cond_ptr; /* top of the condition stack */ +integer if_limit; /* upper bound on |fi_or_else| codes */ +quarterword cur_if; /* type of conditional being worked on */ +integer if_line; /* line where that conditional began */ @ @<Set init...@>= -mp->cond_ptr=null; mp->if_limit=normal; mp->cur_if=0; mp->if_line=0; +mp->cond_ptr = NULL; +mp->if_limit = normal; +mp->cur_if = 0; +mp->if_line = 0; @ @<Put each...@>= -mp_primitive(mp, "if",if_test,if_code); -@:if_}{\&{if} primitive@> -mp_primitive(mp, "fi",fi_or_else,fi_code); mp->eqtb[frozen_fi]=mp->eqtb[mp->cur_sym]; -@:fi_}{\&{fi} primitive@> -mp_primitive(mp, "else",fi_or_else,else_code); -@:else_}{\&{else} primitive@> -mp_primitive(mp, "elseif",fi_or_else,else_if_code); +mp_primitive (mp, "if", if_test, if_code); +@:if_}{\&{if} primitive@>; +mp_primitive (mp, "fi", fi_or_else, fi_code); +mp->frozen_fi = mp_frozen_primitive (mp, "fi", fi_or_else, fi_code); +@:fi_}{\&{fi} primitive@>; +mp_primitive (mp, "else", fi_or_else, else_code); +@:else_}{\&{else} primitive@>; +mp_primitive (mp, "elseif", fi_or_else, else_if_code); @:else_if_}{\&{elseif} primitive@> + @ @<Cases of |print_cmd_mod|...@>= case if_test: -case fi_or_else: - switch (m) { - case if_code:mp_print(mp, "if"); break; - case fi_code:mp_print(mp, "fi"); break; - case else_code:mp_print(mp, "else"); break; - default: mp_print(mp, "elseif"); break; - } +case fi_or_else: +switch (m) { +case if_code: + mp_print (mp, "if"); + break; +case fi_code: + mp_print (mp, "fi"); break; +case else_code: + mp_print (mp, "else"); + break; +default: + mp_print (mp, "elseif"); + break; +} +break; @ Here is a procedure that ignores text until coming to an \&{elseif}, \&{else}, or \&{fi} at level zero of $\&{if}\ldots\&{fi}$ @@ -15633,30 +17078,35 @@ was found. \MP's smallest two command codes are |if_test| and |fi_or_else|; this makes the skipping process a bit simpler. -@c +@c void mp_pass_text (MP mp) { integer l = 0; - mp->scanner_status=skipping; - mp->warning_info=mp_true_line(mp); - while (1) { - get_t_next; - if ( mp->cur_cmd<=fi_or_else ) { - if ( mp->cur_cmd<fi_or_else ) { - incr(l); - } else { - if ( l==0 ) break; - if ( mp->cur_mod==fi_code ) decr(l); + mp->scanner_status = skipping; + mp->warning_info_line = mp_true_line (mp); + while (1) { + get_t_next (mp); + if (mp->cur_cmd <= fi_or_else) { + if (mp->cur_cmd < fi_or_else) { + incr (l); + } else { + if (l == 0) + break; + if (mp->cur_mod == fi_code) + decr (l); } } else { @<Decrease the string reference count, if the current token is a string@>; } } - mp->scanner_status=normal; + mp->scanner_status = normal; } + @ @<Decrease the string reference count...@>= -if ( mp->cur_cmd==string_token ) { delete_str_ref(mp->cur_mod); } +if (mp->cur_cmd == string_token) { + delete_str_ref (mp->cur_mod_str); +} @ When we begin to process a new \&{if}, we set |if_limit:=if_code|; then if \&{elseif} or \&{else} or \&{fi} occurs before the current \&{if} @@ -15664,448 +17114,573 @@ condition has been evaluated, a colon will be inserted. A construction like `\.{if fi}' would otherwise get \MP\ confused. @<Push the condition stack@>= -{ p=mp_get_node(mp, if_node_size); mp_link(p)=mp->cond_ptr; mp_type(p)=(quarterword)mp->if_limit; - mp_name_type(p)=mp->cur_if; if_line_field(p)=mp->if_line; - mp->cond_ptr=p; mp->if_limit=if_code; mp->if_line=mp_true_line(mp); - mp->cur_if=if_code; +{ + p = mp_get_if_node (mp); + mp_link (p) = mp->cond_ptr; + mp_type (p) = (quarterword) mp->if_limit; + mp_name_type (p) = mp->cur_if; + if_line_field (p) = mp->if_line; + mp->cond_ptr = p; + mp->if_limit = if_code; + mp->if_line = mp_true_line (mp); + mp->cur_if = if_code; } + @ @<Pop the condition stack@>= -{ p=mp->cond_ptr; mp->if_line=if_line_field(p); - mp->cur_if=mp_name_type(p); mp->if_limit=mp_type(p); mp->cond_ptr=mp_link(p); - mp_free_node(mp, p,if_node_size); +{ + mp_node p = mp->cond_ptr; + mp->if_line = if_line_field (p); + mp->cur_if = mp_name_type (p); + mp->if_limit = mp_type (p); + mp->cond_ptr = mp_link (p); + mp_free_node (mp, p, if_node_size); } + @ Here's a procedure that changes the |if_limit| code corresponding to a given value of |cond_ptr|. -@c -static void mp_change_if_limit (MP mp,quarterword l, pointer p) { - pointer q; - if ( p==mp->cond_ptr ) { - mp->if_limit=l; /* that's the easy case */ - } else { - q=mp->cond_ptr; - while (1) { - if ( q==null ) mp_confusion(mp, "if"); -@:this can't happen if}{\quad if@> - if ( mp_link(q)==p ) { - mp_type(q)=l; return; +@c +static void mp_change_if_limit (MP mp, quarterword l, mp_node p) { + mp_node q; + if (p == mp->cond_ptr) { + mp->if_limit = l; /* that's the easy case */ + } else { + q = mp->cond_ptr; + while (1) { + if (q == NULL) + mp_confusion (mp, "if"); +@:this can't happen if}{\quad if@>; + if (mp_link (q) == p) { + mp_type (q) = l; + return; } - q=mp_link(q); + q = mp_link (q); } } } + @ The user is supposed to put colons into the proper parts of conditional statements. Therefore, \MP\ has to check for their presence. -@c -static void mp_check_colon (MP mp) { - if ( mp->cur_cmd!=colon ) { - mp_missing_err(mp, ":"); -@.Missing `:'@> - help2("There should've been a colon after the condition.", - "I shall pretend that one was there."); - mp_back_error(mp); +@c +static void mp_check_colon (MP mp) { + if (mp->cur_cmd != colon) { + mp_missing_err (mp, ":"); +@.Missing `:'@>; + help2 ("There should've been a colon after the condition.", + "I shall pretend that one was there."); + mp_back_error (mp); } } + @ A condition is started when the |get_x_next| procedure encounters an |if_test| command; in that case |get_x_next| calls |conditional|, which is a recursive procedure. @^recursion@> -@c +@c void mp_conditional (MP mp) { - pointer save_cond_ptr; /* |cond_ptr| corresponding to this conditional */ - int new_if_limit; /* future value of |if_limit| */ - pointer p; /* temporary register */ - @<Push the condition stack@>; - save_cond_ptr=mp->cond_ptr; -RESWITCH: - mp_get_boolean(mp); new_if_limit=else_if_code; - if ( mp->internal[mp_tracing_commands]>unity ) { + mp_node save_cond_ptr; /* |cond_ptr| corresponding to this conditional */ + int new_if_limit; /* future value of |if_limit| */ + mp_node p; /* temporary register */ + @<Push the condition stack@>; + save_cond_ptr = mp->cond_ptr; +RESWITCH: + mp_get_boolean (mp); + new_if_limit = else_if_code; + if (internal_value (mp_tracing_commands) > unity) { @<Display the boolean value of |cur_exp|@>; } -FOUND: - mp_check_colon(mp); - if ( mp->cur_exp==true_code ) { - mp_change_if_limit(mp, (quarterword)new_if_limit, save_cond_ptr); - return; /* wait for \&{elseif}, \&{else}, or \&{fi} */ +FOUND: + mp_check_colon (mp); + if (cur_exp_value () == true_code) { + mp_change_if_limit (mp, (quarterword) new_if_limit, save_cond_ptr); + return; /* wait for \&{elseif}, \&{else}, or \&{fi} */ }; @<Skip to \&{elseif} or \&{else} or \&{fi}, then |goto done|@>; -DONE: - mp->cur_if=(quarterword)mp->cur_mod; mp->if_line=mp_true_line(mp); - if ( mp->cur_mod==fi_code ) { +DONE: + mp->cur_if = (quarterword) mp->cur_mod; + mp->if_line = mp_true_line (mp); + if (mp->cur_mod == fi_code) { @<Pop the condition stack@> - } else if ( mp->cur_mod==else_if_code ) { + } else if (mp->cur_mod == else_if_code) { goto RESWITCH; - } else { - mp->cur_exp=true_code; new_if_limit=fi_code; mp_get_x_next(mp); + } else { + set_cur_exp_value (true_code); + new_if_limit = fi_code; + mp_get_x_next (mp); goto FOUND; } } + @ In a construction like `\&{if} \&{if} \&{true}: $0=1$: \\{foo} \&{else}: \\{bar} \&{fi}', the first \&{else} that we come to after learning that the \&{if} is false is not the \&{else} we're looking for. Hence the following curious logic is needed. @<Skip to \&{elseif}...@>= -while (1) { - mp_pass_text(mp); - if ( mp->cond_ptr==save_cond_ptr ) goto DONE; - else if ( mp->cur_mod==fi_code ) @<Pop the condition stack@>; +while (1) { + mp_pass_text (mp); + if (mp->cond_ptr == save_cond_ptr) + goto DONE; + else if (mp->cur_mod == fi_code) + @<Pop the condition stack@>; } @ @<Display the boolean value...@>= -{ mp_begin_diagnostic(mp); - if ( mp->cur_exp==true_code ) mp_print(mp, "{true}"); - else mp_print(mp, "{false}"); - mp_end_diagnostic(mp, false); +{ + mp_begin_diagnostic (mp); + if (cur_exp_value () == true_code) + mp_print (mp, "{true}"); + else + mp_print (mp, "{false}"); + mp_end_diagnostic (mp, false); } + @ The processing of conditionals is complete except for the following code, which is actually part of |get_x_next|. It comes into play when \&{elseif}, \&{else}, or \&{fi} is scanned. @<Terminate the current conditional and skip to \&{fi}@>= -if ( mp->cur_mod>mp->if_limit ) { - if ( mp->if_limit==if_code ) { /* condition not yet evaluated */ - mp_missing_err(mp, ":"); -@.Missing `:'@> - mp_back_input(mp); mp->cur_sym=frozen_colon; mp_ins_error(mp); - } else { - print_err("Extra "); mp_print_cmd_mod(mp, fi_or_else,mp->cur_mod); +if (mp->cur_mod > mp->if_limit) { + if (mp->if_limit == if_code) { /* condition not yet evaluated */ + mp_missing_err (mp, ":"); +@.Missing `:'@>; + mp_back_input (mp); + mp->cur_sym = mp->frozen_colon; + mp_ins_error (mp); + } else { + print_err ("Extra "); + mp_print_cmd_mod (mp, fi_or_else, mp->cur_mod); @.Extra else@> @.Extra elseif@> -@.Extra fi@> - help1("I'm ignoring this; it doesn't match any if."); - mp_error(mp); +@.Extra fi@>; + help1 ("I'm ignoring this; it doesn't match any if."); + mp_error (mp); } -} else { - while ( mp->cur_mod!=fi_code ) mp_pass_text(mp); /* skip to \&{fi} */ +} else { + while (mp->cur_mod != fi_code) + mp_pass_text (mp); /* skip to \&{fi} */ @<Pop the condition stack@>; } -@* \[34] Iterations. + +@* Iterations. To bring our treatment of |get_x_next| to a close, we need to consider what \MP\ does when it sees \&{for}, \&{forsuffixes}, and \&{forever}. There's a global variable |loop_ptr| that keeps track of the \&{for} loops -that are currently active. If |loop_ptr=null|, no loops are in progress; -otherwise |mp_info(loop_ptr)| points to the iterative text of the current -(innermost) loop, and |mp_link(loop_ptr)| points to the data for any other +that are currently active. If |loop_ptr=NULL|, no loops are in progress; +otherwise |loop_ptr.info| points to the iterative text of the current +(innermost) loop, and |loop_ptr.link| points to the data for any other loops that enclose the current one. -A loop-control node also has two other fields, called |loop_type| and -|loop_list|, whose contents depend on the type of loop: +A loop-control node also has two other fields, called |type| and +|list|, whose contents depend on the type of loop: -\yskip\indent|loop_type(loop_ptr)=null| means that |loop_list(loop_ptr)| -points to a list of one-word nodes whose |info| fields point to the +\yskip\indent|loop_ptr.type=NULL| means that the link of |loop_ptr.list| +points to a list of symbolic nodes whose |info| fields point to the remaining argument values of a suffix list and expression list. +In this case, an extra field |loop_ptr.start_list| is needed to +make sure that |resume_operation| skips ahead. -\yskip\indent|loop_type(loop_ptr)=mp_void| means that the current loop is +\yskip\indent|loop_ptr.type=MP_VOID| means that the current loop is `\&{forever}'. -\yskip\indent|loop_type(loop_ptr)=progression_flag| means that -|p=loop_list(loop_ptr)| points to a ``progression node'' and |value(p)|, -|step_size(p)|, and |final_value(p)| contain the data for an arithmetic -progression. +\yskip\indent|loop_ptr.type=PROGRESSION_FLAG| means that +|loop_ptr.value|, |loop_ptr.step_size|, and |loop_ptr.final_value| +contain the data for an arithmetic progression. -\yskip\indent|loop_type(loop_ptr)=p>mp_void| means that |p| points to an edge -header and |loop_list(loop_ptr)| points into the graphical object list for +\yskip\indent|loop_ptr.type=p>PROGRESSION_FLAG| means that |p| points to an edge +header and |loop_ptr.list| points into the graphical object list for that edge header. -\yskip\noindent In the case of a progression node, the first word is not used -because the link field of words in the dynamic memory area cannot be arbitrary. - -@d loop_list_loc(A) ((A)+1) /* where the |loop_list| field resides */ -@d loop_type(A) mp_info(loop_list_loc((A))) /* the type of \&{for} loop */ -@d loop_list(A) mp_link(loop_list_loc((A))) /* the remaining list elements */ -@d loop_node_size 2 /* the number of words in a loop control node */ -@d progression_node_size 4 /* the number of words in a progression node */ -@d step_size(A) mp->mem[(A)+2].sc /* the step size in an arithmetic progression */ -@d final_value(A) mp->mem[(A)+3].sc /* the final value in an arithmetic progression */ -@d progression_flag (null+2) +@d PROGRESSION_FLAG (mp_node)(2) /* |NULL+2| */ /* |loop_type| value when |loop_list| points to a progression node */ -@<Glob...@>= -pointer loop_ptr; /* top of the loop-control-node stack */ +@<Types...@>= +typedef struct mp_loop_data { + mp_node info; /* iterative text of this loop */ + mp_node type; /* the special type of this loop, or a pointer into + mem */ + mp_node list; /* the remaining list elements */ + mp_node list_start; /* head fo the list of elements */ + scaled value; /* current arithmetic value */ + scaled step_size; /* arithmetic step size */ + scaled final_value; /* end arithmetic value */ + struct mp_loop_data *link; /* the enclosing loop, if any */ +} mp_loop_data; + +@ @<Glob...@>= +mp_loop_data *loop_ptr; /* top of the loop-control-node stack */ @ @<Set init...@>= -mp->loop_ptr=null; +mp->loop_ptr = NULL; -@ If the expressions that define an arithmetic progression in -a \&{for} loop don't have known numeric values, the |bad_for| -subroutine screams at the user. +@ If the expressions that define an arithmetic progression in a +\&{for} loop don't have known numeric values, the |bad_for| subroutine +screams at the user. -@c -static void mp_bad_for (MP mp, const char * s) { - mp_disp_err(mp, null,"Improper "); /* show the bad expression above the message */ -@.Improper...replaced by 0@> - mp_print(mp, s); mp_print(mp, " has been replaced by 0"); - help4("When you say `for x=a step b until c',", - "the initial value `a' and the step size `b'", - "and the final value `c' must have known numeric values.", - "I'm zeroing this one. Proceed, with fingers crossed."); - mp_put_get_flush_error(mp, 0); +@c +static void mp_bad_for (MP mp, const char *s) { + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + mp_disp_err (mp, NULL, "Improper "); /* show the bad expression above + the message */ +@.Improper...replaced by 0@>; + mp_print (mp, s); + mp_print (mp, " has been replaced by 0"); + help4 ("When you say `for x=a step b until c',", + "the initial value `a' and the step size `b'", + "and the final value `c' must have known numeric values.", + "I'm zeroing this one. Proceed, with fingers crossed."); + mp_put_get_flush_error (mp, new_expr); } + @ Here's what \MP\ does when \&{for}, \&{forsuffixes}, or \&{forever} has just been scanned. (This code requires slight familiarity with -expression-parsing routines that we have not yet discussed; but it seems -to belong in the present part of the program, even though the original author -didn't write it until later. The reader may wish to come back to it.) - -@c void mp_begin_iteration (MP mp) { - halfword m; /* |expr_base| (\&{for}) or |suffix_base| (\&{forsuffixes}) */ - halfword n; /* hash address of the current symbol */ - pointer s; /* the new loop-control node */ - pointer p; /* substitution list for |scan_toks| */ - pointer q; /* link manipulation register */ - pointer pp; /* a new progression node */ - m=mp->cur_mod; n=mp->cur_sym; s=mp_get_node(mp, loop_node_size); - if ( m==start_forever ){ - loop_type(s)=mp_void; p=null; mp_get_x_next(mp); - } else { - mp_get_symbol(mp); p=mp_get_node(mp, token_node_size); - mp_info(p)=mp->cur_sym; value(p)=m; - mp_get_x_next(mp); - if ( mp->cur_cmd==within_token ) { +expression-parsing routines that we have not yet discussed; but it +seems to belong in the present part of the program, even though the +original author didn't write it until later. The reader may wish to +come back to it.) + +@c +void mp_begin_iteration (MP mp) { + halfword m; /* |start_for| (\&{for}) or |start_forsuffixes| + (\&{forsuffixes}) */ + mp_sym n; /* hash address of the current symbol */ + mp_loop_data *s; /* the new loop-control node */ + mp_subst_list_item *p = NULL; /* substitution list for |scan_toks| + */ + mp_node q; /* link manipulation register */ + m = mp->cur_mod; + n = mp->cur_sym; + s = xmalloc (1, sizeof (mp_loop_data)); + s->type = s->list = s->info = s->list_start = NULL; + s->link = NULL; + if (m == start_forever) { + s->type = MP_VOID; + p = NULL; + mp_get_x_next (mp); + } else { + mp_get_symbol (mp); + p = xmalloc (1, sizeof (mp_subst_list_item)); + p->link = NULL; + p->info = mp->cur_sym; + p->info_mod = mp->cur_sym_mod; + p->value_data = 0; + if (m == start_for) { + p->value_mod = mp_expr_sym; + } else { /* |start_forsuffixes| */ + p->value_mod = mp_suffix_sym; + } + mp_get_x_next (mp); + if (mp->cur_cmd == within_token) { @<Set up a picture iteration@>; - } else { - @<Check for the |"="| or |":="| in a loop header@>; + } else { + @<Check for the assignment in a loop header@>; @<Scan the values to be used in the loop@>; } } @<Check for the presence of a colon@>; @<Scan the loop text and put it on the loop control stack@>; - mp_resume_iteration(mp); + mp_resume_iteration (mp); } -@ @<Check for the |"="| or |":="| in a loop header@>= -if ( (mp->cur_cmd!=equals)&&(mp->cur_cmd!=assignment) ) { - mp_missing_err(mp, "="); -@.Missing `='@> - help3("The next thing in this loop should have been `=' or `:='.", - "But don't worry; I'll pretend that an equals sign", - "was present, and I'll look for the values next."); - mp_back_error(mp); + +@ @<Check for the assignment in a loop header@>= +if ((mp->cur_cmd != equals) && (mp->cur_cmd != assignment)) { + mp_missing_err (mp, "="); +@.Missing `='@>; + help3 ("The next thing in this loop should have been `=' or `:='.", + "But don't worry; I'll pretend that an equals sign", + "was present, and I'll look for the values next."); + mp_back_error (mp); } @ @<Check for the presence of a colon@>= -if ( mp->cur_cmd!=colon ) { - mp_missing_err(mp, ":"); -@.Missing `:'@> - help3("The next thing in this loop should have been a `:'.", - "So I'll pretend that a colon was present;", - "everything from here to `endfor' will be iterated."); - mp_back_error(mp); +if (mp->cur_cmd != colon) { + mp_missing_err (mp, ":"); +@.Missing `:'@>; + help3 ("The next thing in this loop should have been a `:'.", + "So I'll pretend that a colon was present;", + "everything from here to `endfor' will be iterated."); + mp_back_error (mp); } -@ We append a special |frozen_repeat_loop| token in place of the -`\&{endfor}' at the end of the loop. This will come through \MP's scanner -at the proper time to cause the loop to be repeated. +@ We append a special |mp->frozen_repeat_loop| token in place of the +`\&{endfor}' at the end of the loop. This will come through \MP's +scanner at the proper time to cause the loop to be repeated. -(If the user tries some shenanigan like `\&{for} $\ldots$ \&{let} \&{endfor}', -he will be foiled by the |get_symbol| routine, which keeps frozen -tokens unchanged. Furthermore the |frozen_repeat_loop| is an \&{outer} -token, so it won't be lost accidentally.) +(If the user tries some shenanigan like `\&{for} $\ldots$ \&{let} +\&{endfor}', he will be foiled by the |get_symbol| routine, which +keeps frozen tokens unchanged. Furthermore the +|mp->frozen_repeat_loop| is an \&{outer} token, so it won't be lost +accidentally.) @ @<Scan the loop text...@>= -q=mp_get_avail(mp); mp_info(q)=frozen_repeat_loop; -mp->scanner_status=loop_defining; mp->warning_info=n; -mp_info(s)=mp_scan_toks(mp, iteration,p,q,0); mp->scanner_status=normal; -mp_link(s)=mp->loop_ptr; mp->loop_ptr=s +q = mp_get_symbolic_node (mp); +set_mp_sym_sym (q, mp->frozen_repeat_loop); +mp->scanner_status = loop_defining; +mp->warning_info = n; +s->info = mp_scan_toks (mp, iteration, p, q, 0); +mp->scanner_status = normal; +s->link = mp->loop_ptr; +mp->loop_ptr = s @ @<Initialize table...@>= -eq_type(frozen_repeat_loop)=repeat_loop+outer_tag; -text(frozen_repeat_loop)=intern(" ENDFOR"); +mp->frozen_repeat_loop = +mp_frozen_primitive (mp, " ENDFOR", repeat_loop + outer_tag, 0); @ The loop text is inserted into \MP's scanning apparatus by the |resume_iteration| routine. -@c void mp_resume_iteration (MP mp) { - pointer p,q; /* link registers */ - p=loop_type(mp->loop_ptr); - if ( p==progression_flag ) { - p=loop_list(mp->loop_ptr); /* now |p| points to a progression node */ - mp->cur_exp=value(p); - if ( @<The arithmetic progression has ended@> ) { - mp_stop_iteration(mp); +@c +void mp_resume_iteration (MP mp) { + mp_node p, q; /* link registers */ + p = mp->loop_ptr->type; + if (p == PROGRESSION_FLAG) { + set_cur_exp_value (mp->loop_ptr->value); + if (@<The arithmetic progression has ended@>) { + mp_stop_iteration (mp); return; } - mp->cur_type=mp_known; q=mp_stash_cur_exp(mp); /* make |q| an \&{expr} argument */ - value(p)=mp->cur_exp+step_size(p); /* set |value(p)| for the next iteration */ + mp->cur_exp.type = mp_known; + q = mp_stash_cur_exp (mp); /* make |q| an \&{expr} argument */ + mp->loop_ptr->value = cur_exp_value () + mp->loop_ptr->step_size; /* + set |value(p)| for the next iteration */ /* detect numeric overflow */ - if ((step_size(p)>0) && (value(p)<mp->cur_exp)) { - if (final_value(p)>0) { - value(p)=final_value(p); - final_value(p) = final_value(p) - 1; - } else { - value(p)=final_value(p)+1; - } - } else if ((step_size(p)<0) && (value(p)>mp->cur_exp)) { - if (final_value(p)<0) { - value(p)=final_value(p); - final_value(p) = final_value(p)+1; - } else { - value(p)=final_value(p)-1; - } + if ((mp->loop_ptr->step_size > 0) && + (mp->loop_ptr->value < cur_exp_value ())) { + if (mp->loop_ptr->final_value > 0) { + mp->loop_ptr->value = mp->loop_ptr->final_value; + mp->loop_ptr->final_value--; + } else { + mp->loop_ptr->value = mp->loop_ptr->final_value + 1; + } + } else if ((mp->loop_ptr->step_size < 0) && + (mp->loop_ptr->value > cur_exp_value ())) { + if (mp->loop_ptr->final_value < 0) { + mp->loop_ptr->value = mp->loop_ptr->final_value; + mp->loop_ptr->final_value++; + } else { + mp->loop_ptr->value = mp->loop_ptr->final_value - 1; + } } - - } else if ( p==null ) { - p=loop_list(mp->loop_ptr); - if ( p==null ) { - mp_stop_iteration(mp); + } else if (p == NULL) { + p = mp->loop_ptr->list; + if (p != NULL && p == mp->loop_ptr->list_start) { + q = p; + p = mp_link (p); + mp_free_symbolic_node (mp, q); + mp->loop_ptr->list = p; + } + if (p == NULL) { + mp_stop_iteration (mp); return; } - loop_list(mp->loop_ptr)=mp_link(p); q=mp_info(p); free_avail(p); - } else if ( p==mp_void ) { - mp_begin_token_list(mp, mp_info(mp->loop_ptr), (quarterword)forever_text); return; + mp->loop_ptr->list = mp_link (p); + q = (mp_node)mp_sym_sym (p); + mp_free_symbolic_node (mp, p); + } else if (p == MP_VOID) { + mp_begin_token_list (mp, mp->loop_ptr->info, (quarterword) forever_text); + return; } else { @<Make |q| a capsule containing the next picture component from |loop_list(loop_ptr)| or |goto not_found|@>; } - mp_begin_token_list(mp, mp_info(mp->loop_ptr), (quarterword)loop_text); - mp_stack_argument(mp, q); - if ( mp->internal[mp_tracing_commands]>unity ) { - @<Trace the start of a loop@>; + mp_begin_token_list (mp, mp->loop_ptr->info, (quarterword) loop_text); + mp_stack_argument (mp, q); + if (internal_value (mp_tracing_commands) > unity) { + @<Trace the start of a loop@>; } return; NOT_FOUND: - mp_stop_iteration(mp); + mp_stop_iteration (mp); } + @ @<The arithmetic progression has ended@>= -((step_size(p)>0)&&(mp->cur_exp>final_value(p)))|| - ((step_size(p)<0)&&(mp->cur_exp<final_value(p))) +((mp->loop_ptr->step_size > 0) + && (cur_exp_value () > mp->loop_ptr->final_value)) + || ((mp->loop_ptr->step_size < 0) + && (cur_exp_value () < mp->loop_ptr->final_value)) + @ @<Trace the start of a loop@>= -{ - mp_begin_diagnostic(mp); mp_print_nl(mp, "{loop value="); -@.loop value=n@> - if ( (q!=null)&&(mp_link(q)==mp_void) ) mp_print_exp(mp, q,1); - else mp_show_token_list(mp, q,null,50,0); - mp_print_char(mp, xord('}')); mp_end_diagnostic(mp, false); -} - -@ @<Make |q| a capsule containing the next picture component from...@>= -{ q=loop_list(mp->loop_ptr); - if ( q==null ) goto NOT_FOUND; - skip_component(q) goto NOT_FOUND; - mp->cur_exp=mp_copy_objects(mp, loop_list(mp->loop_ptr),q); - mp_init_bbox(mp, mp->cur_exp); - mp->cur_type=mp_picture_type; - loop_list(mp->loop_ptr)=q; - q=mp_stash_cur_exp(mp); -} - -@ A level of loop control disappears when |resume_iteration| has decided -not to resume, or when an \&{exitif} construction has removed the loop text -from the input stack. +{ + mp_begin_diagnostic (mp); + mp_print_nl (mp, "{loop value="); +@.loop value=n@>; + if ((q != NULL) && (mp_link (q) == MP_VOID)) + mp_print_exp (mp, q, 1); + else + mp_show_token_list (mp, q, NULL, 50, 0); + mp_print_char (mp, xord ('}')); + mp_end_diagnostic (mp, false); +} + -@c void mp_stop_iteration (MP mp) { - pointer p,q; /* the usual */ - p=loop_type(mp->loop_ptr); - if ( p==progression_flag ) { - mp_free_node(mp, loop_list(mp->loop_ptr),progression_node_size); - } else if ( p==null ){ - q=loop_list(mp->loop_ptr); - while ( q!=null ) { - p=mp_info(q); - if ( p!=null ) { - if ( mp_link(p)==mp_void ) { /* it's an \&{expr} parameter */ - mp_recycle_value(mp, p); mp_free_node(mp, p,value_node_size); +@ @<Make |q| a capsule containing the next picture component +from...@>= +{ + q = mp->loop_ptr->list; + if (q == NULL) + goto NOT_FOUND; + skip_component (q) goto NOT_FOUND; + set_cur_exp_node (mp_copy_objects (mp, mp->loop_ptr->list, q)); + mp_init_bbox (mp, cur_exp_node ()); + mp->cur_exp.type = mp_picture_type; + mp->loop_ptr->list = q; + q = mp_stash_cur_exp (mp); +} + + +@ A level of loop control disappears when |resume_iteration| has +decided not to resume, or when an \&{exitif} construction has removed +the loop text from the input stack. + +@c +void mp_stop_iteration (MP mp) { + mp_node p, q; /* the usual */ + mp_loop_data *tmp; /* for free() */ + p = mp->loop_ptr->type; + if (p == PROGRESSION_FLAG) { + ; + } else if (p == NULL) { + q = mp->loop_ptr->list; + while (q != NULL) { + p = (mp_node)mp_sym_sym (q); + if (p != NULL) { + if (mp_link (p) == MP_VOID) { /* it's an \&{expr} parameter */ + mp_recycle_value (mp, p); + mp_free_node (mp, p, value_node_size); } else { - mp_flush_token_list(mp, p); /* it's a \&{suffix} or \&{text} parameter */ + mp_flush_token_list (mp, p); /* it's a \&{suffix} or \&{text} + parameter */ } } - p=q; q=mp_link(q); free_avail(p); + p = q; + q = mp_link (q); + mp_free_symbolic_node (mp, p); } - } else if ( p>progression_flag ) { - delete_edge_ref(p); + } else if (p > PROGRESSION_FLAG) { + delete_edge_ref (p); } - p=mp->loop_ptr; mp->loop_ptr=mp_link(p); mp_flush_token_list(mp, mp_info(p)); - mp_free_node(mp, p,loop_node_size); + tmp = mp->loop_ptr; + mp->loop_ptr = tmp->link; + mp_flush_token_list (mp, tmp->info); + xfree (tmp); } -@ Now that we know all about loop control, we can finish up -the missing portion of |begin_iteration| and we'll be done. -The following code is performed after the `\.=' has been scanned in -a \&{for} construction (if |m=expr_base|) or a \&{forsuffixes} construction -(if |m=suffix_base|). +@ Now that we know all about loop control, we can finish up the +missing portion of |begin_iteration| and we'll be done. + +The following code is performed after the `\.=' has been scanned in a +\&{for} construction (if |m=start_for|) or a \&{forsuffixes} +construction (if |m=start_forsuffixes|). @<Scan the values to be used in the loop@>= -loop_type(s)=null; q=loop_list_loc(s); mp_link(q)=null; /* |mp_link(q)=loop_list(s)| */ -do { - mp_get_x_next(mp); - if ( m!=expr_base ) { - mp_scan_suffix(mp); - } else { - if ( mp->cur_cmd>=colon ) if ( mp->cur_cmd<=comma ) - goto CONTINUE; - mp_scan_expression(mp); - if ( mp->cur_cmd==step_token ) if ( q==loop_list_loc(s) ) { - @<Prepare for step-until construction and |break|@>; - } - mp->cur_exp=mp_stash_cur_exp(mp); - } - mp_link(q)=mp_get_avail(mp); q=mp_link(q); - mp_info(q)=mp->cur_exp; mp->cur_type=mp_vacuous; +s->type = NULL; +s->list = mp_get_symbolic_node (mp); +s->list_start = s->list; +q = s->list; +do { + mp_get_x_next (mp); + if (m != start_for) { + mp_scan_suffix (mp); + } else { + if (mp->cur_cmd >= colon) + if (mp->cur_cmd <= comma) + goto CONTINUE; + mp_scan_expression (mp); + if (mp->cur_cmd == step_token) + if (q == s->list) { + @<Prepare for step-until construction and |break|@>; + } + set_cur_exp_node (mp_stash_cur_exp (mp)); + } + mp_link (q) = mp_get_symbolic_node (mp); + q = mp_link (q); + set_mp_sym_sym (q, mp->cur_exp.data.node); + if (m == start_for) + mp_name_type (q) = mp_expr_sym; + else if (m == start_forsuffixes) + mp_name_type (q) = mp_suffix_sym; + mp->cur_exp.type = mp_vacuous; CONTINUE: ; -} while (mp->cur_cmd==comma) +} while (mp->cur_cmd == comma) @ @<Prepare for step-until construction and |break|@>= -{ - if ( mp->cur_type!=mp_known ) mp_bad_for(mp, "initial value"); - pp=mp_get_node(mp, progression_node_size); value(pp)=mp->cur_exp; - mp_get_x_next(mp); mp_scan_expression(mp); - if ( mp->cur_type!=mp_known ) mp_bad_for(mp, "step size"); - step_size(pp)=mp->cur_exp; - if ( mp->cur_cmd!=until_token ) { - mp_missing_err(mp, "until"); -@.Missing `until'@> - help2("I assume you meant to say `until' after `step'.", - "So I'll look for the final value and colon next."); - mp_back_error(mp); - } - mp_get_x_next(mp); mp_scan_expression(mp); - if ( mp->cur_type!=mp_known ) mp_bad_for(mp, "final value"); - final_value(pp)=mp->cur_exp; loop_list(s)=pp; - loop_type(s)=progression_flag; +{ + if (mp->cur_exp.type != mp_known) + mp_bad_for (mp, "initial value"); + s->value = cur_exp_value (); + mp_get_x_next (mp); + mp_scan_expression (mp); + if (mp->cur_exp.type != mp_known) + mp_bad_for (mp, "step size"); + s->step_size = cur_exp_value (); + if (mp->cur_cmd != until_token) { + mp_missing_err (mp, "until"); +@.Missing `until'@>; + help2 ("I assume you meant to say `until' after `step'.", + "So I'll look for the final value and colon next."); + mp_back_error (mp); + } + mp_get_x_next (mp); + mp_scan_expression (mp); + if (mp->cur_exp.type != mp_known) + mp_bad_for (mp, "final value"); + s->final_value = cur_exp_value (); + s->type = PROGRESSION_FLAG; break; } + @ The last case is when we have just seen ``\&{within}'', and we need to parse a picture expression and prepare to iterate over it. @<Set up a picture iteration@>= -{ mp_get_x_next(mp); - mp_scan_expression(mp); +{ + mp_get_x_next (mp); + mp_scan_expression (mp); @<Make sure the current expression is a known picture@>; - loop_type(s)=mp->cur_exp; mp->cur_type=mp_vacuous; - q=mp_link(dummy_loc(mp->cur_exp)); - if ( q!= null ) - if ( is_start_or_stop(q) ) - if ( mp_skip_1component(mp, q)==null ) q=mp_link(q); - loop_list(s)=q; + s->type = mp->cur_exp.data.node; + mp->cur_exp.type = mp_vacuous; + q = mp_link (dummy_loc (mp->cur_exp.data.node)); + if (q != NULL) + if (is_start_or_stop (q)) + if (mp_skip_1component (mp, q) == NULL) + q = mp_link (q); + s->list = q; } -@ @<Make sure the current expression is a known picture@>= -if ( mp->cur_type!=mp_picture_type ) { - mp_disp_err(mp, null,"Improper iteration spec has been replaced by nullpicture"); - help1("When you say `for x in p', p must be a known picture."); - mp_put_get_flush_error(mp, mp_get_node(mp, edge_header_size)); - mp_init_edges(mp, mp->cur_exp); mp->cur_type=mp_picture_type; -} -@* \[35] File names. +@ @<Make sure the current expression is a known picture@>= +if (mp->cur_exp.type != mp_picture_type) { + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + new_expr.data.node = mp_get_edge_header_node (mp); + mp_disp_err (mp, NULL, + "Improper iteration spec has been replaced by nullpicture"); + help1 ("When you say `for x in p', p must be a known picture."); + mp_put_get_flush_error (mp, new_expr); + mp_init_edges (mp, mp->cur_exp.data.node); + mp->cur_exp.type = mp_picture_type; +} + +@* File names. It's time now to fret about file names. Besides the fact that different operating systems treat files in different ways, we must cope with the fact that completely different naming conventions are used by different @@ -16120,8 +17695,7 @@ system. The extension of an input file is assumed to be `\.{.mp}' unless otherwise specified; it is `\.{.log}' on the transcript file that records each run of \MP; it is `\.{.tfm}' on the font metric files that describe characters in any fonts created by \MP; it is -`\.{.ps}' or `.{\it nnn}' for some number {\it nnn} on the \ps\ output files; -and it is `\.{.mem}' on the mem files written by \.{INIMP} to initialize \MP. +`\.{.ps}' or `.{\it nnn}' for some number {\it nnn} on the \ps\ output files. The file area can be arbitrary on input files, but files are usually output to the user's current area. If an input file cannot be found on the specified area, \MP\ will look for it on a special system @@ -16151,7 +17725,7 @@ $$|begin_name|;\,|more_name|(c_1);\,\ldots\,;\,|more_name|(c_n); These three procedures communicate with each other via global variables. Afterwards the file name will appear in the string pool as three strings called |cur_name|\penalty10000\hskip-.05em, -|cur_area|, and |cur_ext|; the latter two are null (i.e., +|cur_area|, and |cur_ext|; the latter two are NULL (i.e., |""|), unless they were explicitly specified by the user. Actually the situation is slightly more complicated, because \MP\ needs @@ -16166,21 +17740,21 @@ to finish the assembly of |cur_name|, |cur_area|, and |cur_ext| regardless of whether $|more_name|(c_n)$ returned |true| or |false|. @<Glob...@>= -char * cur_name; /* name of file just scanned */ -char * cur_area; /* file area just scanned, or \.{""} */ -char * cur_ext; /* file extension just scanned, or \.{""} */ +char *cur_name; /* name of file just scanned */ +char *cur_area; /* file area just scanned, or \.{""} */ +char *cur_ext; /* file extension just scanned, or \.{""} */ @ It is easier to maintain reference counts if we assign initial values. @<Set init...@>= -mp->cur_name=xstrdup(""); -mp->cur_area=xstrdup(""); -mp->cur_ext=xstrdup(""); +mp->cur_name = xstrdup (""); +mp->cur_area = xstrdup (""); +mp->cur_ext = xstrdup (""); @ @<Dealloc variables@>= -xfree(mp->cur_area); -xfree(mp->cur_name); -xfree(mp->cur_ext); +xfree (mp->cur_area); +xfree (mp->cur_name); +xfree (mp->cur_ext); @ The file names we shall deal with for illustrative purposes have the following structure: If the name contains `\.>' or `\.:', the file area @@ -16191,222 +17765,244 @@ remaining `\..' to the end, otherwise the file extension is null. @^system dependencies@> We can scan such file names easily by using two global variables that keep track -of the occurrences of area and extension delimiters. Note that these variables -cannot be of type |pool_pointer| because a string pool compaction could occur -while scanning a file name. +of the occurrences of area and extension delimiters. @<Glob...@>= integer area_delimiter; /* most recent `\.>' or `\.:' relative to |str_start[str_ptr]| */ -integer ext_delimiter; /* the relevant `\..', if any */ -boolean quoted_filename; /* whether the filename is wrapped in " markers */ +integer ext_delimiter; /* the relevant `\..', if any */ +boolean quoted_filename; /* whether the filename is wrapped in " markers */ @ Here now is the first of the system-dependent routines for file name scanning. @^system dependencies@> -The file name length is limited to |file_name_size|. That is good, because -in the current configuration we cannot call |mp_do_compaction| while a name -is being scanned, |mp->area_delimiter| and |mp->ext_delimiter| are direct -offsets into |mp->str_pool|. I am not in a great hurry to fix this, because -calling |str_room()| just once is more efficient anyway. TODO. - @<Declarations@>= static void mp_begin_name (MP mp); static boolean mp_more_name (MP mp, ASCII_code c); static void mp_end_name (MP mp); @ @c -void mp_begin_name (MP mp) { - xfree(mp->cur_name); - xfree(mp->cur_area); - xfree(mp->cur_ext); - mp->area_delimiter=-1; - mp->ext_delimiter=-1; - mp->quoted_filename=false; - str_room(file_name_size); +void mp_begin_name (MP mp) { + xfree (mp->cur_name); + xfree (mp->cur_area); + xfree (mp->cur_ext); + mp->area_delimiter = -1; + mp->ext_delimiter = -1; + mp->quoted_filename = false; } + @ And here's the second. @^system dependencies@> -@d IS_DIR_SEP(c) (c=='/' || c=='\\') - -@c +@c +#ifndef IS_DIR_SEP +#define IS_DIR_SEP(c) (c=='/' || c=='\\') +#endif boolean mp_more_name (MP mp, ASCII_code c) { - if (c=='"') { - mp->quoted_filename= ! mp->quoted_filename; - } else if ((c==' '|| c=='\t') && (mp->quoted_filename==false)) { + if (c == '"') { + mp->quoted_filename = !mp->quoted_filename; + } else if ((c == ' ' || c == '\t') && (mp->quoted_filename == false)) { return false; } else { if (IS_DIR_SEP (c)) { - mp->area_delimiter=mp->pool_ptr; - mp->ext_delimiter=-1; - } else if ( c=='.' ) { - mp->ext_delimiter=mp->pool_ptr; + mp->area_delimiter = (integer) mp->cur_length; + mp->ext_delimiter = -1; + } else if (c == '.') { + mp->ext_delimiter = (integer) mp->cur_length; } - append_char(c); /* contribute |c| to the current string */ + append_char (c); /* contribute |c| to the current string */ } return true; } + @ The third. @^system dependencies@> @d copy_pool_segment(A,B,C) { A = xmalloc(C+1,sizeof(char)); - (void)memcpy(A,(char *)(mp->str_pool+B),C); + (void)memcpy(A,(char *)(mp->cur_string+B),C); A[C] = 0;} @c void mp_end_name (MP mp) { - pool_pointer s; /* length of area, name, and extension */ - unsigned int len; + size_t s = 0; /* length of area, name, and extension */ + size_t len; /* "my/w.mp" */ - s = mp->str_start[mp->str_ptr]; - if ( mp->area_delimiter<0 ) { - mp->cur_area=xstrdup(""); + if (mp->area_delimiter < 0) { + mp->cur_area = xstrdup (""); } else { - len = (unsigned)(mp->area_delimiter-s+1); - copy_pool_segment(mp->cur_area,s,len); - s += (pool_pointer)len; + len = (size_t) mp->area_delimiter - s + 1; + copy_pool_segment (mp->cur_area, s, len); + s += len; } - if ( mp->ext_delimiter<0 ) { - mp->cur_ext=xstrdup(""); - len = (unsigned)(mp->pool_ptr-s); + if (mp->ext_delimiter < 0) { + mp->cur_ext = xstrdup (""); + len = (unsigned) (mp->cur_length - s); } else { - copy_pool_segment(mp->cur_ext,mp->ext_delimiter,(size_t)(mp->pool_ptr-mp->ext_delimiter)); - len = (unsigned)(mp->ext_delimiter-s); + copy_pool_segment (mp->cur_ext, mp->ext_delimiter, + (mp->cur_length - (size_t) mp->ext_delimiter)); + len = (size_t) mp->ext_delimiter - s; } - copy_pool_segment(mp->cur_name,s,len); - mp->pool_ptr=s; /* don't need this partial string */ + copy_pool_segment (mp->cur_name, s, len); + reset_cur_string (mp); } + @ Conversely, here is a routine that takes three strings and prints a file name that might have produced them. (The routine is system dependent, because some operating systems put the file area last instead of first.) @^system dependencies@> @<Basic printing...@>= -static void mp_print_file_name (MP mp, char * n, char * a, char * e) { +static void mp_print_file_name (MP mp, char *n, char *a, char *e) { boolean must_quote = false; - if (((a != NULL) && (strchr(a,' ') != NULL)) || - ((n != NULL) && (strchr(n,' ') != NULL)) || - ((e != NULL) && (strchr(e,' ') != NULL))) + if (((a != NULL) && (strchr (a, ' ') != NULL)) || + ((n != NULL) && (strchr (n, ' ') != NULL)) || + ((e != NULL) && (strchr (e, ' ') != NULL))) must_quote = true; - if (must_quote) mp_print_char(mp, (ASCII_code)'"'); - mp_print(mp, a); mp_print(mp, n); mp_print(mp, e); - if (must_quote) mp_print_char(mp, (ASCII_code)'"'); + if (must_quote) + mp_print_char (mp, (ASCII_code) '"'); + mp_print (mp, a); + mp_print (mp, n); + mp_print (mp, e); + if (must_quote) + mp_print_char (mp, (ASCII_code) '"'); } + @ Another system-dependent routine is needed to convert three internal \MP\ strings to the |name_of_file| value that is used to open files. The present code allows both lowercase and uppercase letters in the file name. @^system dependencies@> -@d append_to_name(A) { c=xord((ASCII_code)(A)); - if ( k<file_name_size ) { - mp->name_of_file[k]=(char)xchr(c); - incr(k); - } -} +@d append_to_name(A) { mp->name_of_file[k++]=(char)xchr(xord((ASCII_code)(A))); } @ @c void mp_pack_file_name (MP mp, const char *n, const char *a, const char *e) { - integer k; /* number of positions filled in |name_of_file| */ - ASCII_code c; /* character being packed */ - const char *j; /* a character index */ - k=0; - assert(n!=NULL); - if (a!=NULL) { - for (j=a;*j!='\0';j++) { append_to_name(*j); } + integer k; /* number of positions filled in |name_of_file| */ + const char *j; /* a character index */ + size_t slen; + k = 0; + assert (n != NULL); + xfree (mp->name_of_file); + slen = strlen (n) + 1; + if (a != NULL) + slen += strlen (a); + if (e != NULL) + slen += strlen (e); + mp->name_of_file = xmalloc (slen, 1); + if (a != NULL) { + for (j = a; *j != '\0'; j++) { + append_to_name (*j); + } } - for (j=n;*j!='\0';j++) { append_to_name(*j); } - if (e!=NULL) { - for (j=e;*j!='\0';j++) { append_to_name(*j); } + for (j = n; *j != '\0'; j++) { + append_to_name (*j); } - mp->name_of_file[k]=0; - mp->name_length=k; + if (e != NULL) { + for (j = e; *j != '\0'; j++) { + append_to_name (*j); + } + } + mp->name_of_file[k] = 0; } + @ @<Internal library declarations@>= -void mp_pack_file_name (MP mp, const char *n, const char *a, const char *e) ; +void mp_pack_file_name (MP mp, const char *n, const char *a, const char *e); @ @<Option variables@>= char *mem_name; /* for commandline */ -@ @<Find constant sizes@>= -mp->mem_name = xstrdup(opt->mem_name); -if (mp->mem_name) { - size_t l = strlen(mp->mem_name); - if (l>4) { - char *test = strstr(mp->mem_name,".mem"); - if (test == mp->mem_name+l-4) { - *test = 0; +@ Stripping a |.mem| extension here is for backward compatibility. + +@<Find and load preload file, if required@>= +if (!opt->ini_version) { + mp->mem_name = xstrdup (opt->mem_name); + if (mp->mem_name) { + size_t l = strlen (mp->mem_name); + if (l > 4) { + char *test = strstr (mp->mem_name, ".mem"); + if (test == mp->mem_name + l - 4) { + *test = 0; + } + } + } + if (mp->mem_name != NULL) { + if (!mp_open_mem_file (mp)) { + mp->history = mp_fatal_error_stop; + mp_jump_out (mp); } } } + @ @<Dealloc variables@>= -xfree(mp->mem_name); +xfree (mp->mem_name); @ This part of the program becomes active when a ``virgin'' \MP\ is -trying to get going, just after the preliminary initialization, or -when the user is substituting another mem file by typing `\.\&' after -the initial `\.{**}' prompt. The buffer contains the first line of -input in |buffer[loc..(last-1)]|, where |loc<last| and |buffer[loc]<>""|. +trying to get going, just after the preliminary initialization. +The buffer contains the first line of input in |buffer[loc..(last-1)]|, +where |loc<last| and |buffer[loc]<>""|. @<Declarations@>= -static boolean mp_open_mem_name (MP mp) ; -static boolean mp_open_mem_file (MP mp) ; +static boolean mp_open_mem_name (MP mp); +static boolean mp_open_mem_file (MP mp); @ @c boolean mp_open_mem_name (MP mp) { - if (mp->mem_name!=NULL) { - size_t l = strlen(mp->mem_name); + if (mp->mem_name != NULL) { + size_t l = strlen (mp->mem_name); char *s = xstrdup (mp->mem_name); - if (l>4) { - char *test = strstr(s,".mem"); - if (test == NULL || test != s+l-4) { - s = xrealloc (s, l+5, 1); - strcat (s, ".mem"); + if (l > 4) { + char *test = strstr (s, ".mp"); + if (test == NULL || test != s + l - 4) { + s = xrealloc (s, l + 5, 1); + strcat (s, ".mp"); } } else { - s = xrealloc (s, l+5, 1); - strcat (s, ".mem"); + s = xrealloc (s, l + 5, 1); + strcat (s, ".mp"); } - mp->mem_file = (mp->open_file)(mp,s, "r", mp_filetype_memfile); - xfree(s); - if ( mp->mem_file ) return true; + mp->mem_file = (mp->open_file) (mp, s, "r", mp_filetype_program); + xfree (mp->name_of_file); + mp->name_of_file = xstrdup (s); + free (s); + if (mp->mem_file) + return true; } return false; } boolean mp_open_mem_file (MP mp) { if (mp->mem_file != NULL) return true; - if (mp_open_mem_name(mp)) + if (mp_open_mem_name (mp)) return true; - if (mp_xstrcmp(mp->mem_name, "plain")) { + if (mp_xstrcmp (mp->mem_name, "plain")) { wake_up_terminal; - wterm_ln("Sorry, I can\'t find the '"); - wterm(mp->mem_name); - wterm("' mem file; will try 'plain'."); -@.Sorry, I can't find...@> + wterm_ln ("Sorry, I can\'t find the '"); + wterm (mp->mem_name); + wterm ("' preload file; will try 'plain'."); +@.Sorry, I can't find...@>; update_terminal; /* now pull out all the stops: try for the system \.{plain} file */ - xfree(mp->mem_name); - mp->mem_name = xstrdup("plain"); - if (mp_open_mem_name(mp)) + xfree (mp->mem_name); + mp->mem_name = xstrdup ("plain"); + if (mp_open_mem_name (mp)) return true; } wake_up_terminal; - wterm_ln("I can't find the 'plain' mem file!\n"); + wterm_ln ("I can't find the 'plain' preload file!\n"); @.I can't find PLAIN...@> -@.plain@> +@.plain@>; return false; } + @ Operating systems often make it possible to determine the exact name (and possible version number) of a file that has been opened. The following routine, which simply makes a \MP\ string from the value of |name_of_file|, should @@ -16416,19 +18012,19 @@ most recently opened, if it is possible to do this. @<Declarations@>= #define mp_a_make_name_string(A,B) mp_make_name_string(A) -#define mp_b_make_name_string(A,B) mp_make_name_string(A) -#define mp_w_make_name_string(A,B) mp_make_name_string(A) -@ @c +@ @c static str_number mp_make_name_string (MP mp) { - int k; /* index into |name_of_file| */ - str_room(mp->name_length); - for (k=0;k<mp->name_length;k++) { - append_char(xord((ASCII_code)mp->name_of_file[k])); + int k; /* index into |name_of_file| */ + int name_length = (int) strlen (mp->name_of_file); + str_room (name_length); + for (k = 0; k < name_length; k++) { + append_char (xord ((ASCII_code) mp->name_of_file[k])); } - return mp_make_string(mp); + return mp_make_string (mp); } + @ Now let's consider the ``driver'' routines by which \MP\ deals with file names in a system-independent manner. First comes a procedure that looks for a @@ -16446,50 +18042,59 @@ be made with reluctance, and only when an entire file name that includes special characters is ``quoted'' somehow. @^system dependencies@> -@c -static void mp_scan_file_name (MP mp) { - mp_begin_name(mp); - while ( mp->buffer[loc]==' ' ) incr(loc); - while (1) { - if ( (mp->buffer[loc]==';')||(mp->buffer[loc]=='%') ) break; - if ( ! mp_more_name(mp, mp->buffer[loc]) ) break; - incr(loc); +@c +static void mp_scan_file_name (MP mp) { + mp_begin_name (mp); + while (mp->buffer[loc] == ' ') + incr (loc); + while (1) { + if ((mp->buffer[loc] == ';') || (mp->buffer[loc] == '%')) + break; + if (!mp_more_name (mp, mp->buffer[loc])) + break; + incr (loc); } - mp_end_name(mp); + mp_end_name (mp); } + @ Here is another version that takes its input from a string. @<Declare subroutines for parsing file names@>= -void mp_str_scan_file (MP mp, str_number s) ; +void mp_str_scan_file (MP mp, str_number s); @ @c -void mp_str_scan_file (MP mp, str_number s) { - pool_pointer p,q; /* current position and stopping point */ - mp_begin_name(mp); - p=mp->str_start[s]; q=str_stop(s); - while ( p<q ){ - if ( ! mp_more_name(mp, mp->str_pool[p]) ) break; - incr(p); +void mp_str_scan_file (MP mp, str_number s) { + size_t p, q; /* current position and stopping point */ + mp_begin_name (mp); + p = 0; + q = length (s); + while (p < q) { + if (!mp_more_name (mp, *(s->str + p))) + break; + incr (p); } - mp_end_name(mp); + mp_end_name (mp); } + @ And one that reads from a |char*|. @<Declare subroutines for parsing file names@>= -extern void mp_ptr_scan_file (MP mp, char *s); +extern void mp_ptr_scan_file (MP mp, char *s); @ @c -void mp_ptr_scan_file (MP mp, char *s) { - char *p, *q; /* current position and stopping point */ - mp_begin_name(mp); - p=s; q=p+strlen(s); - while ( p<q ){ - if ( ! mp_more_name(mp, xord((ASCII_code)(*p)))) break; +void mp_ptr_scan_file (MP mp, char *s) { + char *p, *q; /* current position and stopping point */ + mp_begin_name (mp); + p = s; + q = p + strlen (s); + while (p < q) { + if (!mp_more_name (mp, (ASCII_code) (*p))) + break; p++; } - mp_end_name(mp); + mp_end_name (mp); } @@ -16500,7 +18105,7 @@ as well as the |mp_job_name| internal, and is extended by `\.{.log}' and output files. @<Glob...@>= -boolean log_opened; /* has the transcript file been opened? */ +boolean log_opened; /* has the transcript file been opened? */ char *log_name; /* full name of the log file */ @ @<Option variables@>= @@ -16511,47 +18116,34 @@ We have |job_name=NULL| if and only if the `\.{log}' file has not been opened, except of course for a short time just after |job_name| has become nonzero. @<Allocate or ...@>= -mp->job_name=mp_xstrdup(mp, opt->job_name); +mp->job_name = mp_xstrdup (mp, opt->job_name); if (mp->job_name != NULL) { - char *s = mp->job_name+strlen(mp->job_name); - while (s>mp->job_name) { - if (*s == '.') { - if(strcasecmp(s, ".mp") == 0 || strcasecmp(s, ".mem") == 0 || - strcasecmp(s, ".mf") == 0) { - *s = '\0'; - break; - } - } - s--; - } -} -if (opt->noninteractive && opt->ini_version) { - if (mp->job_name == NULL) - mp->job_name=mp_xstrdup(mp,mp->mem_name); - if (mp->job_name != NULL) { - size_t l = strlen(mp->job_name); - if (l>4) { - char *test = strstr(mp->job_name,".mem"); - if (test == mp->job_name+l-4) - *test = 0; + char *s = mp->job_name + strlen (mp->job_name); + while (s > mp->job_name) { + if (*s == '.') { + *s = '\0'; } + s--; } } -mp->log_opened=false; +if (opt->noninteractive) { + if (mp->job_name == NULL) + mp->job_name = mp_xstrdup (mp, mp->mem_name); +} +mp->log_opened = false; -@ Cannot do this earlier because at the |@<Allocate or ...@>|, the string +@ Cannot do this earlier because at the |<Allocate or ...>|, the string pool is not yet initialized. @<Fix up |mp->internal[mp_job_name]|@>= if (mp->job_name != NULL) { - if (mp->internal[mp_job_name]!=0) - delete_str_ref(mp->internal[mp_job_name]); - mp->internal[mp_job_name]=mp_rts(mp,mp->job_name); + if (internal_string (mp_job_name) != 0) + delete_str_ref (internal_string (mp_job_name)); + internal_string (mp_job_name) = mp_rts (mp, mp->job_name); } - @ @<Dealloc variables@>= -xfree(mp->job_name); +xfree (mp->job_name); @ Here is a routine that manufactures the output file names, assuming that |job_name<>0|. It ignores and changes the current settings of |cur_area| @@ -16560,16 +18152,20 @@ and |cur_ext|. @d pack_cur_name mp_pack_file_name(mp, mp->cur_name,mp->cur_area,mp->cur_ext) @<Internal library ...@>= -void mp_pack_job_name (MP mp, const char *s) ; +void mp_pack_job_name (MP mp, const char *s); -@ @c -void mp_pack_job_name (MP mp, const char *s) { /* |s = ".log"|, |".mem"|, |".ps"|, or .\\{nnn} */ - xfree(mp->cur_name); mp->cur_name=xstrdup(mp->job_name); - xfree(mp->cur_area); mp->cur_area=xstrdup(""); - xfree(mp->cur_ext); mp->cur_ext=xstrdup(s); +@ @c +void mp_pack_job_name (MP mp, const char *s) { /* |s = ".log"|, |".mem"|, |".ps"|, or .\\{nnn} */ + xfree (mp->cur_name); + mp->cur_name = xstrdup (mp->job_name); + xfree (mp->cur_area); + mp->cur_area = xstrdup (""); + xfree (mp->cur_ext); + mp->cur_ext = xstrdup (s); pack_cur_name; } + @ If some trouble arises when \MP\ tries to open a file, the following routine calls upon the user to supply another file name. Parameter~|s| is used in the error message to identify the type of file; parameter~|e| @@ -16578,95 +18174,109 @@ variables |cur_name|, |cur_area|, |cur_ext|, and |name_of_file| are ready for another attempt at file opening. @<Internal library ...@>= -void mp_prompt_file_name (MP mp, const char * s, const char * e) ; - -@ @c void mp_prompt_file_name (MP mp, const char * s, const char * e) { - size_t k; /* index into |buffer| */ - char * saved_cur_name; - if ( mp->interaction==mp_scroll_mode ) - wake_up_terminal; - if (strcmp(s,"input file name")==0) { - print_err("I can\'t find file `"); +void mp_prompt_file_name (MP mp, const char *s, const char *e); + +@ @c +void mp_prompt_file_name (MP mp, const char *s, const char *e) { + size_t k; /* index into |buffer| */ + char *saved_cur_name; + if (mp->interaction == mp_scroll_mode) + wake_up_terminal; + if (strcmp (s, "input file name") == 0) { + print_err ("I can\'t open file `"); @.I can't find file x@> } else { - print_err("I can\'t write on file `"); + print_err ("I can\'t write on file `"); @.I can't write on file x@> } - if (strcmp(s,"file name for output")==0) { - mp_print(mp, mp->output_file); + if (strcmp (s, "file name for output") == 0) { + mp_print (mp, mp->name_of_file); } else { - mp_print_file_name(mp, mp->cur_name,mp->cur_area,mp->cur_ext); - } - mp_print(mp, "'."); - if (strcmp(e,"")==0) - mp_show_context(mp); - mp_print_nl(mp, "Please type another "); mp_print(mp, s); -@.Please type...@> - if (mp->noninteractive || mp->interaction<mp_scroll_mode ) - mp_fatal_error(mp, "*** (job aborted, file error in nonstop mode)"); -@.job aborted, file error...@> - saved_cur_name = xstrdup(mp->cur_name); - clear_terminal; prompt_input(": "); @<Scan file name in the buffer@>; - if (strcmp(mp->cur_ext,"")==0) - mp->cur_ext=xstrdup(e); - if (strlen(mp->cur_name)==0) { - mp->cur_name=saved_cur_name; + mp_print_file_name (mp, mp->cur_name, mp->cur_area, mp->cur_ext); + } + mp_print (mp, "'."); + if (strcmp (e, "") == 0) + mp_show_context (mp); + mp_print_nl (mp, "Please type another "); + mp_print (mp, s); +@.Please type...@>; + if (mp->noninteractive || mp->interaction < mp_scroll_mode) + mp_fatal_error (mp, "*** (job aborted, file error in nonstop mode)"); +@.job aborted, file error...@>; + saved_cur_name = xstrdup (mp->cur_name); + clear_terminal; + prompt_input (": "); + @<Scan file name in the buffer@>; + if (strcmp (mp->cur_ext, "") == 0) + mp->cur_ext = xstrdup (e); + if (strlen (mp->cur_name) == 0) { + mp->cur_name = saved_cur_name; } else { - xfree(saved_cur_name); + xfree (saved_cur_name); } pack_cur_name; } + @ @<Scan file name in the buffer@>= -{ - mp_begin_name(mp); k=mp->first; - while ( (mp->buffer[k]==' ')&&(k<mp->last) ) incr(k); - while (1) { - if ( k==mp->last ) break; - if ( ! mp_more_name(mp, mp->buffer[k]) ) break; - incr(k); +{ + mp_begin_name (mp); + k = mp->first; + while ((mp->buffer[k] == ' ') && (k < mp->last)) + incr (k); + while (1) { + if (k == mp->last) + break; + if (!mp_more_name (mp, mp->buffer[k])) + break; + incr (k); } - mp_end_name(mp); + mp_end_name (mp); } + @ The |open_log_file| routine is used to open the transcript file and to help it catch up to what has previously been printed on the terminal. -@c void mp_open_log_file (MP mp) { +@c +void mp_open_log_file (MP mp) { unsigned old_setting; /* previous |selector| setting */ - int k; /* index into |months| and |buffer| */ - int l; /* end of first input line */ - integer m; /* the current month */ - const char *months="JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"; - /* abbreviations of month names */ + int k; /* index into |months| and |buffer| */ + int l; /* end of first input line */ + integer m; /* the current month */ + const char *months = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"; + /* abbreviations of month names */ if (mp->log_opened) return; - old_setting=mp->selector; - if ( mp->job_name==NULL ) { - mp->job_name=xstrdup("mpout"); - @<Fix up |mp->internal[mp_job_name]|@>; + old_setting = mp->selector; + if (mp->job_name == NULL) { + mp->job_name = xstrdup ("mpout"); + @<Fix up |mp->internal[mp_job_name]|@>; } - mp_pack_job_name(mp,".log"); - while ( ! mp_a_open_out(mp, &mp->log_file, mp_filetype_log) ) { + mp_pack_job_name (mp, ".log"); + while (!mp_a_open_out (mp, &mp->log_file, mp_filetype_log)) { @<Try to get a different log file name@>; } - mp->log_name=xstrdup(mp->name_of_file); - mp->selector=log_only; mp->log_opened=true; + mp->log_name = xstrdup (mp->name_of_file); + mp->selector = log_only; + mp->log_opened = true; @<Print the banner line, including the date and time@>; - mp->input_stack[mp->input_ptr]=mp->cur_input; - /* make sure bottom level is in memory */ + mp->input_stack[mp->input_ptr] = mp->cur_input; + /* make sure bottom level is in memory */ if (!mp->noninteractive) { - mp_print_nl(mp, "**"); -@.**@> - l=mp->input_stack[0].limit_field-1; /* last position of first line */ - for (k=0;k<=l;k++) mp_print_str(mp, mp->buffer[k]); - mp_print_ln(mp); /* now the transcript file contains the first line of input */ + mp_print_nl (mp, "**"); +@.**@>; + l = mp->input_stack[0].limit_field - 1; /* last position of first line */ + for (k = 0; k <= l; k++) + mp_print_char (mp, mp->buffer[k]); + mp_print_ln (mp); /* now the transcript file contains the first line of input */ } - mp->selector=old_setting+2; /* |log_only| or |term_and_log| */ + mp->selector = old_setting + 2; /* |log_only| or |term_and_log| */ } + @ @<Dealloc variables@>= -xfree(mp->log_name); +xfree (mp->log_name); @ Sometimes |open_log_file| is called at awkward moments when \MP\ is unable to print error messages or even to |show_context|. @@ -16683,145 +18293,173 @@ file}', because some systems cannot use the extension `\.{.log}' for this file. @<Try to get a different log file name@>= -{ - mp->selector=term_only; - mp_prompt_file_name(mp, "transcript file name",".log"); +{ + mp->selector = term_only; + mp_prompt_file_name (mp, "transcript file name", ".log"); } + @ @<Print the banner...@>= -{ - wlog(mp->banner); - mp_print(mp, mp->mem_ident); mp_print(mp, " "); - mp_print_int(mp, mp_round_unscaled(mp, mp->internal[mp_day])); - mp_print_char(mp, xord(' ')); - m=mp_round_unscaled(mp, mp->internal[mp_month]); - for (k=3*m-3;k<3*m;k++) { wlog_chr((unsigned char)months[k]); } - mp_print_char(mp, xord(' ')); - mp_print_int(mp, mp_round_unscaled(mp, mp->internal[mp_year])); - mp_print_char(mp, xord(' ')); - mp_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_hour])); - mp_print_char(mp, xord(':')); - mp_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_minute])); +{ + wlog (mp->banner); + mp_print (mp, mp->mem_ident); + mp_print (mp, " "); + mp_print_int (mp, mp_round_unscaled (mp, internal_value (mp_day))); + mp_print_char (mp, xord (' ')); + m = mp_round_unscaled (mp, internal_value (mp_month)); + for (k = 3 * m - 3; k < 3 * m; k++) { + wlog_chr ((unsigned char) months[k]); + } + mp_print_char (mp, xord (' ')); + mp_print_int (mp, mp_round_unscaled (mp, internal_value (mp_year))); + mp_print_char (mp, xord (' ')); + mp_print_dd (mp, mp_round_unscaled (mp, internal_value (mp_hour))); + mp_print_char (mp, xord (':')); + mp_print_dd (mp, mp_round_unscaled (mp, internal_value (mp_minute))); } + @ The |try_extension| function tries to open an input file determined by |cur_name|, |cur_area|, and the argument |ext|. It returns |false| if it can't find the file in |cur_area| or the appropriate system area. @c -static boolean mp_try_extension (MP mp, const char *ext) { - mp_pack_file_name(mp, mp->cur_name,mp->cur_area, ext); - in_name=xstrdup(mp->cur_name); - in_area=xstrdup(mp->cur_area); - in_ext=xstrdup(ext); - if ( mp_a_open_in(mp, &cur_file, mp_filetype_program) ) { +static boolean mp_try_extension (MP mp, const char *ext) { + mp_pack_file_name (mp, mp->cur_name, mp->cur_area, ext); + in_name = xstrdup (mp->cur_name); + in_area = xstrdup (mp->cur_area); + in_ext = xstrdup (ext); + if (mp_a_open_in (mp, &cur_file, mp_filetype_program)) { return true; - } else { - mp_pack_file_name(mp, mp->cur_name,NULL,ext); - return mp_a_open_in(mp, &cur_file, mp_filetype_program); + } else { + mp_pack_file_name (mp, mp->cur_name, NULL, ext); + return mp_a_open_in (mp, &cur_file, mp_filetype_program); } } + @ Let's turn now to the procedure that is used to initiate file reading when an `\.{input}' command is being processed. -@c void mp_start_input (MP mp) { /* \MP\ will \.{input} something */ +@c +void mp_start_input (MP mp) { /* \MP\ will \.{input} something */ char *fname = NULL; @<Put the desired file name in |(cur_name,cur_ext,cur_area)|@>; - while (1) { - mp_begin_file_reading(mp); /* set up |cur_file| and new level of input */ - if ( strlen(mp->cur_ext)==0 ) { - if ( mp_try_extension(mp, ".mp") ) break; - else if ( mp_try_extension(mp, "") ) break; - else if ( mp_try_extension(mp, ".mf") ) break; - /* |else do_nothing; | */ - } else if ( mp_try_extension(mp, mp->cur_ext) ) { + while (1) { + mp_begin_file_reading (mp); /* set up |cur_file| and new level of input */ + if (strlen (mp->cur_ext) == 0) { + if (mp_try_extension (mp, ".mp")) + break; + else if (mp_try_extension (mp, "")) + break; + else if (mp_try_extension (mp, ".mf")) + break; + } else if (mp_try_extension (mp, mp->cur_ext)) { break; } - mp_end_file_reading(mp); /* remove the level that didn't work */ - mp_prompt_file_name(mp, "input file name",""); + mp_end_file_reading (mp); /* remove the level that didn't work */ + mp_prompt_file_name (mp, "input file name", ""); } - name=mp_a_make_name_string(mp, cur_file); - fname = xstrdup(mp->name_of_file); - if ( mp->job_name==NULL ) { - mp->job_name=xstrdup(mp->cur_name); + name = mp_a_make_name_string (mp, cur_file); + fname = xstrdup (mp->name_of_file); + if (mp->job_name == NULL) { + mp->job_name = xstrdup (mp->cur_name); @<Fix up |mp->internal[mp_job_name]|@>; } if (!mp->log_opened) { - mp_open_log_file(mp); - } /* |open_log_file| doesn't |show_context|, so |limit| - and |loc| needn't be set to meaningful values yet */ - if ( ((int)mp->term_offset+(int)strlen(fname)) > (mp->max_print_line-2)) mp_print_ln(mp); - else if ( (mp->term_offset>0)||(mp->file_offset>0) ) mp_print_char(mp, xord(' ')); - mp_print_char(mp, xord('(')); incr(mp->open_parens); mp_print(mp, fname); - xfree(fname); + mp_open_log_file (mp); + } /* |open_log_file| doesn't |show_context|, so |limit| + and |loc| needn't be set to meaningful values yet */ + if (((int) mp->term_offset + (int) strlen (fname)) > (mp->max_print_line - 2)) + mp_print_ln (mp); + else if ((mp->term_offset > 0) || (mp->file_offset > 0)) + mp_print_char (mp, xord (' ')); + mp_print_char (mp, xord ('(')); + incr (mp->open_parens); + mp_print (mp, fname); + xfree (fname); update_terminal; @<Flush |name| and replace it with |cur_name| if it won't be needed@>; @<Read the first line of the new file@>; } + @ This code should be omitted if |a_make_name_string| returns something other than just a copy of its argument and the full file name is needed for opening \.{MPX} files or implementing the switch-to-editor option. @^system dependencies@> @<Flush |name| and replace it with |cur_name| if it won't be needed@>= -mp_flush_string(mp, name); name=rts(mp->cur_name); xfree(mp->cur_name) +mp_flush_string (mp, name); +name = mp_rts (mp, mp->cur_name); +xfree (mp->cur_name) + @ If the file is empty, it is considered to contain a single blank line, so there is no need to test the return value. @<Read the first line...@>= -{ - line=1; - (void)mp_input_ln(mp, cur_file ); - mp_firm_up_the_line(mp); - mp->buffer[limit]=xord('%'); mp->first=(size_t)(limit+1); loc=start; +{ + line = 1; + (void) mp_input_ln (mp, cur_file); + mp_firm_up_the_line (mp); + mp->buffer[limit] = xord ('%'); + mp->first = (size_t) (limit + 1); + loc = start; } + @ @<Put the desired file name in |(cur_name,cur_ext,cur_area)|@>= -while ( token_state &&(loc==null) ) mp_end_token_list(mp); -if ( token_state ) { - print_err("File names can't appear within macros"); -@.File names can't...@> - help3("Sorry...I've converted what follows to tokens,", - "possibly garbaging the name you gave.", - "Please delete the tokens and insert the name again."); - mp_error(mp); -} -if ( file_state ) { - mp_scan_file_name(mp); -} else { - xfree(mp->cur_name); mp->cur_name=xstrdup(""); - xfree(mp->cur_ext); mp->cur_ext =xstrdup(""); - xfree(mp->cur_area); mp->cur_area=xstrdup(""); +while (token_state && (nloc == NULL)) + mp_end_token_list (mp); +if (token_state) { + print_err ("File names can't appear within macros"); +@.File names can't...@>; + help3 ("Sorry...I've converted what follows to tokens,", + "possibly garbaging the name you gave.", + "Please delete the tokens and insert the name again."); + mp_error (mp); +} +if (file_state) { + mp_scan_file_name (mp); +} else { + xfree (mp->cur_name); + mp->cur_name = xstrdup (""); + xfree (mp->cur_ext); + mp->cur_ext = xstrdup (""); + xfree (mp->cur_area); + mp->cur_area = xstrdup (""); } + @ The following simple routine starts reading the \.{MPX} file associated with the current input file. -@c void mp_start_mpx_input (MP mp) { - char *origname = NULL; /* a copy of nameoffile */ - mp_pack_file_name(mp, in_name, in_area, in_ext); - origname = xstrdup(mp->name_of_file); - mp_pack_file_name(mp, in_name, in_area, ".mpx"); - if (!(mp->run_make_mpx)(mp, origname, mp->name_of_file)) +@c +void mp_start_mpx_input (MP mp) { + char *origname = NULL; /* a copy of nameoffile */ + mp_pack_file_name (mp, in_name, "", in_ext); + origname = xstrdup (mp->name_of_file); + mp_pack_file_name (mp, in_name, "", ".mpx"); + if (!(mp->run_make_mpx) (mp, origname, mp->name_of_file)) goto NOT_FOUND; - mp_begin_file_reading(mp); - if ( ! mp_a_open_in(mp, &cur_file, mp_filetype_program) ) { - mp_end_file_reading(mp); + mp_begin_file_reading (mp); + if (!mp_a_open_in (mp, &cur_file, mp_filetype_program)) { + mp_end_file_reading (mp); goto NOT_FOUND; } - name=mp_a_make_name_string(mp, cur_file); - mp->mpx_name[iindex]=name; add_str_ref(name); + name = mp_a_make_name_string (mp, cur_file); + mp->mpx_name[iindex] = name; + add_str_ref (name); @<Read the first line of the new file@>; - xfree(origname); + xfree (origname); return; -NOT_FOUND: - @<Explain that the \.{MPX} file can't be read and |succumb|@>; - xfree(origname); +NOT_FOUND: + @<Explain that the \.{MPX} file can't be read and |succumb|@>; + xfree (origname); } + @ This should ideally be changed to do whatever is necessary to create the \.{MPX} file given by |name_of_file| if it does not exist or if it is out of date. This requires invoking \.{MPtoTeX} on the |origname| and passing @@ -16831,38 +18469,40 @@ available to perform the function of \.{DVItoMP}.) @^system dependencies@> @ @<Exported types@>= -typedef int (*mp_makempx_cmd)(MP mp, char *origname, char *mtxname); +typedef int (*mp_makempx_cmd) (MP mp, char *origname, char *mtxname); @ @<Option variables@>= mp_makempx_cmd run_make_mpx; @ @<Allocate or initialize ...@>= -set_callback_option(run_make_mpx); +set_callback_option (run_make_mpx); @ @<Declarations@>= static int mp_run_make_mpx (MP mp, char *origname, char *mtxname); @ The default does nothing. -@c +@c int mp_run_make_mpx (MP mp, char *origname, char *mtxname) { - (void)mp; - (void)origname; - (void)mtxname; + (void) mp; + (void) origname; + (void) mtxname; return false; } + @ @<Explain that the \.{MPX} file can't be read and |succumb|@>= -if ( mp->interaction==mp_error_stop_mode ) wake_up_terminal; -mp_print_nl(mp, ">> "); -mp_print(mp, origname); -mp_print_nl(mp, ">> "); -mp_print(mp, mp->name_of_file); -mp_print_nl(mp, "! Unable to make mpx file"); -help4("The two files given above are one of your source files", - "and an auxiliary file I need to read to find out what your", - "btex..etex blocks mean. If you don't know why I had trouble,", - "try running it manually through MPtoTeX, TeX, and DVItoMP"); -xfree(origname); +if (mp->interaction == mp_error_stop_mode) + wake_up_terminal; +mp_print_nl (mp, ">> "); +mp_print (mp, origname); +mp_print_nl (mp, ">> "); +mp_print (mp, mp->name_of_file); +mp_print_nl (mp, "! Unable to make mpx file"); +help4 ("The two files given above are one of your source files", + "and an auxiliary file I need to read to find out what your", + "btex..etex blocks mean. If you don't know why I had trouble,", + "try running it manually through MPtoTeX, TeX, and DVItoMP"); +xfree (origname); succumb; @ The last file-opening commands are for files accessed via the \&{readfrom} @@ -16871,67 +18511,69 @@ operator and the \&{write} command. Such files are stored in separate arrays. @:write_}{\&{write} primitive@> @<Types in the outer block@>= -typedef unsigned int readf_index; /* |0..max_read_files| */ -typedef unsigned int write_index; /* |0..max_write_files| */ +typedef unsigned int readf_index; /* |0..max_read_files| */ +typedef unsigned int write_index; /* |0..max_write_files| */ @ @<Glob...@>= -readf_index max_read_files; /* maximum number of simultaneously open \&{readfrom} files */ -void ** rd_file; /* \&{readfrom} files */ -char ** rd_fname; /* corresponding file name or 0 if file not open */ +readf_index max_read_files; /* maximum number of simultaneously open \&{readfrom} files */ +void **rd_file; /* \&{readfrom} files */ +char **rd_fname; /* corresponding file name or 0 if file not open */ readf_index read_files; /* number of valid entries in the above arrays */ -write_index max_write_files; /* maximum number of simultaneously open \&{write} */ -void ** wr_file; /* \&{write} files */ -char ** wr_fname; /* corresponding file name or 0 if file not open */ -write_index write_files; /* number of valid entries in the above arrays */ +write_index max_write_files; /* maximum number of simultaneously open \&{write} */ +void **wr_file; /* \&{write} files */ +char **wr_fname; /* corresponding file name or 0 if file not open */ +write_index write_files; /* number of valid entries in the above arrays */ @ @<Allocate or initialize ...@>= -mp->max_read_files=8; -mp->rd_file = xmalloc((mp->max_read_files+1),sizeof(void *)); -mp->rd_fname = xmalloc((mp->max_read_files+1),sizeof(char *)); -memset(mp->rd_fname, 0, sizeof(char *)*(mp->max_read_files+1)); -mp->max_write_files=8; -mp->wr_file = xmalloc((mp->max_write_files+1),sizeof(void *)); -mp->wr_fname = xmalloc((mp->max_write_files+1),sizeof(char *)); -memset(mp->wr_fname, 0, sizeof(char *)*(mp->max_write_files+1)); +mp->max_read_files = 8; +mp->rd_file = xmalloc ((mp->max_read_files + 1), sizeof (void *)); +mp->rd_fname = xmalloc ((mp->max_read_files + 1), sizeof (char *)); +memset (mp->rd_fname, 0, sizeof (char *) * (mp->max_read_files + 1)); +mp->max_write_files = 8; +mp->wr_file = xmalloc ((mp->max_write_files + 1), sizeof (void *)); +mp->wr_fname = xmalloc ((mp->max_write_files + 1), sizeof (char *)); +memset (mp->wr_fname, 0, sizeof (char *) * (mp->max_write_files + 1)); @ This routine starts reading the file named by string~|s| without setting |loc|, |limit|, or |name|. It returns |false| if the file is empty or cannot be opened. Otherwise it updates |rd_file[n]| and |rd_fname[n]|. -@c -static boolean mp_start_read_input (MP mp,char *s, readf_index n) { - mp_ptr_scan_file(mp, s); +@c +static boolean mp_start_read_input (MP mp, char *s, readf_index n) { + mp_ptr_scan_file (mp, s); pack_cur_name; - mp_begin_file_reading(mp); - if ( ! mp_a_open_in(mp, &mp->rd_file[n], (int)(mp_filetype_text+n)) ) - goto NOT_FOUND; - if ( ! mp_input_ln(mp, mp->rd_file[n] ) ) { - (mp->close_file)(mp,mp->rd_file[n]); - goto NOT_FOUND; - } - mp->rd_fname[n]=xstrdup(s); + mp_begin_file_reading (mp); + if (!mp_a_open_in (mp, &mp->rd_file[n], (int) (mp_filetype_text + n))) + goto NOT_FOUND; + if (!mp_input_ln (mp, mp->rd_file[n])) { + (mp->close_file) (mp, mp->rd_file[n]); + goto NOT_FOUND; + } + mp->rd_fname[n] = xstrdup (s); return true; -NOT_FOUND: - mp_end_file_reading(mp); +NOT_FOUND: + mp_end_file_reading (mp); return false; } + @ Open |wr_file[n]| using file name~|s| and update |wr_fname[n]|. @<Declarations@>= -static void mp_open_write_file (MP mp, char *s, readf_index n) ; +static void mp_open_write_file (MP mp, char *s, readf_index n); -@ @c void mp_open_write_file (MP mp,char *s, readf_index n) { - mp_ptr_scan_file(mp, s); +@ @c +void mp_open_write_file (MP mp, char *s, readf_index n) { + mp_ptr_scan_file (mp, s); pack_cur_name; - while ( ! mp_a_open_out(mp, &mp->wr_file[n], (int)(mp_filetype_text+n)) ) - mp_prompt_file_name(mp, "file name for write output",""); - mp->wr_fname[n]=xstrdup(s); + while (!mp_a_open_out (mp, &mp->wr_file[n], (int) (mp_filetype_text + n))) + mp_prompt_file_name (mp, "file name for write output", ""); + mp->wr_fname[n] = xstrdup (s); } -@* \[36] Introduction to the parsing routines. +@* Introduction to the parsing routines. We come now to the central nervous system that sparks many of \MP's activities. By evaluating expressions, from their primary constituents to ever larger subexpressions, \MP\ builds the structures that ultimately define complete @@ -16952,12 +18594,42 @@ Technically speaking, the parsing algorithms are ``LL(1),'' more or less; backup mechanisms have been added in order to provide reasonable error recovery. -@<Glob...@>= -quarterword cur_type; /* the type of the expression just found */ -integer cur_exp; /* the value of the expression just found */ +@d cur_exp_value() mp->cur_exp.data.val +@d cur_exp_node() mp->cur_exp.data.node +@d cur_exp_str() mp->cur_exp.data.str +@d cur_exp_knot() mp->cur_exp.data.p + +@d set_cur_exp_value(A) do { + cur_exp_value() = (A); + cur_exp_node() = NULL; + cur_exp_str() = NULL; + cur_exp_knot() = NULL; + } while (0) +@d set_cur_exp_node(A) do { + cur_exp_node() = A; + cur_exp_str() = NULL; + cur_exp_knot() = NULL; + cur_exp_value() = 0; + } while (0) +@d set_cur_exp_str(A) do { + cur_exp_str() = A; + cur_exp_node() = NULL; + cur_exp_knot() = NULL; + cur_exp_value() = 0; + } while (0) +@d set_cur_exp_knot(A) do { + cur_exp_knot() = A; + cur_exp_node() = NULL; + cur_exp_str() = NULL; + cur_exp_value() = 0; + } while (0) + + +@ @<Glob...@>= +mp_value cur_exp; /* the value of the expression just found */ @ @<Set init...@>= -mp->cur_exp=0; +memset (&mp->cur_exp.data, 0, sizeof (mp_value)); @ Many different kinds of expressions are possible, so it is wise to have precise descriptions of what |cur_type| and |cur_exp| mean in all cases: @@ -17071,7 +18743,7 @@ numerical order. Notice that |cur_type| will never be |mp_numeric_type| or are allowed. Conversely, \MP\ has no variables of type |mp_vacuous| or |token_list|. -@ Capsules are two-word nodes that have a similar meaning +@ Capsules are non-symbolic nodes that have a similar meaning to |cur_type| and |cur_exp|. Such nodes have |name_type=capsule|, and their |type| field is one of the possibilities for |cur_type| listed above. Also |link<=void| in capsules that aren't part of a token list. @@ -17102,33 +18774,42 @@ capsule. It is not used when |cur_type=mp_token_list|. After the operation, |cur_type=mp_vacuous|; hence there is no need to copy path lists or to update reference counts, etc. -The special link |mp_void| is put on the capsule returned by +The special link |MP_VOID| is put on the capsule returned by |stash_cur_exp|, because this procedure is used to store macro parameters that must be easily distinguishable from token lists. @<Declare the stashing/unstashing routines@>= -static pointer mp_stash_cur_exp (MP mp) { - pointer p; /* the capsule that will be returned */ - switch (mp->cur_type) { +static mp_node mp_stash_cur_exp (MP mp) { + mp_node p; /* the capsule that will be returned */ + mp_variable_type exp_type = mp->cur_exp.type; + switch (exp_type) { case unknown_types: case mp_transform_type: case mp_color_type: case mp_pair_type: case mp_dependent: case mp_proto_dependent: - case mp_independent: + case mp_independent: case mp_cmykcolor_type: - p=mp->cur_exp; + p = cur_exp_node (); break; - default: - p=mp_get_node(mp, value_node_size); mp_name_type(p)=mp_capsule; - mp_type(p)=mp->cur_type; value(p)=mp->cur_exp; + /* |case mp_path_type: case mp_pen_type: case mp_string_type:| */ + default: + p = mp_get_value_node (mp); + mp_name_type (p) = mp_capsule; + mp_type (p) = mp->cur_exp.type; + set_value (p, cur_exp_value ()); /* this also resets the rest to 0/NULL */ + str_value (p) = cur_exp_str (); + knot_value (p) = cur_exp_knot (); + value_node (p) = cur_exp_node (); break; } - mp->cur_type=mp_vacuous; mp_link(p)=mp_void; + mp->cur_exp.type = mp_vacuous; + mp_link (p) = MP_VOID; return p; } + @ The inverse of |stash_cur_exp| is the following procedure, which deletes an unnecessary capsule and puts its contents into |cur_type| and |cur_exp|. @@ -17150,31 +18831,45 @@ are alive or dormant. The \\{unstash} procedure assumes that they are dead or dormant; it resuscitates them. @<Declare the stashing/unstashing...@>= -static void mp_unstash_cur_exp (MP mp,pointer p) ; +static void mp_unstash_cur_exp (MP mp, mp_node p); @ @c -void mp_unstash_cur_exp (MP mp,pointer p) { - mp->cur_type=mp_type(p); - switch (mp->cur_type) { +void mp_unstash_cur_exp (MP mp, mp_node p) { + mp->cur_exp.type = mp_type (p); + switch (mp->cur_exp.type) { case unknown_types: case mp_transform_type: case mp_color_type: case mp_pair_type: - case mp_dependent: + case mp_dependent: case mp_proto_dependent: case mp_independent: - case mp_cmykcolor_type: - mp->cur_exp=p; + case mp_cmykcolor_type: + set_cur_exp_node (p); + break; + case mp_path_type: + case mp_pen_type: + set_cur_exp_knot (knot_value (p)); + mp_free_node (mp, p, value_node_size); + break; + case mp_string_type: + set_cur_exp_str (str_value (p)); + mp_free_node (mp, p, value_node_size); + break; + case mp_picture_type: + set_cur_exp_node (value_node (p)); + mp_free_node (mp, p, value_node_size); break; default: - mp->cur_exp=value(p); - mp_free_node(mp, p,value_node_size); + set_cur_exp_value (value (p)); + mp_free_node (mp, p, value_node_size); break; } } + @ The following procedure prints the values of expressions in an -abbreviated format. If its first parameter |p| is null, the value of +abbreviated format. If its first parameter |p| is NULL, the value of |(cur_type,cur_exp)| is displayed; otherwise |p| should be a capsule containing the desired value. The second parameter controls the amount of output. If it is~0, dependency lists will be abbreviated to @@ -17184,216 +18879,428 @@ in full. @.linearform@> @<Declarations@>= -@<Declare the procedure called |print_dp|@> -@<Declare the stashing/unstashing routines@> -static void mp_print_exp (MP mp,pointer p, quarterword verbosity) ; +@<Declare the procedure called |print_dp|@>; +@<Declare the stashing/unstashing routines@>; +static void mp_print_exp (MP mp, mp_node p, quarterword verbosity); @ @c -void mp_print_exp (MP mp,pointer p, quarterword verbosity) { - boolean restore_cur_exp; /* should |cur_exp| be restored? */ - quarterword t; /* the type of the expression */ - pointer q; /* a big node being displayed */ - integer v=0; /* the value of the expression */ - if ( p!=null ) { - restore_cur_exp=false; - } else { - p=mp_stash_cur_exp(mp); restore_cur_exp=true; - } - t=mp_type(p); - if ( t<mp_dependent ) v=value(p); else if ( t<mp_independent ) v=dep_list(p); - @<Print an abbreviated value of |v| with format depending on |t|@>; - if ( restore_cur_exp ) mp_unstash_cur_exp(mp, p); -} - -@ @<Print an abbreviated value of |v| with format depending on |t|@>= +void mp_print_exp (MP mp, mp_node p, quarterword verbosity) { + boolean restore_cur_exp; /* should |cur_exp| be restored? */ + mp_variable_type t; /* the type of the expression */ + integer vv = 0; /* the value of the expression */ + mp_node v = NULL; + if (p != NULL) { + restore_cur_exp = false; + } else { + p = mp_stash_cur_exp (mp); + restore_cur_exp = true; + } + t = mp_type (p); + if (t < mp_dependent) { /* no dep list, could be a capsule */ + if (t != mp_vacuous && t != mp_known && value_node (p) != NULL) + v = value_node (p); + else + vv = value (p); + } else if (t < mp_independent) { + v = (mp_node) dep_list ((mp_value_node) p); + } + @<Print an abbreviated value of |v| or |vv| with format depending on |t|@>; + if (restore_cur_exp) + mp_unstash_cur_exp (mp, p); +} + + +@ @<Print an abbreviated value of |v| or |vv| with format depending on |t|@>= switch (t) { -case mp_vacuous:mp_print(mp, "vacuous"); break; +case mp_vacuous: + mp_print (mp, "vacuous"); + break; case mp_boolean_type: - if ( v==true_code ) mp_print(mp, "true"); else mp_print(mp, "false"); + if (vv == true_code) + mp_print (mp, "true"); + else + mp_print (mp, "false"); break; -case unknown_types: case mp_numeric_type: +case unknown_types: +case mp_numeric_type: @<Display a variable that's been declared but not defined@>; break; case mp_string_type: - mp_print_char(mp, xord('"')); mp_print_str(mp, v); mp_print_char(mp, xord('"')); + mp_print_char (mp, xord ('"')); + mp_print_str (mp, str_value (p)); + mp_print_char (mp, xord ('"')); break; -case mp_pen_type: case mp_path_type: case mp_picture_type: +case mp_pen_type: +case mp_path_type: +case mp_picture_type: @<Display a complex type@>; break; -case mp_transform_type: case mp_color_type: case mp_pair_type: case mp_cmykcolor_type: - if ( v==null ) mp_print_type(mp, t); - else @<Display a big node@>; +case mp_transform_type: + if (vv == 0 && v == NULL) + mp_print_type (mp, t); + else + @<Display a transform node@>; break; -case mp_known:mp_print_scaled(mp, v); break; -case mp_dependent: case mp_proto_dependent: - mp_print_dp(mp, t,v,verbosity); +case mp_color_type: + if (vv == 0 && v == NULL) + mp_print_type (mp, t); + else + @<Display a color node@>; + break; +case mp_pair_type: + if (vv == 0 && v == NULL) + mp_print_type (mp, t); + else + @<Display a pair node@>; + break; +case mp_cmykcolor_type: + if (vv == 0 && v == NULL) + mp_print_type (mp, t); + else + @<Display a cmykcolor node@>; + break; +case mp_known: + mp_print_scaled (mp, vv); + break; +case mp_dependent: +case mp_proto_dependent: + mp_print_dp (mp, t, (mp_value_node) v, verbosity); + break; +case mp_independent: + mp_print_variable_name (mp, p); + break; +default: + mp_confusion (mp, "exp"); break; -case mp_independent:mp_print_variable_name(mp, p); break; -default: mp_confusion(mp, "exp"); break; @:this can't happen exp}{\quad exp@> } -@ @<Display a big node@>= -{ - mp_print_char(mp, xord('(')); q=v+mp->big_node_size[t]; - do { - if ( mp_type(v)==mp_known ) mp_print_scaled(mp, value(v)); - else if ( mp_type(v)==mp_independent ) mp_print_variable_name(mp, v); - else mp_print_dp(mp, mp_type(v),dep_list(v),verbosity); - v=v+2; - if ( v!=q ) mp_print_char(mp, xord(',')); - } while (v!=q); - mp_print_char(mp, xord(')')); + +@ @<Display big node item |v|@>= +{ + if (mp_type (v) == mp_known) + mp_print_scaled (mp, value (v)); + else if (mp_type (v) == mp_independent) + mp_print_variable_name (mp, v); + else + mp_print_dp (mp, mp_type (v), (mp_value_node) dep_list ((mp_value_node) v), + verbosity); +} + + +@ In these cases, |v| starts as the big node. + +@<Display a pair node@>= +{ + mp_node vvv = v; + mp_print_char (mp, xord ('(')); + v = x_part_loc (vvv); + @<Display big node item |v|@>; + mp_print_char (mp, xord (',')); + v = y_part_loc (vvv); + @<Display big node item |v|@>; + mp_print_char (mp, xord (')')); +} + + +@ @<Display a transform node@>= +{ + mp_node vvv = v; + mp_print_char (mp, xord ('(')); + v = tx_part_loc (vvv); + @<Display big node item |v|@>; + mp_print_char (mp, xord (',')); + v = ty_part_loc (vvv); + @<Display big node item |v|@>; + mp_print_char (mp, xord (',')); + v = xx_part_loc (vvv); + @<Display big node item |v|@>; + mp_print_char (mp, xord (',')); + v = xy_part_loc (vvv); + @<Display big node item |v|@>; + mp_print_char (mp, xord (',')); + v = yx_part_loc (vvv); + @<Display big node item |v|@>; + mp_print_char (mp, xord (',')); + v = yy_part_loc (vvv); + @<Display big node item |v|@>; + mp_print_char (mp, xord (')')); +} + + +@ @<Display a color node@>= +{ + mp_node vvv = v; + mp_print_char (mp, xord ('(')); + v = red_part_loc (vvv); + @<Display big node item |v|@>; + mp_print_char (mp, xord (',')); + v = green_part_loc (vvv); + @<Display big node item |v|@>; + mp_print_char (mp, xord (',')); + v = blue_part_loc (vvv); + @<Display big node item |v|@>; + mp_print_char (mp, xord (')')); } + +@ @<Display a cmykcolor node@>= +{ + mp_node vvv = v; + mp_print_char (mp, xord ('(')); + v = cyan_part_loc (vvv); + @<Display big node item |v|@>; + mp_print_char (mp, xord (',')); + v = magenta_part_loc (vvv); + @<Display big node item |v|@>; + mp_print_char (mp, xord (',')); + v = yellow_part_loc (vvv); + @<Display big node item |v|@>; + mp_print_char (mp, xord (',')); + v = black_part_loc (vvv); + @<Display big node item |v|@>; + mp_print_char (mp, xord (')')); +} + + @ Values of type \&{picture}, \&{path}, and \&{pen} are displayed verbosely in the log file only, unless the user has given a positive value to \\{tracingonline}. @<Display a complex type@>= -if ( verbosity<=1 ) { - mp_print_type(mp, t); -} else { - if ( mp->selector==term_and_log ) - if ( mp->internal[mp_tracing_online]<=0 ) { - mp->selector=term_only; - mp_print_type(mp, t); mp_print(mp, " (see the transcript file)"); - mp->selector=term_and_log; - }; +if (verbosity <= 1) { + mp_print_type (mp, t); +} else { + if (mp->selector == term_and_log) + if (internal_value (mp_tracing_online) <= 0) { + mp->selector = term_only; + mp_print_type (mp, t); + mp_print (mp, " (see the transcript file)"); + mp->selector = term_and_log; + }; switch (t) { - case mp_pen_type:mp_print_pen(mp, v,"",false); break; - case mp_path_type:mp_print_path(mp, v,"",false); break; - case mp_picture_type:mp_print_edges(mp, v,"",false); break; - } /* there are no other cases */ + case mp_pen_type: + mp_print_pen (mp, knot_value (p), "", false); + break; + case mp_path_type: + mp_print_path (mp, knot_value (p), "", false); + break; + case mp_picture_type: + mp_print_edges (mp, v, "", false); + break; + default: + break; + } } + @ @<Declare the procedure called |print_dp|@>= -static void mp_print_dp (MP mp, quarterword t, pointer p, - quarterword verbosity) { - pointer q; /* the node following |p| */ - q=mp_link(p); - if ( (mp_info(q)==null) || (verbosity>0) ) mp_print_dependency(mp, p,t); - else mp_print(mp, "linearform"); +static void mp_print_dp (MP mp, quarterword t, mp_value_node p, + quarterword verbosity) { + mp_value_node q; /* the node following |p| */ + q = (mp_value_node) mp_link (p); + if ((dep_info (q) == NULL) || (verbosity > 0)) + mp_print_dependency (mp, p, t); + else + mp_print (mp, "linearform"); } + @ The displayed name of a variable in a ring will not be a capsule unless the ring consists entirely of capsules. @<Display a variable that's been declared but not defined@>= -{ mp_print_type(mp, t); -if ( v!=null ) - { mp_print_char(mp, xord(' ')); - while ( (mp_name_type(v)==mp_capsule) && (v!=p) ) v=value(v); - mp_print_variable_name(mp, v); +{ + mp_print_type (mp, t); + if (v != NULL) { + mp_print_char (mp, xord (' ')); + while ((mp_name_type (v) == mp_capsule) && (v != p)) + v = value_node (v); + mp_print_variable_name (mp, v); }; } + @ When errors are detected during parsing, it is often helpful to display an expression just above the error message, using |exp_err| or |disp_err| instead of |print_err|. -@d exp_err(A) mp_disp_err(mp, null,(A)) /* displays the current expression */ +@d exp_err(A) mp_disp_err(mp, NULL,(A)) /* displays the current expression */ @<Declarations@>= -static void mp_disp_err (MP mp,pointer p, const char *s) ; +static void mp_disp_err (MP mp, mp_node p, const char *s); @ @c -void mp_disp_err (MP mp,pointer p, const char *s) { - if ( mp->interaction==mp_error_stop_mode ) wake_up_terminal; - mp_print_nl(mp, ">> "); -@.>>@> - mp_print_exp(mp, p,1); /* ``medium verbose'' printing of the expression */ - if (strlen(s)>0) { - print_err(s); +void mp_disp_err (MP mp, mp_node p, const char *s) { + if (mp->interaction == mp_error_stop_mode) + wake_up_terminal; + mp_print_nl (mp, ">> "); +@.>>@>; + mp_print_exp (mp, p, 1); /* ``medium verbose'' printing of the expression */ + if (strlen (s) > 0) { + print_err (s); } } + @ If |cur_type| and |cur_exp| contain relevant information that should be recycled, we will use the following procedure, which changes |cur_type| to |known| and stores a given value in |cur_exp|. We can think of |cur_type| and |cur_exp| as either alive or dormant after this has been done, because |cur_exp| will not contain a pointer value. -@ @c -static void mp_flush_cur_exp (MP mp,scaled v) { - switch (mp->cur_type) { - case unknown_types: case mp_transform_type: case mp_color_type: case mp_pair_type: - case mp_dependent: case mp_proto_dependent: case mp_independent: case mp_cmykcolor_type: - mp_recycle_value(mp, mp->cur_exp); - mp_free_node(mp, mp->cur_exp,value_node_size); +@ @c +static void mp_flush_cur_exp (MP mp, mp_value v) { + switch (mp->cur_exp.type) { + case unknown_types: + case mp_transform_type: + case mp_color_type: + case mp_pair_type: + case mp_dependent: + case mp_proto_dependent: + case mp_independent: + case mp_cmykcolor_type: + mp_recycle_value (mp, cur_exp_node ()); + mp_free_node (mp, cur_exp_node (), value_node_size); break; case mp_string_type: - delete_str_ref(mp->cur_exp); break; - case mp_pen_type: case mp_path_type: - mp_toss_knot_list(mp, mp->cur_exp); break; + delete_str_ref (cur_exp_str ()); + break; + case mp_pen_type: + case mp_path_type: + mp_toss_knot_list (mp, cur_exp_knot ()); + break; case mp_picture_type: - delete_edge_ref(mp->cur_exp); break; - default: + delete_edge_ref (cur_exp_node ()); + break; + default: break; } - mp->cur_type=mp_known; mp->cur_exp=v; + mp->cur_exp = v; + mp->cur_exp.type = mp_known; } + @ There's a much more general procedure that is capable of releasing -the storage associated with any two-word value packet. +the storage associated with any non-symbolic value packet. @<Declarations@>= -static void mp_recycle_value (MP mp,pointer p) ; - -@ @c -static void mp_recycle_value (MP mp,pointer p) { - quarterword t; /* a type code */ - integer vv; /* another value */ - pointer q,r,s,pp; /* link manipulation registers */ - integer v=0; /* a value */ - t=mp_type(p); - if ( t<mp_dependent ) v=value(p); +static void mp_recycle_value (MP mp, mp_node p); + +@ @c +static void mp_recycle_value (MP mp, mp_node p) { + mp_variable_type t; /* a type code */ + integer vv; /* another value */ + mp_node pp; /* link manipulation register */ + integer v = 0; /* a value */ + FUNCTION_TRACE2 ("mp_recycle_value(%p)\n", p); + t = mp_type (p); + if (t < mp_dependent) + v = value (p); switch (t) { - case undefined: case mp_vacuous: case mp_boolean_type: case mp_known: + case mp_vacuous: + case mp_boolean_type: + case mp_known: case mp_numeric_type: break; case unknown_types: - mp_ring_delete(mp, p); break; + mp_ring_delete (mp, p); + break; case mp_string_type: - delete_str_ref(v); break; - case mp_path_type: case mp_pen_type: - mp_toss_knot_list(mp, v); break; + delete_str_ref (str_value (p)); + break; + case mp_path_type: + case mp_pen_type: + mp_toss_knot_list (mp, knot_value (p)); + break; case mp_picture_type: - delete_edge_ref(v); break; - case mp_cmykcolor_type: case mp_pair_type: case mp_color_type: + delete_edge_ref (value_node (p)); + break; + case mp_cmykcolor_type: + if (value_node (p) != NULL) { + mp_recycle_value (mp, cyan_part_loc (value_node (p))); + mp_recycle_value (mp, magenta_part_loc (value_node (p))); + mp_recycle_value (mp, yellow_part_loc (value_node (p))); + mp_recycle_value (mp, black_part_loc (value_node (p))); + mp_free_node (mp, cyan_part_loc (value_node (p)), value_node_size); + mp_free_node (mp, magenta_part_loc (value_node (p)), value_node_size); + mp_free_node (mp, black_part_loc (value_node (p)), value_node_size); + mp_free_node (mp, yellow_part_loc (value_node (p)), value_node_size); + mp_free_node (mp, value_node (p), cmykcolor_node_size); + } + break; + case mp_pair_type: + if (value_node (p) != NULL) { + mp_recycle_value (mp, x_part_loc (value_node (p))); + mp_recycle_value (mp, y_part_loc (value_node (p))); + mp_free_node (mp, x_part_loc (value_node (p)), value_node_size); + mp_free_node (mp, y_part_loc (value_node (p)), value_node_size); + mp_free_node (mp, value_node (p), pair_node_size); + } + break; + case mp_color_type: + if (value_node (p) != NULL) { + mp_recycle_value (mp, red_part_loc (value_node (p))); + mp_recycle_value (mp, green_part_loc (value_node (p))); + mp_recycle_value (mp, blue_part_loc (value_node (p))); + mp_free_node (mp, red_part_loc (value_node (p)), value_node_size); + mp_free_node (mp, green_part_loc (value_node (p)), value_node_size); + mp_free_node (mp, blue_part_loc (value_node (p)), value_node_size); + mp_free_node (mp, value_node (p), color_node_size); + } + break; case mp_transform_type: - @<Recycle a big node@>; break; - case mp_dependent: case mp_proto_dependent: - @<Recycle a dependency list@>; break; + if (value_node (p) != NULL) { + mp_recycle_value (mp, tx_part_loc (value_node (p))); + mp_recycle_value (mp, ty_part_loc (value_node (p))); + mp_recycle_value (mp, xx_part_loc (value_node (p))); + mp_recycle_value (mp, xy_part_loc (value_node (p))); + mp_recycle_value (mp, yx_part_loc (value_node (p))); + mp_recycle_value (mp, yy_part_loc (value_node (p))); + mp_free_node (mp, tx_part_loc (value_node (p)), value_node_size); + mp_free_node (mp, ty_part_loc (value_node (p)), value_node_size); + mp_free_node (mp, xx_part_loc (value_node (p)), value_node_size); + mp_free_node (mp, xy_part_loc (value_node (p)), value_node_size); + mp_free_node (mp, yx_part_loc (value_node (p)), value_node_size); + mp_free_node (mp, yy_part_loc (value_node (p)), value_node_size); + mp_free_node (mp, value_node (p), transform_node_size); + } + break; + case mp_dependent: + case mp_proto_dependent: + @<Recycle a dependency list@>; + break; case mp_independent: - @<Recycle an independent variable@>; break; - case mp_token_list: case mp_structured: - mp_confusion(mp, "recycle"); break; + @<Recycle an independent variable@>; + break; + case mp_token_list: + case mp_structured: + mp_confusion (mp, "recycle"); + break; @:this can't happen recycle}{\quad recycle@> - case mp_unsuffixed_macro: case mp_suffixed_macro: - mp_delete_mac_ref(mp, value(p)); break; - } /* there are no other cases */ - mp_type(p)=undefined; + case mp_unsuffixed_macro: + case mp_suffixed_macro: + mp_delete_mac_ref (mp, value_node (p)); + break; + default: /* there are no other valid cases, but please the compiler */ + break; + } /* there are no other cases */ + mp_type (p) = undefined; } -@ @<Recycle a big node@>= -if ( v!=null ){ - q=v+mp->big_node_size[t]; - do { - q=q-2; mp_recycle_value(mp, q); - } while (q!=v); - mp_free_node(mp, v,mp->big_node_size[t]); -} @ @<Recycle a dependency list@>= -{ - q=dep_list(p); - while ( mp_info(q)!=null ) q=mp_link(q); - mp_link(prev_dep(p))=mp_link(q); - prev_dep(mp_link(q))=prev_dep(p); - mp_link(q)=null; mp_flush_node_list(mp, dep_list(p)); +{ + mp_value_node qq = (mp_value_node) dep_list ((mp_value_node) p); + while (dep_info (qq) != NULL) + qq = (mp_value_node) mp_link (qq); + set_mp_link (prev_dep ((mp_value_node) p), mp_link (qq)); + set_prev_dep (mp_link (qq), prev_dep ((mp_value_node) p)); + set_mp_link (qq, NULL); + mp_flush_node_list (mp, (mp_node) dep_list ((mp_value_node) p)); } + @ When an independent variable disappears, it simply fades away, unless something depends on it. In the latter case, a dependent variable whose coefficient of dependence is maximal will take its place. @@ -17424,71 +19331,87 @@ dependencies, maintaining separate lists for the dependent and proto-dependent cases. @<Recycle an independent variable@>= -{ - mp->max_c[mp_dependent]=0; mp->max_c[mp_proto_dependent]=0; - mp->max_link[mp_dependent]=null; mp->max_link[mp_proto_dependent]=null; - q=mp_link(dep_head); - while ( q!=dep_head ) { - s=value_loc(q); /* now |mp_link(s)=dep_list(q)| */ - while (1) { - r=mp_link(s); - if ( mp_info(r)==null ) break; - if ( mp_info(r)!=p ) { - s=r; - } else { - t=mp_type(q); mp_link(s)=mp_link(r); mp_info(r)=q; - if ( abs(value(r))>mp->max_c[t] ) { - @<Record a new maximum coefficient of type |t|@>; - } else { - mp_link(r)=mp->max_link[t]; mp->max_link[t]=r; +{ + mp_value_node q, r, s; + mp->max_c[mp_dependent] = 0; + mp->max_c[mp_proto_dependent] = 0; + mp->max_link[mp_dependent] = NULL; + mp->max_link[mp_proto_dependent] = NULL; + q = (mp_value_node) mp_link (mp->dep_head); + while (q != mp->dep_head) { + s = (mp_value_node) mp->temp_head; + set_mp_link (s, dep_list (q)); + while (1) { + r = (mp_value_node) mp_link (s); + if (dep_info (r) == NULL) + break; + if (dep_info (r) != p) { + s = r; + } else { + t = mp_type (q); + if (mp_link (s) == dep_list (q)) { /* reset the |dep_list| */ + set_dep_list (q, mp_link (r)); + } + set_mp_link (s, mp_link (r)); + set_dep_info (r, (mp_node) q); + if (abs (dep_value (r)) > mp->max_c[t]) { + /* Record a new maximum coefficient of type |t| */ + if (mp->max_c[t] > 0) { + set_mp_link (mp->max_ptr[t], (mp_node) mp->max_link[t]); + mp->max_link[t] = mp->max_ptr[t]; + } + mp->max_c[t] = abs (dep_value (r)); + mp->max_ptr[t] = r; + } else { + set_mp_link (r, (mp_node) mp->max_link[t]); + mp->max_link[t] = r; } } - } - q=mp_link(r); + } + q = (mp_value_node) mp_link (r); } - if ( (mp->max_c[mp_dependent]>0)||(mp->max_c[mp_proto_dependent]>0) ) { + if ((mp->max_c[mp_dependent] > 0) || (mp->max_c[mp_proto_dependent] > 0)) { @<Choose a dependent variable to take the place of the disappearing independent variable, and change all remaining dependencies accordingly@>; } } -@ The code for independency removal makes use of three two-word arrays. -@<Glob...@>= -integer max_c[mp_proto_dependent+1]; /* max coefficient magnitude */ -pointer max_ptr[mp_proto_dependent+1]; /* where |p| occurs with |max_c| */ -pointer max_link[mp_proto_dependent+1]; /* other occurrences of |p| */ +@ The code for independency removal makes use of three non-symbolic arrays. -@ @<Record a new maximum coefficient...@>= -{ - if ( mp->max_c[t]>0 ) { - mp_link(mp->max_ptr[t])=mp->max_link[t]; mp->max_link[t]=mp->max_ptr[t]; - } - mp->max_c[t]=abs(value(r)); mp->max_ptr[t]=r; -} +@<Glob...@>= +integer max_c[mp_proto_dependent + 1]; /* max coefficient magnitude */ +mp_value_node max_ptr[mp_proto_dependent + 1]; /* where |p| occurs with |max_c| */ +mp_value_node max_link[mp_proto_dependent + 1]; /* other occurrences of |p| */ @ @<Choose a dependent...@>= -{ - if ( (mp->max_c[mp_dependent] / 010000) >= mp->max_c[mp_proto_dependent] ) - t=mp_dependent; - else - t=mp_proto_dependent; +{ + if ((mp->max_c[mp_dependent] / 010000) >= mp->max_c[mp_proto_dependent]) + t = mp_dependent; + else + t = mp_proto_dependent; @<Determine the dependency list |s| to substitute for the independent variable~|p|@>; - t=(quarterword)(mp_dependent+mp_proto_dependent-t); /* complement |t| */ - if ( mp->max_c[t]>0 ) { /* we need to pick up an unchosen dependency */ - mp_link(mp->max_ptr[t])=mp->max_link[t]; mp->max_link[t]=mp->max_ptr[t]; - } - if ( t!=mp_dependent ) { @<Substitute new dependencies in place of |p|@>; } - else { @<Substitute new proto-dependencies in place of |p|@>;} - mp_flush_node_list(mp, s); - if ( mp->fix_needed ) mp_fix_dependencies(mp); + t = (quarterword) (mp_dependent + mp_proto_dependent - t); /* complement |t| */ + if (mp->max_c[t] > 0) { /* we need to pick up an unchosen dependency */ + set_mp_link (mp->max_ptr[t], (mp_node) mp->max_link[t]); + mp->max_link[t] = mp->max_ptr[t]; + } + if (t != mp_dependent) { + @<Substitute new dependencies in place of |p|@>; + } else { + @<Substitute new proto-dependencies in place of |p|@>; + } + mp_flush_node_list (mp, (mp_node) s); + if (mp->fix_needed) + mp_fix_dependencies (mp); check_arith; } + @ Let |s=max_ptr[t]|. At this point we have $|value|(s)=\pm|max_c|[t]$, -and |mp_info(s)| points to the dependent variable~|pp| of type~|t| from +and |dep_info(s)| points to the dependent variable~|pp| of type~|t| from whose dependency list we have removed node~|s|. We must reinsert node~|s| into the dependency list, with coefficient $-1.0$, and with |pp| as the new independent variable. Since |pp| will have a larger serial @@ -17496,75 +19419,109 @@ number than any other variable, we can put node |s| at the head of the list. @<Determine the dep...@>= -s=mp->max_ptr[t]; pp=mp_info(s); v=value(s); -if ( t==mp_dependent ) value(s)=-fraction_one; else value(s)=-unity; -r=dep_list(pp); mp_link(s)=r; -while ( mp_info(r)!=null ) r=mp_link(r); -q=mp_link(r); mp_link(r)=null; -prev_dep(q)=prev_dep(pp); mp_link(prev_dep(pp))=q; -new_indep(pp); -if ( mp->cur_exp==pp ) if ( mp->cur_type==t ) mp->cur_type=mp_independent; -if ( mp->internal[mp_tracing_equations]>0 ) { - @<Show the transformed dependency@>; +s = mp->max_ptr[t]; +pp = (mp_node) dep_info (s); +/* |debug_printf ("s=%p, pp=%p, r=%p\n",s, pp, dep_list((mp_value_node)pp));| */ +v = dep_value (s); +if (t == mp_dependent) + set_dep_value (s, -fraction_one); +else + set_dep_value (s, -unity); +r = (mp_value_node) dep_list ((mp_value_node) pp); +set_mp_link (s, (mp_node) r); +while (dep_info (r) != NULL) + r = (mp_value_node) mp_link (r); +q = (mp_value_node) mp_link (r); +set_mp_link (r, NULL); +set_prev_dep (q, prev_dep ((mp_value_node) pp)); +set_mp_link (prev_dep ((mp_value_node) pp), (mp_node) q); +new_indep ((mp_value_node) pp); +if (cur_exp_node () == pp && mp->cur_exp.type == t) + mp->cur_exp.type = mp_independent; +if (internal_value (mp_tracing_equations) > 0) { + @<Show the transformed dependency@>; } @ Now $(-v)$ times the formerly independent variable~|p| is being replaced by the dependency list~|s|. @<Show the transformed...@>= -if ( mp_interesting(mp, p) ) { - mp_begin_diagnostic(mp); mp_print_nl(mp, "### "); -@:]]]\#\#\#_}{\.{\#\#\#}@> - if ( v>0 ) mp_print_char(mp, xord('-')); - if ( t==mp_dependent ) vv=mp_round_fraction(mp, mp->max_c[mp_dependent]); - else vv=mp->max_c[mp_proto_dependent]; - if ( vv!=unity ) mp_print_scaled(mp, vv); - mp_print_variable_name(mp, p); - while ( value(p) % s_scale>0 ) { - mp_print(mp, "*4"); value(p)=value(p)-2; - } - if ( t==mp_dependent ) mp_print_char(mp, xord('=')); else mp_print(mp, " = "); - mp_print_dependency(mp, s,t); - mp_end_diagnostic(mp, false); +if (mp_interesting (mp, p)) { + mp_begin_diagnostic (mp); + mp_print_nl (mp, "### "); +@:]]]\#\#\#_}{\.{\#\#\#}@>; + if (v > 0) + mp_print_char (mp, xord ('-')); + if (t == mp_dependent) + vv = mp_round_fraction (mp, mp->max_c[mp_dependent]); + else + vv = mp->max_c[mp_proto_dependent]; + if (vv != unity) + mp_print_scaled (mp, vv); + mp_print_variable_name (mp, p); + while (indep_scale (p) > 0) { + mp_print (mp, "*4"); + set_indep_scale(p, indep_scale(p)-2); + } + if (t == mp_dependent) + mp_print_char (mp, xord ('=')); + else + mp_print (mp, " = "); + mp_print_dependency (mp, s, t); + mp_end_diagnostic (mp, false); } @ Finally, there are dependent and proto-dependent variables whose dependency lists must be brought up to date. @<Substitute new dependencies...@>= -for (t=mp_dependent;t<=mp_proto_dependent;t++){ - r=mp->max_link[t]; - while ( r!=null ) { - q=mp_info(r); - dep_list(q)=mp_p_plus_fq(mp, dep_list(q), - mp_make_fraction(mp, value(r),-v),s,t,mp_dependent); - if ( dep_list(q)==mp->dep_final ) mp_make_known(mp, q,mp->dep_final); - q=r; r=mp_link(r); mp_free_node(mp, q,dep_node_size); +for (t = mp_dependent; t <= mp_proto_dependent; t++) { + r = mp->max_link[t]; + while (r != NULL) { + q = (mp_value_node) dep_info (r); + set_dep_list (q, (mp_node) mp_p_plus_fq (mp, (mp_value_node) dep_list (q), + mp_make_fraction (mp, + dep_value (r), + -v), s, t, + mp_dependent)); + if (dep_list (q) == (mp_node) mp->dep_final) + mp_make_known (mp, q, mp->dep_final); + q = r; + r = (mp_value_node) mp_link (r); + mp_free_dep_node (mp, q); } } + @ @<Substitute new proto...@>= -for (t=mp_dependent;t<=mp_proto_dependent;t++) { - r=mp->max_link[t]; - while ( r!=null ) { - q=mp_info(r); - if ( t==mp_dependent ) { /* for safety's sake, we change |q| to |mp_proto_dependent| */ - if ( mp->cur_exp==q ) if ( mp->cur_type==mp_dependent ) - mp->cur_type=mp_proto_dependent; - dep_list(q)=mp_p_over_v(mp, dep_list(q),unity, - mp_dependent,mp_proto_dependent); - mp_type(q)=mp_proto_dependent; - value(r)=mp_round_fraction(mp, value(r)); +for (t = mp_dependent; t <= mp_proto_dependent; t++) { + r = mp->max_link[t]; + while (r != NULL) { + q = (mp_value_node) dep_info (r); + if (t == mp_dependent) { /* for safety's sake, we change |q| to |mp_proto_dependent| */ + if (cur_exp_node () == (mp_node) q && mp->cur_exp.type == mp_dependent) + mp->cur_exp.type = mp_proto_dependent; + set_dep_list (q, + (mp_node) mp_p_over_v (mp, (mp_value_node) dep_list (q), + unity, mp_dependent, + mp_proto_dependent)); + mp_type (q) = mp_proto_dependent; + set_dep_value (r, mp_round_fraction (mp, dep_value (r))); } - dep_list(q)=mp_p_plus_fq(mp, dep_list(q), - mp_make_scaled(mp, value(r),-v),s, - mp_proto_dependent,mp_proto_dependent); - if ( dep_list(q)==mp->dep_final ) - mp_make_known(mp, q,mp->dep_final); - q=r; r=mp_link(r); mp_free_node(mp, q,dep_node_size); + set_dep_list (q, (mp_node) mp_p_plus_fq (mp, (mp_value_node) dep_list (q), + mp_make_scaled (mp, dep_value (r), + -v), s, + mp_proto_dependent, + mp_proto_dependent)); + if (dep_list (q) == (mp_node) mp->dep_final) + mp_make_known (mp, q, mp->dep_final); + q = r; + r = (mp_value_node) mp_link (r); + mp_free_dep_node (mp, q); } } + @ Here are some routines that provide handy combinations of actions that are often needed during error recovery. For example, `|flush_error|' flushes the current expression, replaces it by @@ -17576,22 +19533,25 @@ then they get it back again. (Or perhaps they get another token, if the user has changed things.) @<Declarations@>= -static void mp_flush_error (MP mp,scaled v); +static void mp_flush_error (MP mp, mp_value v); static void mp_put_get_error (MP mp); -static void mp_put_get_flush_error (MP mp,scaled v) ; +static void mp_put_get_flush_error (MP mp, mp_value v); @ @c -void mp_flush_error (MP mp,scaled v) { - mp_error(mp); mp_flush_cur_exp(mp, v); +void mp_flush_error (MP mp, mp_value v) { + mp_error (mp); + mp_flush_cur_exp (mp, v); } -void mp_put_get_error (MP mp) { - mp_back_error(mp); mp_get_x_next(mp); +void mp_put_get_error (MP mp) { + mp_back_error (mp); + mp_get_x_next (mp); } -void mp_put_get_flush_error (MP mp,scaled v) { - mp_put_get_error(mp); - mp_flush_cur_exp(mp, v); +void mp_put_get_flush_error (MP mp, mp_value v) { + mp_put_get_error (mp); + mp_flush_cur_exp (mp, v); } + @ A global variable |var_flag| is set to a special command code just before \MP\ calls |scan_expression|, if the expression should be treated as a variable when this command code immediately follows. For @@ -17612,12 +19572,12 @@ that produces boolean expressions. @<Glob...@>= -int var_flag; /* command that wants a variable */ +int var_flag; /* command that wants a variable */ @ @<Set init...@>= -mp->var_flag=0; +mp->var_flag = 0; -@* \[37] Parsing primary expressions. +@* Parsing primary expressions. The first parsing routine, |scan_primary|, is also the most complicated one, since it involves so many different cases. But each case---with one exception---is fairly simple by itself. @@ -17630,238 +19590,295 @@ earlier. If |cur_cmd| is not between |min_primary_command| and @<Declare the basic parsing subroutines@>= void mp_scan_primary (MP mp) { - pointer p,q,r; /* for list manipulation */ - quarterword c; /* a primitive operation code */ - int my_var_flag; /* initial value of |my_var_flag| */ - pointer l_delim,r_delim; /* hash addresses of a delimiter pair */ + mp_node p, q, r; /* for list manipulation */ + quarterword c; /* a primitive operation code */ + int my_var_flag; /* initial value of |my_var_flag| */ + mp_sym l_delim, r_delim; /* hash addresses of a delimiter pair */ + mp_value new_expr; @<Other local variables for |scan_primary|@>; - my_var_flag=mp->var_flag; mp->var_flag=0; + memset(&new_expr,0,sizeof(mp_value)); + my_var_flag = mp->var_flag; + mp->var_flag = 0; RESTART: check_arith; @<Supply diagnostic information, if requested@>; switch (mp->cur_cmd) { case left_delimiter: - @<Scan a delimited primary@>; break; + @<Scan a delimited primary@>; + break; case begin_group: - @<Scan a grouped primary@>; break; + @<Scan a grouped primary@>; + break; case string_token: - @<Scan a string constant@>; break; + @<Scan a string constant@>; + break; case numeric_token: - @<Scan a primary that starts with a numeric token@>; break; + @<Scan a primary that starts with a numeric token@>; + break; case nullary: - @<Scan a nullary operation@>; break; - case unary: case type_name: case cycle: case plus_or_minus: - @<Scan a unary operation@>; break; + @<Scan a nullary operation@>; + break; + case unary: + case type_name: + case cycle: + case plus_or_minus: + @<Scan a unary operation@>; + break; case primary_binary: - @<Scan a binary operation with `\&{of}' between its operands@>; break; + @<Scan a binary operation with `\&{of}' between its operands@>; + break; case str_op: - @<Convert a suffix to a string@>; break; + @<Convert a suffix to a string@>; + break; case internal_quantity: - @<Scan an internal numeric quantity@>; break; + @<Scan an internal numeric quantity@>; + break; case capsule_token: - mp_make_exp_copy(mp, mp->cur_mod); break; + mp_make_exp_copy (mp, mp->cur_mod_node); + break; case tag_token: - @<Scan a variable primary; |goto restart| if it turns out to be a macro@>; break; - default: - mp_bad_exp(mp, "A primary"); goto RESTART; break; + @<Scan a variable primary; |goto restart| if it turns out to be a macro@>; + break; + default: + mp_bad_exp (mp, "A primary"); + goto RESTART; + break; @.A primary expression...@> } - mp_get_x_next(mp); /* the routines |goto done| if they don't want this */ -DONE: - if ( mp->cur_cmd==left_bracket ) { - if ( mp->cur_type>=mp_known ) { + mp_get_x_next (mp); /* the routines |goto done| if they don't want this */ +DONE: + if (mp->cur_cmd == left_bracket) { + if (mp->cur_exp.type >= mp_known) { @<Scan a mediation construction@>; } } } - @ Errors at the beginning of expressions are flagged by |bad_exp|. -@c -static void mp_bad_exp (MP mp, const char * s) { +@c +static void mp_bad_exp (MP mp, const char *s) { int save_flag; - print_err(s); mp_print(mp, " expression can't begin with `"); - mp_print_cmd_mod(mp, mp->cur_cmd,mp->cur_mod); - mp_print_char(mp, xord('\'')); - help4("I'm afraid I need some sort of value in order to continue,", - "so I've tentatively inserted `0'. You may want to", - "delete this zero and insert something else;", - "see Chapter 27 of The METAFONTbook for an example."); -@:METAFONTbook}{\sl The {\logos METAFONT\/}book@> - mp_back_input(mp); mp->cur_sym=0; mp->cur_cmd=numeric_token; - mp->cur_mod=0; mp_ins_error(mp); - save_flag=mp->var_flag; mp->var_flag=0; mp_get_x_next(mp); - mp->var_flag=save_flag; + print_err (s); + mp_print (mp, " expression can't begin with `"); + mp_print_cmd_mod (mp, mp->cur_cmd, mp->cur_mod); + mp_print_char (mp, xord ('\'')); + help4 ("I'm afraid I need some sort of value in order to continue,", + "so I've tentatively inserted `0'. You may want to", + "delete this zero and insert something else;", + "see Chapter 27 of The METAFONTbook for an example."); +@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>; + mp_back_input (mp); + mp->cur_sym = 0; + mp->cur_cmd = numeric_token; + mp->cur_mod = 0; + mp_ins_error (mp); + save_flag = mp->var_flag; + mp->var_flag = 0; + mp_get_x_next (mp); + mp->var_flag = save_flag; } + @ @<Supply diagnostic information, if requested@>= -#ifdef DEBUG -if ( mp->panicking ) mp_check_mem(mp, false); -#endif -if ( mp->interrupt!=0 ) if ( mp->OK_to_interrupt ) { - mp_back_input(mp); check_interrupt; mp_get_x_next(mp); -} +if (mp->interrupt != 0) + if (mp->OK_to_interrupt) { + mp_back_input (mp); + check_interrupt; + mp_get_x_next (mp); + } @ @<Scan a delimited primary@>= -{ - l_delim=mp->cur_sym; r_delim=mp->cur_mod; - mp_get_x_next(mp); mp_scan_expression(mp); - if ( (mp->cur_cmd==comma) && (mp->cur_type>=mp_known) ) { +{ + l_delim = mp->cur_sym; + r_delim = mp->cur_sym2; + mp_get_x_next (mp); + mp_scan_expression (mp); + if ((mp->cur_cmd == comma) && (mp->cur_exp.type >= mp_known)) { @<Scan the rest of a delimited set of numerics@>; } else { - mp_check_delimiter(mp, l_delim,r_delim); + mp_check_delimiter (mp, l_delim, r_delim); } } + @ The |stash_in| subroutine puts the current (numeric) expression into a field within a ``big node.'' -@c -static void mp_stash_in (MP mp,pointer p) { - pointer q; /* temporary register */ - mp_type(p)=mp->cur_type; - if ( mp->cur_type==mp_known ) { - value(p)=mp->cur_exp; - } else { - if ( mp->cur_type==mp_independent ) { +@c +static void mp_stash_in (MP mp, mp_node p) { + mp_value_node q; /* temporary register */ + mp_type (p) = mp->cur_exp.type; + if (mp->cur_exp.type == mp_known) { + set_value (p, cur_exp_value ()); + } else { + if (mp->cur_exp.type == mp_independent) { @<Stash an independent |cur_exp| into a big node@>; - } else { - mp->mem[value_loc(p)]=mp->mem[value_loc(mp->cur_exp)]; - /* |dep_list(p):=dep_list(cur_exp)| and |prev_dep(p):=prev_dep(cur_exp)| */ - mp_link(prev_dep(p))=p; + mp_free_node (mp, cur_exp_node (), value_node_size); + } else { + set_dep_list ((mp_value_node) p, + dep_list ((mp_value_node) cur_exp_node ())); + set_prev_dep ((mp_value_node) p, + prev_dep ((mp_value_node) cur_exp_node ())); + set_mp_link (prev_dep ((mp_value_node) p), p); + mp_free_dep_node (mp, (mp_value_node) cur_exp_node ()); } - mp_free_node(mp, mp->cur_exp,value_node_size); } - mp->cur_type=mp_vacuous; + mp->cur_exp.type = mp_vacuous; } + @ In rare cases the current expression can become |independent|. There may be many dependency lists pointing to such an independent capsule, so we can't simply move it into place within a big node. Instead, we copy it, then recycle it. @ @<Stash an independent |cur_exp|...@>= -{ - q=mp_single_dependency(mp, mp->cur_exp); - if ( q==mp->dep_final ){ - mp_type(p)=mp_known; value(p)=0; mp_free_node(mp, q,dep_node_size); - } else { - mp_type(p)=mp_dependent; mp_new_dep(mp, p,q); +{ + q = mp_single_dependency (mp, cur_exp_node ()); + if (q == mp->dep_final) { + mp_type (p) = mp_known; + set_value (p, 0); + mp_free_dep_node (mp, q); + } else { + mp_new_dep (mp, p, mp_dependent, q); } - mp_recycle_value(mp, mp->cur_exp); + mp_recycle_value (mp, cur_exp_node ()); } + @ This code uses the fact that |red_part_loc| and |green_part_loc| are synonymous with |x_part_loc| and |y_part_loc|. @<Scan the rest of a delimited set of numerics@>= -{ -p=mp_stash_cur_exp(mp); -mp_get_x_next(mp); mp_scan_expression(mp); -@<Make sure the second part of a pair or color has a numeric type@>; -q=mp_get_node(mp, value_node_size); mp_name_type(q)=mp_capsule; -if ( mp->cur_cmd==comma ) mp_type(q)=mp_color_type; -else mp_type(q)=mp_pair_type; -mp_init_big_node(mp, q); r=value(q); -mp_stash_in(mp, y_part_loc(r)); -mp_unstash_cur_exp(mp, p); -mp_stash_in(mp, x_part_loc(r)); -if ( mp->cur_cmd==comma ) { - @<Scan the last of a triplet of numerics@>; -} -if ( mp->cur_cmd==comma ) { - mp_type(q)=mp_cmykcolor_type; - mp_init_big_node(mp, q); t=value(q); - mp->mem[cyan_part_loc(t)]=mp->mem[red_part_loc(r)]; - value(cyan_part_loc(t))=value(red_part_loc(r)); - mp->mem[magenta_part_loc(t)]=mp->mem[green_part_loc(r)]; - value(magenta_part_loc(t))=value(green_part_loc(r)); - mp->mem[yellow_part_loc(t)]=mp->mem[blue_part_loc(r)]; - value(yellow_part_loc(t))=value(blue_part_loc(r)); - mp_recycle_value(mp, r); - r=t; - @<Scan the last of a quartet of numerics@>; -} -mp_check_delimiter(mp, l_delim,r_delim); -mp->cur_type=mp_type(q); -mp->cur_exp=q; +{ + p = mp_stash_cur_exp (mp); + mp_get_x_next (mp); + mp_scan_expression (mp); + @<Make sure the second part of a pair or color has a numeric type@>; + q = mp_get_value_node (mp); + mp_name_type (q) = mp_capsule; + if (mp->cur_cmd == comma) { + mp_init_color_node (mp, q); + r = value_node (q); + mp_stash_in (mp, y_part_loc (r)); + mp_unstash_cur_exp (mp, p); + mp_stash_in (mp, x_part_loc (r)); + @<Scan the last of a triplet of numerics@>; + if (mp->cur_cmd == comma) { + mp_init_cmykcolor_node (mp, q); + t = value_node (q); + mp_type (cyan_part_loc (t)) = mp_type (red_part_loc (r)); + set_value (cyan_part_loc (t), value (red_part_loc (r))); + mp_type (magenta_part_loc (t)) = mp_type (green_part_loc (r)); + set_value (magenta_part_loc (t), value (green_part_loc (r))); + mp_type (yellow_part_loc (t)) = mp_type (blue_part_loc (r)); + set_value (yellow_part_loc (t), value (blue_part_loc (r))); + mp_recycle_value (mp, r); + r = t; + @<Scan the last of a quartet of numerics@>; + } + } else { + mp_init_pair_node (mp, q); + r = value_node (q); + mp_stash_in (mp, y_part_loc (r)); + mp_unstash_cur_exp (mp, p); + mp_stash_in (mp, x_part_loc (r)); + } + mp_check_delimiter (mp, l_delim, r_delim); + mp->cur_exp.type = mp_type (q); + set_cur_exp_node (q); } + @ @<Make sure the second part of a pair or color has a numeric type@>= -if ( mp->cur_type<mp_known ) { - exp_err("Nonnumeric ypart has been replaced by 0"); -@.Nonnumeric...replaced by 0@> - help4("I've started to scan a pair `(a,b)' or a color `(a,b,c)';", - "but after finding a nice `a' I found a `b' that isn't", - "of numeric type. So I've changed that part to zero.", - "(The b that I didn't like appears above the error message.)"); - mp_put_get_flush_error(mp, 0); +if (mp->cur_exp.type < mp_known) { + exp_err ("Nonnumeric ypart has been replaced by 0"); +@.Nonnumeric...replaced by 0@>; + help4 ("I've started to scan a pair `(a,b)' or a color `(a,b,c)';", + "but after finding a nice `a' I found a `b' that isn't", + "of numeric type. So I've changed that part to zero.", + "(The b that I didn't like appears above the error message.)"); + new_expr.data.val = 0; + mp_put_get_flush_error (mp, new_expr); } @ @<Scan the last of a triplet of numerics@>= -{ - mp_get_x_next(mp); mp_scan_expression(mp); - if ( mp->cur_type<mp_known ) { - exp_err("Nonnumeric third part has been replaced by 0"); -@.Nonnumeric...replaced by 0@> - help3("I've just scanned a color `(a,b,c)' or cmykcolor(a,b,c,d); but the `c'", - "isn't of numeric type. So I've changed that part to zero.", - "(The c that I didn't like appears above the error message.)"); - mp_put_get_flush_error(mp, 0); +{ + mp_get_x_next (mp); + mp_scan_expression (mp); + if (mp->cur_exp.type < mp_known) { + exp_err ("Nonnumeric third part has been replaced by 0"); +@.Nonnumeric...replaced by 0@>; + help3 + ("I've just scanned a color `(a,b,c)' or cmykcolor(a,b,c,d); but the `c'", + "isn't of numeric type. So I've changed that part to zero.", + "(The c that I didn't like appears above the error message.)"); + new_expr.data.val = 0; + mp_put_get_flush_error (mp, new_expr); } - mp_stash_in(mp, blue_part_loc(r)); + mp_stash_in (mp, blue_part_loc (r)); } + @ @<Scan the last of a quartet of numerics@>= -{ - mp_get_x_next(mp); mp_scan_expression(mp); - if ( mp->cur_type<mp_known ) { - exp_err("Nonnumeric blackpart has been replaced by 0"); -@.Nonnumeric...replaced by 0@> - help3("I've just scanned a cmykcolor `(c,m,y,k)'; but the `k' isn't", - "of numeric type. So I've changed that part to zero.", - "(The k that I didn't like appears above the error message.)"); - mp_put_get_flush_error(mp, 0); +{ + mp_get_x_next (mp); + mp_scan_expression (mp); + if (mp->cur_exp.type < mp_known) { + exp_err ("Nonnumeric blackpart has been replaced by 0"); +@.Nonnumeric...replaced by 0@>; + help3 ("I've just scanned a cmykcolor `(c,m,y,k)'; but the `k' isn't", + "of numeric type. So I've changed that part to zero.", + "(The k that I didn't like appears above the error message.)"); + new_expr.data.val = 0; + mp_put_get_flush_error (mp, new_expr); } - mp_stash_in(mp, black_part_loc(r)); + mp_stash_in (mp, black_part_loc (r)); } + @ The local variable |group_line| keeps track of the line where a \&{begingroup} command occurred; this will be useful in an error message if the group doesn't actually end. @<Other local variables for |scan_primary|@>= -integer group_line; /* where a group began */ +integer group_line; /* where a group began */ @ @<Scan a grouped primary@>= -{ - group_line=mp_true_line(mp); - if ( mp->internal[mp_tracing_commands]>0 ) show_cur_cmd_mod; - save_boundary_item(p); - do { - mp_do_statement(mp); /* ends with |cur_cmd>=semicolon| */ - } while (mp->cur_cmd==semicolon); - if ( mp->cur_cmd!=end_group ) { - print_err("A group begun on line "); -@.A group...never ended@> - mp_print_int(mp, group_line); - mp_print(mp, " never ended"); - help2("I saw a `begingroup' back there that hasn't been matched", - "by `endgroup'. So I've inserted `endgroup' now."); - mp_back_error(mp); mp->cur_cmd=end_group; - } - mp_unsave(mp); - /* this might change |cur_type|, if independent variables are recycled */ - if ( mp->internal[mp_tracing_commands]>0 ) show_cur_cmd_mod; +{ + group_line = mp_true_line (mp); + if (internal_value (mp_tracing_commands) > 0) + show_cur_cmd_mod; + mp_save_boundary (mp); + do { + mp_do_statement (mp); /* ends with |cur_cmd>=semicolon| */ + } while (mp->cur_cmd == semicolon); + if (mp->cur_cmd != end_group) { + print_err ("A group begun on line "); +@.A group...never ended@>; + mp_print_int (mp, group_line); + mp_print (mp, " never ended"); + help2 ("I saw a `begingroup' back there that hasn't been matched", + "by `endgroup'. So I've inserted `endgroup' now."); + mp_back_error (mp); + mp->cur_cmd = end_group; + } + mp_unsave (mp); + /* this might change |cur_type|, if independent variables are recycled */ + if (internal_value (mp_tracing_commands) > 0) + show_cur_cmd_mod; } + @ @<Scan a string constant@>= -{ - mp->cur_type=mp_string_type; mp->cur_exp=mp->cur_mod; +{ + mp->cur_exp.type = mp_string_type; + set_cur_exp_str (mp->cur_mod_str); } + @ Later we'll come to procedures that perform actual operations like addition, square root, and so on; our purpose now is to do the parsing. But we might as well mention those future procedures now, so that the @@ -17878,14 +19895,20 @@ suspense won't be too bad: |do_binary(p,c)| applies a primitive operation to the capsule~|p| and the current expression. -@<Scan a nullary operation@>=mp_do_nullary(mp, (quarterword)mp->cur_mod) +@<Scan a nullary operation@>= +mp_do_nullary (mp, (quarterword) mp->cur_mod) + @ @<Scan a unary operation@>= -{ - c=(quarterword)mp->cur_mod; mp_get_x_next(mp); mp_scan_primary(mp); - mp_do_unary(mp, c); goto DONE; +{ + c = (quarterword) mp->cur_mod; + mp_get_x_next (mp); + mp_scan_primary (mp); + mp_do_unary (mp, c); + goto DONE; } + @ A numeric token might be a primary by itself, or it might be the numerator of a fraction composed solely of numeric tokens, or it might multiply the primary that follows (provided that the primary doesn't begin @@ -17896,94 +19919,125 @@ than unity, we try to retain higher precision when we use it in scalar multiplication. @<Other local variables for |scan_primary|@>= -scaled num,denom; /* for primaries that are fractions, like `1/2' */ +scaled num, denom; /* for primaries that are fractions, like `1/2' */ @ @<Scan a primary that starts with a numeric token@>= -{ - mp->cur_exp=mp->cur_mod; mp->cur_type=mp_known; mp_get_x_next(mp); - if ( mp->cur_cmd!=slash ) { - num=0; denom=0; - } else { - mp_get_x_next(mp); - if ( mp->cur_cmd!=numeric_token ) { - mp_back_input(mp); - mp->cur_cmd=slash; mp->cur_mod=over; mp->cur_sym=frozen_slash; +{ + set_cur_exp_value (mp->cur_mod); + mp->cur_exp.type = mp_known; + mp_get_x_next (mp); + if (mp->cur_cmd != slash) { + num = 0; + denom = 0; + } else { + mp_get_x_next (mp); + if (mp->cur_cmd != numeric_token) { + mp_back_input (mp); + mp->cur_cmd = slash; + mp->cur_mod = over; + mp->cur_sym = mp->frozen_slash; goto DONE; } - num=mp->cur_exp; denom=mp->cur_mod; - if ( denom==0 ) { @<Protest division by zero@>; } - else { mp->cur_exp=mp_make_scaled(mp, num,denom); } - check_arith; mp_get_x_next(mp); - } - if ( mp->cur_cmd>=min_primary_command ) { - if ( mp->cur_cmd<numeric_token ) { /* in particular, |cur_cmd<>plus_or_minus| */ - p=mp_stash_cur_exp(mp); mp_scan_primary(mp); - if ( (abs(num)>=abs(denom))||(mp->cur_type<mp_color_type) ) { - mp_do_binary(mp, p,times); - } else { - mp_frac_mult(mp, num,denom); - mp_free_node(mp, p,value_node_size); - } + num = cur_exp_value (); + denom = mp->cur_mod; + if (denom == 0) { + @<Protest division by zero@>; + } else { + set_cur_exp_value (mp_make_scaled (mp, num, denom)); + } + check_arith; + mp_get_x_next (mp); + } + if (mp->cur_cmd >= min_primary_command) { + if (mp->cur_cmd < numeric_token) { /* in particular, |cur_cmd<>plus_or_minus| */ + p = mp_stash_cur_exp (mp); + mp_scan_primary (mp); + if ((abs (num) >= abs (denom)) || (mp->cur_exp.type < mp_color_type)) { + mp_do_binary (mp, p, times); + } else { + mp_frac_mult (mp, num, denom); + mp_free_node (mp, p, value_node_size); + } } } goto DONE; } + @ @<Protest division...@>= -{ - print_err("Division by zero"); -@.Division by zero@> - help1("I'll pretend that you meant to divide by 1."); mp_error(mp); +{ + print_err ("Division by zero"); +@.Division by zero@>; + help1 ("I'll pretend that you meant to divide by 1."); + mp_error (mp); } + @ @<Scan a binary operation with `\&{of}' between its operands@>= -{ - c=(quarterword)mp->cur_mod; mp_get_x_next(mp); mp_scan_expression(mp); - if ( mp->cur_cmd!=of_token ) { - mp_missing_err(mp, "of"); mp_print(mp, " for "); - mp_print_cmd_mod(mp, primary_binary,c); -@.Missing `of'@> - help1("I've got the first argument; will look now for the other."); - mp_back_error(mp); - } - p=mp_stash_cur_exp(mp); mp_get_x_next(mp); mp_scan_primary(mp); - mp_do_binary(mp, p,c); goto DONE; +{ + c = (quarterword) mp->cur_mod; + mp_get_x_next (mp); + mp_scan_expression (mp); + if (mp->cur_cmd != of_token) { + mp_missing_err (mp, "of"); + mp_print (mp, " for "); + mp_print_cmd_mod (mp, primary_binary, c); +@.Missing `of'@>; + help1 ("I've got the first argument; will look now for the other."); + mp_back_error (mp); + } + p = mp_stash_cur_exp (mp); + mp_get_x_next (mp); + mp_scan_primary (mp); + mp_do_binary (mp, p, c); + goto DONE; } + @ @<Convert a suffix to a string@>= -{ - mp_get_x_next(mp); mp_scan_suffix(mp); - mp->old_setting=mp->selector; mp->selector=new_string; - mp_show_token_list(mp, mp->cur_exp,null,100000,0); - mp_flush_token_list(mp, mp->cur_exp); - mp->cur_exp=mp_make_string(mp); mp->selector=mp->old_setting; - mp->cur_type=mp_string_type; +{ + mp_get_x_next (mp); + mp_scan_suffix (mp); + mp->old_setting = mp->selector; + mp->selector = new_string; + mp_show_token_list (mp, cur_exp_node (), NULL, 100000, 0); + mp_flush_token_list (mp, cur_exp_node ()); + set_cur_exp_str (mp_make_string (mp)); + mp->selector = mp->old_setting; + mp->cur_exp.type = mp_string_type; goto DONE; } + @ If an internal quantity appears all by itself on the left of an assignment, we return a token list of length one, containing the address -of the internal quantity plus |hash_end|. (This accords with the conventions -of the save stack, as described earlier.) +of the internal quantity, with |name_type| equal to |mp_internal_sym|. +(This accords with the conventions of the save stack, as described earlier.) @<Scan an internal...@>= -{ - q=mp->cur_mod; - if ( my_var_flag==assignment ) { - mp_get_x_next(mp); - if ( mp->cur_cmd==assignment ) { - mp->cur_exp=mp_get_avail(mp); - mp_info(mp->cur_exp)=q+hash_end; mp->cur_type=mp_token_list; +{ + halfword qq = mp->cur_mod; + if (my_var_flag == assignment) { + mp_get_x_next (mp); + if (mp->cur_cmd == assignment) { + set_cur_exp_node (mp_get_symbolic_node (mp)); + set_mp_sym_info (cur_exp_node (), qq); + mp_name_type (cur_exp_node ()) = mp_internal_sym; + mp->cur_exp.type = mp_token_list; goto DONE; } - mp_back_input(mp); + mp_back_input (mp); } - mp->cur_exp=mp->internal[q]; - if (mp->int_type[q]==mp_string_type) - add_str_ref(mp->cur_exp); - mp->cur_type=(quarterword)mp->int_type[q]; + if (internal_type (qq) == mp_string_type) { + set_cur_exp_str (internal_string (qq)); + add_str_ref (cur_exp_str ()); + } else { + set_cur_exp_value (internal_value (qq)); + } + mp->cur_exp.type = (mp_variable_type) internal_type (qq); } + @ The most difficult part of |scan_primary| has been saved for last, since it was necessary to build up some confidence first. We can now face the task of scanning a variable. @@ -18004,111 +20058,147 @@ doesn't bother to update its information about type. And if |undefined<tt<mp_unsuffixed_macro|, the precise value of |tt| isn't critical. @ @<Other local variables for |scan_primary|@>= -pointer pre_head,post_head,tail; +mp_node pre_head, post_head, tail; /* prefix and suffix list variables */ quarterword tt; /* approximation to the type of the variable-so-far */ -pointer t; /* a token */ -pointer macro_ref = 0; /* reference count for a suffixed macro */ +mp_node t; /* a token */ +mp_node macro_ref = 0; /* reference count for a suffixed macro */ @ @<Scan a variable primary...@>= -{ - fast_get_avail(pre_head); tail=pre_head; post_head=null; tt=mp_vacuous; - while (1) { - t=mp_cur_tok(mp); mp_link(tail)=t; - if ( tt!=undefined ) { - @<Find the approximate type |tt| and corresponding~|q|@>; - if ( tt>=mp_unsuffixed_macro ) { +{ + pre_head = mp_get_symbolic_node (mp); + tail = pre_head; + post_head = NULL; + tt = mp_vacuous; + while (1) { + t = mp_cur_tok (mp); + mp_link (tail) = t; + if (tt != undefined) { + @<Find the approximate type |tt| and corresponding~|q|@>; + if (tt >= mp_unsuffixed_macro) { @<Either begin an unsuffixed macro call or prepare for a suffixed one@>; } } - mp_get_x_next(mp); tail=t; - if ( mp->cur_cmd==left_bracket ) { + mp_get_x_next (mp); + tail = t; + if (mp->cur_cmd == left_bracket) { @<Scan for a subscript; replace |cur_cmd| by |numeric_token| if found@>; } - if ( mp->cur_cmd>max_suffix_token ) break; - if ( mp->cur_cmd<min_suffix_token ) break; - } /* now |cur_cmd| is |internal_quantity|, |tag_token|, or |numeric_token| */ + if (mp->cur_cmd > max_suffix_token) + break; + if (mp->cur_cmd < min_suffix_token) + break; + } /* now |cur_cmd| is |internal_quantity|, |tag_token|, or |numeric_token| */ @<Handle unusual cases that masquerade as variables, and |goto restart| or |goto done| if appropriate; otherwise make a copy of the variable and |goto done|@>; } + @ @<Either begin an unsuffixed macro call or...@>= -{ - mp_link(tail)=null; - if ( tt>mp_unsuffixed_macro ) { /* |tt=mp_suffixed_macro| */ - post_head=mp_get_avail(mp); tail=post_head; mp_link(tail)=t; - tt=undefined; macro_ref=value(q); add_mac_ref(macro_ref); +{ + mp_link (tail) = NULL; + if (tt > mp_unsuffixed_macro) { /* |tt=mp_suffixed_macro| */ + post_head = mp_get_symbolic_node (mp); + tail = post_head; + mp_link (tail) = t; + tt = undefined; + macro_ref = value_node (q); + add_mac_ref (macro_ref); } else { @<Set up unsuffixed macro call and |goto restart|@>; } } + @ @<Scan for a subscript; replace |cur_cmd| by |numeric_token| if found@>= -{ - mp_get_x_next(mp); mp_scan_expression(mp); - if ( mp->cur_cmd!=right_bracket ) { +{ + mp_get_x_next (mp); + mp_scan_expression (mp); + if (mp->cur_cmd != right_bracket) { @<Put the left bracket and the expression back to be rescanned@>; - } else { - if ( mp->cur_type!=mp_known ) mp_bad_subscript(mp); - mp->cur_cmd=numeric_token; mp->cur_mod=mp->cur_exp; mp->cur_sym=0; + } else { + if (mp->cur_exp.type != mp_known) + mp_bad_subscript (mp); + mp->cur_cmd = numeric_token; + mp->cur_mod = cur_exp_value (); + mp->cur_sym = 0; } } + @ The left bracket that we thought was introducing a subscript might have actually been the left bracket in a mediation construction like `\.{x[a,b]}'. So we don't issue an error message at this point; but we do want to back up so as to avoid any embarrassment about our incorrect assumption. @<Put the left bracket and the expression back to be rescanned@>= -{ - mp_back_input(mp); /* that was the token following the current expression */ - mp_back_expr(mp); mp->cur_cmd=left_bracket; - mp->cur_mod=0; mp->cur_sym=frozen_left_bracket; +{ + mp_back_input (mp); /* that was the token following the current expression */ + mp_back_expr (mp); + mp->cur_cmd = left_bracket; + mp->cur_mod = 0; + mp->cur_sym = mp->frozen_left_bracket; } + @ Here's a routine that puts the current expression back to be read again. -@c +@c static void mp_back_expr (MP mp) { - pointer p; /* capsule token */ - p=mp_stash_cur_exp(mp); mp_link(p)=null; back_list(p); + mp_node p; /* capsule token */ + p = mp_stash_cur_exp (mp); + mp_link (p) = NULL; + back_list (p); } + @ Unknown subscripts lead to the following error message. -@c -static void mp_bad_subscript (MP mp) { - exp_err("Improper subscript has been replaced by zero"); -@.Improper subscript...@> - help3("A bracketed subscript must have a known numeric value;", - "unfortunately, what I found was the value that appears just", - "above this error message. So I'll try a zero subscript."); - mp_flush_error(mp, 0); +@c +static void mp_bad_subscript (MP mp) { + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + exp_err ("Improper subscript has been replaced by zero"); +@.Improper subscript...@>; + help3 ("A bracketed subscript must have a known numeric value;", + "unfortunately, what I found was the value that appears just", + "above this error message. So I'll try a zero subscript."); + mp_flush_error (mp, new_expr); } + @ Every time we call |get_x_next|, there's a chance that the variable we've been looking at will disappear. Thus, we cannot safely keep |q| pointing into the variable structure; we need to start searching from the root each time. @<Find the approximate type |tt| and corresponding~|q|@>= @^inner loop@> -{ - p=mp_link(pre_head); q=mp_info(p); tt=undefined; - if ( eq_type(q) % outer_tag==tag_token ) { - q=equiv(q); - if ( q==null ) goto DONE2; - while (1) { - p=mp_link(p); - if ( p==null ) { - tt=mp_type(q); goto DONE2; - }; - if ( mp_type(q)!=mp_structured ) goto DONE2; - q=mp_link(attr_head(q)); /* the |collective_subscript| attribute */ - if ( p>=mp->hi_mem_min ) { /* it's not a subscript */ - do { q=mp_link(q); } while (! (attr_loc(q)>=mp_info(p))); - if ( attr_loc(q)>mp_info(p) ) goto DONE2; +{ + mp_sym qq; + p = mp_link (pre_head); + qq = mp_sym_sym (p); + tt = undefined; + if (eq_type (qq) % outer_tag == tag_token) { + q = equiv_node (qq); + if (q == NULL) + goto DONE2; + while (1) { + p = mp_link (p); + if (p == NULL) { + tt = mp_type (q); + goto DONE2; + } + if (mp_type (q) != mp_structured) + goto DONE2; + q = mp_link (attr_head (q)); /* the |collective_subscript| attribute */ + if (mp_type (p) == mp_symbol_node) { /* it's not a subscript */ + do { + q = mp_link (q); + } while (!(hashloc (q) >= mp_sym_sym (p))); + if (hashloc (q) > mp_sym_sym (p)) + goto DONE2; } } } @@ -18116,11 +20206,12 @@ DONE2: ; } + @ How do things stand now? Well, we have scanned an entire variable name, including possible subscripts and/or attributes; |cur_cmd|, |cur_mod|, and -|cur_sym| represent the token that follows. If |post_head=null|, a +|cur_sym| represent the token that follows. If |post_head=NULL|, a token list for this variable name starts at |mp_link(pre_head)|, with all -subscripts evaluated. But if |post_head<>null|, the variable turned out +subscripts evaluated. But if |post_head<>NULL|, the variable turned out to be a suffixed macro; |pre_head| is the head of the prefix list, while |post_head| is the head of a token list containing both `\.{\AT!}' and the suffix. @@ -18134,234 +20225,353 @@ The following procedure prints an error message when a variable unexpectedly disappears. Its help message isn't quite right for our present purposes, but we'll be able to fix that up. -@c -static void mp_obliterated (MP mp,pointer q) { - print_err("Variable "); mp_show_token_list(mp, q,null,1000,0); - mp_print(mp, " has been obliterated"); -@.Variable...obliterated@> - help5("It seems you did a nasty thing---probably by accident,", - "but nevertheless you nearly hornswoggled me...", - "While I was evaluating the right-hand side of this", - "command, something happened, and the left-hand side", - "is no longer a variable! So I won't change anything."); +@c +static void mp_obliterated (MP mp, mp_node q) { + print_err ("Variable "); + mp_show_token_list (mp, q, NULL, 1000, 0); + mp_print (mp, " has been obliterated"); +@.Variable...obliterated@>; + help5 ("It seems you did a nasty thing---probably by accident,", + "but nevertheless you nearly hornswoggled me...", + "While I was evaluating the right-hand side of this", + "command, something happened, and the left-hand side", + "is no longer a variable! So I won't change anything."); } + @ If the variable does exist, we also need to check for a few other special cases before deciding that a plain old ordinary variable has, indeed, been scanned. @<Handle unusual cases that masquerade as variables...@>= -if ( post_head!=null ) { +if (post_head != NULL) { @<Set up suffixed macro call and |goto restart|@>; } -q=mp_link(pre_head); free_avail(pre_head); -if ( mp->cur_cmd==my_var_flag ) { - mp->cur_type=mp_token_list; mp->cur_exp=q; goto DONE; -} -p=mp_find_variable(mp, q); -if ( p!=null ) { - mp_make_exp_copy(mp, p); -} else { - mp_obliterated(mp, q); - mp->help_line[2]="While I was evaluating the suffix of this variable,"; - mp->help_line[1]="something was redefined, and it's no longer a variable!"; - mp->help_line[0]="In order to get back on my feet, I've inserted `0' instead."; - mp_put_get_flush_error(mp, 0); -} -mp_flush_node_list(mp, q); +q = mp_link (pre_head); +mp_free_symbolic_node (mp, pre_head); +if (mp->cur_cmd == my_var_flag) { + mp->cur_exp.type = mp_token_list; + set_cur_exp_node (q); + goto DONE; +} +p = mp_find_variable (mp, q); +if (p != NULL) { + mp_make_exp_copy (mp, p); +} else { + mp_obliterated (mp, q); + mp->help_line[2] = "While I was evaluating the suffix of this variable,"; + mp->help_line[1] = "something was redefined, and it's no longer a variable!"; + mp->help_line[0] = + "In order to get back on my feet, I've inserted `0' instead."; + new_expr.data.val = 0; + mp_put_get_flush_error (mp, new_expr); +} +mp_flush_node_list (mp, q); goto DONE @ The only complication associated with macro calling is that the prefix and ``at'' parameters must be packaged in an appropriate list of lists. @<Set up unsuffixed macro call and |goto restart|@>= -{ - p=mp_get_avail(mp); mp_info(pre_head)=mp_link(pre_head); mp_link(pre_head)=p; - mp_info(p)=t; mp_macro_call(mp, value(q),pre_head,null); - mp_get_x_next(mp); +{ + p = mp_get_symbolic_node (mp); + set_mp_sym_sym (pre_head, mp_link (pre_head)); + mp_link (pre_head) = p; + set_mp_sym_sym (p, t); + mp_macro_call (mp, value_node (q), pre_head, NULL); + mp_get_x_next (mp); goto RESTART; } + @ If the ``variable'' that turned out to be a suffixed macro no longer exists, we don't care, because we have reserved a pointer (|macro_ref|) to its token list. @<Set up suffixed macro call and |goto restart|@>= -{ - mp_back_input(mp); p=mp_get_avail(mp); q=mp_link(post_head); - mp_info(pre_head)=mp_link(pre_head); mp_link(pre_head)=post_head; - mp_info(post_head)=q; mp_link(post_head)=p; mp_info(p)=mp_link(q); mp_link(q)=null; - mp_macro_call(mp, macro_ref,pre_head,null); decr(ref_count(macro_ref)); - mp_get_x_next(mp); goto RESTART; +{ + mp_back_input (mp); + p = mp_get_symbolic_node (mp); + q = mp_link (post_head); + set_mp_sym_sym (pre_head, mp_link (pre_head)); + mp_link (pre_head) = post_head; + set_mp_sym_sym (post_head, q); + mp_link (post_head) = p; + set_mp_sym_sym (p, mp_link (q)); + mp_link (q) = NULL; + mp_macro_call (mp, macro_ref, pre_head, NULL); + decr_mac_ref (macro_ref); + mp_get_x_next (mp); + goto RESTART; } + @ Our remaining job is simply to make a copy of the value that has been found. Some cases are harder than others, but complexity arises solely because of the multiplicity of possible cases. @<Declare the procedure called |make_exp_copy|@>= -@<Declare subroutines needed by |make_exp_copy|@> -static void mp_make_exp_copy (MP mp,pointer p) { - pointer q,r,t; /* registers for list manipulation */ -RESTART: - mp->cur_type=mp_type(p); - switch (mp->cur_type) { - case mp_vacuous: case mp_boolean_type: case mp_known: - mp->cur_exp=value(p); break; +@<Declare subroutines needed by |make_exp_copy|@>; +static void mp_make_exp_copy (MP mp, mp_node p) { + mp_node t; /* register(s) for list manipulation */ + mp_value_node q; +RESTART: + mp->cur_exp.type = mp_type (p); + switch (mp->cur_exp.type) { + case mp_vacuous: + case mp_boolean_type: + case mp_known: + set_cur_exp_value (value (p)); + break; case unknown_types: - mp->cur_exp=mp_new_ring_entry(mp, p); + t = mp_new_ring_entry (mp, p); + set_cur_exp_node (t); break; - case mp_string_type: - mp->cur_exp=value(p); add_str_ref(mp->cur_exp); + case mp_string_type: + set_cur_exp_str (str_value (p)); + add_str_ref (cur_exp_str ()); break; case mp_picture_type: - mp->cur_exp=value(p);add_edge_ref(mp->cur_exp); + set_cur_exp_node (value_node (p)); + add_edge_ref (cur_exp_node ()); break; case mp_pen_type: - mp->cur_exp=copy_pen(value(p)); - break; + set_cur_exp_knot (copy_pen (knot_value (p))); + break; case mp_path_type: - mp->cur_exp=mp_copy_path(mp, value(p)); + set_cur_exp_knot (mp_copy_path (mp, knot_value (p))); break; - case mp_transform_type: case mp_color_type: - case mp_cmykcolor_type: case mp_pair_type: + case mp_transform_type: + case mp_color_type: + case mp_cmykcolor_type: + case mp_pair_type: @<Copy the big node |p|@>; break; - case mp_dependent: case mp_proto_dependent: - mp_encapsulate(mp, mp_copy_dep_list(mp, dep_list(p))); + case mp_dependent: + case mp_proto_dependent: + mp_encapsulate (mp, + mp_copy_dep_list (mp, + (mp_value_node) dep_list ((mp_value_node) + p))); break; - case mp_numeric_type: - new_indep(p); goto RESTART; + case mp_numeric_type: + new_indep (p); + goto RESTART; break; - case mp_independent: - q=mp_single_dependency(mp, p); - if ( q==mp->dep_final ){ - mp->cur_type=mp_known; mp->cur_exp=0; mp_free_node(mp, q,dep_node_size); - } else { - mp->cur_type=mp_dependent; mp_encapsulate(mp, q); + case mp_independent: + q = mp_single_dependency (mp, p); + if (q == mp->dep_final) { + mp->cur_exp.type = mp_known; + set_cur_exp_value (0); + mp_free_dep_node (mp, q); + } else { + mp->cur_exp.type = mp_dependent; + mp_encapsulate (mp, q); } break; - default: - mp_confusion(mp, "copy"); -@:this can't happen copy}{\quad copy@> + default: + mp_confusion (mp, "copy"); +@:this can't happen copy}{\quad copy@>; break; } } + @ The |encapsulate| subroutine assumes that |dep_final| is the tail of dependency list~|p|. @<Declare subroutines needed by |make_exp_copy|@>= -static void mp_encapsulate (MP mp,pointer p) { - mp->cur_exp=mp_get_node(mp, value_node_size); mp_type(mp->cur_exp)=mp->cur_type; - mp_name_type(mp->cur_exp)=mp_capsule; mp_new_dep(mp, mp->cur_exp,p); +static void mp_encapsulate (MP mp, mp_value_node p) { + mp_node q = mp_get_value_node (mp); + FUNCTION_TRACE2 ("mp_encapsulate(%p)\n", p); + mp_name_type (q) = mp_capsule; + mp_new_dep (mp, q, mp->cur_exp.type, p); + set_cur_exp_node (q); } + @ The most tedious case arises when the user refers to a \&{pair}, \&{color}, or \&{transform} variable; we must copy several fields, each of which can be |independent|, |dependent|, |mp_proto_dependent|, or |known|. @<Copy the big node |p|@>= -{ - if ( value(p)==null ) - mp_init_big_node(mp, p); - t=mp_get_node(mp, value_node_size); mp_name_type(t)=mp_capsule; mp_type(t)=mp->cur_type; - mp_init_big_node(mp, t); - q=value(p)+mp->big_node_size[mp->cur_type]; - r=value(t)+mp->big_node_size[mp->cur_type]; - do { - q=q-2; r=r-2; mp_install(mp, r,q); - } while (q!=value(p)); - mp->cur_exp=t; +{ + debug_printf("value_node (%p) = %p\n", p, value_node(p)); + if (value_node (p) == NULL) { + switch (mp_type (p)) { + case mp_pair_type: + mp_init_pair_node (mp, p); + break; + case mp_color_type: + mp_init_color_node (mp, p); + break; + case mp_cmykcolor_type: + mp_init_cmykcolor_node (mp, p); + break; + case mp_transform_type: + mp_init_transform_node (mp, p); + break; + default: /* there are no other valid cases, but please the compiler */ + break; + } + } + t = mp_get_value_node (mp); + mp_name_type (t) = mp_capsule; + switch (mp->cur_exp.type) { + case mp_pair_type: + mp_init_pair_node (mp, t); + mp_install (mp, y_part_loc (value_node (t)), y_part_loc (value_node (p))); + mp_install (mp, x_part_loc (value_node (t)), x_part_loc (value_node (p))); + break; + case mp_color_type: + mp_init_color_node (mp, t); + mp_install (mp, blue_part_loc (value_node (t)), + blue_part_loc (value_node (p))); + mp_install (mp, green_part_loc (value_node (t)), + green_part_loc (value_node (p))); + mp_install (mp, red_part_loc (value_node (t)), + red_part_loc (value_node (p))); + break; + case mp_cmykcolor_type: + mp_init_cmykcolor_node (mp, t); + mp_install (mp, black_part_loc (value_node (t)), + black_part_loc (value_node (p))); + mp_install (mp, yellow_part_loc (value_node (t)), + yellow_part_loc (value_node (p))); + mp_install (mp, magenta_part_loc (value_node (t)), + magenta_part_loc (value_node (p))); + mp_install (mp, cyan_part_loc (value_node (t)), + cyan_part_loc (value_node (p))); + break; + case mp_transform_type: + mp_init_transform_node (mp, t); + mp_install (mp, yy_part_loc (value_node (t)), yy_part_loc (value_node (p))); + mp_install (mp, yx_part_loc (value_node (t)), yx_part_loc (value_node (p))); + mp_install (mp, xy_part_loc (value_node (t)), xy_part_loc (value_node (p))); + mp_install (mp, xx_part_loc (value_node (t)), xx_part_loc (value_node (p))); + mp_install (mp, ty_part_loc (value_node (t)), ty_part_loc (value_node (p))); + mp_install (mp, tx_part_loc (value_node (t)), tx_part_loc (value_node (p))); + break; + default: /* there are no other valid cases, but please the compiler */ + break; + } + set_cur_exp_node (t); } + @ The |install| procedure copies a numeric field~|q| into field~|r| of a big node that will be part of a capsule. @<Declare subroutines needed by |make_exp_copy|@>= -static void mp_install (MP mp,pointer r, pointer q) { - pointer p; /* temporary register */ - if ( mp_type(q)==mp_known ){ - value(r)=value(q); mp_type(r)=mp_known; - } else if ( mp_type(q)==mp_independent ) { - p=mp_single_dependency(mp, q); - if ( p==mp->dep_final ) { - mp_type(r)=mp_known; value(r)=0; mp_free_node(mp, p,dep_node_size); - } else { - mp_type(r)=mp_dependent; mp_new_dep(mp, r,p); +static void mp_install (MP mp, mp_node r, mp_node q) { + mp_value_node p; /* temporary register */ + if (mp_type (q) == mp_known) { + set_value (r, value (q)); + mp_type (r) = mp_known; + } else if (mp_type (q) == mp_independent) { + p = mp_single_dependency (mp, q); + if (p == mp->dep_final) { + mp_type (r) = mp_known; + set_value (r, 0); + mp_free_dep_node (mp, p); + } else { + mp_new_dep (mp, r, mp_dependent, p); } } else { - mp_type(r)=mp_type(q); mp_new_dep(mp, r,mp_copy_dep_list(mp, dep_list(q))); + mp_new_dep (mp, r, mp_type (q), + mp_copy_dep_list (mp, (mp_value_node) dep_list ((mp_value_node) + q))); } } + @ Expressions of the form `\.{a[b,c]}' are converted into `\.{b+a*(c-b)}', without checking the types of \.b~or~\.c, provided that \.a is numeric. @<Scan a mediation...@>= -{ - p=mp_stash_cur_exp(mp); mp_get_x_next(mp); mp_scan_expression(mp); - if ( mp->cur_cmd!=comma ) { +{ + p = mp_stash_cur_exp (mp); + mp_get_x_next (mp); + mp_scan_expression (mp); + if (mp->cur_cmd != comma) { @<Put the left bracket and the expression back...@>; - mp_unstash_cur_exp(mp, p); - } else { - q=mp_stash_cur_exp(mp); mp_get_x_next(mp); mp_scan_expression(mp); - if ( mp->cur_cmd!=right_bracket ) { - mp_missing_err(mp, "]"); -@.Missing `]'@> - help3("I've scanned an expression of the form `a[b,c',", - "so a right bracket should have come next.", - "I shall pretend that one was there."); - mp_back_error(mp); + mp_unstash_cur_exp (mp, p); + } else { + q = mp_stash_cur_exp (mp); + mp_get_x_next (mp); + mp_scan_expression (mp); + if (mp->cur_cmd != right_bracket) { + mp_missing_err (mp, "]"); +@.Missing `]'@>; + help3 ("I've scanned an expression of the form `a[b,c',", + "so a right bracket should have come next.", + "I shall pretend that one was there."); + mp_back_error (mp); } - r=mp_stash_cur_exp(mp); mp_make_exp_copy(mp, q); - mp_do_binary(mp, r,minus); mp_do_binary(mp, p,times); - mp_do_binary(mp, q,plus); mp_get_x_next(mp); + r = mp_stash_cur_exp (mp); + mp_make_exp_copy (mp, q); + mp_do_binary (mp, r, minus); + mp_do_binary (mp, p, times); + mp_do_binary (mp, q, plus); + mp_get_x_next (mp); } } + @ Here is a comparatively simple routine that is used to scan the \&{suffix} parameters of a macro. @<Declare the basic parsing subroutines@>= static void mp_scan_suffix (MP mp) { - pointer h,t; /* head and tail of the list being built */ - pointer p; /* temporary register */ - h=mp_get_avail(mp); t=h; - while (1) { - if ( mp->cur_cmd==left_bracket ) { + mp_node h, t; /* head and tail of the list being built */ + mp_node p; /* temporary register */ + h = mp_get_symbolic_node (mp); + t = h; + while (1) { + if (mp->cur_cmd == left_bracket) { @<Scan a bracketed subscript and set |cur_cmd:=numeric_token|@>; } - if ( mp->cur_cmd==numeric_token ) { - p=mp_new_num_tok(mp, mp->cur_mod); - } else if ((mp->cur_cmd==tag_token)||(mp->cur_cmd==internal_quantity) ) { - p=mp_get_avail(mp); mp_info(p)=mp->cur_sym; + if (mp->cur_cmd == numeric_token) { + p = mp_new_num_tok (mp, mp->cur_mod); + } else if ((mp->cur_cmd == tag_token) || (mp->cur_cmd == internal_quantity)) { + p = mp_get_symbolic_node (mp); + set_mp_sym_sym (p, mp->cur_sym); + mp_name_type (p) = mp->cur_sym_mod; } else { break; } - mp_link(t)=p; t=p; mp_get_x_next(mp); + mp_link (t) = p; + t = p; + mp_get_x_next (mp); } - mp->cur_exp=mp_link(h); free_avail(h); mp->cur_type=mp_token_list; + set_cur_exp_node (mp_link (h)); + mp_free_symbolic_node (mp, h); + mp->cur_exp.type = mp_token_list; } + @ @<Scan a bracketed subscript and set |cur_cmd:=numeric_token|@>= -{ - mp_get_x_next(mp); mp_scan_expression(mp); - if ( mp->cur_type!=mp_known ) mp_bad_subscript(mp); - if ( mp->cur_cmd!=right_bracket ) { - mp_missing_err(mp, "]"); -@.Missing `]'@> - help3("I've seen a `[' and a subscript value, in a suffix,", - "so a right bracket should have come next.", - "I shall pretend that one was there."); - mp_back_error(mp); +{ + mp_get_x_next (mp); + mp_scan_expression (mp); + if (mp->cur_exp.type != mp_known) + mp_bad_subscript (mp); + if (mp->cur_cmd != right_bracket) { + mp_missing_err (mp, "]"); +@.Missing `]'@>; + help3 ("I've seen a `[' and a subscript value, in a suffix,", + "so a right bracket should have come next.", + "I shall pretend that one was there."); + mp_back_error (mp); } - mp->cur_cmd=numeric_token; mp->cur_mod=mp->cur_exp; + mp->cur_cmd = numeric_token; + mp->cur_mod = cur_exp_value (); } -@* \[38] Parsing secondary and higher expressions. + +@* Parsing secondary and higher expressions. After the intricacies of |scan_primary|\kern-1pt, the |scan_secondary| routine is @@ -18375,76 +20585,92 @@ list, so that the macro can be called even after it has been clobbered. @<Declare the basic parsing subroutines@>= static void mp_scan_secondary (MP mp) { - pointer p; /* for list manipulation */ - halfword c,d; /* operation codes or modifiers */ - pointer mac_name; /* token defined with \&{primarydef} */ + mp_node p; /* for list manipulation */ + halfword c, d; /* operation codes or modifiers */ + mp_node cc = NULL; + mp_sym mac_name = NULL; /* token defined with \&{primarydef} */ RESTART: - if ((mp->cur_cmd<min_primary_command)|| - (mp->cur_cmd>max_primary_command) ) - mp_bad_exp(mp, "A secondary"); -@.A secondary expression...@> - mp_scan_primary(mp); -CONTINUE: - if ( mp->cur_cmd<=max_secondary_command && - mp->cur_cmd>=min_secondary_command ) { - p=mp_stash_cur_exp(mp); - c=mp->cur_mod; d=mp->cur_cmd; - if ( d==secondary_primary_macro ) { - mac_name=mp->cur_sym; - add_mac_ref(c); + if ((mp->cur_cmd < min_primary_command) || + (mp->cur_cmd > max_primary_command)) + mp_bad_exp (mp, "A secondary"); +@.A secondary expression...@>; + mp_scan_primary (mp); +CONTINUE: + if (mp->cur_cmd <= max_secondary_command && + mp->cur_cmd >= min_secondary_command) { + p = mp_stash_cur_exp (mp); + d = mp->cur_cmd; + c = mp->cur_mod; + if (d == secondary_primary_macro) { + cc = mp->cur_mod_node; + mac_name = mp->cur_sym; + add_mac_ref (cc); } - mp_get_x_next(mp); - mp_scan_primary(mp); - if ( d!=secondary_primary_macro ) { - mp_do_binary(mp, p,c); - } else { - mp_back_input(mp); - mp_binary_mac(mp, p,c,mac_name); - decr(ref_count(c)); - mp_get_x_next(mp); + mp_get_x_next (mp); + mp_scan_primary (mp); + if (d != secondary_primary_macro) { + mp_do_binary (mp, p, c); + } else { + mp_back_input (mp); + mp_binary_mac (mp, p, cc, mac_name); + decr_mac_ref (cc); + mp_get_x_next (mp); goto RESTART; } goto CONTINUE; } } + @ The following procedure calls a macro that has two parameters, |p| and |cur_exp|. -@c -static void mp_binary_mac (MP mp,pointer p, pointer c, pointer n) { - pointer q,r; /* nodes in the parameter list */ - q=mp_get_avail(mp); r=mp_get_avail(mp); mp_link(q)=r; - mp_info(q)=p; mp_info(r)=mp_stash_cur_exp(mp); - mp_macro_call(mp, c,q,n); +@c +static void mp_binary_mac (MP mp, mp_node p, mp_node c, mp_sym n) { + mp_node q, r; /* nodes in the parameter list */ + q = mp_get_symbolic_node (mp); + r = mp_get_symbolic_node (mp); + mp_link (q) = r; + set_mp_sym_sym (q, p); + set_mp_sym_sym (r, mp_stash_cur_exp (mp)); + mp_macro_call (mp, c, q, n); } + @ The next procedure, |scan_tertiary|, is pretty much the same deal. @<Declare the basic parsing subroutines@>= static void mp_scan_tertiary (MP mp) { - pointer p; /* for list manipulation */ - halfword c,d; /* operation codes or modifiers */ - pointer mac_name; /* token defined with \&{secondarydef} */ + mp_node p; /* for list manipulation */ + halfword c, d; /* operation codes or modifiers */ + mp_node cc = NULL; + mp_sym mac_name = NULL; /* token defined with \&{secondarydef} */ RESTART: - if ((mp->cur_cmd<min_primary_command)|| - (mp->cur_cmd>max_primary_command) ) - mp_bad_exp(mp, "A tertiary"); -@.A tertiary expression...@> - mp_scan_secondary(mp); -CONTINUE: - if ( mp->cur_cmd<=max_tertiary_command ) { - if ( mp->cur_cmd>=min_tertiary_command ) { - p=mp_stash_cur_exp(mp); c=mp->cur_mod; d=mp->cur_cmd; - if ( d==tertiary_secondary_macro ) { - mac_name=mp->cur_sym; add_mac_ref(c); - }; - mp_get_x_next(mp); mp_scan_secondary(mp); - if ( d!=tertiary_secondary_macro ) { - mp_do_binary(mp, p,c); - } else { - mp_back_input(mp); mp_binary_mac(mp, p,c,mac_name); - decr(ref_count(c)); mp_get_x_next(mp); + if ((mp->cur_cmd < min_primary_command) || + (mp->cur_cmd > max_primary_command)) + mp_bad_exp (mp, "A tertiary"); +@.A tertiary expression...@>; + mp_scan_secondary (mp); +CONTINUE: + if (mp->cur_cmd <= max_tertiary_command) { + if (mp->cur_cmd >= min_tertiary_command) { + p = mp_stash_cur_exp (mp); + c = mp->cur_mod; + d = mp->cur_cmd; + if (d == tertiary_secondary_macro) { + cc = mp->cur_mod_node; + mac_name = mp->cur_sym; + add_mac_ref (cc); + } + mp_get_x_next (mp); + mp_scan_secondary (mp); + if (d != tertiary_secondary_macro) { + mp_do_binary (mp, p, c); + } else { + mp_back_input (mp); + mp_binary_mac (mp, p, cc, mac_name); + decr_mac_ref (cc); + mp_get_x_next (mp); goto RESTART; } goto CONTINUE; @@ -18452,6 +20678,7 @@ CONTINUE: } } + @ Finally we reach the deepest level in our quartet of parsing routines. This one is much like the others; but it has an extra complication from paths, which materialize here. @@ -18461,103 +20688,138 @@ paths, which materialize here. @<Declare the basic parsing subroutines@>= static void mp_scan_expression (MP mp) { - pointer p,q,r,pp,qq; /* for list manipulation */ - halfword c,d; /* operation codes or modifiers */ - int my_var_flag; /* initial value of |var_flag| */ - pointer mac_name; /* token defined with \&{tertiarydef} */ - boolean cycle_hit; /* did a path expression just end with `\&{cycle}'? */ - scaled x,y; /* explicit coordinates or tension at a path join */ - int t; /* knot type following a path join */ - t=0; y=0; x=0; - my_var_flag=mp->var_flag; mac_name=null; + mp_node p; /* for list manipulation */ + mp_knot path_p, path_q, r; + mp_knot pp, qq; + halfword c, d; /* operation codes or modifiers */ + mp_node cc = NULL; + int my_var_flag; /* initial value of |var_flag| */ + mp_sym mac_name; /* token defined with \&{tertiarydef} */ + boolean cycle_hit; /* did a path expression just end with `\&{cycle}'? */ + scaled x, y; /* explicit coordinates or tension at a path join */ + int t; /* knot type following a path join */ + mp_value new_expr; + t = 0; + y = 0; + x = 0; + memset(&new_expr,0,sizeof(mp_value)); + my_var_flag = mp->var_flag; + mac_name = NULL; mp->expand_depth_count++; - mp_check_expansion_depth(mp); + mp_check_expansion_depth (mp); RESTART: - if ((mp->cur_cmd<min_primary_command)|| - (mp->cur_cmd>max_primary_command) ) - mp_bad_exp(mp, "An"); -@.An expression...@> - mp_scan_tertiary(mp); -CONTINUE: - if ( mp->cur_cmd<=max_expression_command ) - if ( mp->cur_cmd>=min_expression_command ) { - if ( (mp->cur_cmd!=equals)||(my_var_flag!=assignment) ) { - p=mp_stash_cur_exp(mp); c=mp->cur_mod; d=mp->cur_cmd; - if ( d==expression_tertiary_macro ) { - mac_name=mp->cur_sym; add_mac_ref(c); + if ((mp->cur_cmd < min_primary_command) || + (mp->cur_cmd > max_primary_command)) + mp_bad_exp (mp, "An"); +@.An expression...@>; + mp_scan_tertiary (mp); +CONTINUE: + if (mp->cur_cmd <= max_expression_command) + if (mp->cur_cmd >= min_expression_command) { + if ((mp->cur_cmd != equals) || (my_var_flag != assignment)) { + p = mp_stash_cur_exp (mp); + d = mp->cur_cmd; + c = mp->cur_mod; + if (d == expression_tertiary_macro) { + cc = mp->cur_mod_node; + mac_name = mp->cur_sym; + add_mac_ref (cc); } - if ( (d<ampersand)||((d==ampersand)&& - ((mp_type(p)==mp_pair_type)||(mp_type(p)==mp_path_type))) ) { + if ((d < ampersand) || ((d == ampersand) && + ((mp_type (p) == mp_pair_type) + || (mp_type (p) == mp_path_type)))) { @<Scan a path construction operation; but |return| if |p| has the wrong type@>; - } else { - mp_get_x_next(mp); mp_scan_tertiary(mp); - if ( d!=expression_tertiary_macro ) { - mp_do_binary(mp, p,c); - } else { - mp_back_input(mp); mp_binary_mac(mp, p,c,mac_name); - decr(ref_count(c)); mp_get_x_next(mp); + } else { + mp_get_x_next (mp); + mp_scan_tertiary (mp); + if (d != expression_tertiary_macro) { + mp_do_binary (mp, p, c); + } else { + mp_back_input (mp); + mp_binary_mac (mp, p, cc, mac_name); + decr_mac_ref (cc); + mp_get_x_next (mp); goto RESTART; } } goto CONTINUE; - } - } + } + } mp->expand_depth_count--; } + @ The reader should review the data structure conventions for paths before hoping to understand the next part of this code. @<Scan a path construction operation...@>= -{ - cycle_hit=false; +{ + cycle_hit = false; @<Convert the left operand, |p|, into a partial path ending at~|q|; but |return| if |p| doesn't have a suitable type@>; -CONTINUE_PATH: +CONTINUE_PATH: @<Determine the path join parameters; but |goto finish_path| if there's only a direction specifier@>; - if ( mp->cur_cmd==cycle ) { + if (mp->cur_cmd == cycle) { @<Get ready to close a cycle@>; - } else { - mp_scan_tertiary(mp); + } else { + mp_scan_tertiary (mp); @<Convert the right operand, |cur_exp|, into a partial path from |pp| to~|qq|@>; } @<Join the partial paths and reset |p| and |q| to the head and tail of the result@>; - if ( mp->cur_cmd>=min_expression_command ) - if ( mp->cur_cmd<=ampersand ) if ( ! cycle_hit ) goto CONTINUE_PATH; + if (mp->cur_cmd >= min_expression_command) + if (mp->cur_cmd <= ampersand) + if (!cycle_hit) + goto CONTINUE_PATH; FINISH_PATH: @<Choose control points for the path and put the result into |cur_exp|@>; } + @ @<Convert the left operand, |p|, into a partial path ending at~|q|...@>= -{ - mp_unstash_cur_exp(mp, p); - if ( mp->cur_type==mp_pair_type ) p=mp_new_knot(mp); - else if ( mp->cur_type==mp_path_type ) p=mp->cur_exp; - else return; - q=p; - while ( mp_link(q)!=p ) q=mp_link(q); - if ( mp_left_type(p)!=mp_endpoint ) { /* open up a cycle */ - r=mp_copy_knot(mp, p); mp_link(q)=r; q=r; +{ + mp_unstash_cur_exp (mp, p); + if (mp->cur_exp.type == mp_pair_type) + path_p = mp_pair_to_knot (mp); + else if (mp->cur_exp.type == mp_path_type) + path_p = cur_exp_knot (); + else + return; + path_q = path_p; + while (mp_next_knot (path_q) != path_p) + path_q = mp_next_knot (path_q); + if (mp_left_type (path_p) != mp_endpoint) { /* open up a cycle */ + r = mp_copy_knot (mp, path_p); + mp_next_knot (path_q) = r; + path_q = r; } - mp_left_type(p)=mp_open; mp_right_type(q)=mp_open; + mp_left_type (path_p) = mp_open; + mp_right_type (path_q) = mp_open; } + @ A pair of numeric values is changed into a knot node for a one-point path when \MP\ discovers that the pair is part of a path. -@c -static pointer mp_new_knot (MP mp) { /* convert a pair to a knot with two endpoints */ - pointer q; /* the new node */ - q=mp_get_node(mp, knot_node_size); mp_left_type(q)=mp_endpoint; - mp_right_type(q)=mp_endpoint; mp_originator(q)=mp_metapost_user; mp_link(q)=q; - mp_known_pair(mp); mp_x_coord(q)=mp->cur_x; mp_y_coord(q)=mp->cur_y; +@c +static mp_knot mp_pair_to_knot (MP mp) { /* convert a pair to a knot with two endpoints */ + mp_knot q; /* the new node */ + q = xmalloc (1, sizeof (struct mp_knot_data)); + memset (q, 0, sizeof (struct mp_knot_data)); + mp_left_type (q) = mp_endpoint; + mp_right_type (q) = mp_endpoint; + mp_originator (q) = mp_metapost_user; + mp_next_knot (q) = q; + mp_known_pair (mp); + mp_x_coord (q) = mp->cur_x; + mp_y_coord (q) = mp->cur_y; return q; } + @ The |known_pair| subroutine sets |cur_x| and |cur_y| to the components of the current expression, assuming that the current expression is a pair of known numerics. Unknown components are zeroed, and the @@ -18568,70 +20830,81 @@ static void mp_known_pair (MP mp); @ @c void mp_known_pair (MP mp) { - pointer p; /* the pair node */ - if ( mp->cur_type!=mp_pair_type ) { - exp_err("Undefined coordinates have been replaced by (0,0)"); -@.Undefined coordinates...@> - help5("I need x and y numbers for this part of the path.", - "The value I found (see above) was no good;", - "so I'll try to keep going by using zero instead.", - "(Chapter 27 of The METAFONTbook explains that", + mp_value new_expr; + mp_node p; /* the pair node */ + memset(&new_expr,0,sizeof(mp_value)); + if (mp->cur_exp.type != mp_pair_type) { + exp_err ("Undefined coordinates have been replaced by (0,0)"); +@.Undefined coordinates...@>; + help5 ("I need x and y numbers for this part of the path.", + "The value I found (see above) was no good;", + "so I'll try to keep going by using zero instead.", + "(Chapter 27 of The METAFONTbook explains that", @:METAFONTbook}{\sl The {\logos METAFONT\/}book@> - "you might want to type `I ??" "?' now.)"); - mp_put_get_flush_error(mp, 0); mp->cur_x=0; mp->cur_y=0; - } else { - p=value(mp->cur_exp); - @<Make sure that both |x| and |y| parts of |p| are known; + "you might want to type `I ??" "?' now.)"); + mp_put_get_flush_error (mp, new_expr); + mp->cur_x = 0; + mp->cur_y = 0; + } else { + p = value_node (cur_exp_node ()); + @<Make sure that both |x| and |y| parts of |p| are known; copy them into |cur_x| and |cur_y|@>; - mp_flush_cur_exp(mp, 0); + mp_flush_cur_exp (mp, new_expr); } } + @ @<Make sure that both |x| and |y| parts of |p| are known...@>= -if ( mp_type(x_part_loc(p))==mp_known ) { - mp->cur_x=value(x_part_loc(p)); -} else { - mp_disp_err(mp, x_part_loc(p), - "Undefined x coordinate has been replaced by 0"); -@.Undefined coordinates...@> - help5("I need a `known' x value for this part of the path.", - "The value I found (see above) was no good;", - "so I'll try to keep going by using zero instead.", - "(Chapter 27 of The METAFONTbook explains that", +if (mp_type (x_part_loc (p)) == mp_known) { + mp->cur_x = value (x_part_loc (p)); +} else { + mp_disp_err (mp, x_part_loc (p), + "Undefined x coordinate has been replaced by 0"); +@.Undefined coordinates...@>; + help5 ("I need a `known' x value for this part of the path.", + "The value I found (see above) was no good;", + "so I'll try to keep going by using zero instead.", + "(Chapter 27 of The METAFONTbook explains that", @:METAFONTbook}{\sl The {\logos METAFONT\/}book@> - "you might want to type `I ??" "?' now.)"); - mp_put_get_error(mp); mp_recycle_value(mp, x_part_loc(p)); mp->cur_x=0; -} -if ( mp_type(y_part_loc(p))==mp_known ) { - mp->cur_y=value(y_part_loc(p)); -} else { - mp_disp_err(mp, y_part_loc(p), - "Undefined y coordinate has been replaced by 0"); - help5("I need a `known' y value for this part of the path.", - "The value I found (see above) was no good;", - "so I'll try to keep going by using zero instead.", - "(Chapter 27 of The METAFONTbook explains that", - "you might want to type `I ??" "?' now.)"); - mp_put_get_error(mp); mp_recycle_value(mp, y_part_loc(p)); mp->cur_y=0; + "you might want to type `I ??" "?' now.)"); + mp_put_get_error (mp); + mp_recycle_value (mp, x_part_loc (p)); + mp->cur_x = 0; +} +if (mp_type (y_part_loc (p)) == mp_known) { + mp->cur_y = value (y_part_loc (p)); +} else { + mp_disp_err (mp, y_part_loc (p), + "Undefined y coordinate has been replaced by 0"); + help5 ("I need a `known' y value for this part of the path.", + "The value I found (see above) was no good;", + "so I'll try to keep going by using zero instead.", + "(Chapter 27 of The METAFONTbook explains that", + "you might want to type `I ??" "?' now.)"); + mp_put_get_error (mp); + mp_recycle_value (mp, y_part_loc (p)); + mp->cur_y = 0; } + @ At this point |cur_cmd| is either |ampersand|, |left_brace|, or |path_join|. @<Determine the path join parameters...@>= -if ( mp->cur_cmd==left_brace ) { +if (mp->cur_cmd == left_brace) { @<Put the pre-join direction information into node |q|@>; } -d=mp->cur_cmd; -if ( d==path_join ) { +d = mp->cur_cmd; +if (d == path_join) { @<Determine the tension and/or control points@>; -} else if ( d!=ampersand ) { +} else if (d != ampersand) { goto FINISH_PATH; } -mp_get_x_next(mp); -if ( mp->cur_cmd==left_brace ) { +mp_get_x_next (mp); +if (mp->cur_cmd == left_brace) { @<Put the post-join direction information into |x| and |t|@>; -} else if ( mp_right_type(q)!=mp_explicit ) { - t=mp_open; x=0; +} else if (mp_right_type (path_q) != mp_explicit) { + t = mp_open; + x = 0; } @ The |scan_direction| subroutine looks at the directional information @@ -18644,281 +20917,374 @@ appears in |cur_exp|). There's nothing difficult about this subroutine, but the program is rather lengthy because a variety of potential errors need to be nipped in the bud. -@c +@c static quarterword mp_scan_direction (MP mp) { - int t; /* the type of information found */ - scaled x; /* an |x| coordinate */ - mp_get_x_next(mp); - if ( mp->cur_cmd==curl_command ) { - @<Scan a curl specification@>; + int t; /* the type of information found */ + scaled x; /* an |x| coordinate */ + mp_get_x_next (mp); + if (mp->cur_cmd == curl_command) { + @<Scan a curl specification@>; } else { @<Scan a given direction@>; } - if ( mp->cur_cmd!=right_brace ) { - mp_missing_err(mp, "}"); -@.Missing `\char`\}'@> - help3("I've scanned a direction spec for part of a path,", - "so a right brace should have come next.", - "I shall pretend that one was there."); - mp_back_error(mp); + if (mp->cur_cmd != right_brace) { + mp_missing_err (mp, "}"); +@.Missing `\char`\}'@>; + help3 ("I've scanned a direction spec for part of a path,", + "so a right brace should have come next.", + "I shall pretend that one was there."); + mp_back_error (mp); } - mp_get_x_next(mp); - return (quarterword)t; + mp_get_x_next (mp); + return (quarterword) t; } + @ @<Scan a curl specification@>= -{ mp_get_x_next(mp); mp_scan_expression(mp); -if ( (mp->cur_type!=mp_known)||(mp->cur_exp<0) ){ - exp_err("Improper curl has been replaced by 1"); -@.Improper curl@> - help1("A curl must be a known, nonnegative number."); - mp_put_get_flush_error(mp, unity); -} -t=mp_curl; +{ + mp_get_x_next (mp); + mp_scan_expression (mp); + if ((mp->cur_exp.type != mp_known) || (cur_exp_value () < 0)) { + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + new_expr.data.val = unity; + exp_err ("Improper curl has been replaced by 1"); +@.Improper curl@>; + help1 ("A curl must be a known, nonnegative number."); + mp_put_get_flush_error (mp, new_expr); + } + t = mp_curl; } + @ @<Scan a given direction@>= -{ mp_scan_expression(mp); - if ( mp->cur_type>mp_pair_type ) { +{ + mp_scan_expression (mp); + if (mp->cur_exp.type > mp_pair_type) { @<Get given directions separated by commas@>; } else { - mp_known_pair(mp); + mp_known_pair (mp); + } + if ((mp->cur_x == 0) && (mp->cur_y == 0)) + t = mp_open; + else { + t = mp_given; + set_cur_exp_value (mp_n_arg (mp, mp->cur_x, mp->cur_y)); } - if ( (mp->cur_x==0)&&(mp->cur_y==0) ) t=mp_open; - else { t=mp_given; mp->cur_exp=mp_n_arg(mp, mp->cur_x,mp->cur_y);} } + @ @<Get given directions separated by commas@>= -{ - if ( mp->cur_type!=mp_known ) { - exp_err("Undefined x coordinate has been replaced by 0"); -@.Undefined coordinates...@> - help5("I need a `known' x value for this part of the path.", - "The value I found (see above) was no good;", - "so I'll try to keep going by using zero instead.", - "(Chapter 27 of The METAFONTbook explains that", +{ + if (mp->cur_exp.type != mp_known) { + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + new_expr.data.val = 0; + exp_err ("Undefined x coordinate has been replaced by 0"); +@.Undefined coordinates...@>; + help5 ("I need a `known' x value for this part of the path.", + "The value I found (see above) was no good;", + "so I'll try to keep going by using zero instead.", + "(Chapter 27 of The METAFONTbook explains that", @:METAFONTbook}{\sl The {\logos METAFONT\/}book@> - "you might want to type `I ??" "?' now.)"); - mp_put_get_flush_error(mp, 0); - } - x=mp->cur_exp; - if ( mp->cur_cmd!=comma ) { - mp_missing_err(mp, ","); -@.Missing `,'@> - help2("I've got the x coordinate of a path direction;", - "will look for the y coordinate next."); - mp_back_error(mp); - } - mp_get_x_next(mp); mp_scan_expression(mp); - if ( mp->cur_type!=mp_known ) { - exp_err("Undefined y coordinate has been replaced by 0"); - help5("I need a `known' y value for this part of the path.", - "The value I found (see above) was no good;", - "so I'll try to keep going by using zero instead.", - "(Chapter 27 of The METAFONTbook explains that", - "you might want to type `I ??" "?' now.)"); - mp_put_get_flush_error(mp, 0); - } - mp->cur_y=mp->cur_exp; mp->cur_x=x; + "you might want to type `I ??" "?' now.)"); + mp_put_get_flush_error (mp, new_expr); + } + x = cur_exp_value (); + if (mp->cur_cmd != comma) { + mp_missing_err (mp, ","); +@.Missing `,'@>; + help2 ("I've got the x coordinate of a path direction;", + "will look for the y coordinate next."); + mp_back_error (mp); + } + mp_get_x_next (mp); + mp_scan_expression (mp); + if (mp->cur_exp.type != mp_known) { + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + new_expr.data.val = 0; + exp_err ("Undefined y coordinate has been replaced by 0"); + help5 ("I need a `known' y value for this part of the path.", + "The value I found (see above) was no good;", + "so I'll try to keep going by using zero instead.", + "(Chapter 27 of The METAFONTbook explains that", + "you might want to type `I ??" "?' now.)"); + mp_put_get_flush_error (mp, new_expr); + } + mp->cur_y = cur_exp_value (); + mp->cur_x = x; } + @ At this point |mp_right_type(q)| is usually |open|, but it may have been set to some other value by a previous operation. We must maintain the value of |mp_right_type(q)| in cases such as `\.{..\{curl2\}z\{0,0\}..}'. @<Put the pre-join...@>= -{ - t=mp_scan_direction(mp); - if ( t!=mp_open ) { - mp_right_type(q)=(quarterword)t; right_given(q)=mp->cur_exp; - if ( mp_left_type(q)==mp_open ) { - mp_left_type(q)=(quarterword)t; left_given(q)=mp->cur_exp; - } /* note that |left_given(q)=left_curl(q)| */ +{ + t = mp_scan_direction (mp); + if (t != mp_open) { + mp_right_type (path_q) = (unsigned short) t; + right_given (path_q) = cur_exp_value (); + if (mp_left_type (path_q) == mp_open) { + mp_left_type (path_q) = (unsigned short) t; + left_given (path_q) = cur_exp_value (); + } /* note that |left_given(q)=left_curl(q)| */ } } + @ Since |left_tension| and |mp_left_y| share the same position in knot nodes, and since |left_given| is similarly equivalent to |mp_left_x|, we use |x| and |y| to hold the given direction and tension information when there are no explicit control points. @<Put the post-join...@>= -{ - t=mp_scan_direction(mp); - if ( mp_right_type(q)!=mp_explicit ) x=mp->cur_exp; - else t=mp_explicit; /* the direction information is superfluous */ +{ + t = mp_scan_direction (mp); + if (mp_right_type (path_q) != mp_explicit) + x = cur_exp_value (); + else + t = mp_explicit; /* the direction information is superfluous */ } + @ @<Determine the tension and/or...@>= -{ - mp_get_x_next(mp); - if ( mp->cur_cmd==tension ) { +{ + mp_get_x_next (mp); + if (mp->cur_cmd == tension) { @<Set explicit tensions@>; - } else if ( mp->cur_cmd==controls ) { + } else if (mp->cur_cmd == controls) { @<Set explicit control points@>; - } else { - right_tension(q)=unity; y=unity; mp_back_input(mp); /* default tension */ + } else { + right_tension (path_q) = unity; + y = unity; + mp_back_input (mp); /* default tension */ goto DONE; }; - if ( mp->cur_cmd!=path_join ) { - mp_missing_err(mp, ".."); -@.Missing `..'@> - help1("A path join command should end with two dots."); - mp_back_error(mp); + if (mp->cur_cmd != path_join) { + mp_missing_err (mp, ".."); +@.Missing `..'@>; + help1 ("A path join command should end with two dots."); + mp_back_error (mp); } DONE: ; } + @ @<Set explicit tensions@>= -{ - mp_get_x_next(mp); y=mp->cur_cmd; - if ( mp->cur_cmd==at_least ) mp_get_x_next(mp); - mp_scan_primary(mp); +{ + mp_get_x_next (mp); + y = mp->cur_cmd; + if (mp->cur_cmd == at_least) + mp_get_x_next (mp); + mp_scan_primary (mp); @<Make sure that the current expression is a valid tension setting@>; - if ( y==at_least ) negate(mp->cur_exp); - right_tension(q)=mp->cur_exp; - if ( mp->cur_cmd==and_command ) { - mp_get_x_next(mp); y=mp->cur_cmd; - if ( mp->cur_cmd==at_least ) mp_get_x_next(mp); - mp_scan_primary(mp); + if (y == at_least) + negate (cur_exp_value ()); + right_tension (path_q) = cur_exp_value (); + if (mp->cur_cmd == and_command) { + mp_get_x_next (mp); + y = mp->cur_cmd; + if (mp->cur_cmd == at_least) + mp_get_x_next (mp); + mp_scan_primary (mp); @<Make sure that the current expression is a valid tension setting@>; - if ( y==at_least ) negate(mp->cur_exp); + if (y == at_least) + negate (cur_exp_value ()); } - y=mp->cur_exp; + y = cur_exp_value (); } + @ @d min_tension three_quarter_unit @<Make sure that the current expression is a valid tension setting@>= -if ( (mp->cur_type!=mp_known)||(mp->cur_exp<min_tension) ) { - exp_err("Improper tension has been set to 1"); -@.Improper tension@> - help1("The expression above should have been a number >=3/4."); - mp_put_get_flush_error(mp, unity); +if ((mp->cur_exp.type != mp_known) || (cur_exp_value () < min_tension)) { + exp_err ("Improper tension has been set to 1"); +@.Improper tension@>; + help1 ("The expression above should have been a number >=3/4."); + new_expr.data.val = unity; + mp_put_get_flush_error (mp, new_expr); } @ @<Set explicit control points@>= -{ - mp_right_type(q)=mp_explicit; t=mp_explicit; mp_get_x_next(mp); mp_scan_primary(mp); - mp_known_pair(mp); mp_right_x(q)=mp->cur_x; mp_right_y(q)=mp->cur_y; - if ( mp->cur_cmd!=and_command ) { - x=mp_right_x(q); y=mp_right_y(q); - } else { - mp_get_x_next(mp); mp_scan_primary(mp); - mp_known_pair(mp); x=mp->cur_x; y=mp->cur_y; +{ + mp_right_type (path_q) = mp_explicit; + t = mp_explicit; + mp_get_x_next (mp); + mp_scan_primary (mp); + mp_known_pair (mp); + mp_right_x (path_q) = mp->cur_x; + mp_right_y (path_q) = mp->cur_y; + if (mp->cur_cmd != and_command) { + x = mp_right_x (path_q); + y = mp_right_y (path_q); + } else { + mp_get_x_next (mp); + mp_scan_primary (mp); + mp_known_pair (mp); + x = mp->cur_x; + y = mp->cur_y; } } + @ @<Convert the right operand, |cur_exp|, into a partial path...@>= -{ - if ( mp->cur_type!=mp_path_type ) pp=mp_new_knot(mp); - else pp=mp->cur_exp; - qq=pp; - while ( mp_link(qq)!=pp ) qq=mp_link(qq); - if ( mp_left_type(pp)!=mp_endpoint ) { /* open up a cycle */ - r=mp_copy_knot(mp, pp); mp_link(qq)=r; qq=r; +{ + if (mp->cur_exp.type != mp_path_type) + pp = mp_pair_to_knot (mp); + else + pp = cur_exp_knot (); + qq = pp; + while (mp_next_knot (qq) != pp) + qq = mp_next_knot (qq); + if (mp_left_type (pp) != mp_endpoint) { /* open up a cycle */ + r = mp_copy_knot (mp, pp); + mp_next_knot (qq) = r; + qq = r; } - mp_left_type(pp)=mp_open; mp_right_type(qq)=mp_open; + mp_left_type (pp) = mp_open; + mp_right_type (qq) = mp_open; } + @ If a person tries to define an entire path by saying `\.{(x,y)\&cycle}', we silently change the specification to `\.{(x,y)..cycle}', since a cycle shouldn't have length zero. @<Get ready to close a cycle@>= -{ - cycle_hit=true; mp_get_x_next(mp); pp=p; qq=p; - if ( d==ampersand ) if ( p==q ) { - d=path_join; right_tension(q)=unity; y=unity; - } +{ + cycle_hit = true; + mp_get_x_next (mp); + pp = path_p; + qq = path_p; + if (d == ampersand) + if (path_p == path_q) { + d = path_join; + right_tension (path_q) = unity; + y = unity; + } } + @ @<Join the partial paths and reset |p| and |q|...@>= -{ -if ( d==ampersand ) { - if ( (mp_x_coord(q)!=mp_x_coord(pp))||(mp_y_coord(q)!=mp_y_coord(pp)) ) { - print_err("Paths don't touch; `&' will be changed to `..'"); -@.Paths don't touch@> - help3("When you join paths `p&q', the ending point of p", - "must be exactly equal to the starting point of q.", - "So I'm going to pretend that you said `p..q' instead."); - mp_put_get_error(mp); d=path_join; right_tension(q)=unity; y=unity; +{ + if (d == ampersand) { + if ((mp_x_coord (path_q) != mp_x_coord (pp)) || + (mp_y_coord (path_q) != mp_y_coord (pp))) { + print_err ("Paths don't touch; `&' will be changed to `..'"); +@.Paths don't touch@>; + help3 ("When you join paths `p&q', the ending point of p", + "must be exactly equal to the starting point of q.", + "So I'm going to pretend that you said `p..q' instead."); + mp_put_get_error (mp); + d = path_join; + right_tension (path_q) = unity; + y = unity; + } } + @<Plug an opening in |mp_right_type(pp)|, if possible@>; + if (d == ampersand) { + @<Splice independent paths together@>; + } else { + @<Plug an opening in |mp_right_type(q)|, if possible@>; + mp_next_knot (path_q) = pp; + mp_left_y (pp) = y; + if (t != mp_open) { + mp_left_x (pp) = x; + mp_left_type (pp) = (unsigned short) t; + }; + } + path_q = qq; } -@<Plug an opening in |mp_right_type(pp)|, if possible@>; -if ( d==ampersand ) { - @<Splice independent paths together@>; -} else { - @<Plug an opening in |mp_right_type(q)|, if possible@>; - mp_link(q)=pp; mp_left_y(pp)=y; - if ( t!=mp_open ) { mp_left_x(pp)=x; mp_left_type(pp)=(quarterword)t; }; -} -q=qq; -} + @ @<Plug an opening in |mp_right_type(q)|...@>= -if ( mp_right_type(q)==mp_open ) { - if ( (mp_left_type(q)==mp_curl)||(mp_left_type(q)==mp_given) ) { - mp_right_type(q)=mp_left_type(q); right_given(q)=left_given(q); +if (mp_right_type (path_q) == mp_open) { + if ((mp_left_type (path_q) == mp_curl) || (mp_left_type (path_q) == mp_given)) { + mp_right_type (path_q) = mp_left_type (path_q); + right_given (path_q) = left_given (path_q); } } @ @<Plug an opening in |mp_right_type(pp)|...@>= -if ( mp_right_type(pp)==mp_open ) { - if ( (t==mp_curl)||(t==mp_given) ) { - mp_right_type(pp)=(quarterword)t; right_given(pp)=x; +if (mp_right_type (pp) == mp_open) { + if ((t == mp_curl) || (t == mp_given)) { + mp_right_type (pp) = (unsigned short) t; + right_given (pp) = x; } } @ @<Splice independent paths together@>= -{ - if ( mp_left_type(q)==mp_open ) if ( mp_right_type(q)==mp_open ) { - mp_left_type(q)=mp_curl; left_curl(q)=unity; - } - if ( mp_right_type(pp)==mp_open ) if ( t==mp_open ) { - mp_right_type(pp)=mp_curl; right_curl(pp)=unity; - } - mp_right_type(q)=mp_right_type(pp); mp_link(q)=mp_link(pp); - mp_right_x(q)=mp_right_x(pp); mp_right_y(q)=mp_right_y(pp); - mp_free_node(mp, pp,knot_node_size); - if ( qq==pp ) qq=q; +{ + if (mp_left_type (path_q) == mp_open) + if (mp_right_type (path_q) == mp_open) { + mp_left_type (path_q) = mp_curl; + left_curl (path_q) = unity; + } + if (mp_right_type (pp) == mp_open) + if (t == mp_open) { + mp_right_type (pp) = mp_curl; + right_curl (pp) = unity; + } + mp_right_type (path_q) = mp_right_type (pp); + mp_next_knot (path_q) = mp_next_knot (pp); + mp_right_x (path_q) = mp_right_x (pp); + mp_right_y (path_q) = mp_right_y (pp); + mp_xfree (pp); + if (qq == pp) + qq = path_q; } + @ @<Choose control points for the path...@>= -if ( cycle_hit ) { - if ( d==ampersand ) p=q; -} else { - mp_left_type(p)=mp_endpoint; - if ( mp_right_type(p)==mp_open ) { - mp_right_type(p)=mp_curl; right_curl(p)=unity; +if (cycle_hit) { + if (d == ampersand) + path_p = path_q; +} else { + mp_left_type (path_p) = mp_endpoint; + if (mp_right_type (path_p) == mp_open) { + mp_right_type (path_p) = mp_curl; + right_curl (path_p) = unity; } - mp_right_type(q)=mp_endpoint; - if ( mp_left_type(q)==mp_open ) { - mp_left_type(q)=mp_curl; left_curl(q)=unity; + mp_right_type (path_q) = mp_endpoint; + if (mp_left_type (path_q) == mp_open) { + mp_left_type (path_q) = mp_curl; + left_curl (path_q) = unity; } - mp_link(q)=p; + mp_next_knot (path_q) = path_p; } -mp_make_choices(mp, p); -mp->cur_type=mp_path_type; mp->cur_exp=p +mp_make_choices (mp, path_p); +mp->cur_exp.type = mp_path_type; +set_cur_exp_knot (path_p) + @ Finally, we sometimes need to scan an expression whose value is supposed to be either |true_code| or |false_code|. @<Declare the basic parsing subroutines@>= -static void mp_get_boolean (MP mp) { - mp_get_x_next(mp); mp_scan_expression(mp); - if ( mp->cur_type!=mp_boolean_type ) { - exp_err("Undefined condition will be treated as `false'"); -@.Undefined condition...@> - help2("The expression shown above should have had a definite", - "true-or-false value. I'm changing it to `false'."); - mp_put_get_flush_error(mp, false_code); mp->cur_type=mp_boolean_type; +static void mp_get_boolean (MP mp) { + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + mp_get_x_next (mp); + mp_scan_expression (mp); + if (mp->cur_exp.type != mp_boolean_type) { + exp_err ("Undefined condition will be treated as `false'"); +@.Undefined condition...@>; + help2 ("The expression shown above should have had a definite", + "true-or-false value. I'm changing it to `false'."); + new_expr.data.val = false_code; + mp_put_get_flush_error (mp, new_expr); + mp->cur_exp.type = mp_boolean_type; } } -@* \[39] Doing the operations. + +@* Doing the operations. The purpose of parsing is primarily to permit people to avoid piles of parentheses. But the real work is done after the structure of an expression has been recognized; that's when new expressions are generated. We @@ -18937,206 +21303,208 @@ on the \\{mod} code. For example, |do_binary| doesn't care whether the operation it performs is a |primary_binary| or |secondary_binary|, etc. @<Put each...@>= -mp_primitive(mp, "true",nullary,true_code); -@:true_}{\&{true} primitive@> -mp_primitive(mp, "false",nullary,false_code); -@:false_}{\&{false} primitive@> -mp_primitive(mp, "nullpicture",nullary,null_picture_code); -@:null_picture_}{\&{nullpicture} primitive@> -mp_primitive(mp, "nullpen",nullary,null_pen_code); -@:null_pen_}{\&{nullpen} primitive@> -mp_primitive(mp, "readstring",nullary,read_string_op); -@:read_string_}{\&{readstring} primitive@> -mp_primitive(mp, "pencircle",nullary,pen_circle); -@:pen_circle_}{\&{pencircle} primitive@> -mp_primitive(mp, "normaldeviate",nullary,normal_deviate); -@:normal_deviate_}{\&{normaldeviate} primitive@> -mp_primitive(mp, "readfrom",unary,read_from_op); -@:read_from_}{\&{readfrom} primitive@> -mp_primitive(mp, "closefrom",unary,close_from_op); -@:close_from_}{\&{closefrom} primitive@> -mp_primitive(mp, "odd",unary,odd_op); -@:odd_}{\&{odd} primitive@> -mp_primitive(mp, "known",unary,known_op); -@:known_}{\&{known} primitive@> -mp_primitive(mp, "unknown",unary,unknown_op); -@:unknown_}{\&{unknown} primitive@> -mp_primitive(mp, "not",unary,not_op); -@:not_}{\&{not} primitive@> -mp_primitive(mp, "decimal",unary,decimal); -@:decimal_}{\&{decimal} primitive@> -mp_primitive(mp, "reverse",unary,reverse); -@:reverse_}{\&{reverse} primitive@> -mp_primitive(mp, "makepath",unary,make_path_op); -@:make_path_}{\&{makepath} primitive@> -mp_primitive(mp, "makepen",unary,make_pen_op); -@:make_pen_}{\&{makepen} primitive@> -mp_primitive(mp, "oct",unary,oct_op); -@:oct_}{\&{oct} primitive@> -mp_primitive(mp, "hex",unary,hex_op); -@:hex_}{\&{hex} primitive@> -mp_primitive(mp, "ASCII",unary,ASCII_op); -@:ASCII_}{\&{ASCII} primitive@> -mp_primitive(mp, "char",unary,char_op); -@:char_}{\&{char} primitive@> -mp_primitive(mp, "length",unary,length_op); -@:length_}{\&{length} primitive@> -mp_primitive(mp, "turningnumber",unary,turning_op); -@:turning_number_}{\&{turningnumber} primitive@> -mp_primitive(mp, "xpart",unary,x_part); -@:x_part_}{\&{xpart} primitive@> -mp_primitive(mp, "ypart",unary,y_part); -@:y_part_}{\&{ypart} primitive@> -mp_primitive(mp, "xxpart",unary,xx_part); -@:xx_part_}{\&{xxpart} primitive@> -mp_primitive(mp, "xypart",unary,xy_part); -@:xy_part_}{\&{xypart} primitive@> -mp_primitive(mp, "yxpart",unary,yx_part); -@:yx_part_}{\&{yxpart} primitive@> -mp_primitive(mp, "yypart",unary,yy_part); -@:yy_part_}{\&{yypart} primitive@> -mp_primitive(mp, "redpart",unary,red_part); -@:red_part_}{\&{redpart} primitive@> -mp_primitive(mp, "greenpart",unary,green_part); -@:green_part_}{\&{greenpart} primitive@> -mp_primitive(mp, "bluepart",unary,blue_part); -@:blue_part_}{\&{bluepart} primitive@> -mp_primitive(mp, "cyanpart",unary,cyan_part); -@:cyan_part_}{\&{cyanpart} primitive@> -mp_primitive(mp, "magentapart",unary,magenta_part); -@:magenta_part_}{\&{magentapart} primitive@> -mp_primitive(mp, "yellowpart",unary,yellow_part); -@:yellow_part_}{\&{yellowpart} primitive@> -mp_primitive(mp, "blackpart",unary,black_part); -@:black_part_}{\&{blackpart} primitive@> -mp_primitive(mp, "greypart",unary,grey_part); -@:grey_part_}{\&{greypart} primitive@> -mp_primitive(mp, "colormodel",unary,color_model_part); -@:color_model_part_}{\&{colormodel} primitive@> -mp_primitive(mp, "fontpart",unary,font_part); -@:font_part_}{\&{fontpart} primitive@> -mp_primitive(mp, "textpart",unary,text_part); -@:text_part_}{\&{textpart} primitive@> -mp_primitive(mp, "pathpart",unary,path_part); -@:path_part_}{\&{pathpart} primitive@> -mp_primitive(mp, "penpart",unary,pen_part); -@:pen_part_}{\&{penpart} primitive@> -mp_primitive(mp, "dashpart",unary,dash_part); -@:dash_part_}{\&{dashpart} primitive@> -mp_primitive(mp, "sqrt",unary,sqrt_op); -@:sqrt_}{\&{sqrt} primitive@> -mp_primitive(mp, "mexp",unary,mp_m_exp_op); -@:m_exp_}{\&{mexp} primitive@> -mp_primitive(mp, "mlog",unary,mp_m_log_op); -@:m_log_}{\&{mlog} primitive@> -mp_primitive(mp, "sind",unary,sin_d_op); -@:sin_d_}{\&{sind} primitive@> -mp_primitive(mp, "cosd",unary,cos_d_op); -@:cos_d_}{\&{cosd} primitive@> -mp_primitive(mp, "floor",unary,floor_op); -@:floor_}{\&{floor} primitive@> -mp_primitive(mp, "uniformdeviate",unary,uniform_deviate); -@:uniform_deviate_}{\&{uniformdeviate} primitive@> -mp_primitive(mp, "charexists",unary,char_exists_op); -@:char_exists_}{\&{charexists} primitive@> -mp_primitive(mp, "fontsize",unary,font_size); -@:font_size_}{\&{fontsize} primitive@> -mp_primitive(mp, "llcorner",unary,ll_corner_op); -@:ll_corner_}{\&{llcorner} primitive@> -mp_primitive(mp, "lrcorner",unary,lr_corner_op); -@:lr_corner_}{\&{lrcorner} primitive@> -mp_primitive(mp, "ulcorner",unary,ul_corner_op); -@:ul_corner_}{\&{ulcorner} primitive@> -mp_primitive(mp, "urcorner",unary,ur_corner_op); -@:ur_corner_}{\&{urcorner} primitive@> -mp_primitive(mp, "arclength",unary,arc_length); -@:arc_length_}{\&{arclength} primitive@> -mp_primitive(mp, "angle",unary,angle_op); -@:angle_}{\&{angle} primitive@> -mp_primitive(mp, "cycle",cycle,cycle_op); -@:cycle_}{\&{cycle} primitive@> -mp_primitive(mp, "stroked",unary,stroked_op); -@:stroked_}{\&{stroked} primitive@> -mp_primitive(mp, "filled",unary,filled_op); -@:filled_}{\&{filled} primitive@> -mp_primitive(mp, "textual",unary,textual_op); -@:textual_}{\&{textual} primitive@> -mp_primitive(mp, "clipped",unary,clipped_op); -@:clipped_}{\&{clipped} primitive@> -mp_primitive(mp, "bounded",unary,bounded_op); -@:bounded_}{\&{bounded} primitive@> -mp_primitive(mp, "+",plus_or_minus,plus); -@:+ }{\.{+} primitive@> -mp_primitive(mp, "-",plus_or_minus,minus); -@:- }{\.{-} primitive@> -mp_primitive(mp, "*",secondary_binary,times); -@:* }{\.{*} primitive@> -mp_primitive(mp, "/",slash,over); mp->eqtb[frozen_slash]=mp->eqtb[mp->cur_sym]; -@:/ }{\.{/} primitive@> -mp_primitive(mp, "++",tertiary_binary,pythag_add); -@:++_}{\.{++} primitive@> -mp_primitive(mp, "+-+",tertiary_binary,pythag_sub); -@:+-+_}{\.{+-+} primitive@> -mp_primitive(mp, "or",tertiary_binary,or_op); -@:or_}{\&{or} primitive@> -mp_primitive(mp, "and",and_command,and_op); -@:and_}{\&{and} primitive@> -mp_primitive(mp, "<",expression_binary,less_than); -@:< }{\.{<} primitive@> -mp_primitive(mp, "<=",expression_binary,less_or_equal); -@:<=_}{\.{<=} primitive@> -mp_primitive(mp, ">",expression_binary,greater_than); -@:> }{\.{>} primitive@> -mp_primitive(mp, ">=",expression_binary,greater_or_equal); -@:>=_}{\.{>=} primitive@> -mp_primitive(mp, "=",equals,equal_to); -@:= }{\.{=} primitive@> -mp_primitive(mp, "<>",expression_binary,unequal_to); -@:<>_}{\.{<>} primitive@> -mp_primitive(mp, "substring",primary_binary,substring_of); -@:substring_}{\&{substring} primitive@> -mp_primitive(mp, "subpath",primary_binary,subpath_of); -@:subpath_}{\&{subpath} primitive@> -mp_primitive(mp, "directiontime",primary_binary,direction_time_of); -@:direction_time_}{\&{directiontime} primitive@> -mp_primitive(mp, "point",primary_binary,point_of); -@:point_}{\&{point} primitive@> -mp_primitive(mp, "precontrol",primary_binary,precontrol_of); -@:precontrol_}{\&{precontrol} primitive@> -mp_primitive(mp, "postcontrol",primary_binary,postcontrol_of); -@:postcontrol_}{\&{postcontrol} primitive@> -mp_primitive(mp, "penoffset",primary_binary,pen_offset_of); -@:pen_offset_}{\&{penoffset} primitive@> -mp_primitive(mp, "arctime",primary_binary,arc_time_of); -@:arc_time_of_}{\&{arctime} primitive@> -mp_primitive(mp, "mpversion",nullary,mp_version); -@:mp_verison_}{\&{mpversion} primitive@> -mp_primitive(mp, "&",ampersand,concatenate); -@:!!!}{\.{\&} primitive@> -mp_primitive(mp, "rotated",secondary_binary,rotated_by); -@:rotated_}{\&{rotated} primitive@> -mp_primitive(mp, "slanted",secondary_binary,slanted_by); -@:slanted_}{\&{slanted} primitive@> -mp_primitive(mp, "scaled",secondary_binary,scaled_by); -@:scaled_}{\&{scaled} primitive@> -mp_primitive(mp, "shifted",secondary_binary,shifted_by); -@:shifted_}{\&{shifted} primitive@> -mp_primitive(mp, "transformed",secondary_binary,transformed_by); -@:transformed_}{\&{transformed} primitive@> -mp_primitive(mp, "xscaled",secondary_binary,x_scaled); -@:x_scaled_}{\&{xscaled} primitive@> -mp_primitive(mp, "yscaled",secondary_binary,y_scaled); -@:y_scaled_}{\&{yscaled} primitive@> -mp_primitive(mp, "zscaled",secondary_binary,z_scaled); -@:z_scaled_}{\&{zscaled} primitive@> -mp_primitive(mp, "infont",secondary_binary,in_font); -@:in_font_}{\&{infont} primitive@> -mp_primitive(mp, "intersectiontimes",tertiary_binary,intersect); -@:intersection_times_}{\&{intersectiontimes} primitive@> -mp_primitive(mp, "envelope",primary_binary,envelope_of); -@:envelope_}{\&{envelope} primitive@> -mp_primitive(mp, "glyph",primary_binary,glyph_infont); +mp_primitive (mp, "true", nullary, true_code); +@:true_}{\&{true} primitive@>; +mp_primitive (mp, "false", nullary, false_code); +@:false_}{\&{false} primitive@>; +mp_primitive (mp, "nullpicture", nullary, null_picture_code); +@:null_picture_}{\&{nullpicture} primitive@>; +mp_primitive (mp, "nullpen", nullary, null_pen_code); +@:null_pen_}{\&{nullpen} primitive@>; +mp_primitive (mp, "readstring", nullary, read_string_op); +@:read_string_}{\&{readstring} primitive@>; +mp_primitive (mp, "pencircle", nullary, pen_circle); +@:pen_circle_}{\&{pencircle} primitive@>; +mp_primitive (mp, "normaldeviate", nullary, normal_deviate); +@:normal_deviate_}{\&{normaldeviate} primitive@>; +mp_primitive (mp, "readfrom", unary, read_from_op); +@:read_from_}{\&{readfrom} primitive@>; +mp_primitive (mp, "closefrom", unary, close_from_op); +@:close_from_}{\&{closefrom} primitive@>; +mp_primitive (mp, "odd", unary, odd_op); +@:odd_}{\&{odd} primitive@>; +mp_primitive (mp, "known", unary, known_op); +@:known_}{\&{known} primitive@>; +mp_primitive (mp, "unknown", unary, unknown_op); +@:unknown_}{\&{unknown} primitive@>; +mp_primitive (mp, "not", unary, not_op); +@:not_}{\&{not} primitive@>; +mp_primitive (mp, "decimal", unary, decimal); +@:decimal_}{\&{decimal} primitive@>; +mp_primitive (mp, "reverse", unary, reverse); +@:reverse_}{\&{reverse} primitive@>; +mp_primitive (mp, "makepath", unary, make_path_op); +@:make_path_}{\&{makepath} primitive@>; +mp_primitive (mp, "makepen", unary, make_pen_op); +@:make_pen_}{\&{makepen} primitive@>; +mp_primitive (mp, "oct", unary, oct_op); +@:oct_}{\&{oct} primitive@>; +mp_primitive (mp, "hex", unary, hex_op); +@:hex_}{\&{hex} primitive@>; +mp_primitive (mp, "ASCII", unary, ASCII_op); +@:ASCII_}{\&{ASCII} primitive@>; +mp_primitive (mp, "char", unary, char_op); +@:char_}{\&{char} primitive@>; +mp_primitive (mp, "length", unary, length_op); +@:length_}{\&{length} primitive@>; +mp_primitive (mp, "turningnumber", unary, turning_op); +@:turning_number_}{\&{turningnumber} primitive@>; +mp_primitive (mp, "xpart", unary, x_part); +@:x_part_}{\&{xpart} primitive@>; +mp_primitive (mp, "ypart", unary, y_part); +@:y_part_}{\&{ypart} primitive@>; +mp_primitive (mp, "xxpart", unary, xx_part); +@:xx_part_}{\&{xxpart} primitive@>; +mp_primitive (mp, "xypart", unary, xy_part); +@:xy_part_}{\&{xypart} primitive@>; +mp_primitive (mp, "yxpart", unary, yx_part); +@:yx_part_}{\&{yxpart} primitive@>; +mp_primitive (mp, "yypart", unary, yy_part); +@:yy_part_}{\&{yypart} primitive@>; +mp_primitive (mp, "redpart", unary, red_part); +@:red_part_}{\&{redpart} primitive@>; +mp_primitive (mp, "greenpart", unary, green_part); +@:green_part_}{\&{greenpart} primitive@>; +mp_primitive (mp, "bluepart", unary, blue_part); +@:blue_part_}{\&{bluepart} primitive@>; +mp_primitive (mp, "cyanpart", unary, cyan_part); +@:cyan_part_}{\&{cyanpart} primitive@>; +mp_primitive (mp, "magentapart", unary, magenta_part); +@:magenta_part_}{\&{magentapart} primitive@>; +mp_primitive (mp, "yellowpart", unary, yellow_part); +@:yellow_part_}{\&{yellowpart} primitive@>; +mp_primitive (mp, "blackpart", unary, black_part); +@:black_part_}{\&{blackpart} primitive@>; +mp_primitive (mp, "greypart", unary, grey_part); +@:grey_part_}{\&{greypart} primitive@>; +mp_primitive (mp, "colormodel", unary, color_model_part); +@:color_model_part_}{\&{colormodel} primitive@>; +mp_primitive (mp, "fontpart", unary, font_part); +@:font_part_}{\&{fontpart} primitive@>; +mp_primitive (mp, "textpart", unary, text_part); +@:text_part_}{\&{textpart} primitive@>; +mp_primitive (mp, "pathpart", unary, path_part); +@:path_part_}{\&{pathpart} primitive@>; +mp_primitive (mp, "penpart", unary, pen_part); +@:pen_part_}{\&{penpart} primitive@>; +mp_primitive (mp, "dashpart", unary, dash_part); +@:dash_part_}{\&{dashpart} primitive@>; +mp_primitive (mp, "sqrt", unary, sqrt_op); +@:sqrt_}{\&{sqrt} primitive@>; +mp_primitive (mp, "mexp", unary, mp_m_exp_op); +@:m_exp_}{\&{mexp} primitive@>; +mp_primitive (mp, "mlog", unary, mp_m_log_op); +@:m_log_}{\&{mlog} primitive@>; +mp_primitive (mp, "sind", unary, sin_d_op); +@:sin_d_}{\&{sind} primitive@>; +mp_primitive (mp, "cosd", unary, cos_d_op); +@:cos_d_}{\&{cosd} primitive@>; +mp_primitive (mp, "floor", unary, floor_op); +@:floor_}{\&{floor} primitive@>; +mp_primitive (mp, "uniformdeviate", unary, uniform_deviate); +@:uniform_deviate_}{\&{uniformdeviate} primitive@>; +mp_primitive (mp, "charexists", unary, char_exists_op); +@:char_exists_}{\&{charexists} primitive@>; +mp_primitive (mp, "fontsize", unary, font_size); +@:font_size_}{\&{fontsize} primitive@>; +mp_primitive (mp, "llcorner", unary, ll_corner_op); +@:ll_corner_}{\&{llcorner} primitive@>; +mp_primitive (mp, "lrcorner", unary, lr_corner_op); +@:lr_corner_}{\&{lrcorner} primitive@>; +mp_primitive (mp, "ulcorner", unary, ul_corner_op); +@:ul_corner_}{\&{ulcorner} primitive@>; +mp_primitive (mp, "urcorner", unary, ur_corner_op); +@:ur_corner_}{\&{urcorner} primitive@>; +mp_primitive (mp, "arclength", unary, arc_length); +@:arc_length_}{\&{arclength} primitive@>; +mp_primitive (mp, "angle", unary, angle_op); +@:angle_}{\&{angle} primitive@>; +mp_primitive (mp, "cycle", cycle, cycle_op); +@:cycle_}{\&{cycle} primitive@>; +mp_primitive (mp, "stroked", unary, stroked_op); +@:stroked_}{\&{stroked} primitive@>; +mp_primitive (mp, "filled", unary, filled_op); +@:filled_}{\&{filled} primitive@>; +mp_primitive (mp, "textual", unary, textual_op); +@:textual_}{\&{textual} primitive@>; +mp_primitive (mp, "clipped", unary, clipped_op); +@:clipped_}{\&{clipped} primitive@>; +mp_primitive (mp, "bounded", unary, bounded_op); +@:bounded_}{\&{bounded} primitive@>; +mp_primitive (mp, "+", plus_or_minus, plus); +@:+ }{\.{+} primitive@>; +mp_primitive (mp, "-", plus_or_minus, minus); +@:- }{\.{-} primitive@>; +mp_primitive (mp, "*", secondary_binary, times); +@:* }{\.{*} primitive@>; +mp_primitive (mp, "/", slash, over); +mp->frozen_slash = mp_frozen_primitive (mp, "/", slash, over); +@:/ }{\.{/} primitive@>; +mp_primitive (mp, "++", tertiary_binary, pythag_add); +@:++_}{\.{++} primitive@>; +mp_primitive (mp, "+-+", tertiary_binary, pythag_sub); +@:+-+_}{\.{+-+} primitive@>; +mp_primitive (mp, "or", tertiary_binary, or_op); +@:or_}{\&{or} primitive@>; +mp_primitive (mp, "and", and_command, and_op); +@:and_}{\&{and} primitive@>; +mp_primitive (mp, "<", expression_binary, less_than); +@:< }{\.{<} primitive@>; +mp_primitive (mp, "<=", expression_binary, less_or_equal); +@:<=_}{\.{<=} primitive@>; +mp_primitive (mp, ">", expression_binary, greater_than); +@:> }{\.{>} primitive@>; +mp_primitive (mp, ">=", expression_binary, greater_or_equal); +@:>=_}{\.{>=} primitive@>; +mp_primitive (mp, "=", equals, equal_to); +@:= }{\.{=} primitive@>; +mp_primitive (mp, "<>", expression_binary, unequal_to); +@:<>_}{\.{<>} primitive@>; +mp_primitive (mp, "substring", primary_binary, substring_of); +@:substring_}{\&{substring} primitive@>; +mp_primitive (mp, "subpath", primary_binary, subpath_of); +@:subpath_}{\&{subpath} primitive@>; +mp_primitive (mp, "directiontime", primary_binary, direction_time_of); +@:direction_time_}{\&{directiontime} primitive@>; +mp_primitive (mp, "point", primary_binary, point_of); +@:point_}{\&{point} primitive@>; +mp_primitive (mp, "precontrol", primary_binary, precontrol_of); +@:precontrol_}{\&{precontrol} primitive@>; +mp_primitive (mp, "postcontrol", primary_binary, postcontrol_of); +@:postcontrol_}{\&{postcontrol} primitive@>; +mp_primitive (mp, "penoffset", primary_binary, pen_offset_of); +@:pen_offset_}{\&{penoffset} primitive@>; +mp_primitive (mp, "arctime", primary_binary, arc_time_of); +@:arc_time_of_}{\&{arctime} primitive@>; +mp_primitive (mp, "mpversion", nullary, mp_version); +@:mp_verison_}{\&{mpversion} primitive@>; +mp_primitive (mp, "&", ampersand, concatenate); +@:!!!}{\.{\&} primitive@>; +mp_primitive (mp, "rotated", secondary_binary, rotated_by); +@:rotated_}{\&{rotated} primitive@>; +mp_primitive (mp, "slanted", secondary_binary, slanted_by); +@:slanted_}{\&{slanted} primitive@>; +mp_primitive (mp, "scaled", secondary_binary, scaled_by); +@:scaled_}{\&{scaled} primitive@>; +mp_primitive (mp, "shifted", secondary_binary, shifted_by); +@:shifted_}{\&{shifted} primitive@>; +mp_primitive (mp, "transformed", secondary_binary, transformed_by); +@:transformed_}{\&{transformed} primitive@>; +mp_primitive (mp, "xscaled", secondary_binary, x_scaled); +@:x_scaled_}{\&{xscaled} primitive@>; +mp_primitive (mp, "yscaled", secondary_binary, y_scaled); +@:y_scaled_}{\&{yscaled} primitive@>; +mp_primitive (mp, "zscaled", secondary_binary, z_scaled); +@:z_scaled_}{\&{zscaled} primitive@>; +mp_primitive (mp, "infont", secondary_binary, in_font); +@:in_font_}{\&{infont} primitive@>; +mp_primitive (mp, "intersectiontimes", tertiary_binary, intersect); +@:intersection_times_}{\&{intersectiontimes} primitive@>; +mp_primitive (mp, "envelope", primary_binary, envelope_of); +@:envelope_}{\&{envelope} primitive@>; +mp_primitive (mp, "glyph", primary_binary, glyph_infont); @:glyph_infont_}{\&{envelope} primitive@> + @ @<Cases of |print_cmd...@>= case nullary: @@ -19151,151 +21519,195 @@ case slash: case ampersand: case equals: case and_command: - mp_print_op(mp, (quarterword)m); - break; +mp_print_op (mp, (quarterword) m); +break; @ OK, let's look at the simplest \\{do} procedure first. -@c @<Declare nullary action procedure@> -static void mp_do_nullary (MP mp,quarterword c) { +@c +@<Declare nullary action procedure@>; +static void mp_do_nullary (MP mp, quarterword c) { check_arith; - if ( mp->internal[mp_tracing_commands]>two ) - mp_show_cmd_mod(mp, nullary,c); + if (internal_value (mp_tracing_commands) > two) + mp_show_cmd_mod (mp, nullary, c); switch (c) { - case true_code: case false_code: - mp->cur_type=mp_boolean_type; mp->cur_exp=c; + case true_code: + case false_code: + mp->cur_exp.type = mp_boolean_type; + set_cur_exp_value (c); break; - case null_picture_code: - mp->cur_type=mp_picture_type; - mp->cur_exp=mp_get_node(mp, edge_header_size); - mp_init_edges(mp, mp->cur_exp); + case null_picture_code: + mp->cur_exp.type = mp_picture_type; + set_cur_exp_node (mp_get_edge_header_node (mp)); + mp_init_edges (mp, cur_exp_node ()); break; - case null_pen_code: - mp->cur_type=mp_pen_type; mp->cur_exp=mp_get_pen_circle(mp, 0); + case null_pen_code: + mp->cur_exp.type = mp_pen_type; + set_cur_exp_knot (mp_get_pen_circle (mp, 0)); break; - case normal_deviate: - mp->cur_type=mp_known; mp->cur_exp=mp_norm_rand(mp); + case normal_deviate: + mp->cur_exp.type = mp_known; + set_cur_exp_value (mp_norm_rand (mp)); break; - case pen_circle: - mp->cur_type=mp_pen_type; mp->cur_exp=mp_get_pen_circle(mp, unity); + case pen_circle: + mp->cur_exp.type = mp_pen_type; + set_cur_exp_knot (mp_get_pen_circle (mp, unity)); break; - case mp_version: - mp->cur_type=mp_string_type; - mp->cur_exp=intern(metapost_version) ; + case mp_version: + mp->cur_exp.type = mp_string_type; + set_cur_exp_str (mp_intern (mp, metapost_version)); break; case read_string_op: @<Read a string from the terminal@>; break; - } /* there are no other cases */ + } /* there are no other cases */ check_arith; } + @ @<Read a string...@>= -{ - if (mp->noninteractive || mp->interaction<=mp_nonstop_mode ) - mp_fatal_error(mp, "*** (cannot readstring in nonstop modes)"); - mp_begin_file_reading(mp); name=is_read; - limit=start; prompt_input(""); - mp_finish_read(mp); +{ + if (mp->noninteractive || mp->interaction <= mp_nonstop_mode) + mp_fatal_error (mp, "*** (cannot readstring in nonstop modes)"); + mp_begin_file_reading (mp); + name = is_read; + limit = start; + prompt_input (""); + mp_finish_read (mp); } + @ @<Declare nullary action procedure@>= -static void mp_finish_read (MP mp) { /* copy |buffer| line to |cur_exp| */ +static void mp_finish_read (MP mp) { /* copy |buffer| line to |cur_exp| */ size_t k; - str_room((int)mp->last-start); - for (k=(size_t)start;k<mp->last;k++) { - append_char(mp->buffer[k]); + str_room (((int) mp->last - (int) start)); + for (k = (size_t) start; k < mp->last; k++) { + append_char (mp->buffer[k]); } - mp_end_file_reading(mp); mp->cur_type=mp_string_type; - mp->cur_exp=mp_make_string(mp); + mp_end_file_reading (mp); + mp->cur_exp.type = mp_string_type; + set_cur_exp_str (mp_make_string (mp)); } + @ Things get a bit more interesting when there's an operand. The operand to |do_unary| appears in |cur_type| and |cur_exp|. -@c @<Declare unary action procedures@> -static void mp_do_unary (MP mp,quarterword c) { - pointer p,q,r; /* for list manipulation */ - integer x; /* a temporary register */ +@c +@<Declare unary action procedures@>; +static void mp_do_unary (MP mp, quarterword c) { + mp_node p, q, r; /* for list manipulation */ + integer x; /* a temporary register */ + halfword vv; /* a temporary place for |cur_exp_value| */ + mp_value new_expr; check_arith; - if ( mp->internal[mp_tracing_commands]>two ) + memset(&new_expr,0,sizeof(mp_value)); + if (internal_value (mp_tracing_commands) > two) @<Trace the current unary operation@>; switch (c) { case plus: - if ( mp->cur_type<mp_color_type ) mp_bad_unary(mp, plus); + if (mp->cur_exp.type < mp_color_type) + mp_bad_unary (mp, plus); break; case minus: @<Negate the current expression@>; break; - @<Additional cases of unary operators@>; - } /* there are no other cases */ + @<Additional cases of unary operators@>; + } /* there are no other cases */ check_arith; } + @ The |nice_pair| function returns |true| if both components of a pair are known. @<Declare unary action procedures@>= -static boolean mp_nice_pair (MP mp,integer p, quarterword t) { - if ( t==mp_pair_type ) { - p=value(p); - if ( mp_type(x_part_loc(p))==mp_known ) - if ( mp_type(y_part_loc(p))==mp_known ) +static boolean mp_nice_pair (MP mp, mp_node p, quarterword t) { + (void) mp; + if (t == mp_pair_type) { + p = value_node (p); + if (mp_type (x_part_loc (p)) == mp_known) + if (mp_type (y_part_loc (p)) == mp_known) return true; } return false; } + @ The |nice_color_or_pair| function is analogous except that it also accepts fully known colors. @<Declare unary action procedures@>= -static boolean mp_nice_color_or_pair (MP mp,integer p, quarterword t) { - pointer q,r; /* for scanning the big node */ - if ( (t!=mp_pair_type)&&(t!=mp_color_type)&&(t!=mp_cmykcolor_type) ) { - return false; - } else { - q=value(p); - r=q+mp->big_node_size[mp_type(p)]; - do { - r=r-2; - if ( mp_type(r)!=mp_known ) - return false; - } while (r!=q); - return true; +static boolean mp_nice_color_or_pair (MP mp, mp_node p, quarterword t) { + mp_node q; + (void) mp; + switch (t) { + case mp_pair_type: + q = value_node (p); + if (mp_type (x_part_loc (q)) == mp_known) + if (mp_type (y_part_loc (q)) == mp_known) + return true; + break; + case mp_color_type: + q = value_node (p); + if (mp_type (red_part_loc (q)) == mp_known) + if (mp_type (green_part_loc (q)) == mp_known) + if (mp_type (blue_part_loc (q)) == mp_known) + return true; + break; + case mp_cmykcolor_type: + q = value_node (p); + if (mp_type (cyan_part_loc (q)) == mp_known) + if (mp_type (magenta_part_loc (q)) == mp_known) + if (mp_type (yellow_part_loc (q)) == mp_known) + if (mp_type (black_part_loc (q)) == mp_known) + return true; + break; } + return false; } + @ @<Declare unary action...@>= -static void mp_print_known_or_unknown_type (MP mp,quarterword t, integer v) { - mp_print_char(mp, xord('(')); - if ( t>mp_known ) mp_print(mp, "unknown numeric"); - else { if ( (t==mp_pair_type)||(t==mp_color_type)||(t==mp_cmykcolor_type) ) - if ( ! mp_nice_color_or_pair(mp, v,t) ) mp_print(mp, "unknown "); - mp_print_type(mp, t); +static void mp_print_known_or_unknown_type (MP mp, quarterword t, mp_node v) { + mp_print_char (mp, xord ('(')); + if (t > mp_known) + mp_print (mp, "unknown numeric"); + else { + if ((t == mp_pair_type) || (t == mp_color_type) || (t == mp_cmykcolor_type)) + if (!mp_nice_color_or_pair (mp, v, t)) + mp_print (mp, "unknown "); + mp_print_type (mp, t); } - mp_print_char(mp, xord(')')); + mp_print_char (mp, xord (')')); } + @ @<Declare unary action...@>= -static void mp_bad_unary (MP mp,quarterword c) { - exp_err("Not implemented: "); mp_print_op(mp, c); -@.Not implemented...@> - mp_print_known_or_unknown_type(mp, mp->cur_type,mp->cur_exp); - help3("I'm afraid I don't know how to apply that operation to that", - "particular type. Continue, and I'll simply return the", - "argument (shown above) as the result of the operation."); - mp_put_get_error(mp); +static void mp_bad_unary (MP mp, quarterword c) { + exp_err ("Not implemented: "); + mp_print_op (mp, c); +@.Not implemented...@>; + mp_print_known_or_unknown_type (mp, mp->cur_exp.type, cur_exp_node ()); + help3 ("I'm afraid I don't know how to apply that operation to that", + "particular type. Continue, and I'll simply return the", + "argument (shown above) as the result of the operation."); + mp_put_get_error (mp); } + @ @<Trace the current unary operation@>= -{ - mp_begin_diagnostic(mp); mp_print_nl(mp, "{"); - mp_print_op(mp, c); mp_print_char(mp, xord('(')); - mp_print_exp(mp, null,0); /* show the operand, but not verbosely */ - mp_print(mp, ")}"); mp_end_diagnostic(mp, false); +{ + mp_begin_diagnostic (mp); + mp_print_nl (mp, "{"); + mp_print_op (mp, c); + mp_print_char (mp, xord ('(')); + mp_print_exp (mp, NULL, 0); /* show the operand, but not verbosely */ + mp_print (mp, ")}"); + mp_end_diagnostic (mp, false); } + @ Negation is easy except when the current expression is of type |independent|, or when it is a pair with one or more |independent| components. @@ -19310,54 +21722,116 @@ Instead, we work around the problem by copying the current expression and recycling it afterwards (cf.~the |stash_in| routine). @<Negate the current expression@>= -switch (mp->cur_type) { +switch (mp->cur_exp.type) { case mp_color_type: case mp_cmykcolor_type: case mp_pair_type: -case mp_independent: - q=mp->cur_exp; mp_make_exp_copy(mp, q); - if ( mp->cur_type==mp_dependent ) { - mp_negate_dep_list(mp, dep_list(mp->cur_exp)); - } else if ( mp->cur_type<=mp_pair_type ) { /* |mp_color_type| or |mp_pair_type| */ - p=value(mp->cur_exp); - r=p+mp->big_node_size[mp->cur_type]; - do { - r=r-2; - if ( mp_type(r)==mp_known ) negate(value(r)); - else mp_negate_dep_list(mp, dep_list(r)); - } while (r!=p); - } /* if |cur_type=mp_known| then |cur_exp=0| */ - mp_recycle_value(mp, q); mp_free_node(mp, q,value_node_size); +case mp_independent: + q = cur_exp_node (); + mp_make_exp_copy (mp, q); + if (mp->cur_exp.type == mp_dependent) { + mp_negate_dep_list (mp, (mp_value_node) dep_list ((mp_value_node) + cur_exp_node ())); + } else if (mp->cur_exp.type <= mp_pair_type) { + /* |mp_color_type| |mp_cmykcolor_type|, or |mp_pair_type| */ + p = value_node (cur_exp_node ()); + switch (mp->cur_exp.type) { + case mp_pair_type: + r = x_part_loc (p); + if (mp_type (r) == mp_known) + negate (value (r)); + else + mp_negate_dep_list (mp, (mp_value_node) dep_list ((mp_value_node) r)); + r = y_part_loc (p); + if (mp_type (r) == mp_known) + negate (value (r)); + else + mp_negate_dep_list (mp, (mp_value_node) dep_list ((mp_value_node) r)); + break; + case mp_color_type: + r = red_part_loc (p); + if (mp_type (r) == mp_known) + negate (value (r)); + else + mp_negate_dep_list (mp, (mp_value_node) dep_list ((mp_value_node) r)); + r = green_part_loc (p); + if (mp_type (r) == mp_known) + negate (value (r)); + else + mp_negate_dep_list (mp, (mp_value_node) dep_list ((mp_value_node) r)); + r = blue_part_loc (p); + if (mp_type (r) == mp_known) + negate (value (r)); + else + mp_negate_dep_list (mp, (mp_value_node) dep_list ((mp_value_node) r)); + break; + case mp_cmykcolor_type: + r = cyan_part_loc (p); + if (mp_type (r) == mp_known) + negate (value (r)); + else + mp_negate_dep_list (mp, (mp_value_node) dep_list ((mp_value_node) r)); + r = magenta_part_loc (p); + if (mp_type (r) == mp_known) + negate (value (r)); + else + mp_negate_dep_list (mp, (mp_value_node) dep_list ((mp_value_node) r)); + r = yellow_part_loc (p); + if (mp_type (r) == mp_known) + negate (value (r)); + else + mp_negate_dep_list (mp, (mp_value_node) dep_list ((mp_value_node) r)); + r = black_part_loc (p); + if (mp_type (r) == mp_known) + negate (value (r)); + else + mp_negate_dep_list (mp, (mp_value_node) dep_list ((mp_value_node) r)); + break; + default: /* there are no other valid cases, but please the compiler */ + break; + } + } /* if |cur_type=mp_known| then |cur_exp=0| */ + mp_recycle_value (mp, q); + mp_free_node (mp, q, value_node_size); break; case mp_dependent: case mp_proto_dependent: - mp_negate_dep_list(mp, dep_list(mp->cur_exp)); + mp_negate_dep_list (mp, (mp_value_node) dep_list ((mp_value_node) + cur_exp_node ())); break; case mp_known: - negate(mp->cur_exp); + negate (cur_exp_value ()); break; default: - mp_bad_unary(mp, minus); + mp_bad_unary (mp, minus); break; } + @ @<Declare unary action...@>= -static void mp_negate_dep_list (MP mp,pointer p) { - while (1) { - negate(value(p)); - if ( mp_info(p)==null ) return; - p=mp_link(p); +static void mp_negate_dep_list (MP mp, mp_value_node p) { + (void) mp; + while (1) { + set_dep_value (p, -dep_value (p)); + if (dep_info (p) == NULL) + return; + p = (mp_value_node) mp_link (p); } } + @ @<Additional cases of unary operators@>= -case not_op: - if ( mp->cur_type!=mp_boolean_type ) mp_bad_unary(mp, not_op); - else mp->cur_exp=true_code+false_code-mp->cur_exp; - break; +case not_op: +if (mp->cur_exp.type != mp_boolean_type) { + mp_bad_unary (mp, not_op); +} else { + halfword bb = true_code + false_code - cur_exp_value (); + set_cur_exp_value (bb); +} +break; @ @d three_sixty_units 23592960 /* that's |360*unity| */ -@d boolean_reset(A) if ( (A) ) mp->cur_exp=true_code; else mp->cur_exp=false_code +@d boolean_reset(A) if ( (A) ) set_cur_exp_value(true_code); else set_cur_exp_value(false_code) @<Additional cases of unary operators@>= case sqrt_op: @@ -19366,163 +21840,266 @@ case mp_m_log_op: case sin_d_op: case cos_d_op: case floor_op: -case uniform_deviate: +case uniform_deviate: case odd_op: case char_exists_op: - if ( mp->cur_type!=mp_known ) { - mp_bad_unary(mp, c); - } else { - switch (c) { - case sqrt_op:mp->cur_exp=mp_square_rt(mp, mp->cur_exp);break; - case mp_m_exp_op:mp->cur_exp=mp_m_exp(mp, mp->cur_exp);break; - case mp_m_log_op:mp->cur_exp=mp_m_log(mp, mp->cur_exp);break; - case sin_d_op: - case cos_d_op: - mp_n_sin_cos(mp, (mp->cur_exp % three_sixty_units)*16); - if ( c==sin_d_op ) mp->cur_exp=mp_round_fraction(mp, mp->n_sin); - else mp->cur_exp=mp_round_fraction(mp, mp->n_cos); - break; - case floor_op:mp->cur_exp=mp_floor_scaled(mp, mp->cur_exp);break; - case uniform_deviate:mp->cur_exp=mp_unif_rand(mp, mp->cur_exp);break; - case odd_op: - boolean_reset(odd(mp_round_unscaled(mp, mp->cur_exp))); - mp->cur_type=mp_boolean_type; - break; - case char_exists_op: - @<Determine if a character has been shipped out@>; - break; - } /* there are no other cases */ - } - break; +if (mp->cur_exp.type != mp_known) { + mp_bad_unary (mp, c); +} else { + switch (c) { + case sqrt_op: + vv = mp_square_rt (mp, cur_exp_value ()); + set_cur_exp_value (vv); + break; + case mp_m_exp_op: + vv = mp_m_exp (mp, cur_exp_value ()); + set_cur_exp_value (vv); + break; + case mp_m_log_op: + vv = mp_m_log (mp, cur_exp_value ()); + set_cur_exp_value (vv); + break; + case sin_d_op: + case cos_d_op: + { + fraction n_sin; + fraction n_cos; /* results computed by |n_sin_cos| */ + mp_n_sin_cos (mp, (cur_exp_value () % three_sixty_units) * 16, &n_cos, &n_sin); + if (c == sin_d_op) + set_cur_exp_value (mp_round_fraction (mp, n_sin)); + else + set_cur_exp_value (mp_round_fraction (mp, n_cos)); + } + break; + case floor_op: + vv = mp_floor_scaled (mp, cur_exp_value ()); + set_cur_exp_value (vv); + break; + case uniform_deviate: + vv = mp_unif_rand (mp, cur_exp_value ()); + set_cur_exp_value (vv); + break; + case odd_op: + vv = odd (mp_round_unscaled (mp, cur_exp_value ())); + boolean_reset (vv); + mp->cur_exp.type = mp_boolean_type; + break; + case char_exists_op: + @<Determine if a character has been shipped out@>; + break; + } /* there are no other cases */ +} +break; @ @<Additional cases of unary operators@>= case angle_op: - if ( mp_nice_pair(mp, mp->cur_exp,mp->cur_type) ) { - p=value(mp->cur_exp); - x=mp_n_arg(mp, value(x_part_loc(p)),value(y_part_loc(p))); - if ( x>=0 ) mp_flush_cur_exp(mp, (x+8)/ 16); - else mp_flush_cur_exp(mp, -((-x+8)/ 16)); - } else { - mp_bad_unary(mp, angle_op); - } - break; +if (mp_nice_pair (mp, cur_exp_node (), mp->cur_exp.type)) { + p = value_node (cur_exp_node ()); + x = mp_n_arg (mp, value (x_part_loc (p)), value (y_part_loc (p))); + if (x >= 0) + new_expr.data.val = (x + 8) / 16; + else + new_expr.data.val = -((-x + 8) / 16); + mp_flush_cur_exp (mp, new_expr); +} else { + mp_bad_unary (mp, angle_op); +} +break; @ If the current expression is a pair, but the context wants it to be a path, we call |pair_to_path|. @<Declare unary action...@>= -static void mp_pair_to_path (MP mp) { - mp->cur_exp=mp_new_knot(mp); - mp->cur_type=mp_path_type; +static void mp_pair_to_path (MP mp) { + set_cur_exp_knot (mp_pair_to_knot (mp)); + mp->cur_exp.type = mp_path_type; } + @ This complicated if test makes sure that any |bounds| or |clip| picture objects that get passed into \&{within} do not raise an error when queried using the color part primitives (this is needed for backward compatibility) . -@d cur_pic_item mp_link(dummy_loc(mp->cur_exp)) -@d pict_color_type(A) ((cur_pic_item!=null) && +@d cur_pic_item mp_link(dummy_loc(cur_exp_node())) +@d pict_color_type(A) ((cur_pic_item!=NULL) && ((!has_color(cur_pic_item)) || (((mp_color_model(cur_pic_item)==A) || ((mp_color_model(cur_pic_item)==mp_uninitialized_model) && - (mp->internal[mp_default_color_model]/unity)==(A)))))) + (internal_value(mp_default_color_model)/unity)==(A)))))) @<Additional cases of unary operators@>= case x_part: case y_part: - if ( (mp->cur_type==mp_pair_type)||(mp->cur_type==mp_transform_type) ) - mp_take_part(mp, c); - else if ( mp->cur_type==mp_picture_type ) mp_take_pict_part(mp, c); - else mp_bad_unary(mp, c); - break; +if ((mp->cur_exp.type == mp_pair_type) + || (mp->cur_exp.type == mp_transform_type)) + mp_take_part (mp, c); +else if (mp->cur_exp.type == mp_picture_type) + mp_take_pict_part (mp, c); +else + mp_bad_unary (mp, c); +break; case xx_part: case xy_part: case yx_part: -case yy_part: - if ( mp->cur_type==mp_transform_type ) mp_take_part(mp, c); - else if ( mp->cur_type==mp_picture_type ) mp_take_pict_part(mp, c); - else mp_bad_unary(mp, c); - break; +case yy_part: +if (mp->cur_exp.type == mp_transform_type) + mp_take_part (mp, c); +else if (mp->cur_exp.type == mp_picture_type) + mp_take_pict_part (mp, c); +else + mp_bad_unary (mp, c); +break; case red_part: case green_part: -case blue_part: - if ( mp->cur_type==mp_color_type ) mp_take_part(mp, c); - else if ( mp->cur_type==mp_picture_type ) { - if pict_color_type(mp_rgb_model) mp_take_pict_part(mp, c); - else mp_bad_color_part(mp, c); - } - else mp_bad_unary(mp, c); - break; +case blue_part: +if (mp->cur_exp.type == mp_color_type) + mp_take_part (mp, c); +else if (mp->cur_exp.type == mp_picture_type) { + if pict_color_type + (mp_rgb_model) mp_take_pict_part (mp, c); + else + mp_bad_color_part (mp, c); +} else + mp_bad_unary (mp, c); +break; case cyan_part: case magenta_part: case yellow_part: -case black_part: - if ( mp->cur_type==mp_cmykcolor_type) mp_take_part(mp, c); - else if ( mp->cur_type==mp_picture_type ) { - if pict_color_type(mp_cmyk_model) mp_take_pict_part(mp, c); - else mp_bad_color_part(mp, c); - } - else mp_bad_unary(mp, c); - break; -case grey_part: - if ( mp->cur_type==mp_known ) ; /* mp->cur_exp=mp->cur_exp */ - else if ( mp->cur_type==mp_picture_type ) { - if pict_color_type(mp_grey_model) mp_take_pict_part(mp, c); - else mp_bad_color_part(mp, c); - } - else mp_bad_unary(mp, c); - break; -case color_model_part: - if ( mp->cur_type==mp_picture_type ) mp_take_pict_part(mp, c); - else mp_bad_unary(mp, c); - break; +case black_part: +if (mp->cur_exp.type == mp_cmykcolor_type) + mp_take_part (mp, c); +else if (mp->cur_exp.type == mp_picture_type) { + if pict_color_type + (mp_cmyk_model) mp_take_pict_part (mp, c); + else + mp_bad_color_part (mp, c); +} else + mp_bad_unary (mp, c); +break; +case grey_part: +if (mp->cur_exp.type == mp_known); /* |cur_exp_value()=cur_exp_value()| */ +else if (mp->cur_exp.type == mp_picture_type) { + if pict_color_type + (mp_grey_model) mp_take_pict_part (mp, c); + else + mp_bad_color_part (mp, c); +} else + mp_bad_unary (mp, c); +break; +case color_model_part: +if (mp->cur_exp.type == mp_picture_type) + mp_take_pict_part (mp, c); +else + mp_bad_unary (mp, c); +break; @ @<Declarations@>= -static void mp_bad_color_part(MP mp, quarterword c); +static void mp_bad_color_part (MP mp, quarterword c); @ @c -static void mp_bad_color_part(MP mp, quarterword c) { - pointer p; /* the big node */ - p=mp_link(dummy_loc(mp->cur_exp)); - exp_err("Wrong picture color model: "); mp_print_op(mp, c); -@.Wrong picture color model...@> - if (mp_color_model(p)==mp_grey_model) - mp_print(mp, " of grey object"); - else if (mp_color_model(p)==mp_cmyk_model) - mp_print(mp, " of cmyk object"); - else if (mp_color_model(p)==mp_rgb_model) - mp_print(mp, " of rgb object"); - else if (mp_color_model(p)==mp_no_model) - mp_print(mp, " of marking object"); - else - mp_print(mp," of defaulted object"); - help3("You can only ask for the redpart, greenpart, bluepart of a rgb object,", - "the cyanpart, magentapart, yellowpart or blackpart of a cmyk object, ", - "or the greypart of a grey object. No mixing and matching, please."); - mp_error(mp); - if (c==black_part) - mp_flush_cur_exp(mp,unity); +static void mp_bad_color_part (MP mp, quarterword c) { + mp_node p; /* the big node */ + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + p = mp_link (dummy_loc (cur_exp_node ())); + exp_err ("Wrong picture color model: "); + mp_print_op (mp, c); +@.Wrong picture color model...@>; + if (mp_color_model (p) == mp_grey_model) + mp_print (mp, " of grey object"); + else if (mp_color_model (p) == mp_cmyk_model) + mp_print (mp, " of cmyk object"); + else if (mp_color_model (p) == mp_rgb_model) + mp_print (mp, " of rgb object"); + else if (mp_color_model (p) == mp_no_model) + mp_print (mp, " of marking object"); + else + mp_print (mp, " of defaulted object"); + help3 + ("You can only ask for the redpart, greenpart, bluepart of a rgb object,", + "the cyanpart, magentapart, yellowpart or blackpart of a cmyk object, ", + "or the greypart of a grey object. No mixing and matching, please."); + mp_error (mp); + if (c == black_part) + new_expr.data.val = unity; else - mp_flush_cur_exp(mp,0); + new_expr.data.val = 0; + mp_flush_cur_exp (mp, new_expr); } + @ In the following procedure, |cur_exp| points to a capsule, which points to a big node. We want to delete all but one part of the big node. @<Declare unary action...@>= -static void mp_take_part (MP mp,quarterword c) { - pointer p; /* the big node */ - p=value(mp->cur_exp); value(temp_val)=p; mp_type(temp_val)=mp->cur_type; - mp_link(p)=temp_val; mp_free_node(mp, mp->cur_exp,value_node_size); - mp_make_exp_copy(mp, p+mp->sector_offset[c+mp_x_part_sector-x_part]); - mp_recycle_value(mp, temp_val); +static void mp_take_part (MP mp, quarterword c) { + mp_node p; /* the big node */ + p = value_node (cur_exp_node ()); + set_value_node (mp->temp_val, p); + mp_type (mp->temp_val) = mp->cur_exp.type; + mp_link (p) = mp->temp_val; + mp_free_node (mp, cur_exp_node (), value_node_size); + switch (c) { + case x_part: + if (mp->cur_exp.type == mp_pair_type) + mp_make_exp_copy (mp, x_part_loc (p)); + else + mp_make_exp_copy (mp, tx_part_loc (p)); + break; + case y_part: + if (mp->cur_exp.type == mp_pair_type) + mp_make_exp_copy (mp, y_part_loc (p)); + else + mp_make_exp_copy (mp, ty_part_loc (p)); + break; + case xx_part: + mp_make_exp_copy (mp, xx_part_loc (p)); + break; + case xy_part: + mp_make_exp_copy (mp, xy_part_loc (p)); + break; + case yx_part: + mp_make_exp_copy (mp, yx_part_loc (p)); + break; + case yy_part: + mp_make_exp_copy (mp, yy_part_loc (p)); + break; + case red_part: + mp_make_exp_copy (mp, red_part_loc (p)); + break; + case green_part: + mp_make_exp_copy (mp, green_part_loc (p)); + break; + case blue_part: + mp_make_exp_copy (mp, blue_part_loc (p)); + break; + case cyan_part: + mp_make_exp_copy (mp, cyan_part_loc (p)); + break; + case magenta_part: + mp_make_exp_copy (mp, magenta_part_loc (p)); + break; + case yellow_part: + mp_make_exp_copy (mp, yellow_part_loc (p)); + break; + case black_part: + mp_make_exp_copy (mp, black_part_loc (p)); + break; + } + mp_recycle_value (mp, mp->temp_val); } -@ @<Initialize table entries...@>= -mp_name_type(temp_val)=mp_capsule; + +@ @<Initialize table entries@>= +mp->temp_val = mp_get_value_node (mp); +mp_name_type (mp->temp_val) = mp_capsule; + +@ @<Free table entries@>= +mp_free_value_node (mp, mp->temp_val); + @ @<Additional cases of unary operators@>= case font_part: @@ -19530,221 +22107,385 @@ case text_part: case path_part: case pen_part: case dash_part: - if ( mp->cur_type==mp_picture_type ) mp_take_pict_part(mp, c); - else mp_bad_unary(mp, c); - break; +if (mp->cur_exp.type == mp_picture_type) + mp_take_pict_part (mp, c); +else + mp_bad_unary (mp, c); +break; @ @<Declarations@>= static void mp_scale_edges (MP mp); @ @<Declare unary action...@>= -static void mp_take_pict_part (MP mp,quarterword c) { - pointer p; /* first graphical object in |cur_exp| */ - p=mp_link(dummy_loc(mp->cur_exp)); - if ( p!=null ) { +static void mp_take_pict_part (MP mp, quarterword c) { + mp_node p; /* first graphical object in |cur_exp| */ + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + p = mp_link (dummy_loc (cur_exp_node ())); + if (p != NULL) { switch (c) { - case x_part: case y_part: case xx_part: - case xy_part: case yx_part: case yy_part: - if ( mp_type(p)==mp_text_code ) mp_flush_cur_exp(mp, text_trans_part(p+c)); - else goto NOT_FOUND; + case x_part: + case y_part: + case xx_part: + case xy_part: + case yx_part: + case yy_part: + if (mp_type (p) == mp_text_node_type) { + switch (c) { + case x_part: + new_expr.data.val = tx_val (p); + break; + case y_part: + new_expr.data.val = ty_val (p); + break; + case xx_part: + new_expr.data.val = txx_val (p); + break; + case xy_part: + new_expr.data.val = txy_val (p); + break; + case yx_part: + new_expr.data.val = tyx_val (p); + break; + case yy_part: + new_expr.data.val = tyy_val (p); + break; + } + mp_flush_cur_exp (mp, new_expr); + } else + goto NOT_FOUND; break; - case red_part: case green_part: case blue_part: - if ( has_color(p) ) mp_flush_cur_exp(mp, obj_color_part(p+c)); - else goto NOT_FOUND; + case red_part: + case green_part: + case blue_part: + if (has_color (p)) { + switch (c) { + case red_part: + new_expr.data.val = red_val (p); + break; + case green_part: + new_expr.data.val = green_val (p); + break; + case blue_part: + new_expr.data.val = blue_val (p); + break; + } + mp_flush_cur_exp (mp, new_expr); + } else + goto NOT_FOUND; break; - case cyan_part: case magenta_part: case yellow_part: + case cyan_part: + case magenta_part: + case yellow_part: case black_part: - if ( has_color(p) ) { - if ( mp_color_model(p)==mp_uninitialized_model && c==black_part) - mp_flush_cur_exp(mp, unity); - else - mp_flush_cur_exp(mp, obj_color_part(p+c+(red_part-cyan_part))); - } else goto NOT_FOUND; + if (has_color (p)) { + if (mp_color_model (p) == mp_uninitialized_model && c == black_part) { + new_expr.data.val = unity; + } else { + switch (c) { + case cyan_part: + new_expr.data.val = cyan_val (p); + break; + case magenta_part: + new_expr.data.val = magenta_val (p); + break; + case yellow_part: + new_expr.data.val = yellow_val (p); + break; + case black_part: + new_expr.data.val = black_val (p); + break; + } + } + mp_flush_cur_exp (mp, new_expr); + } else + goto NOT_FOUND; break; case grey_part: - if ( has_color(p) ) - mp_flush_cur_exp(mp, obj_color_part(p+c+(red_part-grey_part))); - else goto NOT_FOUND; + if (has_color (p)) { + new_expr.data.val = grey_val (p); + mp_flush_cur_exp (mp, new_expr); + } else + goto NOT_FOUND; break; case color_model_part: - if ( has_color(p) ) { - if ( mp_color_model(p)==mp_uninitialized_model ) - mp_flush_cur_exp(mp, mp->internal[mp_default_color_model]); + if (has_color (p)) { + if (mp_color_model (p) == mp_uninitialized_model) + new_expr.data.val = internal_value (mp_default_color_model); else - mp_flush_cur_exp(mp, mp_color_model(p)*unity); - } else goto NOT_FOUND; + new_expr.data.val = mp_color_model (p) * unity; + mp_flush_cur_exp (mp, new_expr); + } else + goto NOT_FOUND; break; - @<Handle other cases in |take_pict_part| or |goto not_found|@>; - } /* all cases have been enumerated */ + @<Handle other cases in |take_pict_part| or |goto not_found|@>; + } /* all cases have been enumerated */ return; }; NOT_FOUND: - @<Convert the current expression to a null value appropriate + @<Convert the current expression to a NULL value appropriate for |c|@>; } + @ @<Handle other cases in |take_pict_part| or |goto not_found|@>= -case text_part: - if ( mp_type(p)!=mp_text_code ) goto NOT_FOUND; - else { - mp_flush_cur_exp(mp, mp_text_p(p)); - add_str_ref(mp->cur_exp); - mp->cur_type=mp_string_type; - }; - break; -case font_part: - if ( mp_type(p)!=mp_text_code ) goto NOT_FOUND; - else { - mp_flush_cur_exp(mp, rts(mp->font_name[mp_font_n(p)])); - add_str_ref(mp->cur_exp); - mp->cur_type=mp_string_type; - }; - break; +case text_part: +if (mp_type (p) != mp_text_node_type) + goto NOT_FOUND; +else { + new_expr.data.str = mp_text_p (p); + add_str_ref (new_expr.data.str); + mp_flush_cur_exp (mp, new_expr); + mp->cur_exp.type = mp_string_type; +}; +break; +case font_part: +if (mp_type (p) != mp_text_node_type) + goto NOT_FOUND; +else { + new_expr.data.str = mp_rts (mp, mp->font_name[mp_font_n (p)]); + add_str_ref (new_expr.data.str); + mp_flush_cur_exp (mp, new_expr); + mp->cur_exp.type = mp_string_type; +}; +break; case path_part: - if ( mp_type(p)==mp_text_code ) goto NOT_FOUND; - else if ( is_stop(p) ) mp_confusion(mp, "pict"); +if (mp_type (p) == mp_text_node_type) + goto NOT_FOUND; +else if (is_stop (p)) + mp_confusion (mp, "pict"); @:this can't happen pict}{\quad pict@> - else { - mp_flush_cur_exp(mp, mp_copy_path(mp, mp_path_p(p))); - mp->cur_type=mp_path_type; - } - break; -case pen_part: - if ( ! has_pen(p) ) goto NOT_FOUND; else { - if ( mp_pen_p(p)==null ) goto NOT_FOUND; - else { mp_flush_cur_exp(mp, copy_pen(mp_pen_p(p))); - mp->cur_type=mp_pen_type; - }; + new_expr.data.node = NULL; + switch (mp_type (p)) { + case mp_fill_node_type: + new_expr.data.p = mp_copy_path (mp, mp_path_p ((mp_fill_node) p)); + break; + case mp_stroked_node_type: + new_expr.data.p = mp_copy_path (mp, mp_path_p ((mp_stroked_node) p)); + break; + case mp_start_bounds_node_type: + new_expr.data.p = mp_copy_path (mp, mp_path_p ((mp_start_bounds_node) p)); + break; + case mp_start_clip_node_type: + new_expr.data.p = mp_copy_path (mp, mp_path_p ((mp_start_clip_node) p)); + break; + default: + assert (0); + break; } - break; -case dash_part: - if ( mp_type(p)!=mp_stroked_code ) goto NOT_FOUND; - else { if ( mp_dash_p(p)==null ) goto NOT_FOUND; - else { add_edge_ref(mp_dash_p(p)); - mp->se_sf=dash_scale(p); - mp->se_pic=mp_dash_p(p); - mp_scale_edges(mp); - mp_flush_cur_exp(mp, mp->se_pic); - mp->cur_type=mp_picture_type; - }; + mp_flush_cur_exp (mp, new_expr); + mp->cur_exp.type = mp_path_type; +} +break; +case pen_part: +if (!has_pen (p)) + goto NOT_FOUND; +else { + switch (mp_type (p)) { + case mp_fill_node_type: + if (mp_pen_p ((mp_fill_node) p) == NULL) + goto NOT_FOUND; + else { + new_expr.data.p = copy_pen (mp_pen_p ((mp_fill_node) p)); + mp_flush_cur_exp (mp, new_expr); + mp->cur_exp.type = mp_pen_type; + } + break; + case mp_stroked_node_type: + if (mp_pen_p ((mp_stroked_node) p) == NULL) + goto NOT_FOUND; + else { + new_expr.data.p = copy_pen (mp_pen_p ((mp_stroked_node) p)); + mp_flush_cur_exp (mp, new_expr); + mp->cur_exp.type = mp_pen_type; + } + break; + default: + assert (0); + break; } - break; +} +break; +case dash_part: +if (mp_type (p) != mp_stroked_node_type) + goto NOT_FOUND; +else { + if (mp_dash_p (p) == NULL) + goto NOT_FOUND; + else { + add_edge_ref (mp_dash_p (p)); + mp->se_sf = dash_scale (p); + mp->se_pic = mp_dash_p (p); + mp_scale_edges (mp); + new_expr.data.node = mp->se_pic; + mp_flush_cur_exp (mp, new_expr); + mp->cur_exp.type = mp_picture_type; + } +} +break; @ Since |scale_edges| had to be declared |forward|, it had to be declared as a parameterless procedure even though it really takes two arguments and updates one of them. Hence the following globals are needed. @<Global...@>= -pointer se_pic; /* edge header used and updated by |scale_edges| */ -scaled se_sf; /* the scale factor argument to |scale_edges| */ +mp_node se_pic; /* edge header used and updated by |scale_edges| */ +scaled se_sf; /* the scale factor argument to |scale_edges| */ -@ @<Convert the current expression to a null value appropriate...@>= +@ @<Convert the current expression to a NULL value appropriate...@>= switch (c) { -case text_part: case font_part: - mp_flush_cur_exp(mp, null_str); - mp->cur_type=mp_string_type; +case text_part: +case font_part: + new_expr.data.str = null_str; + mp_flush_cur_exp (mp, new_expr); + mp->cur_exp.type = mp_string_type; break; -case path_part: - mp_flush_cur_exp(mp, mp_get_node(mp, knot_node_size)); - mp_left_type(mp->cur_exp)=mp_endpoint; - mp_right_type(mp->cur_exp)=mp_endpoint; - mp_link(mp->cur_exp)=mp->cur_exp; - mp_x_coord(mp->cur_exp)=0; - mp_y_coord(mp->cur_exp)=0; - mp_originator(mp->cur_exp)=mp_metapost_user; - mp->cur_type=mp_path_type; +case path_part: + new_expr.data.p = mp_new_knot (mp); + mp_flush_cur_exp (mp, new_expr); + mp_left_type (cur_exp_knot ()) = mp_endpoint; + mp_right_type (cur_exp_knot ()) = mp_endpoint; + mp_next_knot (cur_exp_knot ()) = cur_exp_knot (); + mp_x_coord (cur_exp_knot ()) = 0; + mp_y_coord (cur_exp_knot ()) = 0; + mp_originator (cur_exp_knot ()) = mp_metapost_user; + mp->cur_exp.type = mp_path_type; break; -case pen_part: - mp_flush_cur_exp(mp, mp_get_pen_circle(mp, 0)); - mp->cur_type=mp_pen_type; +case pen_part: + new_expr.data.p = mp_get_pen_circle (mp, 0); + mp_flush_cur_exp (mp, new_expr); + mp->cur_exp.type = mp_pen_type; break; -case dash_part: - mp_flush_cur_exp(mp, mp_get_node(mp, edge_header_size)); - mp_init_edges(mp, mp->cur_exp); - mp->cur_type=mp_picture_type; +case dash_part: + new_expr.data.node = mp_get_edge_header_node (mp); + mp_flush_cur_exp (mp, new_expr); + mp_init_edges (mp, cur_exp_node ()); + mp->cur_exp.type = mp_picture_type; break; -default: - mp_flush_cur_exp(mp, 0); +default: + new_expr.data.val = 0; + mp_flush_cur_exp (mp, new_expr); break; } + @ @<Additional cases of unary...@>= -case char_op: - if ( mp->cur_type!=mp_known ) { - mp_bad_unary(mp, char_op); - } else { - mp->cur_exp=mp_round_unscaled(mp, mp->cur_exp) % 256; - mp->cur_type=mp_string_type; - if ( mp->cur_exp<0 ) mp->cur_exp=mp->cur_exp+256; +case char_op: +if (mp->cur_exp.type != mp_known) { + mp_bad_unary (mp, char_op); +} else { + vv = mp_round_unscaled (mp, cur_exp_value ()) % 256; + set_cur_exp_value (vv); + mp->cur_exp.type = mp_string_type; + if (cur_exp_value () < 0) { + vv = cur_exp_value () + 256; + set_cur_exp_value (vv); } - break; -case decimal: - if ( mp->cur_type!=mp_known ) { - mp_bad_unary(mp, decimal); - } else { - mp->old_setting=mp->selector; mp->selector=new_string; - mp_print_scaled(mp, mp->cur_exp); mp->cur_exp=mp_make_string(mp); - mp->selector=mp->old_setting; mp->cur_type=mp_string_type; + { + unsigned char ss[2]; + ss[0] = (unsigned char) cur_exp_value (); + ss[1] = '\0'; + set_cur_exp_str (mp_rtsl (mp, (char *) ss, 1)); } - break; +} +break; +case decimal: +if (mp->cur_exp.type != mp_known) { + mp_bad_unary (mp, decimal); +} else { + mp->old_setting = mp->selector; + mp->selector = new_string; + mp_print_scaled (mp, cur_exp_value ()); + set_cur_exp_str (mp_make_string (mp)); + mp->selector = mp->old_setting; + mp->cur_exp.type = mp_string_type; +} +break; case oct_op: case hex_op: -case ASCII_op: - if ( mp->cur_type!=mp_string_type ) mp_bad_unary(mp, c); - else mp_str_to_num(mp, c); - break; -case font_size: - if ( mp->cur_type!=mp_string_type ) mp_bad_unary(mp, font_size); - else @<Find the design size of the font whose name is |cur_exp|@>; - break; +case ASCII_op: +if (mp->cur_exp.type != mp_string_type) + mp_bad_unary (mp, c); +else + mp_str_to_num (mp, c); +break; +case font_size: +if (mp->cur_exp.type != mp_string_type) + mp_bad_unary (mp, font_size); +else + @<Find the design size of the font whose name is |cur_exp|@>; +break; @ @<Declare unary action...@>= -static void mp_str_to_num (MP mp,quarterword c) { /* converts a string to a number */ - integer n; /* accumulator */ +static void mp_str_to_num (MP mp, quarterword c) { /* converts a string to a number */ + integer n; /* accumulator */ ASCII_code m; /* current character */ - pool_pointer k; /* index into |str_pool| */ - int b; /* radix of conversion */ - boolean bad_char; /* did the string contain an invalid digit? */ - if ( c==ASCII_op ) { - if ( length(mp->cur_exp)==0 ) n=-1; - else n=mp->str_pool[mp->str_start[mp->cur_exp]]; - } else { - if ( c==oct_op ) b=8; else b=16; - n=0; bad_char=false; - for (k=mp->str_start[mp->cur_exp];k<str_stop(mp->cur_exp);k++) { - m=mp->str_pool[k]; - if ( (m>='0')&&(m<='9') ) m=(ASCII_code)(m-'0'); - else if ( (m>='A')&&(m<='F') ) m=(ASCII_code)(m-'A'+10); - else if ( (m>='a')&&(m<='f') ) m=(ASCII_code)(m-'a'+10); - else { bad_char=true; m=0; }; - if ( (int)m>=b ) { bad_char=true; m=0; }; - if ( n<32768 / b ) n=n*b+m; else n=32767; + unsigned k; /* index into |str_pool| */ + int b; /* radix of conversion */ + boolean bad_char; /* did the string contain an invalid digit? */ + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + if (c == ASCII_op) { + if (length (cur_exp_str ()) == 0) + n = -1; + else + n = cur_exp_str ()->str[0]; + } else { + if (c == oct_op) + b = 8; + else + b = 16; + n = 0; + bad_char = false; + for (k = 0; k < length (cur_exp_str ()); k++) { + m = (ASCII_code) (*(cur_exp_str ()->str + k)); + if ((m >= '0') && (m <= '9')) + m = (ASCII_code) (m - '0'); + else if ((m >= 'A') && (m <= 'F')) + m = (ASCII_code) (m - 'A' + 10); + else if ((m >= 'a') && (m <= 'f')) + m = (ASCII_code) (m - 'a' + 10); + else { + bad_char = true; + m = 0; + }; + if ((int) m >= b) { + bad_char = true; + m = 0; + }; + if (n < 32768 / b) + n = n * b + m; + else + n = 32767; } @<Give error messages if |bad_char| or |n>=4096|@>; } - mp_flush_cur_exp(mp, n*unity); + new_expr.data.val = n * unity; + mp_flush_cur_exp (mp, new_expr); } + @ @<Give error messages if |bad_char|...@>= -if ( bad_char ) { - exp_err("String contains illegal digits"); -@.String contains illegal digits@> - if ( c==oct_op ) { - help1("I zeroed out characters that weren't in the range 0..7."); - } else { - help1("I zeroed out characters that weren't hex digits."); - } - mp_put_get_error(mp); -} -if ( (n>4095) ) { - if ( mp->internal[mp_warning_check]>0 ) { - print_err("Number too large ("); - mp_print_int(mp, n); mp_print_char(mp, xord(')')); -@.Number too large@> - help2("I have trouble with numbers greater than 4095; watch out.", +if (bad_char) { + exp_err ("String contains illegal digits"); +@.String contains illegal digits@>; + if (c == oct_op) { + help1 ("I zeroed out characters that weren't in the range 0..7."); + } else { + help1 ("I zeroed out characters that weren't hex digits."); + } + mp_put_get_error (mp); +} +if ((n > 4095)) { + if (internal_value (mp_warning_check) > 0) { + print_err ("Number too large ("); + mp_print_int (mp, n); + mp_print_char (mp, xord (')')); +@.Number too large@>; + help2 ("I have trouble with numbers greater than 4095; watch out.", "(Set warningcheck:=0 to suppress this message.)"); - mp_put_get_error(mp); + mp_put_get_error (mp); } } @@ -19752,69 +22493,100 @@ if ( (n>4095) ) { of different types of operands. @<Additional cases of unary...@>= -case length_op: - switch (mp->cur_type) { - case mp_string_type: mp_flush_cur_exp(mp, length(mp->cur_exp)*unity); break; - case mp_path_type: mp_flush_cur_exp(mp, mp_path_length(mp)); break; - case mp_known: mp->cur_exp=abs(mp->cur_exp); break; - case mp_picture_type: mp_flush_cur_exp(mp, mp_pict_length(mp)); break; - default: - if ( mp_nice_pair(mp, mp->cur_exp,mp->cur_type) ) - mp_flush_cur_exp(mp, mp_pyth_add(mp, - value(x_part_loc(value(mp->cur_exp))), - value(y_part_loc(value(mp->cur_exp))))); - else mp_bad_unary(mp, c); - break; - } +case length_op: +switch (mp->cur_exp.type) { +case mp_string_type: + new_expr.data.val = (integer) (length (cur_exp_str ()) * unity); + mp_flush_cur_exp (mp, new_expr); + break; +case mp_path_type: + new_expr.data.val = mp_path_length (mp); + mp_flush_cur_exp (mp, new_expr); + break; +case mp_known: + vv = abs (cur_exp_value ()); + set_cur_exp_value (vv); + break; +case mp_picture_type: + new_expr.data.val = mp_pict_length (mp); + mp_flush_cur_exp (mp, new_expr); break; +default: + if (mp_nice_pair (mp, cur_exp_node (), mp->cur_exp.type)) { + new_expr.data.val = mp_pyth_add (mp, + value (x_part_loc + (value_node (cur_exp_node ()))), + value (y_part_loc + (value_node (cur_exp_node ())))); + mp_flush_cur_exp (mp, new_expr); + } else + mp_bad_unary (mp, c); + break; +} +break; @ @<Declare unary action...@>= -static scaled mp_path_length (MP mp) { /* computes the length of the current path */ - scaled n; /* the path length so far */ - pointer p; /* traverser */ - p=mp->cur_exp; - if ( mp_left_type(p)==mp_endpoint ) n=-unity; else n=0; - do { p=mp_link(p); n=n+unity; } while (p!=mp->cur_exp); +static scaled mp_path_length (MP mp) { /* computes the length of the current path */ + scaled n; /* the path length so far */ + mp_knot p; /* traverser */ + p = cur_exp_knot (); + if (mp_left_type (p) == mp_endpoint) + n = -unity; + else + n = 0; + do { + p = mp_next_knot (p); + n = n + unity; + } while (p != cur_exp_knot ()); return n; } + @ @<Declare unary action...@>= -static scaled mp_pict_length (MP mp) { +static scaled mp_pict_length (MP mp) { /* counts interior components in picture |cur_exp| */ - scaled n; /* the count so far */ - pointer p; /* traverser */ - n=0; - p=mp_link(dummy_loc(mp->cur_exp)); - if ( p!=null ) { - if ( is_start_or_stop(p) ) - if ( mp_skip_1component(mp, p)==null ) p=mp_link(p); - while ( p!=null ) { - skip_component(p) return n; - n=n+unity; + scaled n; /* the count so far */ + mp_node p; /* traverser */ + n = 0; + p = mp_link (dummy_loc (cur_exp_node ())); + if (p != NULL) { + if (is_start_or_stop (p)) + if (mp_skip_1component (mp, p) == NULL) + p = mp_link (p); + while (p != NULL) { + skip_component (p) return n; + n = n + unity; } } return n; } + @ Implement |turningnumber| @<Additional cases of unary...@>= case turning_op: - if ( mp->cur_type==mp_pair_type ) mp_flush_cur_exp(mp, 0); - else if ( mp->cur_type!=mp_path_type ) mp_bad_unary(mp, turning_op); - else if ( mp_left_type(mp->cur_exp)==mp_endpoint ) - mp_flush_cur_exp(mp, 0); /* not a cyclic path */ - else - mp_flush_cur_exp(mp, mp_turn_cycles_wrapper(mp, mp->cur_exp)); - break; +if (mp->cur_exp.type == mp_pair_type) { + new_expr.data.val = 0; + mp_flush_cur_exp (mp, new_expr); +} else if (mp->cur_exp.type != mp_path_type) { + mp_bad_unary (mp, turning_op); +} else if (mp_left_type (cur_exp_knot ()) == mp_endpoint) { + new_expr.data.p = NULL; + mp_flush_cur_exp (mp, new_expr); /* not a cyclic path */ +} else { + new_expr.data.val = mp_turn_cycles_wrapper (mp, cur_exp_knot ()); + mp_flush_cur_exp (mp, new_expr); +} +break; @ The function |an_angle| returns the value of the |angle| primitive, or $0$ if the argument is |origin|. @<Declare unary action...@>= -static angle mp_an_angle (MP mp,scaled xpar, scaled ypar) { - if ( (! ((xpar==0) && (ypar==0))) ) - return mp_n_arg(mp, xpar,ypar); +static angle mp_an_angle (MP mp, scaled xpar, scaled ypar) { + if ((!((xpar == 0) && (ypar == 0)))) + return mp_n_arg (mp, xpar, ypar); return 0; } @@ -19832,153 +22604,188 @@ moves at the actual points. @d double2angle(a) (int)mp_floor(a*256.0*256.0*16.0) @<Declare unary action...@>= -static angle mp_bezier_slope(MP mp, integer AX,integer AY,integer BX,integer BY, - integer CX,integer CY,integer DX,integer DY); +static angle mp_bezier_slope (MP mp, integer AX, integer AY, integer BX, + integer BY, integer CX, integer CY, integer DX, + integer DY); -@ @c -static angle mp_bezier_slope(MP mp, integer AX,integer AY,integer BX,integer BY, - integer CX,integer CY,integer DX,integer DY) { +@ @c +static angle mp_bezier_slope (MP mp, integer AX, integer AY, integer BX, + integer BY, integer CX, integer CY, integer DX, + integer DY) { double a, b, c; - integer deltax,deltay; - double ax,ay,bx,by,cx,cy,dx,dy; + integer deltax, deltay; + double ax, ay, bx, by, cx, cy, dx, dy; angle xi = 0, xo = 0, xm = 0; double res = 0; - ax=(double)(AX/divisor); ay=(double)(AY/divisor); - bx=(double)(BX/divisor); by=(double)(BY/divisor); - cx=(double)(CX/divisor); cy=(double)(CY/divisor); - dx=(double)(DX/divisor); dy=(double)(DY/divisor); - - deltax = (BX-AX); deltay = (BY-AY); - if (deltax==0 && deltay == 0) { deltax=(CX-AX); deltay=(CY-AY); } - if (deltax==0 && deltay == 0) { deltax=(DX-AX); deltay=(DY-AY); } - xi = mp_an_angle(mp,deltax,deltay); - - deltax = (CX-BX); deltay = (CY-BY); - xm = mp_an_angle(mp,deltax,deltay); - - deltax = (DX-CX); deltay = (DY-CY); - if (deltax==0 && deltay == 0) { deltax=(DX-BX); deltay=(DY-BY); } - if (deltax==0 && deltay == 0) { deltax=(DX-AX); deltay=(DY-AY); } - xo = mp_an_angle(mp,deltax,deltay); - - a = (bx-ax)*(cy-by) - (cx-bx)*(by-ay); /* a = (bp-ap)x(cp-bp); */ - b = (bx-ax)*(dy-cy) - (by-ay)*(dx-cx);; /* b = (bp-ap)x(dp-cp);*/ - c = (cx-bx)*(dy-cy) - (dx-cx)*(cy-by); /* c = (cp-bp)x(dp-cp);*/ - - if ((a==0)&&(c==0)) { - res = (b==0 ? 0 : (mp_out(xo)-mp_out(xi))); - } else if ((a==0)||(c==0)) { - if ((mp_sign(b) == mp_sign(a)) || (mp_sign(b) == mp_sign(c))) { - res = mp_out(xo)-mp_out(xi); /* ? */ - if (res<-180.0) - res += 360.0; - else if (res>180.0) - res -= 360.0; + ax = (double) (AX / divisor); + ay = (double) (AY / divisor); + bx = (double) (BX / divisor); + by = (double) (BY / divisor); + cx = (double) (CX / divisor); + cy = (double) (CY / divisor); + dx = (double) (DX / divisor); + dy = (double) (DY / divisor); + deltax = (BX - AX); + deltay = (BY - AY); + if (deltax == 0 && deltay == 0) { + deltax = (CX - AX); + deltay = (CY - AY); + } + if (deltax == 0 && deltay == 0) { + deltax = (DX - AX); + deltay = (DY - AY); + } + xi = mp_an_angle (mp, deltax, deltay); + deltax = (CX - BX); + deltay = (CY - BY); + xm = mp_an_angle (mp, deltax, deltay); + deltax = (DX - CX); + deltay = (DY - CY); + if (deltax == 0 && deltay == 0) { + deltax = (DX - BX); + deltay = (DY - BY); + } + if (deltax == 0 && deltay == 0) { + deltax = (DX - AX); + deltay = (DY - AY); + } + xo = mp_an_angle (mp, deltax, deltay); + a = (bx - ax) * (cy - by) - (cx - bx) * (by - ay); /* a = (bp-ap)x(cp-bp); */ + b = (bx - ax) * (dy - cy) - (by - ay) * (dx - cx);; /* b = (bp-ap)x(dp-cp); */ + c = (cx - bx) * (dy - cy) - (dx - cx) * (cy - by); /* c = (cp-bp)x(dp-cp); */ + if ((a == 0) && (c == 0)) { + res = (b == 0 ? 0 : (mp_out (xo) - mp_out (xi))); + } else if ((a == 0) || (c == 0)) { + if ((mp_sign (b) == mp_sign (a)) || (mp_sign (b) == mp_sign (c))) { + res = mp_out (xo) - mp_out (xi); /* ? */ + if (res < -180.0) + res += 360.0; + else if (res > 180.0) + res -= 360.0; } else { - res = mp_out(xo)-mp_out(xi); /* ? */ + res = mp_out (xo) - mp_out (xi); /* ? */ } - } else if ((mp_sign(a)*mp_sign(c))<0) { - res = mp_out(xo)-mp_out(xi); /* ? */ - if (res<-180.0) - res += 360.0; - else if (res>180.0) - res -= 360.0; + } else if ((mp_sign (a) * mp_sign (c)) < 0) { + res = mp_out (xo) - mp_out (xi); /* ? */ + if (res < -180.0) + res += 360.0; + else if (res > 180.0) + res -= 360.0; } else { - if (mp_sign(a) == mp_sign(b)) { - res = mp_out(xo)-mp_out(xi); /* ? */ - if (res<-180.0) - res += 360.0; - else if (res>180.0) - res -= 360.0; + if (mp_sign (a) == mp_sign (b)) { + res = mp_out (xo) - mp_out (xi); /* ? */ + if (res < -180.0) + res += 360.0; + else if (res > 180.0) + res -= 360.0; } else { - if ((b*b) == (4*a*c)) { - res = (double)bezier_error; - } else if ((b*b) < (4*a*c)) { - res = mp_out(xo)-mp_out(xi); /* ? */ - if (res<=0.0 &&res>-180.0) - res += 360.0; - else if (res>=0.0 && res<180.0) - res -= 360.0; + if ((b * b) == (4 * a * c)) { + res = (double) bezier_error; + } else if ((b * b) < (4 * a * c)) { + res = mp_out (xo) - mp_out (xi); /* ? */ + if (res <= 0.0 && res > -180.0) + res += 360.0; + else if (res >= 0.0 && res < 180.0) + res -= 360.0; } else { - res = mp_out(xo)-mp_out(xi); - if (res<-180.0) - res += 360.0; - else if (res>180.0) - res -= 360.0; + res = mp_out (xo) - mp_out (xi); + if (res < -180.0) + res += 360.0; + else if (res > 180.0) + res -= 360.0; } } } - return double2angle(res); + return double2angle (res); } + @ -@d p_nextnext mp_link(mp_link(p)) -@d p_next mp_link(p) +@d p_nextnext mp_next_knot(mp_next_knot(p)) +@d p_next mp_next_knot(p) @d seven_twenty_deg 05500000000 /* $720\cdot2^{20}$, represents $720^\circ$ */ @<Declare unary action...@>= -static scaled mp_new_turn_cycles (MP mp,pointer c) { - angle res,ang; /* the angles of intermediate results */ - scaled turns; /* the turn counter */ - pointer p; /* for running around the path */ - integer xp,yp; /* coordinates of next point */ - integer x,y; /* helper coordinates */ - angle in_angle,out_angle; /* helper angles */ +static scaled mp_new_turn_cycles (MP mp, mp_knot c) { + angle res, ang; /* the angles of intermediate results */ + scaled turns; /* the turn counter */ + mp_knot p; /* for running around the path */ + integer xp, yp; /* coordinates of next point */ + integer x, y; /* helper coordinates */ + angle in_angle, out_angle; /* helper angles */ unsigned old_setting; /* saved |selector| setting */ - res=0; - turns= 0; - p=c; - old_setting = mp->selector; mp->selector=term_only; - if ( mp->internal[mp_tracing_commands]>unity ) { - mp_begin_diagnostic(mp); - mp_print_nl(mp, ""); - mp_end_diagnostic(mp, false); - } - do { - xp = mp_x_coord(p_next); yp = mp_y_coord(p_next); - ang = mp_bezier_slope(mp,mp_x_coord(p), mp_y_coord(p), mp_right_x(p), mp_right_y(p), - mp_left_x(p_next), mp_left_y(p_next), xp, yp); - if ( ang>seven_twenty_deg ) { - print_err("Strange path"); - mp_error(mp); - mp->selector=old_setting; + res = 0; + turns = 0; + p = c; + old_setting = mp->selector; + mp->selector = term_only; + if (internal_value (mp_tracing_commands) > unity) { + mp_begin_diagnostic (mp); + mp_print_nl (mp, ""); + mp_end_diagnostic (mp, false); + } + do { + xp = mp_x_coord (p_next); + yp = mp_y_coord (p_next); + ang = + mp_bezier_slope (mp, mp_x_coord (p), mp_y_coord (p), mp_right_x (p), + mp_right_y (p), mp_left_x (p_next), mp_left_y (p_next), + xp, yp); + if (ang > seven_twenty_deg) { + print_err ("Strange path"); + mp_error (mp); + mp->selector = old_setting; return 0; } - res = res + ang; - if ( res > one_eighty_deg ) { + res = res + ang; + if (res > one_eighty_deg) { res = res - three_sixty_deg; turns = turns + unity; } - if ( res <= -one_eighty_deg ) { + if (res <= -one_eighty_deg) { res = res + three_sixty_deg; turns = turns - unity; } /* incoming angle at next point */ - x = mp_left_x(p_next); y = mp_left_y(p_next); - if ( (xp==x)&&(yp==y) ) { x = mp_right_x(p); y = mp_right_y(p); }; - if ( (xp==x)&&(yp==y) ) { x = mp_x_coord(p); y = mp_y_coord(p); }; - in_angle = mp_an_angle(mp, xp - x, yp - y); + x = mp_left_x (p_next); + y = mp_left_y (p_next); + if ((xp == x) && (yp == y)) { + x = mp_right_x (p); + y = mp_right_y (p); + }; + if ((xp == x) && (yp == y)) { + x = mp_x_coord (p); + y = mp_y_coord (p); + }; + in_angle = mp_an_angle (mp, xp - x, yp - y); /* outgoing angle at next point */ - x = mp_right_x(p_next); y = mp_right_y(p_next); - if ( (xp==x)&&(yp==y) ) { x = mp_left_x(p_nextnext); y = mp_left_y(p_nextnext); }; - if ( (xp==x)&&(yp==y) ) { x = mp_x_coord(p_nextnext); y = mp_y_coord(p_nextnext); }; - out_angle = mp_an_angle(mp, x - xp, y- yp); - ang = (out_angle - in_angle); - reduce_angle(ang); - if ( ang!=0 ) { - res = res + ang; - if ( res >= one_eighty_deg ) { + x = mp_right_x (p_next); + y = mp_right_y (p_next); + if ((xp == x) && (yp == y)) { + x = mp_left_x (p_nextnext); + y = mp_left_y (p_nextnext); + }; + if ((xp == x) && (yp == y)) { + x = mp_x_coord (p_nextnext); + y = mp_y_coord (p_nextnext); + }; + out_angle = mp_an_angle (mp, x - xp, y - yp); + ang = (out_angle - in_angle); + reduce_angle (ang); + if (ang != 0) { + res = res + ang; + if (res >= one_eighty_deg) { res = res - three_sixty_deg; turns = turns + unity; }; - if ( res <= -one_eighty_deg ) { + if (res <= -one_eighty_deg) { res = res + three_sixty_deg; turns = turns - unity; }; }; - p = mp_link(p); - } while (p!=c); - mp->selector=old_setting; + p = mp_next_knot (p); + } while (p != c); + mp->selector = old_setting; return turns; } @@ -20022,140 +22829,216 @@ at than the pascal code, because it could use |point -1 of p|. In pascal, the fastest way to loop around the path is not to look backward once, but forward twice. These defines help hide the trick. -@d p_to mp_link(mp_link(p)) -@d p_here mp_link(p) +@d p_to mp_next_knot(mp_next_knot(p)) +@d p_here mp_next_knot(p) @d p_from p @<Declare unary action...@>= -static scaled mp_turn_cycles (MP mp,pointer c) { - angle res,ang; /* the angles of intermediate results */ - scaled turns; /* the turn counter */ - pointer p; /* for running around the path */ - res=0; turns= 0; p=c; - do { - ang = mp_an_angle (mp, mp_x_coord(p_to) - mp_x_coord(p_here), - mp_y_coord(p_to) - mp_y_coord(p_here)) - - mp_an_angle (mp, mp_x_coord(p_here) - mp_x_coord(p_from), - mp_y_coord(p_here) - mp_y_coord(p_from)); - reduce_angle(ang); - res = res + ang; - if ( res >= three_sixty_deg ) { +static scaled mp_turn_cycles (MP mp, mp_knot c) { + angle res, ang; /* the angles of intermediate results */ + scaled turns; /* the turn counter */ + mp_knot p; /* for running around the path */ + res = 0; + turns = 0; + p = c; + do { + ang = mp_an_angle (mp, mp_x_coord (p_to) - mp_x_coord (p_here), + mp_y_coord (p_to) - mp_y_coord (p_here)) + - mp_an_angle (mp, mp_x_coord (p_here) - mp_x_coord (p_from), + mp_y_coord (p_here) - mp_y_coord (p_from)); + reduce_angle (ang); + res = res + ang; + if (res >= three_sixty_deg) { res = res - three_sixty_deg; turns = turns + unity; }; - if ( res <= -three_sixty_deg ) { + if (res <= -three_sixty_deg) { res = res + three_sixty_deg; turns = turns - unity; }; - p = mp_link(p); - } while (p!=c); + p = mp_next_knot (p); + } while (p != c); return turns; } + @ @<Declare unary action...@>= -static scaled mp_turn_cycles_wrapper (MP mp,pointer c) { - scaled nval,oval; - scaled saved_t_o; /* tracing\_online saved */ - if ( (mp_link(c)==c)||(mp_link(mp_link(c))==c) ) { - if ( mp_an_angle (mp, mp_x_coord(c) - mp_right_x(c), mp_y_coord(c) - mp_right_y(c)) > 0 ) +static scaled mp_turn_cycles_wrapper (MP mp, mp_knot c) { + scaled nval, oval; + scaled saved_t_o; /* tracing\_online saved */ + if ((mp_next_knot (c) == c) || (mp_next_knot (mp_next_knot (c)) == c)) { + if (mp_an_angle + (mp, mp_x_coord (c) - mp_right_x (c), + mp_y_coord (c) - mp_right_y (c)) > 0) return unity; else return -unity; } else { - nval = mp_new_turn_cycles(mp, c); - oval = mp_turn_cycles(mp, c); - if ( nval!=oval && mp->internal[mp_tracing_choices]>(2*unity)) { - saved_t_o=mp->internal[mp_tracing_online]; - mp->internal[mp_tracing_online]=unity; - mp_begin_diagnostic(mp); + nval = mp_new_turn_cycles (mp, c); + oval = mp_turn_cycles (mp, c); + if (nval != oval && internal_value (mp_tracing_choices) > (2 * unity)) { + saved_t_o = internal_value (mp_tracing_online); + internal_value (mp_tracing_online) = unity; + mp_begin_diagnostic (mp); mp_print_nl (mp, "Warning: the turningnumber algorithms do not agree." - " The current computed value is "); - mp_print_scaled(mp, nval); - mp_print(mp, ", but the 'connect-the-dots' algorithm returned "); - mp_print_scaled(mp, oval); - mp_end_diagnostic(mp, false); - mp->internal[mp_tracing_online]=saved_t_o; + " The current computed value is "); + mp_print_scaled (mp, nval); + mp_print (mp, ", but the 'connect-the-dots' algorithm returned "); + mp_print_scaled (mp, oval); + mp_end_diagnostic (mp, false); + internal_value (mp_tracing_online) = saved_t_o; } return nval; } } + @ @d type_range(A,B) { - if ( (mp->cur_type>=(A)) && (mp->cur_type<=(B)) ) - mp_flush_cur_exp(mp, true_code); - else mp_flush_cur_exp(mp, false_code); - mp->cur_type=mp_boolean_type; + if ( (mp->cur_exp.type>=(A)) && (mp->cur_exp.type<=(B)) ) + new_expr.data.val = true_code; + else + new_expr.data.val = false_code; + mp_flush_cur_exp(mp, new_expr); + mp->cur_exp.type=mp_boolean_type; } @d type_test(A) { - if ( mp->cur_type==(A) ) mp_flush_cur_exp(mp, true_code); - else mp_flush_cur_exp(mp, false_code); - mp->cur_type=mp_boolean_type; + if ( mp->cur_exp.type==(mp_variable_type)(A) ) + new_expr.data.val = true_code; + else + new_expr.data.val = false_code; + mp_flush_cur_exp(mp, new_expr); + mp->cur_exp.type=mp_boolean_type; } @<Additional cases of unary operators@>= -case mp_boolean_type: - type_range(mp_boolean_type,mp_unknown_boolean); break; -case mp_string_type: - type_range(mp_string_type,mp_unknown_string); break; -case mp_pen_type: - type_range(mp_pen_type,mp_unknown_pen); break; -case mp_path_type: - type_range(mp_path_type,mp_unknown_path); break; -case mp_picture_type: - type_range(mp_picture_type,mp_unknown_picture); break; -case mp_transform_type: case mp_color_type: case mp_cmykcolor_type: -case mp_pair_type: - type_test(c); break; -case mp_numeric_type: - type_range(mp_known,mp_independent); break; -case known_op: case unknown_op: - mp_test_known(mp, c); break; +case mp_boolean_type: +type_range (mp_boolean_type, mp_unknown_boolean); +break; +case mp_string_type: +type_range (mp_string_type, mp_unknown_string); +break; +case mp_pen_type: +type_range (mp_pen_type, mp_unknown_pen); +break; +case mp_path_type: +type_range (mp_path_type, mp_unknown_path); +break; +case mp_picture_type: +type_range (mp_picture_type, mp_unknown_picture); +break; +case mp_transform_type: +case mp_color_type: +case mp_cmykcolor_type: +case mp_pair_type: +type_test (c); +break; +case mp_numeric_type: +type_range (mp_known, mp_independent); +break; +case known_op: +case unknown_op: +mp_test_known (mp, c); +break; @ @<Declare unary action procedures@>= -static void mp_test_known (MP mp,quarterword c) { - int b; /* is the current expression known? */ - pointer p,q; /* locations in a big node */ - b=false_code; - switch (mp->cur_type) { - case mp_vacuous: case mp_boolean_type: case mp_string_type: - case mp_pen_type: case mp_path_type: case mp_picture_type: - case mp_known: - b=true_code; +static void mp_test_known (MP mp, quarterword c) { + int b; /* is the current expression known? */ + mp_node p; /* location in a big node */ + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + b = false_code; + switch (mp->cur_exp.type) { + case mp_vacuous: + case mp_boolean_type: + case mp_string_type: + case mp_pen_type: + case mp_path_type: + case mp_picture_type: + case mp_known: + b = true_code; break; case mp_transform_type: - case mp_color_type: case mp_cmykcolor_type: case mp_pair_type: - p=value(mp->cur_exp); - q=p+mp->big_node_size[mp->cur_type]; - do { - q=q-2; - if ( mp_type(q)!=mp_known ) - goto DONE; - } while (q!=p); - b=true_code; - DONE: + p = value_node (cur_exp_node ()); + if (mp_type (tx_part_loc (p)) != mp_known) + break; + if (mp_type (ty_part_loc (p)) != mp_known) + break; + if (mp_type (xx_part_loc (p)) != mp_known) + break; + if (mp_type (xy_part_loc (p)) != mp_known) + break; + if (mp_type (yx_part_loc (p)) != mp_known) + break; + if (mp_type (yy_part_loc (p)) != mp_known) + break; + b = true_code; break; - default: + case mp_color_type: + p = value_node (cur_exp_node ()); + if (mp_type (red_part_loc (p)) != mp_known) + break; + if (mp_type (green_part_loc (p)) != mp_known) + break; + if (mp_type (blue_part_loc (p)) != mp_known) + break; + b = true_code; + break; + case mp_cmykcolor_type: + p = value_node (cur_exp_node ()); + if (mp_type (cyan_part_loc (p)) != mp_known) + break; + if (mp_type (magenta_part_loc (p)) != mp_known) + break; + if (mp_type (yellow_part_loc (p)) != mp_known) + break; + if (mp_type (black_part_loc (p)) != mp_known) + break; + b = true_code; + break; + case mp_pair_type: + p = value_node (cur_exp_node ()); + if (mp_type (x_part_loc (p)) != mp_known) + break; + if (mp_type (y_part_loc (p)) != mp_known) + break; + b = true_code; + break; + default: break; } - if ( c==known_op ) mp_flush_cur_exp(mp, b); - else mp_flush_cur_exp(mp, true_code+false_code-b); - mp->cur_type=mp_boolean_type; + if (c == known_op) + new_expr.data.val = b; + else + new_expr.data.val = true_code + false_code - b; + mp_flush_cur_exp (mp, new_expr); + cur_exp_node () = NULL; + mp->cur_exp.type = mp_boolean_type; } + @ @<Additional cases of unary operators@>= -case cycle_op: - if ( mp->cur_type!=mp_path_type ) mp_flush_cur_exp(mp, false_code); - else if ( mp_left_type(mp->cur_exp)!=mp_endpoint ) mp_flush_cur_exp(mp, true_code); - else mp_flush_cur_exp(mp, false_code); - mp->cur_type=mp_boolean_type; - break; +case cycle_op: +if (mp->cur_exp.type != mp_path_type) + new_expr.data.val = false_code; +else if (mp_left_type (cur_exp_knot ()) != mp_endpoint) + new_expr.data.val = true_code; +else + new_expr.data.val = false_code; +mp_flush_cur_exp (mp, new_expr); +mp->cur_exp.type = mp_boolean_type; +break; @ @<Additional cases of unary operators@>= -case arc_length: - if ( mp->cur_type==mp_pair_type ) mp_pair_to_path(mp); - if ( mp->cur_type!=mp_path_type ) mp_bad_unary(mp, arc_length); - else mp_flush_cur_exp(mp, mp_get_arc_length(mp, mp->cur_exp)); - break; +case arc_length: +if (mp->cur_exp.type == mp_pair_type) + mp_pair_to_path (mp); +if (mp->cur_exp.type != mp_path_type) { + mp_bad_unary (mp, arc_length); +} else { + new_expr.data.val = mp_get_arc_length (mp, cur_exp_knot ()); + mp_flush_cur_exp (mp, new_expr); +} +break; @ Here we use the fact that |c-filled_op+fill_code| is the desired graphical object |type|. @@ -20167,268 +23050,334 @@ case stroked_op: case textual_op: case clipped_op: case bounded_op: - if ( mp->cur_type!=mp_picture_type ) mp_flush_cur_exp(mp, false_code); - else if ( mp_link(dummy_loc(mp->cur_exp))==null ) mp_flush_cur_exp(mp, false_code); - else if ( mp_type(mp_link(dummy_loc(mp->cur_exp)))==c+mp_fill_code-filled_op ) - mp_flush_cur_exp(mp, true_code); - else mp_flush_cur_exp(mp, false_code); - mp->cur_type=mp_boolean_type; - break; +if (mp->cur_exp.type != mp_picture_type) { + new_expr.data.val = false_code; +} else if (mp_link (dummy_loc (cur_exp_node ())) == NULL) { + new_expr.data.val = false_code; +} else if (mp_type (mp_link (dummy_loc (cur_exp_node ()))) == + (mp_variable_type) (c + mp_fill_node_type - filled_op)) { + new_expr.data.val = true_code; +} else { + new_expr.data.val = false_code; +} +mp_flush_cur_exp (mp, new_expr); +mp->cur_exp.type = mp_boolean_type; +break; @ @<Additional cases of unary operators@>= -case make_pen_op: - if ( mp->cur_type==mp_pair_type ) mp_pair_to_path(mp); - if ( mp->cur_type!=mp_path_type ) mp_bad_unary(mp, make_pen_op); - else { - mp->cur_type=mp_pen_type; - mp->cur_exp=mp_make_pen(mp, mp->cur_exp,true); - }; - break; -case make_path_op: - if ( mp->cur_type!=mp_pen_type ) mp_bad_unary(mp, make_path_op); - else { - mp->cur_type=mp_path_type; - mp_make_path(mp, mp->cur_exp); - }; - break; -case reverse: - if ( mp->cur_type==mp_path_type ) { - p=mp_htap_ypoc(mp, mp->cur_exp); - if ( mp_right_type(p)==mp_endpoint ) p=mp_link(p); - mp_toss_knot_list(mp, mp->cur_exp); mp->cur_exp=p; - } else if ( mp->cur_type==mp_pair_type ) mp_pair_to_path(mp); - else mp_bad_unary(mp, reverse); - break; +case make_pen_op: +if (mp->cur_exp.type == mp_pair_type) + mp_pair_to_path (mp); +if (mp->cur_exp.type != mp_path_type) + mp_bad_unary (mp, make_pen_op); +else { + mp->cur_exp.type = mp_pen_type; + set_cur_exp_knot (mp_make_pen (mp, cur_exp_knot (), true)); +} +break; +case make_path_op: +if (mp->cur_exp.type != mp_pen_type) { + mp_bad_unary (mp, make_path_op); +} else { + mp->cur_exp.type = mp_path_type; + mp_make_path (mp, cur_exp_knot ()); +} +break; +case reverse: +if (mp->cur_exp.type == mp_path_type) { + mp_knot pk = mp_htap_ypoc (mp, cur_exp_knot ()); + if (mp_right_type (pk) == mp_endpoint) + pk = mp_next_knot (pk); + mp_toss_knot_list (mp, cur_exp_knot ()); + set_cur_exp_knot (pk); +} else if (mp->cur_exp.type == mp_pair_type) { + mp_pair_to_path (mp); +} else { + mp_bad_unary (mp, reverse); +} +break; @ The |pair_value| routine changes the current expression to a given ordered pair of values. @<Declare unary action procedures@>= -static void mp_pair_value (MP mp,scaled x, scaled y) { - pointer p; /* a pair node */ - p=mp_get_node(mp, value_node_size); - mp_flush_cur_exp(mp, p); mp->cur_type=mp_pair_type; - mp_type(p)=mp_pair_type; mp_name_type(p)=mp_capsule; mp_init_big_node(mp, p); - p=value(p); - mp_type(x_part_loc(p))=mp_known; value(x_part_loc(p))=x; - mp_type(y_part_loc(p))=mp_known; value(y_part_loc(p))=y; +static void mp_pair_value (MP mp, scaled x, scaled y) { + mp_node p; /* a pair node */ + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + p = mp_get_value_node (mp); + new_expr.type = mp_type (p); + new_expr.data.node = p; + mp_flush_cur_exp (mp, new_expr); + mp->cur_exp.type = mp_pair_type; + mp_name_type (p) = mp_capsule; + mp_init_pair_node (mp, p); + p = value_node (p); + mp_type (x_part_loc (p)) = mp_known; + set_value (x_part_loc (p), x); + mp_type (y_part_loc (p)) = mp_known; + set_value (y_part_loc (p), y); } + @ @<Additional cases of unary operators@>= -case ll_corner_op: - if ( ! mp_get_cur_bbox(mp) ) mp_bad_unary(mp, ll_corner_op); - else mp_pair_value(mp, mp_minx, mp_miny); - break; -case lr_corner_op: - if ( ! mp_get_cur_bbox(mp) ) mp_bad_unary(mp, lr_corner_op); - else mp_pair_value(mp, mp_maxx, mp_miny); - break; -case ul_corner_op: - if ( ! mp_get_cur_bbox(mp) ) mp_bad_unary(mp, ul_corner_op); - else mp_pair_value(mp, mp_minx, mp_maxy); - break; -case ur_corner_op: - if ( ! mp_get_cur_bbox(mp) ) mp_bad_unary(mp, ur_corner_op); - else mp_pair_value(mp, mp_maxx, mp_maxy); - break; +case ll_corner_op: +if (!mp_get_cur_bbox (mp)) + mp_bad_unary (mp, ll_corner_op); +else + mp_pair_value (mp, mp_minx, mp_miny); +break; +case lr_corner_op: +if (!mp_get_cur_bbox (mp)) + mp_bad_unary (mp, lr_corner_op); +else + mp_pair_value (mp, mp_maxx, mp_miny); +break; +case ul_corner_op: +if (!mp_get_cur_bbox (mp)) + mp_bad_unary (mp, ul_corner_op); +else + mp_pair_value (mp, mp_minx, mp_maxy); +break; +case ur_corner_op: +if (!mp_get_cur_bbox (mp)) + mp_bad_unary (mp, ur_corner_op); +else + mp_pair_value (mp, mp_maxx, mp_maxy); +break; @ Here is a function that sets |minx|, |maxx|, |miny|, |maxy| to the bounding box of the current expression. The boolean result is |false| if the expression has the wrong type. @<Declare unary action procedures@>= -static boolean mp_get_cur_bbox (MP mp) { - switch (mp->cur_type) { - case mp_picture_type: - mp_set_bbox(mp, mp->cur_exp,true); - if ( minx_val(mp->cur_exp)>maxx_val(mp->cur_exp) ) { - mp_minx=0; mp_maxx=0; mp_miny=0; mp_maxy=0; - } else { - mp_minx=minx_val(mp->cur_exp); - mp_maxx=maxx_val(mp->cur_exp); - mp_miny=miny_val(mp->cur_exp); - mp_maxy=maxy_val(mp->cur_exp); +static boolean mp_get_cur_bbox (MP mp) { + switch (mp->cur_exp.type) { + case mp_picture_type: + mp_set_bbox (mp, cur_exp_node (), true); + if (minx_val (cur_exp_node ()) > maxx_val (cur_exp_node ())) { + mp_minx = 0; + mp_maxx = 0; + mp_miny = 0; + mp_maxy = 0; + } else { + mp_minx = minx_val (cur_exp_node ()); + mp_maxx = maxx_val (cur_exp_node ()); + mp_miny = miny_val (cur_exp_node ()); + mp_maxy = maxy_val (cur_exp_node ()); } break; - case mp_path_type: - mp_path_bbox(mp, mp->cur_exp); + case mp_path_type: + mp_path_bbox (mp, cur_exp_knot ()); break; - case mp_pen_type: - mp_pen_bbox(mp, mp->cur_exp); + case mp_pen_type: + mp_pen_bbox (mp, cur_exp_knot ()); break; - default: + default: return false; } return true; } + @ @<Additional cases of unary operators@>= case read_from_op: -case close_from_op: - if ( mp->cur_type!=mp_string_type ) mp_bad_unary(mp, c); - else mp_do_read_or_close(mp,c); - break; +case close_from_op: +if (mp->cur_exp.type != mp_string_type) + mp_bad_unary (mp, c); +else + mp_do_read_or_close (mp, c); +break; @ Here is a routine that interprets |cur_exp| as a file name and tries to read a line from the file or to close the file. @<Declare unary action procedures@>= -static void mp_do_read_or_close (MP mp,quarterword c) { - readf_index n,n0; /* indices for searching |rd_fname| */ +static void mp_do_read_or_close (MP mp, quarterword c) { + mp_value new_expr; + readf_index n, n0; /* indices for searching |rd_fname| */ + memset(&new_expr,0,sizeof(mp_value)); @<Find the |n| where |rd_fname[n]=cur_exp|; if |cur_exp| must be inserted, call |start_read_input| and |goto found| or |not_found|@>; - mp_begin_file_reading(mp); - name=is_read; - if ( mp_input_ln(mp, mp->rd_file[n] ) ) + mp_begin_file_reading (mp); + name = is_read; + if (mp_input_ln (mp, mp->rd_file[n])) goto FOUND; - mp_end_file_reading(mp); + mp_end_file_reading (mp); NOT_FOUND: @<Record the end of file and set |cur_exp| to a dummy value@>; return; CLOSE_FILE: - mp_flush_cur_exp(mp, 0); mp->cur_type=mp_vacuous; + mp_flush_cur_exp (mp, new_expr); + mp->cur_exp.type = mp_vacuous; return; FOUND: - mp_flush_cur_exp(mp, 0); - mp_finish_read(mp); + mp_flush_cur_exp (mp, new_expr); + mp_finish_read (mp); } + @ Free slots in the |rd_file| and |rd_fname| arrays are marked with NULL's in |rd_fname|. @<Find the |n| where |rd_fname[n]=cur_exp|...@>= -{ +{ char *fn; - n=mp->read_files; - n0=mp->read_files; - fn = str(mp->cur_exp); - while (mp_xstrcmp(fn,mp->rd_fname[n])!=0) { - if ( n>0 ) { - decr(n); - } else if ( c==close_from_op ) { + n = mp->read_files; + n0 = mp->read_files; + fn = mp_xstrdup (mp, mp_str (mp, cur_exp_str ())); + while (mp_xstrcmp (fn, mp->rd_fname[n]) != 0) { + if (n > 0) { + decr (n); + } else if (c == close_from_op) { goto CLOSE_FILE; } else { - if ( n0==mp->read_files ) { - if ( mp->read_files<mp->max_read_files ) { - incr(mp->read_files); + if (n0 == mp->read_files) { + if (mp->read_files < mp->max_read_files) { + incr (mp->read_files); } else { void **rd_file; char **rd_fname; - readf_index l,k; - l = mp->max_read_files + (mp->max_read_files/4); - rd_file = xmalloc((l+1), sizeof(void *)); - rd_fname = xmalloc((l+1), sizeof(char *)); - for (k=0;k<=l;k++) { - if (k<=mp->max_read_files) { - rd_file[k]=mp->rd_file[k]; - rd_fname[k]=mp->rd_fname[k]; + readf_index l, k; + l = mp->max_read_files + (mp->max_read_files / 4); + rd_file = xmalloc ((l + 1), sizeof (void *)); + rd_fname = xmalloc ((l + 1), sizeof (char *)); + for (k = 0; k <= l; k++) { + if (k <= mp->max_read_files) { + rd_file[k] = mp->rd_file[k]; + rd_fname[k] = mp->rd_fname[k]; } else { - rd_file[k]=0; - rd_fname[k]=NULL; + rd_file[k] = 0; + rd_fname[k] = NULL; } } - xfree(mp->rd_file); xfree(mp->rd_fname); + xfree (mp->rd_file); + xfree (mp->rd_fname); mp->max_read_files = l; mp->rd_file = rd_file; mp->rd_fname = rd_fname; } } - n=n0; - if ( mp_start_read_input(mp,fn,n) ) + n = n0; + if (mp_start_read_input (mp, fn, n)) goto FOUND; - else + else goto NOT_FOUND; } - if ( mp->rd_fname[n]==NULL ) { n0=n; } - } - if ( c==close_from_op ) { - (mp->close_file)(mp,mp->rd_file[n]); - goto NOT_FOUND; + if (mp->rd_fname[n] == NULL) { + n0 = n; + } + } + if (c == close_from_op) { + (mp->close_file) (mp, mp->rd_file[n]); + goto NOT_FOUND; } } + @ @<Record the end of file and set |cur_exp| to a dummy value@>= -xfree(mp->rd_fname[n]); -mp->rd_fname[n]=NULL; -if ( n==mp->read_files-1 ) mp->read_files=n; -if ( c==close_from_op ) +xfree (mp->rd_fname[n]); +mp->rd_fname[n] = NULL; +if (n == mp->read_files - 1) + mp->read_files = n; +if (c == close_from_op) goto CLOSE_FILE; -mp_flush_cur_exp(mp, mp->eof_line); -mp->cur_type=mp_string_type +{ + new_expr.data.str = mp->eof_line; + add_str_ref (new_expr.data.str); + mp_flush_cur_exp (mp, new_expr); +} +mp->cur_exp.type = mp_string_type -@ The string denoting end-of-file is a one-byte string at position zero, by definition +@ The string denoting end-of-file is a one-byte string at position zero, by definition. +I have to cheat a little here because @<Glob...@>= str_number eof_line; @ @<Set init...@>= -mp->eof_line=0; +mp->eof_line = mp_rtsl (mp, "\0", 1); +mp->eof_line->refs = MAX_STR_REF; @ Finally, we have the operations that combine a capsule~|p| with the current expression. @d binary_return { mp_finish_binary(mp, old_p, old_exp); return; } -@c @<Declare binary action procedures@> -static void mp_finish_binary (MP mp, pointer old_p, pointer old_exp ){ - check_arith; +@c +@<Declare binary action procedures@>; +static void mp_finish_binary (MP mp, mp_node old_p, mp_node old_exp) { + check_arith; @<Recycle any sidestepped |independent| capsules@>; } -static void mp_do_binary (MP mp,pointer p, integer c) { - pointer q,r,rr; /* for list manipulation */ - pointer old_p,old_exp; /* capsules to recycle */ - integer v; /* for numeric manipulation */ +static void mp_do_binary (MP mp, mp_node p, integer c) { + mp_node q, r, rr; /* for list manipulation */ + mp_node old_p, old_exp; /* capsules to recycle */ + integer v; /* for numeric manipulation */ + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); check_arith; - if ( mp->internal[mp_tracing_commands]>two ) { + if (internal_value (mp_tracing_commands) > two) { @<Trace the current binary operation@>; } @<Sidestep |independent| cases in capsule |p|@>; @<Sidestep |independent| cases in the current expression@>; switch (c) { - case plus: case minus: + case plus: + case minus: @<Add or subtract the current expression from |p|@>; break; - @<Additional cases of binary operators@>; - }; /* there are no other cases */ - mp_recycle_value(mp, p); - mp_free_node(mp, p,value_node_size); /* |return| to avoid this */ - mp_finish_binary(mp, old_p, old_exp); + @<Additional cases of binary operators@>; + }; /* there are no other cases */ + mp_recycle_value (mp, p); + mp_free_node (mp, p, value_node_size); /* |return| to avoid this */ + mp_finish_binary (mp, old_p, old_exp); } + @ @<Declare binary action...@>= -static void mp_bad_binary (MP mp,pointer p, quarterword c) { - mp_disp_err(mp, p,""); - exp_err("Not implemented: "); -@.Not implemented...@> - if ( c>=min_of ) mp_print_op(mp, c); - mp_print_known_or_unknown_type(mp, mp_type(p),p); - if ( c>=min_of ) mp_print(mp, "of"); else mp_print_op(mp, c); - mp_print_known_or_unknown_type(mp, mp->cur_type,mp->cur_exp); - help3("I'm afraid I don't know how to apply that operation to that", - "combination of types. Continue, and I'll return the second", - "argument (see above) as the result of the operation."); - mp_put_get_error(mp); +static void mp_bad_binary (MP mp, mp_node p, quarterword c) { + mp_disp_err (mp, p, ""); + exp_err ("Not implemented: "); +@.Not implemented...@>; + if (c >= min_of) + mp_print_op (mp, c); + mp_print_known_or_unknown_type (mp, mp_type (p), p); + if (c >= min_of) + mp_print (mp, "of"); + else + mp_print_op (mp, c); + mp_print_known_or_unknown_type (mp, mp->cur_exp.type, cur_exp_node ()); + help3 ("I'm afraid I don't know how to apply that operation to that", + "combination of types. Continue, and I'll return the second", + "argument (see above) as the result of the operation."); + mp_put_get_error (mp); } static void mp_bad_envelope_pen (MP mp) { - mp_disp_err(mp, null,""); - exp_err("Not implemented: envelope(elliptical pen)of(path)"); -@.Not implemented...@> - help3("I'm afraid I don't know how to apply that operation to that", - "combination of types. Continue, and I'll return the second", - "argument (see above) as the result of the operation."); - mp_put_get_error(mp); + mp_disp_err (mp, NULL, ""); + exp_err ("Not implemented: envelope(elliptical pen)of(path)"); +@.Not implemented...@>; + help3 ("I'm afraid I don't know how to apply that operation to that", + "combination of types. Continue, and I'll return the second", + "argument (see above) as the result of the operation."); + mp_put_get_error (mp); } + @ @<Trace the current binary operation@>= -{ - mp_begin_diagnostic(mp); mp_print_nl(mp, "{("); - mp_print_exp(mp,p,0); /* show the operand, but not verbosely */ - mp_print_char(mp,xord(')')); mp_print_op(mp, (quarterword)c); - mp_print_char(mp,xord('(')); - mp_print_exp(mp,null,0); mp_print(mp,")}"); - mp_end_diagnostic(mp, false); +{ + mp_begin_diagnostic (mp); + mp_print_nl (mp, "{("); + mp_print_exp (mp, p, 0); /* show the operand, but not verbosely */ + mp_print_char (mp, xord (')')); + mp_print_op (mp, (quarterword) c); + mp_print_char (mp, xord ('(')); + mp_print_exp (mp, NULL, 0); + mp_print (mp, ")}"); + mp_end_diagnostic (mp, false); } + @ Several of the binary operations are potentially complicated by the fact that |independent| values can sneak into capsules. For example, we've seen an instance of this difficulty in the unary operation @@ -20439,84 +23388,192 @@ put into capsules called |old_p| and |old_exp|, which will be recycled after the binary operation has been safely carried out. @<Recycle any sidestepped |independent| capsules@>= -if ( old_p!=null ) { - mp_recycle_value(mp, old_p); mp_free_node(mp, old_p,value_node_size); +if (old_p != NULL) { + mp_recycle_value (mp, old_p); + mp_free_node (mp, old_p, value_node_size); } -if ( old_exp!=null ) { - mp_recycle_value(mp, old_exp); mp_free_node(mp, old_exp,value_node_size); +if (old_exp != NULL) { + mp_recycle_value (mp, old_exp); + mp_free_node (mp, old_exp, value_node_size); } @ A big node is considered to be ``tarnished'' if it contains at least one independent component. We will define a simple function called `|tarnished|' -that returns |null| if and only if its argument is not tarnished. +that returns |NULL| if and only if its argument is not tarnished. @<Sidestep |independent| cases in capsule |p|@>= -switch (mp_type(p)) { +switch (mp_type (p)) { case mp_transform_type: case mp_color_type: case mp_cmykcolor_type: -case mp_pair_type: - old_p=mp_tarnished(mp, p); +case mp_pair_type: + old_p = mp_tarnished (mp, p); + break; +case mp_independent: + old_p = MP_VOID; + break; +default: + old_p = NULL; break; -case mp_independent: old_p=mp_void; break; -default: old_p=null; break; } -if ( old_p!=null ) { - q=mp_stash_cur_exp(mp); old_p=p; mp_make_exp_copy(mp, old_p); - p=mp_stash_cur_exp(mp); mp_unstash_cur_exp(mp, q); +if (old_p != NULL) { + q = mp_stash_cur_exp (mp); + old_p = p; + mp_make_exp_copy (mp, old_p); + p = mp_stash_cur_exp (mp); + mp_unstash_cur_exp (mp, q); } @ @<Sidestep |independent| cases in the current expression@>= -switch (mp->cur_type) { +switch (mp->cur_exp.type) { case mp_transform_type: case mp_color_type: case mp_cmykcolor_type: -case mp_pair_type: - old_exp=mp_tarnished(mp, mp->cur_exp); +case mp_pair_type: + old_exp = mp_tarnished (mp, cur_exp_node ()); + break; +case mp_independent: + old_exp = MP_VOID; + break; +default: + old_exp = NULL; break; -case mp_independent:old_exp=mp_void; break; -default: old_exp=null; break; } -if ( old_exp!=null ) { - old_exp=mp->cur_exp; mp_make_exp_copy(mp, old_exp); +if (old_exp != NULL) { + old_exp = cur_exp_node (); + mp_make_exp_copy (mp, old_exp); } @ @<Declare binary action...@>= -static pointer mp_tarnished (MP mp,pointer p) { - pointer q; /* beginning of the big node */ - pointer r; /* current position in the big node */ - q=value(p); r=q+mp->big_node_size[mp_type(p)]; - do { - r=r-2; - if ( mp_type(r)==mp_independent ) return mp_void; - } while (r!=q); - return null; +static mp_node mp_tarnished (MP mp, mp_node p) { + mp_node q; /* beginning of the big node */ + mp_node r; /* moving value node pointer */ + (void) mp; + q = value_node (p); + switch (mp_type (p)) { + case mp_pair_type: + r = x_part_loc (q); + if (mp_type (r) == mp_independent) + return MP_VOID; + r = y_part_loc (q); + if (mp_type (r) == mp_independent) + return MP_VOID; + break; + case mp_color_type: + r = red_part_loc (q); + if (mp_type (r) == mp_independent) + return MP_VOID; + r = green_part_loc (q); + if (mp_type (r) == mp_independent) + return MP_VOID; + r = blue_part_loc (q); + if (mp_type (r) == mp_independent) + return MP_VOID; + break; + case mp_cmykcolor_type: + r = cyan_part_loc (q); + if (mp_type (r) == mp_independent) + return MP_VOID; + r = magenta_part_loc (q); + if (mp_type (r) == mp_independent) + return MP_VOID; + r = yellow_part_loc (q); + if (mp_type (r) == mp_independent) + return MP_VOID; + r = black_part_loc (q); + if (mp_type (r) == mp_independent) + return MP_VOID; + break; + case mp_transform_type: + r = tx_part_loc (q); + if (mp_type (r) == mp_independent) + return MP_VOID; + r = ty_part_loc (q); + if (mp_type (r) == mp_independent) + return MP_VOID; + r = xx_part_loc (q); + if (mp_type (r) == mp_independent) + return MP_VOID; + r = xy_part_loc (q); + if (mp_type (r) == mp_independent) + return MP_VOID; + r = yx_part_loc (q); + if (mp_type (r) == mp_independent) + return MP_VOID; + r = yy_part_loc (q); + if (mp_type (r) == mp_independent) + return MP_VOID; + break; + default: /* there are no other valid cases, but please the compiler */ + break; + } + return NULL; } + @ @<Add or subtract the current expression from |p|@>= -if ( (mp->cur_type<mp_color_type)||(mp_type(p)<mp_color_type) ) { - mp_bad_binary(mp, p, (quarterword)c); -} else { - if ((mp->cur_type>mp_pair_type)&&(mp_type(p)>mp_pair_type) ) { - mp_add_or_subtract(mp, p,null, (quarterword)c); +if ((mp->cur_exp.type < mp_color_type) || (mp_type (p) < mp_color_type)) { + mp_bad_binary (mp, p, (quarterword) c); +} else { + if ((mp->cur_exp.type > mp_pair_type) && (mp_type (p) > mp_pair_type)) { + mp_add_or_subtract (mp, p, NULL, (quarterword) c); } else { - if ( mp->cur_type!=mp_type(p) ) { - mp_bad_binary(mp, p, (quarterword)c); - } else { - q=value(p); r=value(mp->cur_exp); - rr=r+mp->big_node_size[mp->cur_type]; - while ( r<rr ) { - mp_add_or_subtract(mp, q, r, (quarterword)c); - q=q+2; r=r+2; + if (mp->cur_exp.type != mp_type (p)) { + mp_bad_binary (mp, p, (quarterword) c); + } else { + q = value_node (p); + r = value_node (cur_exp_node ()); + switch (mp->cur_exp.type) { + case mp_pair_type: + mp_add_or_subtract (mp, x_part_loc (q), x_part_loc (r), + (quarterword) c); + mp_add_or_subtract (mp, y_part_loc (q), y_part_loc (r), + (quarterword) c); + break; + case mp_color_type: + mp_add_or_subtract (mp, red_part_loc (q), red_part_loc (r), + (quarterword) c); + mp_add_or_subtract (mp, green_part_loc (q), green_part_loc (r), + (quarterword) c); + mp_add_or_subtract (mp, blue_part_loc (q), blue_part_loc (r), + (quarterword) c); + break; + case mp_cmykcolor_type: + mp_add_or_subtract (mp, cyan_part_loc (q), cyan_part_loc (r), + (quarterword) c); + mp_add_or_subtract (mp, magenta_part_loc (q), magenta_part_loc (r), + (quarterword) c); + mp_add_or_subtract (mp, yellow_part_loc (q), yellow_part_loc (r), + (quarterword) c); + mp_add_or_subtract (mp, black_part_loc (q), black_part_loc (r), + (quarterword) c); + break; + case mp_transform_type: + mp_add_or_subtract (mp, tx_part_loc (q), tx_part_loc (r), + (quarterword) c); + mp_add_or_subtract (mp, ty_part_loc (q), ty_part_loc (r), + (quarterword) c); + mp_add_or_subtract (mp, xx_part_loc (q), xx_part_loc (r), + (quarterword) c); + mp_add_or_subtract (mp, xy_part_loc (q), xy_part_loc (r), + (quarterword) c); + mp_add_or_subtract (mp, yx_part_loc (q), yx_part_loc (r), + (quarterword) c); + mp_add_or_subtract (mp, yy_part_loc (q), yy_part_loc (r), + (quarterword) c); + break; + default: /* there are no other valid cases, but please the compiler */ + break; } } } } + @ The first argument to |add_or_subtract| is the location of a value node in a capsule or pair node that will soon be recycled. The second argument is either a location within a pair or transform node of |cur_exp|, -or it is null (which means that |cur_exp| itself should be the second +or it is NULL (which means that |cur_exp| itself should be the second argument). The third argument is either |plus| or |minus|. The sum or difference of the numeric quantities will replace the second @@ -20525,405 +23582,710 @@ be monkeying around with really big values. @^overflow in arithmetic@> @<Declare binary action...@>= -@<Declare the procedure called |dep_finish|@> -static void mp_add_or_subtract (MP mp,pointer p, pointer q, quarterword c) { - quarterword s,t; /* operand types */ - pointer r; /* list traverser */ - integer v; /* second operand value */ - if ( q==null ) { - t=mp->cur_type; - if ( t<mp_dependent ) v=mp->cur_exp; else v=dep_list(mp->cur_exp); - } else { - t=mp_type(q); - if ( t<mp_dependent ) v=value(q); else v=dep_list(q); - } - if ( t==mp_known ) { - if ( c==minus ) negate(v); - if ( mp_type(p)==mp_known ) { - v=mp_slow_add(mp, value(p),v); - if ( q==null ) mp->cur_exp=v; else value(q)=v; +@<Declare the procedure called |dep_finish|@>; +static void mp_add_or_subtract (MP mp, mp_node p, mp_node q, quarterword c) { + mp_variable_type s, t; /* operand types */ + mp_value_node r; /* dependency list traverser */ + mp_value_node v = NULL; /* second operand value for dep lists */ + integer vv = 0; /* second operand value for known values */ + if (q == NULL) { + t = mp->cur_exp.type; + if (t < mp_dependent) + vv = cur_exp_value (); + else + v = (mp_value_node) dep_list ((mp_value_node) cur_exp_node ()); + } else { + t = mp_type (q); + if (t < mp_dependent) + vv = value (q); + else + v = (mp_value_node) dep_list ((mp_value_node) q); + } + if (t == mp_known) { + mp_value_node qq = (mp_value_node) q; + if (c == minus) + negate (vv); + if (mp_type (p) == mp_known) { + vv = mp_slow_add (mp, value (p), vv); + if (q == NULL) + set_cur_exp_value (vv); + else + set_value (q, vv); return; } - @<Add a known value to the constant term of |dep_list(p)|@>; - } else { - if ( c==minus ) mp_negate_dep_list(mp, v); + /* Add a known value to the constant term of |dep_list(p)| */ + r = (mp_value_node) dep_list ((mp_value_node) p); + while (dep_info (r) != NULL) + r = (mp_value_node) mp_link (r); + set_dep_value (r, mp_slow_add (mp, dep_value (r), vv)); + if (qq == NULL) { + qq = mp_get_dep_node (mp); + set_cur_exp_node ((mp_node) qq); + mp->cur_exp.type = mp_type (p); + mp_name_type (qq) = mp_capsule; + q = (mp_node) qq; + } + set_dep_list (qq, dep_list ((mp_value_node) p)); + mp_type (qq) = mp_type (p); + set_prev_dep (qq, prev_dep ((mp_value_node) p)); + mp_link (prev_dep ((mp_value_node) p)) = (mp_node) qq; + mp_type (p) = mp_known; /* this will keep the recycler from collecting non-garbage */ + } else { + if (c == minus) + mp_negate_dep_list (mp, v); @<Add operand |p| to the dependency list |v|@>; } } -@ @<Add a known value to the constant term of |dep_list(p)|@>= -r=dep_list(p); -while ( mp_info(r)!=null ) r=mp_link(r); -value(r)=mp_slow_add(mp, value(r),v); -if ( q==null ) { - q=mp_get_node(mp, value_node_size); mp->cur_exp=q; mp->cur_type=mp_type(p); - mp_name_type(q)=mp_capsule; -} -dep_list(q)=dep_list(p); mp_type(q)=mp_type(p); -prev_dep(q)=prev_dep(p); mp_link(prev_dep(p))=q; -mp_type(p)=mp_known; /* this will keep the recycler from collecting non-garbage */ @ We prefer |dependent| lists to |mp_proto_dependent| ones, because it is nice to retain the extra accuracy of |fraction| coefficients. But we have to handle both kinds, and mixtures too. @<Add operand |p| to the dependency list |v|@>= -if ( mp_type(p)==mp_known ) { - @<Add the known |value(p)| to the constant term of |v|@>; -} else { - s=mp_type(p); r=dep_list(p); - if ( t==mp_dependent ) { - if ( s==mp_dependent ) { - if ( mp_max_coef(mp, r)+mp_max_coef(mp, v)<coef_bound ) { - v=mp_p_plus_q(mp, v,r,mp_dependent); goto DONE; - } - } /* |fix_needed| will necessarily be false */ - t=mp_proto_dependent; - v=mp_p_over_v(mp, v,unity,mp_dependent,mp_proto_dependent); - } - if ( s==mp_proto_dependent ) v=mp_p_plus_q(mp, v,r,mp_proto_dependent); - else v=mp_p_plus_fq(mp, v,unity,r,mp_proto_dependent,mp_dependent); - DONE: - @<Output the answer, |v| (which might have become |known|)@>; -} - -@ @<Add the known |value(p)| to the constant term of |v|@>= -{ - while ( mp_info(v)!=null ) v=mp_link(v); - value(v)=mp_slow_add(mp, value(p),value(v)); -} - -@ @<Output the answer, |v| (which might have become |known|)@>= -if ( q!=null ) mp_dep_finish(mp, v,q,t); -else { mp->cur_type=t; mp_dep_finish(mp, v,null,t); } +if (mp_type (p) == mp_known) { + /* Add the known |value(p)| to the constant term of |v| */ + while (dep_info (v) != NULL) + v = (mp_value_node) mp_link (v); + set_dep_value (v, mp_slow_add (mp, value (p), dep_value (v))); +} else { + s = mp_type (p); + r = (mp_value_node) dep_list ((mp_value_node) p); + if (t == mp_dependent) { + if (s == mp_dependent) { + if (mp_max_coef (mp, r) + mp_max_coef (mp, v) < coef_bound) { + v = mp_p_plus_q (mp, v, r, mp_dependent); + goto DONE; + } + } /* |fix_needed| will necessarily be false */ + t = mp_proto_dependent; + v = mp_p_over_v (mp, v, unity, mp_dependent, mp_proto_dependent); + } + if (s == mp_proto_dependent) + v = mp_p_plus_q (mp, v, r, mp_proto_dependent); + else + v = mp_p_plus_fq (mp, v, unity, r, mp_proto_dependent, mp_dependent); +DONE: + /* Output the answer, |v| (which might have become |known|) */ + if (q != NULL) { + mp_dep_finish (mp, v, (mp_value_node) q, t); + } else { + mp->cur_exp.type = t; + mp_dep_finish (mp, v, NULL, t); + } +} + @ Here's the current situation: The dependency list |v| of type |t| -should either be put into the current expression (if |q=null|) or +should either be put into the current expression (if |q=NULL|) or into location |q| within a pair node (otherwise). The destination (|cur_exp| or |q|) formerly held a dependency list with the same final pointer as the list |v|. @<Declare the procedure called |dep_finish|@>= -static void mp_dep_finish (MP mp, pointer v, pointer q, quarterword t) { - pointer p; /* the destination */ - scaled vv; /* the value, if it is |known| */ - if ( q==null ) p=mp->cur_exp; else p=q; - dep_list(p)=v; mp_type(p)=t; - if ( mp_info(v)==null ) { - vv=value(v); - if ( q==null ) { - mp_flush_cur_exp(mp, vv); - } else { - mp_recycle_value(mp, p); mp_type(q)=mp_known; value(q)=vv; +static void mp_dep_finish (MP mp, mp_value_node v, mp_value_node q, + quarterword t) { + mp_value_node p; /* the destination */ + scaled vv; /* the value, if it is |known| */ + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + if (q == NULL) + p = (mp_value_node) cur_exp_node (); + else + p = q; + set_dep_list (p, (mp_node) v); + mp_type (p) = t; + if (dep_info (v) == NULL) { + vv = value (v); + if (q == NULL) { + new_expr.data.val = vv; + mp_flush_cur_exp (mp, new_expr); + } else { + mp_recycle_value (mp, (mp_node) p); + mp_type (q) = mp_known; + set_value (q, vv); } - } else if ( q==null ) { - mp->cur_type=t; + } else if (q == NULL) { + mp->cur_exp.type = t; } - if ( mp->fix_needed ) mp_fix_dependencies(mp); + if (mp->fix_needed) + mp_fix_dependencies (mp); } + @ Let's turn now to the six basic relations of comparison. @<Additional cases of binary operators@>= -case less_than: case less_or_equal: case greater_than: -case greater_or_equal: case equal_to: case unequal_to: - check_arith; /* at this point |arith_error| should be |false|? */ - if ( (mp->cur_type>mp_pair_type)&&(mp_type(p)>mp_pair_type) ) { - mp_add_or_subtract(mp, p,null,minus); /* |cur_exp:=(p)-cur_exp| */ - } else if ( mp->cur_type!=mp_type(p) ) { - mp_bad_binary(mp, p, (quarterword)c); goto DONE; - } else if ( mp->cur_type==mp_string_type ) { - mp_flush_cur_exp(mp, mp_str_vs_str(mp, value(p),mp->cur_exp)); - } else if ((mp->cur_type==mp_unknown_string)|| - (mp->cur_type==mp_unknown_boolean) ) { - @<Check if unknowns have been equated@>; - } else if ( (mp->cur_type<=mp_pair_type)&&(mp->cur_type>=mp_transform_type)) { - @<Reduce comparison of big nodes to comparison of scalars@>; - } else if ( mp->cur_type==mp_boolean_type ) { - mp_flush_cur_exp(mp, mp->cur_exp-value(p)); - } else { - mp_bad_binary(mp, p, (quarterword)c); goto DONE; - } - @<Compare the current expression with zero@>; -DONE: - mp->arith_error=false; /* ignore overflow in comparisons */ - break; +case less_than: +case less_or_equal: +case greater_than: +case greater_or_equal: +case equal_to: +case unequal_to: +check_arith; /* at this point |arith_error| should be |false|? */ +if ((mp->cur_exp.type > mp_pair_type) && (mp_type (p) > mp_pair_type)) { + mp_add_or_subtract (mp, p, NULL, minus); /* |cur_exp:=(p)-cur_exp| */ +} else if (mp->cur_exp.type != mp_type (p)) { + mp_bad_binary (mp, p, (quarterword) c); + goto DONE; +} else if (mp->cur_exp.type == mp_string_type) { + new_expr.data.val = mp_str_vs_str (mp, str_value (p), cur_exp_str ()); + mp_flush_cur_exp (mp, new_expr); +} else if ((mp->cur_exp.type == mp_unknown_string) || + (mp->cur_exp.type == mp_unknown_boolean)) { + @<Check if unknowns have been equated@>; +} else if ((mp->cur_exp.type <= mp_pair_type) + && (mp->cur_exp.type >= mp_transform_type)) { + @<Reduce comparison of big nodes to comparison of scalars@>; +} else if (mp->cur_exp.type == mp_boolean_type) { + new_expr.data.val = cur_exp_value () - value (p); + mp_flush_cur_exp (mp, new_expr); +} else { + mp_bad_binary (mp, p, (quarterword) c); + goto DONE; +} +@<Compare the current expression with zero@>; +DONE: +mp->arith_error = false; /* ignore overflow in comparisons */ +break; @ @<Compare the current expression with zero@>= -if ( mp->cur_type!=mp_known ) { - if ( mp->cur_type<mp_known ) { - mp_disp_err(mp, p,""); - help1("The quantities shown above have not been equated.") - } else { - help2("Oh dear. I can\'t decide if the expression above is positive,", - "negative, or zero. So this comparison test won't be `true'."); - } - exp_err("Unknown relation will be considered false"); -@.Unknown relation...@> - mp_put_get_flush_error(mp, false_code); +if (mp->cur_exp.type != mp_known) { + if (mp->cur_exp.type < mp_known) { + mp_disp_err (mp, p, ""); + help1 ("The quantities shown above have not been equated.") + } else { + help2 ("Oh dear. I can\'t decide if the expression above is positive,", + "negative, or zero. So this comparison test won't be `true'."); + } + exp_err ("Unknown relation will be considered false"); +@.Unknown relation...@>; + new_expr.data.val = false_code; + mp_put_get_flush_error (mp, new_expr); } else { switch (c) { - case less_than: boolean_reset(mp->cur_exp<0); break; - case less_or_equal: boolean_reset(mp->cur_exp<=0); break; - case greater_than: boolean_reset(mp->cur_exp>0); break; - case greater_or_equal: boolean_reset(mp->cur_exp>=0); break; - case equal_to: boolean_reset(mp->cur_exp==0); break; - case unequal_to: boolean_reset(mp->cur_exp!=0); break; - }; /* there are no other cases */ + case less_than: + boolean_reset (cur_exp_value () < 0); + break; + case less_or_equal: + boolean_reset (cur_exp_value () <= 0); + break; + case greater_than: + boolean_reset (cur_exp_value () > 0); + break; + case greater_or_equal: + boolean_reset (cur_exp_value () >= 0); + break; + case equal_to: + boolean_reset (cur_exp_value () == 0); + break; + case unequal_to: + boolean_reset (cur_exp_value () != 0); + break; + }; /* there are no other cases */ } -mp->cur_type=mp_boolean_type +mp->cur_exp.type = mp_boolean_type @ When two unknown strings are in the same ring, we know that they are equal. Otherwise, we don't know whether they are equal or not, so we make no change. @<Check if unknowns have been equated@>= -{ - q=value(mp->cur_exp); - while ( (q!=mp->cur_exp)&&(q!=p) ) q=value(q); - if ( q==p ) mp_flush_cur_exp(mp, 0); +{ + q = value_node (cur_exp_node ()); + while ((q != cur_exp_node ()) && (q != p)) + q = value_node (q); + if (q == p) { + set_cur_exp_node (NULL); + mp_flush_cur_exp (mp, new_expr); + } } -@ @<Reduce comparison of big nodes to comparison of scalars@>= -{ - q=value(p); r=value(mp->cur_exp); - rr=r+mp->big_node_size[mp->cur_type]-2; - while (1) { mp_add_or_subtract(mp, q,r,minus); - if ( mp_type(r)!=mp_known ) break; - if ( value(r)!=0 ) break; - if ( r==rr ) break; - q=q+2; r=r+2; + +@ In the following, the |while| loops exist just so that |break| can be used, +each loop runs exactly once. + +@<Reduce comparison of big nodes to comparison of scalars@>= +{ + quarterword part_type; + q = value_node (p); + r = value_node (cur_exp_node ()); + part_type = 0; + switch (mp->cur_exp.type) { + case mp_pair_type: + while (part_type==0) { + rr = x_part_loc (r); + part_type = x_part; + mp_add_or_subtract (mp, x_part_loc (q), rr, minus); + if (mp_type (rr) != mp_known || value (rr) != 0) + break; + rr = y_part_loc (r); + part_type = y_part; + mp_add_or_subtract (mp, y_part_loc (q), rr, minus); + if (mp_type (rr) != mp_known || value (rr) != 0) + break; + } + mp_take_part (mp, part_type); + break; + case mp_color_type: + while (part_type==0) { + rr = red_part_loc (r); + part_type = red_part; + mp_add_or_subtract (mp, red_part_loc (q), rr, minus); + if (mp_type (rr) != mp_known || value (rr) != 0) + break; + rr = green_part_loc (r); + part_type = green_part; + mp_add_or_subtract (mp, green_part_loc (q), rr, minus); + if (mp_type (rr) != mp_known || value (rr) != 0) + break; + rr = blue_part_loc (r); + part_type = blue_part; + mp_add_or_subtract (mp, blue_part_loc (q), rr, minus); + if (mp_type (rr) != mp_known || value (rr) != 0) + break; + } + mp_take_part (mp, part_type); + break; + case mp_cmykcolor_type: + while (part_type==0) { + rr = cyan_part_loc (r); + part_type = cyan_part; + mp_add_or_subtract (mp, cyan_part_loc (q), rr, minus); + if (mp_type (rr) != mp_known || value (rr) != 0) + break; + rr = magenta_part_loc (r); + part_type = magenta_part; + mp_add_or_subtract (mp, magenta_part_loc (q), rr, minus); + if (mp_type (rr) != mp_known || value (rr) != 0) + break; + rr = yellow_part_loc (r); + part_type = yellow_part; + mp_add_or_subtract (mp, yellow_part_loc (q), rr, minus); + if (mp_type (rr) != mp_known || value (rr) != 0) + break; + rr = black_part_loc (r); + part_type = black_part; + mp_add_or_subtract (mp, black_part_loc (q), rr, minus); + if (mp_type (rr) != mp_known || value (rr) != 0) + break; + } + mp_take_part (mp, part_type); + break; + case mp_transform_type: + while (part_type==0) { + rr = tx_part_loc (r); + part_type = x_part; + mp_add_or_subtract (mp, tx_part_loc (q), rr, minus); + if (mp_type (rr) != mp_known || value (rr) != 0) + break; + rr = ty_part_loc (r); + part_type = y_part; + mp_add_or_subtract (mp, ty_part_loc (q), rr, minus); + if (mp_type (rr) != mp_known || value (rr) != 0) + break; + rr = xx_part_loc (r); + part_type = xx_part; + mp_add_or_subtract (mp, xx_part_loc (q), rr, minus); + if (mp_type (rr) != mp_known || value (rr) != 0) + break; + rr = xy_part_loc (r); + part_type = xy_part; + mp_add_or_subtract (mp, xy_part_loc (q), rr, minus); + if (mp_type (rr) != mp_known || value (rr) != 0) + break; + rr = yx_part_loc (r); + part_type = yx_part; + mp_add_or_subtract (mp, yx_part_loc (q), rr, minus); + if (mp_type (rr) != mp_known || value (rr) != 0) + break; + rr = yy_part_loc (r); + part_type = yy_part; + mp_add_or_subtract (mp, yy_part_loc (q), rr, minus); + if (mp_type (rr) != mp_known || value (rr) != 0) + break; + } + mp_take_part (mp, part_type); + break; + default: + assert (0); /* todo: |mp->cur_exp.type>mp_transform_node_type| ? */ + break; } - mp_take_part(mp, (quarterword)(mp_name_type(r)+x_part-mp_x_part_sector)); } + @ Here we use the sneaky fact that |and_op-false_code=or_op-true_code|. @<Additional cases of binary operators@>= case and_op: -case or_op: - if ( (mp_type(p)!=mp_boolean_type)||(mp->cur_type!=mp_boolean_type) ) - mp_bad_binary(mp, p, (quarterword)c); - else if ( value(p)==c+false_code-and_op ) mp->cur_exp=value(p); - break; +case or_op: +if ((mp_type (p) != mp_boolean_type) || (mp->cur_exp.type != mp_boolean_type)) + mp_bad_binary (mp, p, (quarterword) c); +else if (value (p) == c + false_code - and_op) + set_cur_exp_value (value (p)); +break; @ @<Additional cases of binary operators@>= -case times: - if ( (mp->cur_type<mp_color_type)||(mp_type(p)<mp_color_type) ) { - mp_bad_binary(mp, p, times); - } else if ( (mp->cur_type==mp_known)||(mp_type(p)==mp_known) ) { - @<Multiply when at least one operand is known@>; - } else if ( (mp_nice_color_or_pair(mp, p,mp_type(p))&&(mp->cur_type>mp_pair_type)) - ||(mp_nice_color_or_pair(mp, mp->cur_exp,mp->cur_type)&& - (mp_type(p)>mp_pair_type)) ) { - mp_hard_times(mp, p); - binary_return; - } else { - mp_bad_binary(mp, p,times); - } - break; +case times: +if ((mp->cur_exp.type < mp_color_type) || (mp_type (p) < mp_color_type)) { + mp_bad_binary (mp, p, times); +} else if ((mp->cur_exp.type == mp_known) || (mp_type (p) == mp_known)) { + @<Multiply when at least one operand is known@>; +} else if ((mp_nice_color_or_pair (mp, p, mp_type (p)) + && (mp->cur_exp.type > mp_pair_type)) + || (mp_nice_color_or_pair (mp, cur_exp_node (), mp->cur_exp.type) + && (mp_type (p) > mp_pair_type))) { + mp_hard_times (mp, p); + binary_return; +} else { + mp_bad_binary (mp, p, times); +} +break; @ @<Multiply when at least one operand is known@>= -{ - if ( mp_type(p)==mp_known ) { - v=value(p); mp_free_node(mp, p,value_node_size); +{ + if (mp_type (p) == mp_known) { + v = value (p); + mp_free_node (mp, p, value_node_size); } else { - v=mp->cur_exp; mp_unstash_cur_exp(mp, p); - } - if ( mp->cur_type==mp_known ) { - mp->cur_exp=mp_take_scaled(mp, mp->cur_exp,v); - } else if ( (mp->cur_type==mp_pair_type)|| - (mp->cur_type==mp_color_type)|| - (mp->cur_type==mp_cmykcolor_type) ) { - p=value(mp->cur_exp)+mp->big_node_size[mp->cur_type]; - do { - p=p-2; mp_dep_mult(mp, p,v,true); - } while (p!=value(mp->cur_exp)); + v = cur_exp_value (); + mp_unstash_cur_exp (mp, p); + } + if (mp->cur_exp.type == mp_known) { + set_cur_exp_value (mp_take_scaled (mp, cur_exp_value (), v)); + } else if (mp->cur_exp.type == mp_pair_type) { + mp_dep_mult (mp, (mp_value_node) x_part_loc (value_node (cur_exp_node ())), + v, true); + mp_dep_mult (mp, (mp_value_node) y_part_loc (value_node (cur_exp_node ())), + v, true); + } else if (mp->cur_exp.type == mp_color_type) { + mp_dep_mult (mp, + (mp_value_node) red_part_loc (value_node (cur_exp_node ())), v, + true); + mp_dep_mult (mp, + (mp_value_node) green_part_loc (value_node (cur_exp_node ())), + v, true); + mp_dep_mult (mp, + (mp_value_node) blue_part_loc (value_node (cur_exp_node ())), + v, true); + } else if (mp->cur_exp.type == mp_cmykcolor_type) { + mp_dep_mult (mp, + (mp_value_node) cyan_part_loc (value_node (cur_exp_node ())), + v, true); + mp_dep_mult (mp, (mp_value_node) + magenta_part_loc (value_node (cur_exp_node ())), v, true); + mp_dep_mult (mp, + (mp_value_node) yellow_part_loc (value_node (cur_exp_node ())), + v, true); + mp_dep_mult (mp, + (mp_value_node) black_part_loc (value_node (cur_exp_node ())), + v, true); } else { - mp_dep_mult(mp, null,v,true); + mp_dep_mult (mp, NULL, v, true); } binary_return; } + @ @<Declare binary action...@>= -static void mp_dep_mult (MP mp,pointer p, integer v, boolean v_is_scaled) { - pointer q; /* the dependency list being multiplied by |v| */ - quarterword s,t; /* its type, before and after */ - if ( p==null ) { - q=mp->cur_exp; - } else if ( mp_type(p)!=mp_known ) { - q=p; - } else { - if ( v_is_scaled ) value(p)=mp_take_scaled(mp, value(p),v); - else value(p)=mp_take_fraction(mp, value(p),v); +static void mp_dep_mult (MP mp, mp_value_node p, integer v, boolean v_is_scaled) { + mp_value_node q; /* the dependency list being multiplied by |v| */ + quarterword s, t; /* its type, before and after */ + if (p == NULL) { + q = (mp_value_node) cur_exp_node (); + } else if (mp_type (p) != mp_known) { + q = p; + } else { + if (v_is_scaled) + set_dep_value (p, mp_take_scaled (mp, dep_value (p), v)); + else + set_dep_value (p, mp_take_fraction (mp, dep_value (p), v)); return; }; - t=mp_type(q); q=dep_list(q); s=t; - if ( t==mp_dependent ) if ( v_is_scaled ) - if (mp_ab_vs_cd(mp, mp_max_coef(mp,q),abs(v),coef_bound-1,unity)>=0 ) - t=mp_proto_dependent; - q=mp_p_times_v(mp, q,v,s,t,v_is_scaled); - mp_dep_finish(mp, q,p,t); + t = mp_type (q); + q = (mp_value_node) dep_list (q); + s = t; + if (t == mp_dependent) + if (v_is_scaled) + if (mp_ab_vs_cd (mp, mp_max_coef (mp, q), abs (v), coef_bound - 1, unity) + >= 0) + t = mp_proto_dependent; + q = mp_p_times_v (mp, q, v, s, t, v_is_scaled); + mp_dep_finish (mp, q, p, t); } + @ Here is a routine that is similar to |times|; but it is invoked only internally, when |v| is a |fraction| whose magnitude is at most~1, and when |cur_type>=mp_color_type|. -@c -static void mp_frac_mult (MP mp,scaled n, scaled d) { +@c +static void mp_frac_mult (MP mp, scaled n, scaled d) { /* multiplies |cur_exp| by |n/d| */ - pointer p; /* a pair node */ - pointer old_exp; /* a capsule to recycle */ - fraction v; /* |n/d| */ - if ( mp->internal[mp_tracing_commands]>two ) { + mp_node old_exp; /* a capsule to recycle */ + fraction v; /* |n/d| */ + if (internal_value (mp_tracing_commands) > two) { @<Trace the fraction multiplication@>; } - switch (mp->cur_type) { + switch (mp->cur_exp.type) { case mp_transform_type: case mp_color_type: case mp_cmykcolor_type: case mp_pair_type: - old_exp=mp_tarnished(mp, mp->cur_exp); - break; - case mp_independent: old_exp=mp_void; break; - default: old_exp=null; break; - } - if ( old_exp!=null ) { - old_exp=mp->cur_exp; mp_make_exp_copy(mp, old_exp); - } - v=mp_make_fraction(mp, n,d); - if ( mp->cur_type==mp_known ) { - mp->cur_exp=mp_take_fraction(mp, mp->cur_exp,v); - } else if ( mp->cur_type<=mp_pair_type ) { - p=value(mp->cur_exp)+mp->big_node_size[mp->cur_type]; - do { - p=p-2; - mp_dep_mult(mp, p,v,false); - } while (p!=value(mp->cur_exp)); + old_exp = mp_tarnished (mp, cur_exp_node ()); + break; + case mp_independent: + old_exp = MP_VOID; + break; + default: + old_exp = NULL; + break; + } + if (old_exp != NULL) { + old_exp = cur_exp_node (); + mp_make_exp_copy (mp, old_exp); + } + v = mp_make_fraction (mp, n, d); + if (mp->cur_exp.type == mp_known) { + set_cur_exp_value (mp_take_fraction (mp, cur_exp_value (), v)); + } else if (mp->cur_exp.type == mp_pair_type) { + mp_dep_mult (mp, (mp_value_node) x_part_loc (value_node (cur_exp_node ())), + v, false); + mp_dep_mult (mp, (mp_value_node) y_part_loc (value_node (cur_exp_node ())), + v, false); + } else if (mp->cur_exp.type == mp_color_type) { + mp_dep_mult (mp, + (mp_value_node) red_part_loc (value_node (cur_exp_node ())), v, + false); + mp_dep_mult (mp, + (mp_value_node) green_part_loc (value_node (cur_exp_node ())), + v, false); + mp_dep_mult (mp, + (mp_value_node) blue_part_loc (value_node (cur_exp_node ())), + v, false); + } else if (mp->cur_exp.type == mp_cmykcolor_type) { + mp_dep_mult (mp, + (mp_value_node) cyan_part_loc (value_node (cur_exp_node ())), + v, false); + mp_dep_mult (mp, (mp_value_node) + magenta_part_loc (value_node (cur_exp_node ())), v, false); + mp_dep_mult (mp, + (mp_value_node) yellow_part_loc (value_node (cur_exp_node ())), + v, false); + mp_dep_mult (mp, + (mp_value_node) black_part_loc (value_node (cur_exp_node ())), + v, false); } else { - mp_dep_mult(mp, null,v,false); + mp_dep_mult (mp, NULL, v, false); } - if ( old_exp!=null ) { - mp_recycle_value(mp, old_exp); - mp_free_node(mp, old_exp,value_node_size); + if (old_exp != NULL) { + mp_recycle_value (mp, old_exp); + mp_free_node (mp, old_exp, value_node_size); } } + @ @<Trace the fraction multiplication@>= -{ - mp_begin_diagnostic(mp); - mp_print_nl(mp, "{("); mp_print_scaled(mp,n); mp_print_char(mp,xord('/')); - mp_print_scaled(mp,d); mp_print(mp,")*("); mp_print_exp(mp,null,0); - mp_print(mp,")}"); - mp_end_diagnostic(mp, false); +{ + mp_begin_diagnostic (mp); + mp_print_nl (mp, "{("); + mp_print_scaled (mp, n); + mp_print_char (mp, xord ('/')); + mp_print_scaled (mp, d); + mp_print (mp, ")*("); + mp_print_exp (mp, NULL, 0); + mp_print (mp, ")}"); + mp_end_diagnostic (mp, false); } + @ The |hard_times| routine multiplies a nice color or pair by a dependency list. @<Declare binary action procedures@>= -static void mp_hard_times (MP mp,pointer p) { - pointer q; /* a copy of the dependent variable |p| */ - pointer r; /* a component of the big node for the nice color or pair */ - scaled v; /* the known value for |r| */ - if ( mp_type(p)<=mp_pair_type ) { - q=mp_stash_cur_exp(mp); mp_unstash_cur_exp(mp, p); p=q; - }; /* now |cur_type=mp_pair_type| or |cur_type=mp_color_type| */ - r=value(mp->cur_exp)+mp->big_node_size[mp->cur_type]; - while (1) { - r=r-2; - v=value(r); - mp_type(r)=mp_type(p); - if ( r==value(mp->cur_exp) ) - break; - mp_new_dep(mp, r,mp_copy_dep_list(mp, dep_list(p))); - mp_dep_mult(mp, r,v,true); +static void mp_hard_times (MP mp, mp_node p) { + mp_value_node q; /* a copy of the dependent variable |p| */ + mp_value_node pp; /* for typecasting p */ + mp_node r; /* a component of the big node for the nice color or pair */ + scaled v; /* the known value for |r| */ + if (mp_type (p) <= mp_pair_type) { + q = (mp_value_node) mp_stash_cur_exp (mp); + mp_unstash_cur_exp (mp, p); + p = (mp_node) q; + } /* now |cur_type=mp_pair_type| or |cur_type=mp_color_type| or |cur_type=mp_cmykcolor_type| */ + pp = (mp_value_node) p; + if (mp->cur_exp.type == mp_pair_type) { + r = x_part_loc (value_node (cur_exp_node ())); + v = value (r); + mp_new_dep (mp, r, mp_type (pp), + mp_copy_dep_list (mp, (mp_value_node) dep_list (pp))); + mp_dep_mult (mp, (mp_value_node) r, v, true); + r = y_part_loc (value_node (cur_exp_node ())); + v = value (r); + mp_new_dep (mp, r, mp_type (pp), + mp_copy_dep_list (mp, (mp_value_node) dep_list (pp))); + mp_dep_mult (mp, (mp_value_node) r, v, true); + } else if (mp->cur_exp.type == mp_color_type) { + r = red_part_loc (value_node (cur_exp_node ())); + v = value (r); + mp_new_dep (mp, r, mp_type (pp), + mp_copy_dep_list (mp, (mp_value_node) dep_list (pp))); + mp_dep_mult (mp, (mp_value_node) r, v, true); + r = green_part_loc (value_node (cur_exp_node ())); + v = value (r); + mp_new_dep (mp, r, mp_type (pp), + mp_copy_dep_list (mp, (mp_value_node) dep_list (pp))); + mp_dep_mult (mp, (mp_value_node) r, v, true); + r = blue_part_loc (value_node (cur_exp_node ())); + v = value (r); + mp_new_dep (mp, r, mp_type (pp), + mp_copy_dep_list (mp, (mp_value_node) dep_list (pp))); + mp_dep_mult (mp, (mp_value_node) r, v, true); + } else if (mp->cur_exp.type == mp_cmykcolor_type) { + r = cyan_part_loc (value_node (cur_exp_node ())); + v = value (r); + mp_new_dep (mp, r, mp_type (pp), + mp_copy_dep_list (mp, (mp_value_node) dep_list (pp))); + mp_dep_mult (mp, (mp_value_node) r, v, true); + r = yellow_part_loc (value_node (cur_exp_node ())); + v = value (r); + mp_new_dep (mp, r, mp_type (pp), + mp_copy_dep_list (mp, (mp_value_node) dep_list (pp))); + mp_dep_mult (mp, (mp_value_node) r, v, true); + r = magenta_part_loc (value_node (cur_exp_node ())); + v = value (r); + mp_new_dep (mp, r, mp_type (pp), + mp_copy_dep_list (mp, (mp_value_node) dep_list (pp))); + mp_dep_mult (mp, (mp_value_node) r, v, true); + r = black_part_loc (value_node (cur_exp_node ())); + v = value (r); + mp_new_dep (mp, r, mp_type (pp), + mp_copy_dep_list (mp, (mp_value_node) dep_list (pp))); + mp_dep_mult (mp, (mp_value_node) r, v, true); } - mp->mem[value_loc(r)]=mp->mem[value_loc(p)]; - mp_link(prev_dep(p))=r; - mp_free_node(mp, p,value_node_size); - mp_dep_mult(mp, r,v,true); } + @ @<Additional cases of binary operators@>= -case over: - if ( (mp->cur_type!=mp_known)||(mp_type(p)<mp_color_type) ) { - mp_bad_binary(mp, p,over); - } else { - v=mp->cur_exp; mp_unstash_cur_exp(mp, p); - if ( v==0 ) { - @<Squeal about division by zero@>; - } else { - if ( mp->cur_type==mp_known ) { - mp->cur_exp=mp_make_scaled(mp, mp->cur_exp,v); - } else if ( mp->cur_type<=mp_pair_type ) { - p=value(mp->cur_exp)+mp->big_node_size[mp->cur_type]; - do { - p=p-2; mp_dep_div(mp, p,v); - } while (p!=value(mp->cur_exp)); - } else { - mp_dep_div(mp, null,v); - } +case over: +if ((mp->cur_exp.type != mp_known) || (mp_type (p) < mp_color_type)) { + mp_bad_binary (mp, p, over); +} else { + v = cur_exp_value (); + mp_unstash_cur_exp (mp, p); + if (v == 0) { + @<Squeal about division by zero@>; + } else { + if (mp->cur_exp.type == mp_known) { + set_cur_exp_value (mp_make_scaled (mp, cur_exp_value (), v)); + } else if (mp->cur_exp.type == mp_pair_type) { + mp_dep_div (mp, (mp_value_node) x_part_loc (value_node (cur_exp_node ())), + v); + mp_dep_div (mp, (mp_value_node) y_part_loc (value_node (cur_exp_node ())), + v); + } else if (mp->cur_exp.type == mp_color_type) { + mp_dep_div (mp, + (mp_value_node) red_part_loc (value_node (cur_exp_node ())), + v); + mp_dep_div (mp, + (mp_value_node) green_part_loc (value_node (cur_exp_node ())), + v); + mp_dep_div (mp, + (mp_value_node) blue_part_loc (value_node (cur_exp_node ())), + v); + } else if (mp->cur_exp.type == mp_cmykcolor_type) { + mp_dep_div (mp, + (mp_value_node) cyan_part_loc (value_node (cur_exp_node ())), + v); + mp_dep_div (mp, (mp_value_node) + magenta_part_loc (value_node (cur_exp_node ())), v); + mp_dep_div (mp, (mp_value_node) + yellow_part_loc (value_node (cur_exp_node ())), v); + mp_dep_div (mp, + (mp_value_node) black_part_loc (value_node (cur_exp_node ())), + v); + } else { + mp_dep_div (mp, NULL, v); } - binary_return; } - break; + binary_return; +} +break; @ @<Declare binary action...@>= -static void mp_dep_div (MP mp,pointer p, scaled v) { - pointer q; /* the dependency list being divided by |v| */ - quarterword s,t; /* its type, before and after */ - if ( p==null ) q=mp->cur_exp; - else if ( mp_type(p)!=mp_known ) q=p; - else { value(p)=mp_make_scaled(mp, value(p),v); return; }; - t=mp_type(q); q=dep_list(q); s=t; - if ( t==mp_dependent ) - if ( mp_ab_vs_cd(mp, mp_max_coef(mp,q),unity,coef_bound-1,abs(v))>=0 ) - t=mp_proto_dependent; - q=mp_p_over_v(mp, q,v,s,t); - mp_dep_finish(mp, q,p,t); +static void mp_dep_div (MP mp, mp_value_node p, scaled v) { + mp_value_node q; /* the dependency list being divided by |v| */ + quarterword s, t; /* its type, before and after */ + if (p == NULL) + q = (mp_value_node) cur_exp_node (); + else if (mp_type (p) != mp_known) + q = p; + else { + set_value (p, mp_make_scaled (mp, value (p), v)); + return; + } + t = mp_type (q); + q = (mp_value_node) dep_list (q); + s = t; + if (t == mp_dependent) + if (mp_ab_vs_cd (mp, mp_max_coef (mp, q), unity, coef_bound - 1, abs (v)) >= + 0) + t = mp_proto_dependent; + q = mp_p_over_v (mp, q, v, s, t); + mp_dep_finish (mp, q, p, t); } + @ @<Squeal about division by zero@>= -{ - exp_err("Division by zero"); -@.Division by zero@> - help2("You're trying to divide the quantity shown above the error", - "message by zero. I'm going to divide it by one instead."); - mp_put_get_error(mp); +{ + exp_err ("Division by zero"); +@.Division by zero@>; + help2 ("You're trying to divide the quantity shown above the error", + "message by zero. I'm going to divide it by one instead."); + mp_put_get_error (mp); } + @ @<Additional cases of binary operators@>= case pythag_add: -case pythag_sub: - if ( (mp->cur_type==mp_known)&&(mp_type(p)==mp_known) ) { - if ( c==pythag_add ) mp->cur_exp=mp_pyth_add(mp, value(p),mp->cur_exp); - else mp->cur_exp=mp_pyth_sub(mp, value(p),mp->cur_exp); - } else mp_bad_binary(mp, p, (quarterword)c); - break; +case pythag_sub: +if ((mp->cur_exp.type == mp_known) && (mp_type (p) == mp_known)) { + if (c == pythag_add) + set_cur_exp_value (mp_pyth_add (mp, value (p), cur_exp_value ())); + else + set_cur_exp_value (mp_pyth_sub (mp, value (p), cur_exp_value ())); +} else + mp_bad_binary (mp, p, (quarterword) c); +break; @ The next few sections of the program deal with affine transformations of coordinate data. @<Additional cases of binary operators@>= -case rotated_by: case slanted_by: -case scaled_by: case shifted_by: case transformed_by: -case x_scaled: case y_scaled: case z_scaled: - if ( mp_type(p)==mp_path_type ) { - path_trans((quarterword)c, p); binary_return; - } else if ( mp_type(p)==mp_pen_type ) { - pen_trans((quarterword)c, p); - mp->cur_exp=mp_convex_hull(mp, mp->cur_exp); - /* rounding error could destroy convexity */ - binary_return; - } else if ( (mp_type(p)==mp_pair_type)||(mp_type(p)==mp_transform_type) ) { - mp_big_trans(mp, p, (quarterword)c); - } else if ( mp_type(p)==mp_picture_type ) { - mp_do_edges_trans(mp, p, (quarterword)c); binary_return; - } else { - mp_bad_binary(mp, p, (quarterword)c); - } - break; +case rotated_by: +case slanted_by: +case scaled_by: +case shifted_by: +case transformed_by: +case x_scaled: +case y_scaled: +case z_scaled: +if (mp_type (p) == mp_path_type) { + path_trans ((quarterword) c, p); + binary_return; +} else if (mp_type (p) == mp_pen_type) { + pen_trans ((quarterword) c, p); + set_cur_exp_knot (mp_convex_hull (mp, cur_exp_knot ())); + /* rounding error could destroy convexity */ + binary_return; +} else if ((mp_type (p) == mp_pair_type) || (mp_type (p) == mp_transform_type)) { + mp_big_trans (mp, p, (quarterword) c); +} else if (mp_type (p) == mp_picture_type) { + mp_do_edges_trans (mp, p, (quarterword) c); + binary_return; +} else { + mp_bad_binary (mp, p, (quarterword) c); +} +break; @ Let |c| be one of the eight transform operators. The procedure call |set_up_trans(c)| first changes |cur_exp| to a transform that corresponds to @@ -20935,151 +24297,188 @@ moved to the global variables |txx|, |txy|, |tyx|, |tyy|, |tx|, |ty|; and |cur_exp| is changed to the known value zero. @<Declare binary action...@>= -static void mp_set_up_trans (MP mp,quarterword c) { - pointer p,q,r; /* list manipulation registers */ - if ( (c!=transformed_by)||(mp->cur_type!=mp_transform_type) ) { +static void mp_set_up_trans (MP mp, quarterword c) { + mp_node p, q, r; /* list manipulation registers */ + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + if ((c != transformed_by) || (mp->cur_exp.type != mp_transform_type)) { @<Put the current transform into |cur_exp|@>; } @<If the current transform is entirely known, stash it in global variables; otherwise |return|@>; } + @ @<Glob...@>= scaled txx; scaled txy; scaled tyx; scaled tyy; scaled tx; -scaled ty; /* current transform coefficients */ +scaled ty; /* current transform coefficients */ @ @<Put the current transform...@>= -{ - p=mp_stash_cur_exp(mp); - mp->cur_exp=mp_id_transform(mp); - mp->cur_type=mp_transform_type; - q=value(mp->cur_exp); +{ + p = mp_stash_cur_exp (mp); + set_cur_exp_node (mp_id_transform (mp)); + mp->cur_exp.type = mp_transform_type; + q = value_node (cur_exp_node ()); switch (c) { - @<For each of the eight cases, change the relevant fields of |cur_exp| + @<For each of the eight cases, change the relevant fields of |cur_exp| and |goto done|; but do nothing if capsule |p| doesn't have the appropriate type@>; - }; /* there are no other cases */ - mp_disp_err(mp, p,"Improper transformation argument"); -@.Improper transformation argument@> - help3("The expression shown above has the wrong type,", - "so I can\'t transform anything using it.", - "Proceed, and I'll omit the transformation."); - mp_put_get_error(mp); -DONE: - mp_recycle_value(mp, p); - mp_free_node(mp, p,value_node_size); + }; /* there are no other cases */ + mp_disp_err (mp, p, "Improper transformation argument"); +@.Improper transformation argument@>; + help3 ("The expression shown above has the wrong type,", + "so I can\'t transform anything using it.", + "Proceed, and I'll omit the transformation."); + mp_put_get_error (mp); +DONE: + mp_recycle_value (mp, p); + mp_free_node (mp, p, value_node_size); } + @ @<If the current transform is entirely known, ...@>= -q=value(mp->cur_exp); r=q+transform_node_size; -do { - r=r-2; - if ( mp_type(r)!=mp_known ) return; -} while (r!=q); -mp->txx=value(xx_part_loc(q)); -mp->txy=value(xy_part_loc(q)); -mp->tyx=value(yx_part_loc(q)); -mp->tyy=value(yy_part_loc(q)); -mp->tx=value(x_part_loc(q)); -mp->ty=value(y_part_loc(q)); -mp_flush_cur_exp(mp, 0) +q = value_node (cur_exp_node ()); +if (mp_type (tx_part_loc (q)) != mp_known) + return; +if (mp_type (ty_part_loc (q)) != mp_known) + return; +if (mp_type (xx_part_loc (q)) != mp_known) + return; +if (mp_type (xy_part_loc (q)) != mp_known) + return; +if (mp_type (yx_part_loc (q)) != mp_known) + return; +if (mp_type (yy_part_loc (q)) != mp_known) + return; +mp->txx = value (xx_part_loc (q)); +mp->txy = value (xy_part_loc (q)); +mp->tyx = value (yx_part_loc (q)); +mp->tyy = value (yy_part_loc (q)); +mp->tx = value (tx_part_loc (q)); +mp->ty = value (ty_part_loc (q)); +new_expr.data.val = 0; +mp_flush_cur_exp (mp, new_expr) + @ @<For each of the eight cases...@>= case rotated_by: - if ( mp_type(p)==mp_known ) - @<Install sines and cosines, then |goto done|@>; - break; +if (mp_type (p) == mp_known) + @<Install sines and cosines, then |goto done|@>; +break; case slanted_by: - if ( mp_type(p)>mp_pair_type ) { - mp_install(mp, xy_part_loc(q),p); goto DONE; - }; - break; +if (mp_type (p) > mp_pair_type) { + mp_install (mp, xy_part_loc (q), p); + goto DONE; +} +break; case scaled_by: - if ( mp_type(p)>mp_pair_type ) { - mp_install(mp, xx_part_loc(q),p); mp_install(mp, yy_part_loc(q),p); - goto DONE; - }; - break; +if (mp_type (p) > mp_pair_type) { + mp_install (mp, xx_part_loc (q), p); + mp_install (mp, yy_part_loc (q), p); + goto DONE; +} +break; case shifted_by: - if ( mp_type(p)==mp_pair_type ) { - r=value(p); mp_install(mp, x_part_loc(q),x_part_loc(r)); - mp_install(mp, y_part_loc(q),y_part_loc(r)); goto DONE; - }; - break; +if (mp_type (p) == mp_pair_type) { + r = value_node (p); + mp_install (mp, tx_part_loc (q), x_part_loc (r)); + mp_install (mp, ty_part_loc (q), y_part_loc (r)); + goto DONE; +} +break; case x_scaled: - if ( mp_type(p)>mp_pair_type ) { - mp_install(mp, xx_part_loc(q),p); goto DONE; - }; - break; +if (mp_type (p) > mp_pair_type) { + mp_install (mp, xx_part_loc (q), p); + goto DONE; +} +break; case y_scaled: - if ( mp_type(p)>mp_pair_type ) { - mp_install(mp, yy_part_loc(q),p); goto DONE; - }; - break; +if (mp_type (p) > mp_pair_type) { + mp_install (mp, yy_part_loc (q), p); + goto DONE; +} +break; case z_scaled: - if ( mp_type(p)==mp_pair_type ) - @<Install a complex multiplier, then |goto done|@>; - break; +if (mp_type (p) == mp_pair_type) + @<Install a complex multiplier, then |goto done|@>; +break; case transformed_by: - break; - +break; + @ @<Install sines and cosines, then |goto done|@>= -{ mp_n_sin_cos(mp, (value(p) % three_sixty_units)*16); - value(xx_part_loc(q))=mp_round_fraction(mp, mp->n_cos); - value(yx_part_loc(q))=mp_round_fraction(mp, mp->n_sin); - value(xy_part_loc(q))=-value(yx_part_loc(q)); - value(yy_part_loc(q))=value(xx_part_loc(q)); +{ + fraction n_sin; + fraction n_cos; + mp_n_sin_cos (mp, (value (p) % three_sixty_units) * 16, &n_cos, &n_sin); + set_value (xx_part_loc (q), mp_round_fraction (mp, n_cos)); + set_value (yx_part_loc (q), mp_round_fraction (mp, n_sin)); + set_value (xy_part_loc (q), -value (yx_part_loc (q))); + set_value (yy_part_loc (q), value (xx_part_loc (q))); goto DONE; } + @ @<Install a complex multiplier, then |goto done|@>= -{ - r=value(p); - mp_install(mp, xx_part_loc(q),x_part_loc(r)); - mp_install(mp, yy_part_loc(q),x_part_loc(r)); - mp_install(mp, yx_part_loc(q),y_part_loc(r)); - if ( mp_type(y_part_loc(r))==mp_known ) negate(value(y_part_loc(r))); - else mp_negate_dep_list(mp, dep_list(y_part_loc(r))); - mp_install(mp, xy_part_loc(q),y_part_loc(r)); +{ + r = value_node (p); + mp_install (mp, xx_part_loc (q), x_part_loc (r)); + mp_install (mp, yy_part_loc (q), x_part_loc (r)); + mp_install (mp, yx_part_loc (q), y_part_loc (r)); + if (mp_type (y_part_loc (r)) == mp_known) + negate (value (y_part_loc (r))); + else + mp_negate_dep_list (mp, (mp_value_node) dep_list ((mp_value_node) + y_part_loc (r))); + mp_install (mp, xy_part_loc (q), y_part_loc (r)); goto DONE; } + @ Procedure |set_up_known_trans| is like |set_up_trans|, but it insists that the transformation be entirely known. @<Declare binary action...@>= -static void mp_set_up_known_trans (MP mp,quarterword c) { - mp_set_up_trans(mp, c); - if ( mp->cur_type!=mp_known ) { - exp_err("Transform components aren't all known"); -@.Transform components...@> - help3("I'm unable to apply a partially specified transformation", - "except to a fully known pair or transform.", - "Proceed, and I'll omit the transformation."); - mp_put_get_flush_error(mp, 0); - mp->txx=unity; mp->txy=0; mp->tyx=0; mp->tyy=unity; - mp->tx=0; mp->ty=0; +static void mp_set_up_known_trans (MP mp, quarterword c) { + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + mp_set_up_trans (mp, c); + if (mp->cur_exp.type != mp_known) { + exp_err ("Transform components aren't all known"); +@.Transform components...@>; + help3 ("I'm unable to apply a partially specified transformation", + "except to a fully known pair or transform.", + "Proceed, and I'll omit the transformation."); + new_expr.data.val = 0; + mp_put_get_flush_error (mp, new_expr); + mp->txx = unity; + mp->txy = 0; + mp->tyx = 0; + mp->tyy = unity; + mp->tx = 0; + mp->ty = 0; } } + @ Here's a procedure that applies the transform |txx..ty| to a pair of coordinates in locations |p| and~|q|. -@<Declare binary action...@>= -static void mp_trans (MP mp,pointer p, pointer q) { - scaled v; /* the new |x| value */ - v=mp_take_scaled(mp, mp->mem[p].sc,mp->txx)+ - mp_take_scaled(mp, mp->mem[q].sc,mp->txy)+mp->tx; - mp->mem[q].sc=mp_take_scaled(mp, mp->mem[p].sc,mp->tyx)+ - mp_take_scaled(mp, mp->mem[q].sc,mp->tyy)+mp->ty; - mp->mem[p].sc=v; +@<Declare binary action...@>= +static void mp_trans (MP mp, scaled * p, scaled * q) { + scaled v; /* the new |x| value */ + v = mp_take_scaled (mp, *p, mp->txx) + + mp_take_scaled (mp, *q, mp->txy) + mp->tx; + *q = mp_take_scaled (mp, *p, mp->tyx) + + mp_take_scaled (mp, *q, mp->tyy) + mp->ty; + *p = v; } + @ The simplest transformation procedure applies a transform to all coordinates of a path. The |path_trans(c)(p)| macro applies a transformation defined by |cur_exp| and the transform operator |c| @@ -21087,45 +24486,45 @@ to the path~|p|. @d path_trans(A,B) { mp_set_up_known_trans(mp, (A)); mp_unstash_cur_exp(mp, (B)); - mp_do_path_trans(mp, mp->cur_exp); } + mp_do_path_trans(mp, cur_exp_knot()); } @<Declare binary action...@>= -static void mp_do_path_trans (MP mp,pointer p) { - pointer q; /* list traverser */ - q=p; - do { - if ( mp_left_type(q)!=mp_endpoint ) - mp_trans(mp, q+3,q+4); /* that's |mp_left_x| and |mp_left_y| */ - mp_trans(mp, q+1,q+2); /* that's |mp_x_coord| and |mp_y_coord| */ - if ( mp_right_type(q)!=mp_endpoint ) - mp_trans(mp, q+5,q+6); /* that's |mp_right_x| and |mp_right_y| */ -@^data structure assumptions@> - q=mp_link(q); - } while (q!=p); +static void mp_do_path_trans (MP mp, mp_knot p) { + mp_knot q; /* list traverser */ + q = p; + do { + if (mp_left_type (q) != mp_endpoint) + mp_trans (mp, &mp_left_x (q), &mp_left_y (q)); + mp_trans (mp, &mp_x_coord (q), &mp_y_coord (q)); + if (mp_right_type (q) != mp_endpoint) + mp_trans (mp, &mp_right_x (q), &mp_right_y (q)); + q = mp_next_knot (q); + } while (q != p); } + @ Transforming a pen is very similar, except that there are no |mp_left_type| and |mp_right_type| fields. @d pen_trans(A,B) { mp_set_up_known_trans(mp, (A)); mp_unstash_cur_exp(mp, (B)); - mp_do_pen_trans(mp, mp->cur_exp); } + mp_do_pen_trans(mp, cur_exp_knot()); } @<Declare binary action...@>= -static void mp_do_pen_trans (MP mp,pointer p) { - pointer q; /* list traverser */ - if ( pen_is_elliptical(p) ) { - mp_trans(mp, p+3,p+4); /* that's |mp_left_x| and |mp_left_y| */ - mp_trans(mp, p+5,p+6); /* that's |mp_right_x| and |mp_right_y| */ - }; - q=p; - do { - mp_trans(mp, q+1,q+2); /* that's |mp_x_coord| and |mp_y_coord| */ -@^data structure assumptions@> - q=mp_link(q); - } while (q!=p); +static void mp_do_pen_trans (MP mp, mp_knot p) { + mp_knot q; /* list traverser */ + if (pen_is_elliptical (p)) { + mp_trans (mp, &mp_left_x (p), &mp_left_y (p)); + mp_trans (mp, &mp_right_x (p), &mp_right_y (p)); + } + q = p; + do { + mp_trans (mp, &mp_x_coord (q), &mp_y_coord (q)); + q = mp_next_knot (q); + } while (q != p); } + @ The next transformation procedure applies to edge structures. It will do any transformation, but the results may be substandard if the picture contains text that uses downloaded bitmap fonts. The binary action procedure is @@ -21136,126 +24535,164 @@ that they have to return a (possibly new) structure because of the need to call |private_edges|. @<Declare binary action...@>= -static pointer mp_edges_trans (MP mp, pointer h) { - pointer q; /* the object being transformed */ - pointer r,s; /* for list manipulation */ - scaled sx,sy; /* saved transformation parameters */ +static mp_node mp_edges_trans (MP mp, mp_node h) { + mp_node q; /* the object being transformed */ + mp_node r, s; /* for list manipulation */ + scaled sx, sy; /* saved transformation parameters */ scaled sqdet; /* square root of determinant for |dash_scale| */ - integer sgndet; /* sign of the determinant */ - scaled v; /* a temporary value */ - h=mp_private_edges(mp, h); - sqdet=mp_sqrt_det(mp, mp->txx,mp->txy,mp->tyx,mp->tyy); - sgndet=mp_ab_vs_cd(mp, mp->txx,mp->tyy,mp->txy,mp->tyx); - if ( dash_list(h)!=null_dash ) { + integer sgndet; /* sign of the determinant */ + scaled v; /* a temporary value */ + h = mp_private_edges (mp, h); + sqdet = mp_sqrt_det (mp, mp->txx, mp->txy, mp->tyx, mp->tyy); + sgndet = mp_ab_vs_cd (mp, mp->txx, mp->tyy, mp->txy, mp->tyx); + if (dash_list (h) != mp->null_dash) { @<Try to transform the dash list of |h|@>; } @<Make the bounding box of |h| unknown if it can't be updated properly - without scanning the whole structure@>; - q=mp_link(dummy_loc(h)); - while ( q!=null ) { + without scanning the whole structure@>; + q = mp_link (dummy_loc (h)); + while (q != NULL) { @<Transform graphical object |q|@>; - q=mp_link(q); + q = mp_link (q); } return h; } -static void mp_do_edges_trans (MP mp,pointer p, quarterword c) { - mp_set_up_known_trans(mp, c); - value(p)=mp_edges_trans(mp, value(p)); - mp_unstash_cur_exp(mp, p); +static void mp_do_edges_trans (MP mp, mp_node p, quarterword c) { + mp_set_up_known_trans (mp, c); + set_value_node (p, mp_edges_trans (mp, value_node (p))); + mp_unstash_cur_exp (mp, p); } -static void mp_scale_edges (MP mp) { - mp->txx=mp->se_sf; mp->tyy=mp->se_sf; - mp->txy=0; mp->tyx=0; mp->tx=0; mp->ty=0; - mp->se_pic=mp_edges_trans(mp, mp->se_pic); +static void mp_scale_edges (MP mp) { + mp->txx = mp->se_sf; + mp->tyy = mp->se_sf; + mp->txy = 0; + mp->tyx = 0; + mp->tx = 0; + mp->ty = 0; + mp->se_pic = mp_edges_trans (mp, mp->se_pic); } + @ @<Try to transform the dash list of |h|@>= -if ( (mp->txy!=0)||(mp->tyx!=0)|| - (mp->ty!=0)||(abs(mp->txx)!=abs(mp->tyy))) { - mp_flush_dash_list(mp, h); -} else { - if ( mp->txx<0 ) { @<Reverse the dash list of |h|@>; } +if ((mp->txy != 0) || (mp->tyx != 0) || + (mp->ty != 0) || (abs (mp->txx) != abs (mp->tyy))) { + mp_flush_dash_list (mp, h); +} else { + if (mp->txx < 0) { + @<Reverse the dash list of |h|@>; + } @<Scale the dash list by |txx| and shift it by |tx|@>; - dash_y(h)=mp_take_scaled(mp, dash_y(h),abs(mp->tyy)); + dash_y (h) = mp_take_scaled (mp, dash_y (h), abs (mp->tyy)); } + @ @<Reverse the dash list of |h|@>= -{ - r=dash_list(h); - dash_list(h)=null_dash; - while ( r!=null_dash ) { - s=r; r=mp_link(r); - v=start_x(s); start_x(s)=stop_x(s); stop_x(s)=v; - mp_link(s)=dash_list(h); - dash_list(h)=s; +{ + r = dash_list (h); + dash_list (h) = mp->null_dash; + while (r != mp->null_dash) { + s = r; + r = mp_link (r); + v = start_x (s); + start_x (s) = stop_x (s); + stop_x (s) = v; + mp_link (s) = dash_list (h); + dash_list (h) = s; } } + @ @<Scale the dash list by |txx| and shift it by |tx|@>= -r=dash_list(h); -while ( r!=null_dash ) { - start_x(r)=mp_take_scaled(mp, start_x(r),mp->txx)+mp->tx; - stop_x(r)=mp_take_scaled(mp, stop_x(r),mp->txx)+mp->tx; - r=mp_link(r); +r = dash_list (h); +while (r != mp->null_dash) { + start_x (r) = mp_take_scaled (mp, start_x (r), mp->txx) + mp->tx; + stop_x (r) = mp_take_scaled (mp, stop_x (r), mp->txx) + mp->tx; + r = mp_link (r); } + @ @<Make the bounding box of |h| unknown if it can't be updated properly...@>= -if ( (mp->txx==0)&&(mp->tyy==0) ) { +if ((mp->txx == 0) && (mp->tyy == 0)) { @<Swap the $x$ and $y$ parameters in the bounding box of |h|@>; -} else if ( (mp->txy!=0)||(mp->tyx!=0) ) { - mp_init_bbox(mp, h); +} else if ((mp->txy != 0) || (mp->tyx != 0)) { + mp_init_bbox (mp, h); goto DONE1; } -if ( minx_val(h)<=maxx_val(h) ) { +if (minx_val (h) <= maxx_val (h)) { @<Scale the bounding box by |txx+txy| and |tyx+tyy|; then shift by |(tx,ty)|@>; } DONE1: - @ @<Swap the $x$ and $y$ parameters in the bounding box of |h|@>= -{ - v=minx_val(h); minx_val(h)=miny_val(h); miny_val(h)=v; - v=maxx_val(h); maxx_val(h)=maxy_val(h); maxy_val(h)=v; +{ + v = minx_val (h); + minx_val (h) = miny_val (h); + miny_val (h) = v; + v = maxx_val (h); + maxx_val (h) = maxy_val (h); + maxy_val (h) = v; } + @ The sum ``|txx+txy|'' is whichever of |txx| or |txy| is nonzero. The other sum is similar. @<Scale the bounding box by |txx+txy| and |tyx+tyy|; then shift...@>= -{ - minx_val(h)=mp_take_scaled(mp, minx_val(h),mp->txx+mp->txy)+mp->tx; - maxx_val(h)=mp_take_scaled(mp, maxx_val(h),mp->txx+mp->txy)+mp->tx; - miny_val(h)=mp_take_scaled(mp, miny_val(h),mp->tyx+mp->tyy)+mp->ty; - maxy_val(h)=mp_take_scaled(mp, maxy_val(h),mp->tyx+mp->tyy)+mp->ty; - if ( mp->txx+mp->txy<0 ) { - v=minx_val(h); minx_val(h)=maxx_val(h); maxx_val(h)=v; +{ + minx_val (h) = mp_take_scaled (mp, minx_val (h), mp->txx + mp->txy) + mp->tx; + maxx_val (h) = mp_take_scaled (mp, maxx_val (h), mp->txx + mp->txy) + mp->tx; + miny_val (h) = mp_take_scaled (mp, miny_val (h), mp->tyx + mp->tyy) + mp->ty; + maxy_val (h) = mp_take_scaled (mp, maxy_val (h), mp->tyx + mp->tyy) + mp->ty; + if (mp->txx + mp->txy < 0) { + v = minx_val (h); + minx_val (h) = maxx_val (h); + maxx_val (h) = v; } - if ( mp->tyx+mp->tyy<0 ) { - v=miny_val(h); miny_val(h)=maxy_val(h); maxy_val(h)=v; + if (mp->tyx + mp->tyy < 0) { + v = miny_val (h); + miny_val (h) = maxy_val (h); + maxy_val (h) = v; } } + @ Now we ready for the main task of transforming the graphical objects in edge structure~|h|. @<Transform graphical object |q|@>= -switch (mp_type(q)) { -case mp_fill_code: case mp_stroked_code: - mp_do_path_trans(mp, mp_path_p(q)); - @<Transform |mp_pen_p(q)|, making sure polygonal pens stay counter-clockwise@>; +switch (mp_type (q)) { +case mp_fill_node_type: + { + mp_fill_node qq = (mp_fill_node) q; + mp_do_path_trans (mp, mp_path_p (qq)); + @<Transform |mp_pen_p(qq)|, making sure polygonal pens stay counter-clockwise@>; + } + break; +case mp_stroked_node_type: + { + mp_stroked_node qq = (mp_stroked_node) q; + mp_do_path_trans (mp, mp_path_p (qq)); + @<Transform |mp_pen_p(qq)|, making sure polygonal pens stay counter-clockwise@>; + } + break; +case mp_start_clip_node_type: + mp_do_path_trans (mp, mp_path_p ((mp_start_clip_node) q)); break; -case mp_start_clip_code: case mp_start_bounds_code: - mp_do_path_trans(mp, mp_path_p(q)); +case mp_start_bounds_node_type: + mp_do_path_trans (mp, mp_path_p ((mp_start_bounds_node) q)); break; -case mp_text_code: - r=text_tx_loc(q); - @<Transform the compact transformation starting at |r|@>; +case mp_text_node_type: + @<Transform the compact transformation@>; break; -case mp_stop_clip_code: case mp_stop_bounds_code: +case mp_stop_clip_node_type: +case mp_stop_bounds_node_type: break; -} /* there are no other cases */ +default: /* there are no other valid cases, but please the compiler */ + break; +} + @ Note that the shift parameters |(tx,ty)| apply only to the path being stroked. The |dash_scale| has to be adjusted to scale the dash lengths in |mp_dash_p(q)| @@ -21266,411 +24703,532 @@ root of the determinant, |sqdet| is the appropriate factor. We pass the mptrap test only if |dash_scale| is not adjusted, nowadays (backend is changed?) -@<Transform |mp_pen_p(q)|, making sure...@>= -if ( mp_pen_p(q)!=null ) { - sx=mp->tx; sy=mp->ty; - mp->tx=0; mp->ty=0; - mp_do_pen_trans(mp, mp_pen_p(q)); - if ( sqdet !=0 && ((mp_type(q)==mp_stroked_code)&&(mp_dash_p(q)!=null)) ) - dash_scale(q)=mp_take_scaled(mp, dash_scale(q),sqdet); - if ( ! pen_is_elliptical(mp_pen_p(q)) ) - if ( sgndet<0 ) - mp_pen_p(q)=mp_make_pen(mp, mp_copy_path(mp, mp_pen_p(q)),true); - /* this unreverses the pen */ - mp->tx=sx; mp->ty=sy; -} - -@ This uses the fact that transformations are stored in the order -|(tx,ty,txx,txy,tyx,tyy)|. -@^data structure assumptions@> - -@<Transform the compact transformation starting at |r|@>= -mp_trans(mp, r,r+1); -sx=mp->tx; sy=mp->ty; -mp->tx=0; mp->ty=0; -mp_trans(mp, r+2,r+4); -mp_trans(mp, r+3,r+5); -mp->tx=sx; mp->ty=sy +@<Transform |mp_pen_p(qq)|, making sure...@>= +if (mp_pen_p (qq) != NULL) { + sx = mp->tx; + sy = mp->ty; + mp->tx = 0; + mp->ty = 0; + mp_do_pen_trans (mp, mp_pen_p (qq)); + if (sqdet != 0 + && ((mp_type (q) == mp_stroked_node_type) && (mp_dash_p (q) != NULL))) + dash_scale (q) = mp_take_scaled (mp, dash_scale (q), sqdet); + if (!pen_is_elliptical (mp_pen_p (qq))) + if (sgndet < 0) + mp_pen_p (qq) = mp_make_pen (mp, mp_copy_path (mp, mp_pen_p (qq)), true); + /* this unreverses the pen */ + mp->tx = sx; + mp->ty = sy; +} + +@ @<Transform the compact transformation@>= +mp_trans (mp, &tx_val (q), &ty_val (q)); +sx = mp->tx; +sy = mp->ty; +mp->tx = 0; +mp->ty = 0; +mp_trans (mp, &txx_val (q), &tyx_val (q)); +mp_trans (mp, &txy_val (q), &tyy_val (q)); +mp->tx = sx; +mp->ty = sy @ The hard cases of transformation occur when big nodes are involved, and when some of their components are unknown. @<Declare binary action...@>= -@<Declare subroutines needed by |big_trans|@> -static void mp_big_trans (MP mp,pointer p, quarterword c) { - pointer q,r,pp,qq; /* list manipulation registers */ - quarterword s; /* size of a big node */ - s=mp->big_node_size[mp_type(p)]; q=value(p); r=q+s; - do { - r=r-2; - if ( mp_type(r)!=mp_known ) { +@<Declare subroutines needed by |big_trans|@>; +static void mp_big_trans (MP mp, mp_node p, quarterword c) { + mp_node q, r, pp, qq; /* list manipulation registers */ + q = value_node (p); + if (mp_type (q) == mp_pair_node_type) { + if (mp_type (x_part_loc (q)) != mp_known || + mp_type (y_part_loc (q)) != mp_known) { + @<Transform an unknown big node and |return|@>; + } + } else { /* |mp_transform_type| */ + if (mp_type (tx_part_loc (q)) != mp_known || + mp_type (ty_part_loc (q)) != mp_known || + mp_type (xx_part_loc (q)) != mp_known || + mp_type (xy_part_loc (q)) != mp_known || + mp_type (yx_part_loc (q)) != mp_known || + mp_type (yy_part_loc (q)) != mp_known) { @<Transform an unknown big node and |return|@>; } - } while (r!=q); + } @<Transform a known big node@>; -} /* node |p| will now be recycled by |do_binary| */ +} /* node |p| will now be recycled by |do_binary| */ + @ @<Transform an unknown big node and |return|@>= -{ - mp_set_up_known_trans(mp, c); mp_make_exp_copy(mp, p); - r=value(mp->cur_exp); - if ( mp->cur_type==mp_transform_type ) { - mp_bilin1(mp, yy_part_loc(r),mp->tyy,xy_part_loc(q),mp->tyx,0); - mp_bilin1(mp, yx_part_loc(r),mp->tyy,xx_part_loc(q),mp->tyx,0); - mp_bilin1(mp, xy_part_loc(r),mp->txx,yy_part_loc(q),mp->txy,0); - mp_bilin1(mp, xx_part_loc(r),mp->txx,yx_part_loc(q),mp->txy,0); - } - mp_bilin1(mp, y_part_loc(r),mp->tyy,x_part_loc(q),mp->tyx,mp->ty); - mp_bilin1(mp, x_part_loc(r),mp->txx,y_part_loc(q),mp->txy,mp->tx); +{ + mp_set_up_known_trans (mp, c); + mp_make_exp_copy (mp, p); + r = value_node (cur_exp_node ()); + if (mp->cur_exp.type == mp_transform_type) { + mp_bilin1 (mp, yy_part_loc (r), mp->tyy, xy_part_loc (q), mp->tyx, 0); + mp_bilin1 (mp, yx_part_loc (r), mp->tyy, xx_part_loc (q), mp->tyx, 0); + mp_bilin1 (mp, xy_part_loc (r), mp->txx, yy_part_loc (q), mp->txy, 0); + mp_bilin1 (mp, xx_part_loc (r), mp->txx, yx_part_loc (q), mp->txy, 0); + } + mp_bilin1 (mp, y_part_loc (r), mp->tyy, x_part_loc (q), mp->tyx, mp->ty); + mp_bilin1 (mp, x_part_loc (r), mp->txx, y_part_loc (q), mp->txy, mp->tx); return; } -@ Let |p| point to a two-word value field inside a big node of |cur_exp|, + +@ Let |p| point to a value field inside a big node of |cur_exp|, and let |q| point to a another value field. The |bilin1| procedure replaces |p| by $p\cdot t+q\cdot u+\delta$. @<Declare subroutines needed by |big_trans|@>= -static void mp_bilin1 (MP mp, pointer p, scaled t, pointer q, - scaled u, scaled delta) { - pointer r; /* list traverser */ - if ( t!=unity ) mp_dep_mult(mp, p,t,true); - if ( u!=0 ) { - if ( mp_type(q)==mp_known ) { - delta+=mp_take_scaled(mp, value(q),u); - } else { - @<Ensure that |type(p)=mp_proto_dependent|@>; - dep_list(p)=mp_p_plus_fq(mp, dep_list(p),u,dep_list(q), - mp_proto_dependent,mp_type(q)); +static void mp_bilin1 (MP mp, mp_node p, scaled t, mp_node q, + scaled u, scaled delta) { + if (t != unity) + mp_dep_mult (mp, (mp_value_node) p, t, true); + if (u != 0) { + if (mp_type (q) == mp_known) { + delta += mp_take_scaled (mp, value (q), u); + } else { + /* Ensure that |type(p)=mp_proto_dependent| */ + if (mp_type (p) != mp_proto_dependent) { + if (mp_type (p) == mp_known) { + mp_new_dep (mp, p, mp_type (p), mp_const_dependency (mp, value (p))); + } else { + dep_list ((mp_value_node) p) = + (mp_node) mp_p_times_v (mp, + (mp_value_node) dep_list ((mp_value_node) + p), unity, + mp_dependent, mp_proto_dependent, true); + } + mp_type (p) = mp_proto_dependent; + } + dep_list ((mp_value_node) p) = + (mp_node) mp_p_plus_fq (mp, + (mp_value_node) dep_list ((mp_value_node) p), u, + (mp_value_node) dep_list ((mp_value_node) q), + mp_proto_dependent, mp_type (q)); } } - if ( mp_type(p)==mp_known ) { - value(p)+=delta; + if (mp_type (p) == mp_known) { + set_value (p, value (p) + delta); } else { - r=dep_list(p); - while ( mp_info(r)!=null ) r=mp_link(r); - delta+=value(r); - if ( r!=dep_list(p) ) value(r)=delta; - else { mp_recycle_value(mp, p); mp_type(p)=mp_known; value(p)=delta; }; + mp_value_node r; /* list traverser */ + r = (mp_value_node) dep_list ((mp_value_node) p); + while (dep_info (r) != NULL) + r = (mp_value_node) mp_link (r); + delta += value (r); + if (r != (mp_value_node) dep_list ((mp_value_node) p)) + set_value (r, delta); + else { + mp_recycle_value (mp, p); + mp_type (p) = mp_known; + set_value (p, delta); + } } - if ( mp->fix_needed ) mp_fix_dependencies(mp); + if (mp->fix_needed) + mp_fix_dependencies (mp); } -@ @<Ensure that |type(p)=mp_proto_dependent|@>= -if ( mp_type(p)!=mp_proto_dependent ) { - if ( mp_type(p)==mp_known ) - mp_new_dep(mp, p,mp_const_dependency(mp, value(p))); - else - dep_list(p)=mp_p_times_v(mp, dep_list(p),unity,mp_dependent, - mp_proto_dependent,true); - mp_type(p)=mp_proto_dependent; -} @ @<Transform a known big node@>= -mp_set_up_trans(mp, c); -if ( mp->cur_type==mp_known ) { +mp_set_up_trans (mp, c); +if (mp->cur_exp.type == mp_known) { @<Transform known by known@>; -} else { - pp=mp_stash_cur_exp(mp); qq=value(pp); - mp_make_exp_copy(mp, p); r=value(mp->cur_exp); - if ( mp->cur_type==mp_transform_type ) { - mp_bilin2(mp, yy_part_loc(r),yy_part_loc(qq), - value(xy_part_loc(q)),yx_part_loc(qq),null); - mp_bilin2(mp, yx_part_loc(r),yy_part_loc(qq), - value(xx_part_loc(q)),yx_part_loc(qq),null); - mp_bilin2(mp, xy_part_loc(r),xx_part_loc(qq), - value(yy_part_loc(q)),xy_part_loc(qq),null); - mp_bilin2(mp, xx_part_loc(r),xx_part_loc(qq), - value(yx_part_loc(q)),xy_part_loc(qq),null); - }; - mp_bilin2(mp, y_part_loc(r),yy_part_loc(qq), - value(x_part_loc(q)),yx_part_loc(qq),y_part_loc(qq)); - mp_bilin2(mp, x_part_loc(r),xx_part_loc(qq), - value(y_part_loc(q)),xy_part_loc(qq),x_part_loc(qq)); - mp_recycle_value(mp, pp); mp_free_node(mp, pp,value_node_size); +} else { + pp = mp_stash_cur_exp (mp); + qq = value_node (pp); + mp_make_exp_copy (mp, p); + r = value_node (cur_exp_node ()); + if (mp->cur_exp.type == mp_transform_type) { + mp_bilin2 (mp, yy_part_loc (r), yy_part_loc (qq), value (xy_part_loc (q)), + yx_part_loc (qq), NULL); + mp_bilin2 (mp, yx_part_loc (r), yy_part_loc (qq), value (xx_part_loc (q)), + yx_part_loc (qq), NULL); + mp_bilin2 (mp, xy_part_loc (r), xx_part_loc (qq), value (yy_part_loc (q)), + xy_part_loc (qq), NULL); + mp_bilin2 (mp, xx_part_loc (r), xx_part_loc (qq), value (yx_part_loc (q)), + xy_part_loc (qq), NULL); + } + mp_bilin2 (mp, y_part_loc (r), yy_part_loc (qq), value (x_part_loc (q)), + yx_part_loc (qq), y_part_loc (qq)); + mp_bilin2 (mp, x_part_loc (r), xx_part_loc (qq), value (y_part_loc (q)), + xy_part_loc (qq), x_part_loc (qq)); + mp_recycle_value (mp, pp); + mp_free_node (mp, pp, value_node_size); } + @ Let |p| be a |mp_proto_dependent| value whose dependency list ends at |dep_final|. The following procedure adds |v| times another numeric quantity to~|p|. @<Declare subroutines needed by |big_trans|@>= -static void mp_add_mult_dep (MP mp,pointer p, scaled v, pointer r) { - if ( mp_type(r)==mp_known ) { - value(mp->dep_final)+=mp_take_scaled(mp, value(r),v); - } else { - dep_list(p)=mp_p_plus_fq(mp, dep_list(p),v,dep_list(r), - mp_proto_dependent,mp_type(r)); - if ( mp->fix_needed ) mp_fix_dependencies(mp); +static void mp_add_mult_dep (MP mp, mp_value_node p, scaled v, mp_node r) { + if (mp_type (r) == mp_known) { + set_dep_value (mp->dep_final, + dep_value (mp->dep_final) + mp_take_scaled (mp, value (r), + v)); + } else { + dep_list (p) = + (mp_node) mp_p_plus_fq (mp, (mp_value_node) dep_list (p), v, + (mp_value_node) dep_list ((mp_value_node) r), + mp_proto_dependent, mp_type (r)); + if (mp->fix_needed) + mp_fix_dependencies (mp); } } + @ The |bilin2| procedure is something like |bilin1|, but with known and unknown quantities reversed. Parameter |p| points to a value field within the big node for |cur_exp|; and |type(p)=mp_known|. Parameters |t| and~|u| point to value fields elsewhere; so does parameter~|q|, -unless it is |null| (which stands for zero). Location~|p| will be +unless it is |NULL| (which stands for zero). Location~|p| will be replaced by $p\cdot t+v\cdot u+q$. @<Declare subroutines needed by |big_trans|@>= -static void mp_bilin2 (MP mp,pointer p, pointer t, scaled v, - pointer u, pointer q) { - scaled vv; /* temporary storage for |value(p)| */ - vv=value(p); mp_type(p)=mp_proto_dependent; - mp_new_dep(mp, p,mp_const_dependency(mp, 0)); /* this sets |dep_final| */ - if ( vv!=0 ) - mp_add_mult_dep(mp, p,vv,t); /* |dep_final| doesn't change */ - if ( v!=0 ) mp_add_mult_dep(mp, p,v,u); - if ( q!=null ) mp_add_mult_dep(mp, p,unity,q); - if ( dep_list(p)==mp->dep_final ) { - vv=value(mp->dep_final); mp_recycle_value(mp, p); - mp_type(p)=mp_known; value(p)=vv; +static void mp_bilin2 (MP mp, mp_node p, mp_node t, scaled v, + mp_node u, mp_node q) { + scaled vv; /* temporary storage for |value(p)| */ + vv = value (p); + mp_new_dep (mp, p, mp_proto_dependent, mp_const_dependency (mp, 0)); /* this sets |dep_final| */ + if (vv != 0) + mp_add_mult_dep (mp, (mp_value_node) p, vv, t); /* |dep_final| doesn't change */ + if (v != 0) + mp_add_mult_dep (mp, (mp_value_node) p, v, u); + if (q != NULL) + mp_add_mult_dep (mp, (mp_value_node) p, unity, q); + if (dep_list ((mp_value_node) p) == (mp_node) mp->dep_final) { + vv = dep_value (mp->dep_final); + mp_recycle_value (mp, p); + mp_type (p) = mp_known; + set_value (p, vv); } } + @ @<Transform known by known@>= -{ - mp_make_exp_copy(mp, p); r=value(mp->cur_exp); - if ( mp->cur_type==mp_transform_type ) { - mp_bilin3(mp, yy_part_loc(r),mp->tyy,value(xy_part_loc(q)),mp->tyx,0); - mp_bilin3(mp, yx_part_loc(r),mp->tyy,value(xx_part_loc(q)),mp->tyx,0); - mp_bilin3(mp, xy_part_loc(r),mp->txx,value(yy_part_loc(q)),mp->txy,0); - mp_bilin3(mp, xx_part_loc(r),mp->txx,value(yx_part_loc(q)),mp->txy,0); +{ + mp_make_exp_copy (mp, p); + r = value_node (cur_exp_node ()); + if (mp->cur_exp.type == mp_transform_type) { + mp_bilin3 (mp, yy_part_loc (r), mp->tyy, value (xy_part_loc (q)), mp->tyx, + 0); + mp_bilin3 (mp, yx_part_loc (r), mp->tyy, value (xx_part_loc (q)), mp->tyx, + 0); + mp_bilin3 (mp, xy_part_loc (r), mp->txx, value (yy_part_loc (q)), mp->txy, + 0); + mp_bilin3 (mp, xx_part_loc (r), mp->txx, value (yx_part_loc (q)), mp->txy, + 0); } - mp_bilin3(mp, y_part_loc(r),mp->tyy,value(x_part_loc(q)),mp->tyx,mp->ty); - mp_bilin3(mp, x_part_loc(r),mp->txx,value(y_part_loc(q)),mp->txy,mp->tx); + mp_bilin3 (mp, y_part_loc (r), mp->tyy, value (x_part_loc (q)), mp->tyx, + mp->ty); + mp_bilin3 (mp, x_part_loc (r), mp->txx, value (y_part_loc (q)), mp->txy, + mp->tx); } + @ Finally, in |bilin3| everything is |known|. @<Declare subroutines needed by |big_trans|@>= -static void mp_bilin3 (MP mp,pointer p, scaled t, - scaled v, scaled u, scaled delta) { - if ( t!=unity ) - delta+=mp_take_scaled(mp, value(p),t); - else - delta+=value(p); - if ( u!=0 ) value(p)=delta+mp_take_scaled(mp, v,u); - else value(p)=delta; +static void mp_bilin3 (MP mp, mp_node p, scaled t, + scaled v, scaled u, scaled delta) { + if (t != unity) + delta += mp_take_scaled (mp, value (p), t); + else + delta += value (p); + if (u != 0) + set_value (p, delta + mp_take_scaled (mp, v, u)); + else + set_value (p, delta); } + @ @<Additional cases of binary operators@>= -case concatenate: - if ( (mp->cur_type==mp_string_type)&&(mp_type(p)==mp_string_type) ) mp_cat(mp, p); - else mp_bad_binary(mp, p,concatenate); - break; -case substring_of: - if ( mp_nice_pair(mp, p,mp_type(p))&&(mp->cur_type==mp_string_type) ) - mp_chop_string(mp, value(p)); - else mp_bad_binary(mp, p,substring_of); - break; -case subpath_of: - if ( mp->cur_type==mp_pair_type ) mp_pair_to_path(mp); - if ( mp_nice_pair(mp, p,mp_type(p))&&(mp->cur_type==mp_path_type) ) - mp_chop_path(mp, value(p)); - else mp_bad_binary(mp, p,subpath_of); - break; +case concatenate: +if ((mp->cur_exp.type == mp_string_type) && (mp_type (p) == mp_string_type)) + mp_cat (mp, p); +else + mp_bad_binary (mp, p, concatenate); +break; +case substring_of: +if (mp_nice_pair (mp, p, mp_type (p)) && (mp->cur_exp.type == mp_string_type)) + mp_chop_string (mp, value_node (p)); +else + mp_bad_binary (mp, p, substring_of); +break; +case subpath_of: +if (mp->cur_exp.type == mp_pair_type) + mp_pair_to_path (mp); +if (mp_nice_pair (mp, p, mp_type (p)) && (mp->cur_exp.type == mp_path_type)) + mp_chop_path (mp, value_node (p)); +else + mp_bad_binary (mp, p, subpath_of); +break; @ @<Declare binary action...@>= -static void mp_cat (MP mp,pointer p) { - str_number a,b; /* the strings being concatenated */ - integer k; /* run length */ - integer needed; - a=value(p); b=mp->cur_exp; k=length(a); - needed=mp->pool_ptr+k+length(b); - /* this will free some memory, hopefully */ - if (mp->pool_ptr>(11*mp->old_pool_size)/10) { - mp->old_pool_size = mp->pool_ptr; - mp_do_compaction(mp, mp->pool_size); - } - if ( needed > mp->max_pool_ptr ) { - if ( needed > mp->pool_size ) { - mp_reallocate_pool(mp,needed); - } - mp->max_pool_ptr=needed; - } - (void)memcpy(mp->str_pool+mp->pool_ptr, mp->str_pool+mp->str_start[a],(size_t)k); - mp->pool_ptr+=k; - k=length(b); - (void)memcpy(mp->str_pool+mp->pool_ptr, mp->str_pool+mp->str_start[b],(size_t)k); - mp->pool_ptr+=k; - mp->cur_exp=mp_make_string(mp); delete_str_ref(b); +static void mp_cat (MP mp, mp_node p) { + str_number a, b; /* the strings being concatenated */ + size_t needed; + size_t saved_cur_length = mp->cur_length; + unsigned char *saved_cur_string = mp->cur_string; + size_t saved_cur_string_size = mp->cur_string_size; + mp->cur_length = 0; + mp->cur_string = NULL; + mp->cur_string_size = 0; + a = str_value (p); + b = cur_exp_str (); + needed = length (a) + length (b); + str_room (needed+1); + (void) memcpy (mp->cur_string, a->str, a->len); + (void) memcpy (mp->cur_string + a->len, b->str, b->len); + mp->cur_length = needed; + mp->cur_string[needed] = '\0'; + set_cur_exp_str (mp_make_string (mp)); + delete_str_ref (b); + mp->cur_length = saved_cur_length; + mp->cur_string = saved_cur_string; + mp->cur_string_size = saved_cur_string_size; } + @ @<Declare binary action...@>= -static void mp_chop_string (MP mp,pointer p) { +static void mp_chop_string (MP mp, mp_node p) { integer a, b; /* start and stop points */ - integer l; /* length of the original string */ - integer k; /* runs from |a| to |b| */ + integer l; /* length of the original string */ + integer k; /* runs from |a| to |b| */ str_number s; /* the original string */ - boolean reversed; /* was |a>b|? */ - a=mp_round_unscaled(mp, value(x_part_loc(p))); - b=mp_round_unscaled(mp, value(y_part_loc(p))); - if ( a<=b ) reversed=false; - else { reversed=true; k=a; a=b; b=k; }; - s=mp->cur_exp; l=length(s); - if ( a<0 ) { - a=0; - if ( b<0 ) b=0; - } - if ( b>l ) { - b=l; - if ( a>l ) a=l; - } - str_room(b-a); - if ( reversed ) { - for (k=mp->str_start[s]+b-1;k>=mp->str_start[s]+a;k--) { - append_char(mp->str_pool[k]); + boolean reversed; /* was |a>b|? */ + a = mp_round_unscaled (mp, value (x_part_loc (p))); + b = mp_round_unscaled (mp, value (y_part_loc (p))); + if (a <= b) + reversed = false; + else { + reversed = true; + k = a; + a = b; + b = k; + }; + s = cur_exp_str (); + l = (integer) length (s); + if (a < 0) { + a = 0; + if (b < 0) + b = 0; + } + if (b > l) { + b = l; + if (a > l) + a = l; + } + str_room ((size_t) (b - a)); + if (reversed) { + for (k = b - 1; k >= a; k--) { + append_char (*(s->str + k)); } - } else { - for (k=mp->str_start[s]+a;k<mp->str_start[s]+b;k++) { - append_char(mp->str_pool[k]); + } else { + for (k = a; k < b; k++) { + append_char (*(s->str + k)); } } - mp->cur_exp=mp_make_string(mp); delete_str_ref(s); + set_cur_exp_str (mp_make_string (mp)); + delete_str_ref (s); } + @ @<Declare binary action...@>= -static void mp_chop_path (MP mp,pointer p) { - pointer q; /* a knot in the original path */ - pointer pp,qq,rr,ss; /* link variables for copies of path nodes */ - scaled a,b,k,l; /* indices for chopping */ - boolean reversed; /* was |a>b|? */ - l=mp_path_length(mp); a=value(x_part_loc(p)); b=value(y_part_loc(p)); - if ( a<=b ) reversed=false; - else { reversed=true; k=a; a=b; b=k; }; +static void mp_chop_path (MP mp, mp_node p) { + mp_knot q; /* a knot in the original path */ + mp_knot pp, qq, rr, ss; /* link variables for copies of path nodes */ + scaled a, b, k, l; /* indices for chopping */ + boolean reversed; /* was |a>b|? */ + l = mp_path_length (mp); + a = value (x_part_loc (p)); + b = value (y_part_loc (p)); + if (a <= b) { + reversed = false; + } else { + reversed = true; + k = a; + a = b; + b = k; + } @<Dispense with the cases |a<0| and/or |b>l|@>; - q=mp->cur_exp; - while ( a>=unity ) { - q=mp_link(q); a=a-unity; b=b-unity; - } - if ( b==a ) { - @<Construct a path from |pp| to |qq| of length zero@>; - } else { - @<Construct a path from |pp| to |qq| of length $\lceil b\rceil$@>; - } - mp_left_type(pp)=mp_endpoint; mp_right_type(qq)=mp_endpoint; mp_link(qq)=pp; - mp_toss_knot_list(mp, mp->cur_exp); - if ( reversed ) { - mp->cur_exp=mp_link(mp_htap_ypoc(mp, pp)); mp_toss_knot_list(mp, pp); + q = cur_exp_knot (); + while (a >= unity) { + q = mp_next_knot (q); + a = a - unity; + b = b - unity; + } + if (b == a) { + @<Construct a path from |pp| to |qq| of length zero@>; + } else { + @<Construct a path from |pp| to |qq| of length $\lceil b\rceil$@>; + } + mp_left_type (pp) = mp_endpoint; + mp_right_type (qq) = mp_endpoint; + mp_next_knot (qq) = pp; + mp_toss_knot_list (mp, cur_exp_knot ()); + if (reversed) { + set_cur_exp_knot (mp_next_knot (mp_htap_ypoc (mp, pp))); + mp_toss_knot_list (mp, pp); } else { - mp->cur_exp=pp; + set_cur_exp_knot (pp); } } + @ @<Dispense with the cases |a<0| and/or |b>l|@>= -if ( a<0 ) { - if ( mp_left_type(mp->cur_exp)==mp_endpoint ) { - a=0; if ( b<0 ) b=0; - } else { - do { a=a+l; b=b+l; } while (a<0); /* a cycle always has length |l>0| */ +if (a < 0) { + if (mp_left_type (cur_exp_knot ()) == mp_endpoint) { + a = 0; + if (b < 0) + b = 0; + } else { + do { + a = a + l; + b = b + l; + } while (a < 0); /* a cycle always has length |l>0| */ } } -if ( b>l ) { - if ( mp_left_type(mp->cur_exp)==mp_endpoint ) { - b=l; if ( a>l ) a=l; +if (b > l) { + if (mp_left_type (cur_exp_knot ()) == mp_endpoint) { + b = l; + if (a > l) + a = l; } else { - while ( a>=l ) { - a=a-l; b=b-l; + while (a >= l) { + a = a - l; + b = b - l; } } } @ @<Construct a path from |pp| to |qq| of length $\lceil b\rceil$@>= -{ - pp=mp_copy_knot(mp, q); qq=pp; - do { - q=mp_link(q); rr=qq; qq=mp_copy_knot(mp, q); mp_link(rr)=qq; b=b-unity; - } while (b>0); - if ( a>0 ) { - ss=pp; pp=mp_link(pp); - mp_split_cubic(mp, ss,a*010000); pp=mp_link(ss); - mp_free_node(mp, ss,knot_node_size); - if ( rr==ss ) { - b=mp_make_scaled(mp, b,unity-a); rr=pp; +{ + pp = mp_copy_knot (mp, q); + qq = pp; + do { + q = mp_next_knot (q); + rr = qq; + qq = mp_copy_knot (mp, q); + mp_next_knot (rr) = qq; + b = b - unity; + } while (b > 0); + if (a > 0) { + ss = pp; + pp = mp_next_knot (pp); + mp_split_cubic (mp, ss, a * 010000); + pp = mp_next_knot (ss); + mp_xfree (ss); + if (rr == ss) { + b = mp_make_scaled (mp, b, unity - a); + rr = pp; } } - if ( b<0 ) { - mp_split_cubic(mp, rr,(b+unity)*010000); - mp_free_node(mp, qq,knot_node_size); - qq=mp_link(rr); + if (b < 0) { + mp_split_cubic (mp, rr, (b + unity) * 010000); + mp_xfree (qq); + qq = mp_next_knot (rr); } } + @ @<Construct a path from |pp| to |qq| of length zero@>= -{ - if ( a>0 ) { mp_split_cubic(mp, q,a*010000); q=mp_link(q); }; - pp=mp_copy_knot(mp, q); qq=pp; +{ + if (a > 0) { + mp_split_cubic (mp, q, a * 010000); + q = mp_next_knot (q); + } + pp = mp_copy_knot (mp, q); + qq = pp; } + @ @<Additional cases of binary operators@>= -case point_of: case precontrol_of: case postcontrol_of: - if ( mp->cur_type==mp_pair_type ) - mp_pair_to_path(mp); - if ( (mp->cur_type==mp_path_type)&&(mp_type(p)==mp_known) ) - mp_find_point(mp, value(p), (quarterword)c); - else - mp_bad_binary(mp, p, (quarterword)c); - break; -case pen_offset_of: - if ( (mp->cur_type==mp_pen_type)&& mp_nice_pair(mp, p,mp_type(p)) ) - mp_set_up_offset(mp, value(p)); - else - mp_bad_binary(mp, p,pen_offset_of); - break; -case direction_time_of: - if ( mp->cur_type==mp_pair_type ) mp_pair_to_path(mp); - if ( (mp->cur_type==mp_path_type)&& mp_nice_pair(mp, p,mp_type(p)) ) - mp_set_up_direction_time(mp, value(p)); - else - mp_bad_binary(mp, p,direction_time_of); - break; +case point_of: +case precontrol_of: +case postcontrol_of: +if (mp->cur_exp.type == mp_pair_type) + mp_pair_to_path (mp); +if ((mp->cur_exp.type == mp_path_type) && (mp_type (p) == mp_known)) + mp_find_point (mp, value (p), (quarterword) c); +else + mp_bad_binary (mp, p, (quarterword) c); +break; +case pen_offset_of: +if ((mp->cur_exp.type == mp_pen_type) && mp_nice_pair (mp, p, mp_type (p))) + mp_set_up_offset (mp, value_node (p)); +else + mp_bad_binary (mp, p, pen_offset_of); +break; +case direction_time_of: +if (mp->cur_exp.type == mp_pair_type) + mp_pair_to_path (mp); +if ((mp->cur_exp.type == mp_path_type) && mp_nice_pair (mp, p, mp_type (p))) + mp_set_up_direction_time (mp, value_node (p)); +else + mp_bad_binary (mp, p, direction_time_of); +break; case envelope_of: - if ( (mp_type(p) != mp_pen_type) || (mp->cur_type != mp_path_type) ) - mp_bad_binary(mp, p,envelope_of); - else - mp_set_up_envelope(mp, p); - break; +if ((mp_type (p) != mp_pen_type) || (mp->cur_exp.type != mp_path_type)) + mp_bad_binary (mp, p, envelope_of); +else + mp_set_up_envelope (mp, p); +break; case glyph_infont: - if ( (mp_type(p) != mp_string_type && - mp_type(p) != mp_known) || (mp->cur_type != mp_string_type) ) - mp_bad_binary(mp, p,glyph_infont); - else - mp_set_up_glyph_infont(mp, p); - break; - break; +if ((mp_type (p) != mp_string_type && + mp_type (p) != mp_known) || (mp->cur_exp.type != mp_string_type)) + mp_bad_binary (mp, p, glyph_infont); +else + mp_set_up_glyph_infont (mp, p); +break; +break; @ @<Declare binary action...@>= -static void mp_set_up_offset (MP mp,pointer p) { - mp_find_offset(mp, value(x_part_loc(p)),value(y_part_loc(p)),mp->cur_exp); - mp_pair_value(mp, mp->cur_x,mp->cur_y); -} -static void mp_set_up_direction_time (MP mp,pointer p) { - mp_flush_cur_exp(mp, mp_find_direction_time(mp, value(x_part_loc(p)), - value(y_part_loc(p)),mp->cur_exp)); -} -static void mp_set_up_envelope (MP mp,pointer p) { +static void mp_set_up_offset (MP mp, mp_node p) { + mp_find_offset (mp, value (x_part_loc (p)), value (y_part_loc (p)), + cur_exp_knot ()); + mp_pair_value (mp, mp->cur_x, mp->cur_y); +} +static void mp_set_up_direction_time (MP mp, mp_node p) { + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + new_expr.data.val = mp_find_direction_time (mp, value (x_part_loc (p)), + value (y_part_loc (p)), + cur_exp_knot ()); + mp_flush_cur_exp (mp, new_expr); +} +static void mp_set_up_envelope (MP mp, mp_node p) { quarterword ljoin, lcap; scaled miterlim; - pointer q = mp_copy_path(mp, mp->cur_exp); /* the original path */ + mp_knot q = mp_copy_path (mp, cur_exp_knot ()); /* the original path */ /* TODO: accept elliptical pens for straight paths */ - if (pen_is_elliptical(value(p))) { - mp_bad_envelope_pen(mp); - mp->cur_exp = q; - mp->cur_type = mp_path_type; + if (pen_is_elliptical (knot_value (p))) { + mp_bad_envelope_pen (mp); + set_cur_exp_knot (q); + mp->cur_exp.type = mp_path_type; return; } - if ( mp->internal[mp_linejoin]>unity ) ljoin=2; - else if ( mp->internal[mp_linejoin]>0 ) ljoin=1; - else ljoin=0; - if ( mp->internal[mp_linecap]>unity ) lcap=2; - else if ( mp->internal[mp_linecap]>0 ) lcap=1; - else lcap=0; - if ( mp->internal[mp_miterlimit]<unity ) - miterlim=unity; + if (internal_value (mp_linejoin) > unity) + ljoin = 2; + else if (internal_value (mp_linejoin) > 0) + ljoin = 1; + else + ljoin = 0; + if (internal_value (mp_linecap) > unity) + lcap = 2; + else if (internal_value (mp_linecap) > 0) + lcap = 1; else - miterlim=mp->internal[mp_miterlimit]; - mp->cur_exp = mp_make_envelope(mp, q, value(p), ljoin,lcap,miterlim); - mp->cur_type = mp_path_type; + lcap = 0; + if (internal_value (mp_miterlimit) < unity) + miterlim = unity; + else + miterlim = internal_value (mp_miterlimit); + set_cur_exp_knot (mp_make_envelope + (mp, q, knot_value (p), ljoin, lcap, miterlim)); + mp->cur_exp.type = mp_path_type; } @@ -21678,128 +25236,165 @@ static void mp_set_up_envelope (MP mp,pointer p) { the output of |mp_ps_do_font_charstring| has to be un-exported. @<Declare binary action...@>= -static void mp_set_up_glyph_infont (MP mp, pointer p) { +static void mp_set_up_glyph_infont (MP mp, mp_node p) { mp_edge_object *h = NULL; mp_ps_font *f = NULL; - char *n = mp_str(mp, mp->cur_exp); - f = mp_ps_font_parse(mp, (int)mp_find_font(mp, n)); - if (f!=NULL) { - if (mp_type(p) == mp_known) { - int v = mp_round_unscaled(mp,value(p)); - if (v<0 || v>255) { + char *n = mp_str (mp, cur_exp_str ()); + f = mp_ps_font_parse (mp, (int) mp_find_font (mp, n)); + if (f != NULL) { + if (mp_type (p) == mp_known) { + int v = mp_round_unscaled (mp, value (p)); + if (v < 0 || v > 255) { print_err ("glyph index too high ("); - mp_print_int(mp, v); - mp_print(mp,")"); - mp_error(mp); + mp_print_int (mp, v); + mp_print (mp, ")"); + mp_error (mp); } else { - h = mp_ps_font_charstring (mp,f,v); + h = mp_ps_font_charstring (mp, f, v); } } else { - n = mp_str(mp, value(p)); - h = mp_ps_do_font_charstring (mp,f,n); - free(n); + n = mp_str (mp, str_value (p)); + h = mp_ps_do_font_charstring (mp, f, n); } - mp_ps_font_free(mp,f); + mp_ps_font_free (mp, f); } - if (h!=NULL) { - mp->cur_exp=mp_gr_unexport(mp, h); + if (h != NULL) { + set_cur_exp_node (mp_gr_unexport (mp, h)); } else { - mp->cur_exp=mp_get_node(mp, edge_header_size); - mp_init_edges(mp, mp->cur_exp); + set_cur_exp_node (mp_get_edge_header_node (mp)); + mp_init_edges (mp, cur_exp_node ()); } - mp->cur_type=mp_picture_type; + mp->cur_exp.type = mp_picture_type; } + @ @<Declare binary action...@>= -static void mp_find_point (MP mp,scaled v, quarterword c) { - pointer p; /* the path */ - scaled n; /* its length */ - p=mp->cur_exp; - if ( mp_left_type(p)==mp_endpoint ) n=-unity; else n=0; - do { p=mp_link(p); n=n+unity; } while (p!=mp->cur_exp); - if ( n==0 ) { - v=0; - } else if ( v<0 ) { - if ( mp_left_type(p)==mp_endpoint ) v=0; - else v=n-1-((-v-1) % n); - } else if ( v>n ) { - if ( mp_left_type(p)==mp_endpoint ) v=n; - else v=v % n; - } - p=mp->cur_exp; - while ( v>=unity ) { p=mp_link(p); v=v-unity; }; - if ( v!=0 ) { - @<Insert a fractional node by splitting the cubic@>; +static void mp_find_point (MP mp, scaled v, quarterword c) { + mp_knot p; /* the path */ + scaled n; /* its length */ + p = cur_exp_knot (); + if (mp_left_type (p) == mp_endpoint) + n = -unity; + else + n = 0; + do { + p = mp_next_knot (p); + n = n + unity; + } while (p != cur_exp_knot ()); + if (n == 0) { + v = 0; + } else if (v < 0) { + if (mp_left_type (p) == mp_endpoint) + v = 0; + else + v = n - 1 - ((-v - 1) % n); + } else if (v > n) { + if (mp_left_type (p) == mp_endpoint) + v = n; + else + v = v % n; + } + p = cur_exp_knot (); + while (v >= unity) { + p = mp_next_knot (p); + v = v - unity; + } + if (v != 0) { + @<Insert a fractional node by splitting the cubic@>; } @<Set the current expression to the desired path coordinates@>; } + @ @<Insert a fractional node...@>= -{ mp_split_cubic(mp, p,v*010000); p=mp_link(p); } +{ + mp_split_cubic (mp, p, v * 010000); + p = mp_next_knot (p); +} + @ @<Set the current expression to the desired path coordinates...@>= switch (c) { -case point_of: - mp_pair_value(mp, mp_x_coord(p),mp_y_coord(p)); +case point_of: + mp_pair_value (mp, mp_x_coord (p), mp_y_coord (p)); break; -case precontrol_of: - if ( mp_left_type(p)==mp_endpoint ) mp_pair_value(mp, mp_x_coord(p),mp_y_coord(p)); - else mp_pair_value(mp, mp_left_x(p),mp_left_y(p)); +case precontrol_of: + if (mp_left_type (p) == mp_endpoint) + mp_pair_value (mp, mp_x_coord (p), mp_y_coord (p)); + else + mp_pair_value (mp, mp_left_x (p), mp_left_y (p)); break; -case postcontrol_of: - if ( mp_right_type(p)==mp_endpoint ) mp_pair_value(mp, mp_x_coord(p),mp_y_coord(p)); - else mp_pair_value(mp, mp_right_x(p),mp_right_y(p)); +case postcontrol_of: + if (mp_right_type (p) == mp_endpoint) + mp_pair_value (mp, mp_x_coord (p), mp_y_coord (p)); + else + mp_pair_value (mp, mp_right_x (p), mp_right_y (p)); break; -} /* there are no other cases */ +} /* there are no other cases */ + @ @<Additional cases of binary operators@>= -case arc_time_of: - if ( mp->cur_type==mp_pair_type ) - mp_pair_to_path(mp); - if ( (mp->cur_type==mp_path_type)&&(mp_type(p)==mp_known) ) - mp_flush_cur_exp(mp, mp_get_arc_time(mp, mp->cur_exp,value(p))); - else - mp_bad_binary(mp, p, (quarterword)c); - break; +case arc_time_of: +if (mp->cur_exp.type == mp_pair_type) + mp_pair_to_path (mp); +if ((mp->cur_exp.type == mp_path_type) && (mp_type (p) == mp_known)) { + new_expr.data.val = mp_get_arc_time (mp, cur_exp_knot (), value (p)); + mp_flush_cur_exp (mp, new_expr); +} else { + mp_bad_binary (mp, p, (quarterword) c); +} +break; @ @<Additional cases of bin...@>= -case intersect: - if ( mp_type(p)==mp_pair_type ) { - q=mp_stash_cur_exp(mp); mp_unstash_cur_exp(mp, p); - mp_pair_to_path(mp); p=mp_stash_cur_exp(mp); mp_unstash_cur_exp(mp, q); - }; - if ( mp->cur_type==mp_pair_type ) mp_pair_to_path(mp); - if ( (mp->cur_type==mp_path_type)&&(mp_type(p)==mp_path_type) ) { - mp_path_intersection(mp, value(p),mp->cur_exp); - mp_pair_value(mp, mp->cur_t,mp->cur_tt); - } else { - mp_bad_binary(mp, p,intersect); - } - break; +case intersect: +if (mp_type (p) == mp_pair_type) { + q = mp_stash_cur_exp (mp); + mp_unstash_cur_exp (mp, p); + mp_pair_to_path (mp); + p = mp_stash_cur_exp (mp); + mp_unstash_cur_exp (mp, q); +} +if (mp->cur_exp.type == mp_pair_type) + mp_pair_to_path (mp); +if ((mp->cur_exp.type == mp_path_type) && (mp_type (p) == mp_path_type)) { + mp_path_intersection (mp, knot_value (p), cur_exp_knot ()); + mp_pair_value (mp, mp->cur_t, mp->cur_tt); +} else { + mp_bad_binary (mp, p, intersect); +} +break; @ @<Additional cases of bin...@>= case in_font: - if ( (mp->cur_type!=mp_string_type)||mp_type(p)!=mp_string_type) - mp_bad_binary(mp, p,in_font); - else { mp_do_infont(mp, p); binary_return; } - break; +if ((mp->cur_exp.type != mp_string_type) || mp_type (p) != mp_string_type) { + mp_bad_binary (mp, p, in_font); +} else { + mp_do_infont (mp, p); + binary_return; +} +break; @ Function |new_text_node| owns the reference count for its second argument (the text string) but not its first (the font name). @<Declare binary action...@>= -static void mp_do_infont (MP mp,pointer p) { - pointer q; - q=mp_get_node(mp, edge_header_size); - mp_init_edges(mp, q); - mp_link(obj_tail(q))=mp_new_text_node(mp,str(mp->cur_exp),value(p)); - obj_tail(q)=mp_link(obj_tail(q)); - mp_free_node(mp, p,value_node_size); - mp_flush_cur_exp(mp, q); - mp->cur_type=mp_picture_type; -} - -@* \[40] Statements and commands. +static void mp_do_infont (MP mp, mp_node p) { + mp_node q; + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + q = mp_get_edge_header_node (mp); + mp_init_edges (mp, q); + mp_link (obj_tail (q)) = + mp_new_text_node (mp, mp_str (mp, cur_exp_str ()), str_value (p)); + obj_tail (q) = mp_link (obj_tail (q)); + mp_free_node (mp, p, value_node_size); + new_expr.data.node = q; + mp_flush_cur_exp (mp, new_expr); + mp->cur_exp.type = mp_picture_type; +} + + +@* Statements and commands. The chief executive of \MP\ is the |do_statement| routine, which contains the master switch that causes all the various pieces of \MP\ to do their things, in the right order. @@ -21823,115 +25418,141 @@ $$\hbox{|min_primary_command=max_statement_command=type_name|}$$ to interpret a statement that starts with, e.g., `\&{string}', as a type declaration rather than a boolean expression. -@c void mp_do_statement (MP mp) { /* governs \MP's activities */ - mp->cur_type=mp_vacuous; mp_get_x_next(mp); - if ( mp->cur_cmd>max_primary_command ) { +@c +void mp_do_statement (MP mp) { /* governs \MP's activities */ + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + mp->cur_exp.type = mp_vacuous; + mp_get_x_next (mp); + if (mp->cur_cmd > max_primary_command) { @<Worry about bad statement@>; - } else if ( mp->cur_cmd>max_statement_command ) { + } else if (mp->cur_cmd > max_statement_command) { @<Do an equation, assignment, title, or `$\langle\,$expression$\,\rangle\,$\&{endgroup}'@>; } else { @<Do a statement that doesn't begin with an expression@>; } - if ( mp->cur_cmd<semicolon ) + if (mp->cur_cmd < semicolon) @<Flush unparsable junk that was found after the statement@>; - mp->error_count=0; + mp->error_count = 0; } + @ @<Declarations@>= @<Declare action procedures for use by |do_statement|@> + @ The only command codes |>max_primary_command| that can be present at the beginning of a statement are |semicolon| and higher; these occur when the statement is null. @<Worry about bad statement@>= -{ - if ( mp->cur_cmd<semicolon ) { - print_err("A statement can't begin with `"); -@.A statement can't begin with x@> - mp_print_cmd_mod(mp, mp->cur_cmd,mp->cur_mod); mp_print_char(mp, xord('\'')); - help5("I was looking for the beginning of a new statement.", - "If you just proceed without changing anything, I'll ignore", - "everything up to the next `;'. Please insert a semicolon", - "now in front of anything that you don't want me to delete.", - "(See Chapter 27 of The METAFONTbook for an example.)"); -@:METAFONTbook}{\sl The {\logos METAFONT\/}book@> - mp_back_error(mp); mp_get_x_next(mp); +{ + if (mp->cur_cmd < semicolon) { + print_err ("A statement can't begin with `"); +@.A statement can't begin with x@>; + mp_print_cmd_mod (mp, mp->cur_cmd, mp->cur_mod); + mp_print_char (mp, xord ('\'')); + help5 ("I was looking for the beginning of a new statement.", + "If you just proceed without changing anything, I'll ignore", + "everything up to the next `;'. Please insert a semicolon", + "now in front of anything that you don't want me to delete.", + "(See Chapter 27 of The METAFONTbook for an example.)"); +@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>; + mp_back_error (mp); + mp_get_x_next (mp); } } + @ The help message printed here says that everything is flushed up to a semicolon, but actually the commands |end_group| and |stop| will also terminate a statement. @<Flush unparsable junk that was found after the statement@>= -{ - print_err("Extra tokens will be flushed"); -@.Extra tokens will be flushed@> - help6("I've just read as much of that statement as I could fathom,", - "so a semicolon should have been next. It's very puzzling...", - "but I'll try to get myself back together, by ignoring", - "everything up to the next `;'. Please insert a semicolon", - "now in front of anything that you don't want me to delete.", - "(See Chapter 27 of The METAFONTbook for an example.)"); -@:METAFONTbook}{\sl The {\logos METAFONT\/}book@> - mp_back_error(mp); mp->scanner_status=flushing; - do { - get_t_next; +{ + print_err ("Extra tokens will be flushed"); +@.Extra tokens will be flushed@>; + help6 ("I've just read as much of that statement as I could fathom,", + "so a semicolon should have been next. It's very puzzling...", + "but I'll try to get myself back together, by ignoring", + "everything up to the next `;'. Please insert a semicolon", + "now in front of anything that you don't want me to delete.", + "(See Chapter 27 of The METAFONTbook for an example.)"); +@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>; + mp_back_error (mp); + mp->scanner_status = flushing; + do { + get_t_next (mp); @<Decrease the string reference count...@>; - } while (! end_of_statement); /* |cur_cmd=semicolon|, |end_group|, or |stop| */ - mp->scanner_status=normal; + } while (!end_of_statement); /* |cur_cmd=semicolon|, |end_group|, or |stop| */ + mp->scanner_status = normal; } + @ If |do_statement| ends with |cur_cmd=end_group|, we should have |cur_type=mp_vacuous| unless the statement was simply an expression; in the latter case, |cur_type| and |cur_exp| should represent that expression. @<Do a statement that doesn't...@>= -{ - if ( mp->internal[mp_tracing_commands]>0 ) +{ + if (internal_value (mp_tracing_commands) > 0) show_cur_cmd_mod; - switch (mp->cur_cmd ) { - case type_name:mp_do_type_declaration(mp); break; + switch (mp->cur_cmd) { + case type_name: + mp_do_type_declaration (mp); + break; case macro_def: - if ( mp->cur_mod>var_def ) mp_make_op_def(mp); - else if ( mp->cur_mod>end_def ) mp_scan_def(mp); - break; - @<Cases of |do_statement| that invoke particular commands@>; - } /* there are no other cases */ - mp->cur_type=mp_vacuous; + if (mp->cur_mod > var_def) + mp_make_op_def (mp); + else if (mp->cur_mod > end_def) + mp_scan_def (mp); + break; + @<Cases of |do_statement| that invoke particular commands@>; + } /* there are no other cases */ + mp->cur_exp.type = mp_vacuous; } + @ The most important statements begin with expressions. @<Do an equation, assignment, title, or...@>= -{ - mp->var_flag=assignment; mp_scan_expression(mp); - if ( mp->cur_cmd<end_group ) { - if ( mp->cur_cmd==equals ) mp_do_equation(mp); - else if ( mp->cur_cmd==assignment ) mp_do_assignment(mp); - else if ( mp->cur_type==mp_string_type ) {@<Do a title@> ; } - else if ( mp->cur_type!=mp_vacuous ){ - exp_err("Isolated expression"); -@.Isolated expression@> - help3("I couldn't find an `=' or `:=' after the", - "expression that is shown above this error message,", - "so I guess I'll just ignore it and carry on."); - mp_put_get_error(mp); +{ + mp->var_flag = assignment; + mp_scan_expression (mp); + if (mp->cur_cmd < end_group) { + if (mp->cur_cmd == equals) + mp_do_equation (mp); + else if (mp->cur_cmd == assignment) + mp_do_assignment (mp); + else if (mp->cur_exp.type == mp_string_type) { + @<Do a title@>; + } else if (mp->cur_exp.type != mp_vacuous) { + exp_err ("Isolated expression"); +@.Isolated expression@>; + help3 ("I couldn't find an `=' or `:=' after the", + "expression that is shown above this error message,", + "so I guess I'll just ignore it and carry on."); + mp_put_get_error (mp); } - mp_flush_cur_exp(mp, 0); mp->cur_type=mp_vacuous; + new_expr.data.val = 0; + mp_flush_cur_exp (mp, new_expr); + mp->cur_exp.type = mp_vacuous; } } + @ @<Do a title@>= -{ - if ( mp->internal[mp_tracing_titles]>0 ) { - mp_print_nl(mp, ""); mp_print_str(mp, mp->cur_exp); update_terminal; +{ + if (internal_value (mp_tracing_titles) > 0) { + mp_print_nl (mp, ""); + mp_print_str (mp, cur_exp_str ()); + update_terminal; } } + @ Equations and assignments are performed by the pair of mutually recursive @^recursion@> routines |do_equation| and |do_assignment|. These routines are called when @@ -21942,25 +25563,33 @@ will be equal to the right-hand side (which will normally be equal to the left-hand side). @<Declarations@>= -@<Declare the procedure called |make_eq|@> -static void mp_do_equation (MP mp) ; +@<Declare the procedure called |make_eq|@>; +static void mp_do_equation (MP mp); @ @c void mp_do_equation (MP mp) { - pointer lhs; /* capsule for the left-hand side */ - pointer p; /* temporary register */ - lhs=mp_stash_cur_exp(mp); mp_get_x_next(mp); - mp->var_flag=assignment; mp_scan_expression(mp); - if ( mp->cur_cmd==equals ) mp_do_equation(mp); - else if ( mp->cur_cmd==assignment ) mp_do_assignment(mp); - if ( mp->internal[mp_tracing_commands]>two ) + mp_node lhs; /* capsule for the left-hand side */ + mp_node p; /* temporary register */ + lhs = mp_stash_cur_exp (mp); + mp_get_x_next (mp); + mp->var_flag = assignment; + mp_scan_expression (mp); + if (mp->cur_cmd == equals) + mp_do_equation (mp); + else if (mp->cur_cmd == assignment) + mp_do_assignment (mp); + if (internal_value (mp_tracing_commands) > two) @<Trace the current equation@>; - if ( mp->cur_type==mp_unknown_path ) if ( mp_type(lhs)==mp_pair_type ) { - p=mp_stash_cur_exp(mp); mp_unstash_cur_exp(mp, lhs); lhs=p; - }; /* in this case |make_eq| will change the pair to a path */ - mp_make_eq(mp, lhs); /* equate |lhs| to |(cur_type,cur_exp)| */ + if (mp->cur_exp.type == mp_unknown_path) + if (mp_type (lhs) == mp_pair_type) { + p = mp_stash_cur_exp (mp); + mp_unstash_cur_exp (mp, lhs); + lhs = p; + }; /* in this case |make_eq| will change the pair to a path */ + mp_make_eq (mp, lhs); /* equate |lhs| to |(cur_type,cur_exp)| */ } + @ And |do_assignment| is similar to |do_equation|: @<Declarations@>= @@ -21968,97 +25597,120 @@ static void mp_do_assignment (MP mp); @ @c void mp_do_assignment (MP mp) { - pointer lhs; /* token list for the left-hand side */ - pointer p; /* where the left-hand value is stored */ - pointer q; /* temporary capsule for the right-hand value */ - if ( mp->cur_type!=mp_token_list ) { - exp_err("Improper `:=' will be changed to `='"); -@.Improper `:='@> - help2("I didn't find a variable name at the left of the `:=',", - "so I'm going to pretend that you said `=' instead."); - mp_error(mp); mp_do_equation(mp); - } else { - lhs=mp->cur_exp; mp->cur_type=mp_vacuous; - mp_get_x_next(mp); mp->var_flag=assignment; mp_scan_expression(mp); - if ( mp->cur_cmd==equals ) mp_do_equation(mp); - else if ( mp->cur_cmd==assignment ) mp_do_assignment(mp); - if ( mp->internal[mp_tracing_commands]>two ) + mp_node lhs; /* token list for the left-hand side */ + mp_node p; /* where the left-hand value is stored */ + mp_node q; /* temporary capsule for the right-hand value */ + if (mp->cur_exp.type != mp_token_list) { + exp_err ("Improper `:=' will be changed to `='"); +@.Improper `:='@>; + help2 ("I didn't find a variable name at the left of the `:=',", + "so I'm going to pretend that you said `=' instead."); + mp_error (mp); + mp_do_equation (mp); + } else { + lhs = cur_exp_node (); + mp->cur_exp.type = mp_vacuous; + mp_get_x_next (mp); + mp->var_flag = assignment; + mp_scan_expression (mp); + if (mp->cur_cmd == equals) + mp_do_equation (mp); + else if (mp->cur_cmd == assignment) + mp_do_assignment (mp); + if (internal_value (mp_tracing_commands) > two) @<Trace the current assignment@>; - if ( mp_info(lhs)>hash_end ) { + if (mp_name_type (lhs) == mp_internal_sym) { @<Assign the current expression to an internal variable@>; - } else { + } else { @<Assign the current expression to the variable |lhs|@>; } - mp_flush_node_list(mp, lhs); + mp_flush_node_list (mp, lhs); } } + @ @<Trace the current equation@>= -{ - mp_begin_diagnostic(mp); mp_print_nl(mp, "{("); mp_print_exp(mp,lhs,0); - mp_print(mp,")=("); mp_print_exp(mp,null,0); - mp_print(mp,")}"); mp_end_diagnostic(mp, false); +{ + mp_begin_diagnostic (mp); + mp_print_nl (mp, "{("); + mp_print_exp (mp, lhs, 0); + mp_print (mp, ")=("); + mp_print_exp (mp, NULL, 0); + mp_print (mp, ")}"); + mp_end_diagnostic (mp, false); } + @ @<Trace the current assignment@>= -{ - mp_begin_diagnostic(mp); mp_print_nl(mp, "{"); - if ( mp_info(lhs)>hash_end ) - mp_print(mp, mp->int_name[mp_info(lhs)-(hash_end)]); - else - mp_show_token_list(mp, lhs,null,1000,0); - mp_print(mp, ":="); mp_print_exp(mp, null,0); - mp_print_char(mp, xord('}')); mp_end_diagnostic(mp, false); +{ + mp_begin_diagnostic (mp); + mp_print_nl (mp, "{"); + if (mp_name_type (lhs) == mp_internal_sym) + mp_print (mp, internal_name (mp_sym_info (lhs))); + else + mp_show_token_list (mp, lhs, NULL, 1000, 0); + mp_print (mp, ":="); + mp_print_exp (mp, NULL, 0); + mp_print_char (mp, xord ('}')); + mp_end_diagnostic (mp, false); } + @ @<Assign the current expression to an internal variable@>= -if ( mp->cur_type==mp_known || mp->cur_type==mp_string_type ) { - if (mp->cur_type==mp_string_type) { - if (mp->int_type[mp_info(lhs)-(hash_end)]!=mp->cur_type) { - exp_err("Internal quantity `"); -@.Internal quantity...@> - mp_print(mp, mp->int_name[mp_info(lhs)-(hash_end)]); - mp_print(mp, "' must receive a known numeric value"); - help2("I can\'t set this internal quantity to anything but a known", +if (mp->cur_exp.type == mp_known || mp->cur_exp.type == mp_string_type) { + if (mp->cur_exp.type == mp_string_type) { + if (internal_type (mp_sym_info (lhs)) != mp->cur_exp.type) { + exp_err ("Internal quantity `"); +@.Internal quantity...@>; + mp_print (mp, internal_name (mp_sym_info (lhs))); + mp_print (mp, "' must receive a known numeric value"); + help2 ("I can\'t set this internal quantity to anything but a known", "numeric value, so I'll have to ignore this assignment."); - mp_put_get_error(mp); + mp_put_get_error (mp); } else { - add_str_ref(mp->cur_exp); - mp->internal[mp_info(lhs)-(hash_end)]=mp->cur_exp; + add_str_ref (cur_exp_str ()); + internal_string (mp_sym_info (lhs)) = cur_exp_str (); } - } else { /* mp_known */ - if (mp->int_type[mp_info(lhs)-(hash_end)]!=mp->cur_type) { - exp_err("Internal quantity `"); -@.Internal quantity...@> - mp_print(mp, mp->int_name[mp_info(lhs)-(hash_end)]); - mp_print(mp, "' must receive a known string"); - help2("I can\'t set this internal quantity to anything but a known", + } else { /* |mp_known| */ + if (internal_type (mp_sym_info (lhs)) != mp->cur_exp.type) { + exp_err ("Internal quantity `"); +@.Internal quantity...@>; + mp_print (mp, internal_name (mp_sym_info (lhs))); + mp_print (mp, "' must receive a known string"); + help2 ("I can\'t set this internal quantity to anything but a known", "string, so I'll have to ignore this assignment."); - mp_put_get_error(mp); + mp_put_get_error (mp); } else { - mp->internal[mp_info(lhs)-(hash_end)]=mp->cur_exp; + internal_value (mp_sym_info (lhs)) = cur_exp_value (); } } -} else { - exp_err("Internal quantity `"); -@.Internal quantity...@> - mp_print(mp, mp->int_name[mp_info(lhs)-(hash_end)]); - mp_print(mp, "' must receive a known numeric or string"); - help2("I can\'t set an internal quantity to anything but a known string", - "or known numeric value, so I'll have to ignore this assignment."); - mp_put_get_error(mp); +} else { + exp_err ("Internal quantity `"); +@.Internal quantity...@>; + mp_print (mp, internal_name (mp_sym_info (lhs))); + mp_print (mp, "' must receive a known numeric or string"); + help2 ("I can\'t set an internal quantity to anything but a known string", + "or known numeric value, so I'll have to ignore this assignment."); + mp_put_get_error (mp); } + @ @<Assign the current expression to the variable |lhs|@>= -{ - p=mp_find_variable(mp, lhs); - if ( p!=null ) { - q=mp_stash_cur_exp(mp); mp->cur_type=mp_und_type(mp, p); - mp_recycle_value(mp, p); - mp_type(p)=mp->cur_type; value(p)=null; mp_make_exp_copy(mp, p); - p=mp_stash_cur_exp(mp); mp_unstash_cur_exp(mp, q); mp_make_eq(mp, p); - } else { - mp_obliterated(mp, lhs); mp_put_get_error(mp); +{ + p = mp_find_variable (mp, lhs); + if (p != NULL) { + q = mp_stash_cur_exp (mp); + mp->cur_exp.type = mp_und_type (mp, p); + mp_recycle_value (mp, p); + mp_type (p) = mp->cur_exp.type; + set_value (p, 0); /* todo: this was |null| */ + mp_make_exp_copy (mp, p); + p = mp_stash_cur_exp (mp); + mp_unstash_cur_exp (mp, q); + mp_make_eq (mp, p); + } else { + mp_obliterated (mp, lhs); + mp_put_get_error (mp); } } @@ -22067,218 +25719,307 @@ if ( mp->cur_type==mp_known || mp->cur_type==mp_string_type ) { a pointer to a capsule that is to be equated to the current expression. @<Declare the procedure called |make_eq|@>= -static void mp_make_eq (MP mp,pointer lhs) ; +static void mp_make_eq (MP mp, mp_node lhs); @ -@c void mp_make_eq (MP mp,pointer lhs) { - quarterword t; /* type of the left-hand side */ - pointer p,q; /* pointers inside of big nodes */ - integer v=0; /* value of the left-hand side */ -RESTART: - t=mp_type(lhs); - if ( t<=mp_pair_type ) v=value(lhs); +@c +void mp_make_eq (MP mp, mp_node lhs) { + mp_value new_expr; + mp_variable_type t; /* type of the left-hand side */ + mp_node p; /* pointer inside of big nodes */ + integer v = 0; /* value of the left-hand side */ + memset(&new_expr,0,sizeof(mp_value)); +RESTART: + t = mp_type (lhs); + if (t <= mp_pair_type) + v = value (lhs); switch (t) { - @<For each type |t|, make an equation and |goto done| unless |cur_type| + @<For each type |t|, make an equation and |goto done| unless |cur_type| is incompatible with~|t|@>; - } /* all cases have been listed */ + default: /* there are no other valid cases, but please the compiler */ + break; + } /* all valid cases have been listed */ @<Announce that the equation cannot be performed@>; DONE: - check_arith; mp_recycle_value(mp, lhs); - mp_free_node(mp, lhs,value_node_size); + check_arith; + mp_recycle_value (mp, lhs); + mp_free_node (mp, lhs, value_node_size); } + @ @<Announce that the equation cannot be performed@>= -mp_disp_err(mp, lhs,""); -exp_err("Equation cannot be performed ("); -@.Equation cannot be performed@> -if ( mp_type(lhs)<=mp_pair_type ) mp_print_type(mp, mp_type(lhs)); -else mp_print(mp, "numeric"); -mp_print_char(mp, xord('=')); -if ( mp->cur_type<=mp_pair_type ) mp_print_type(mp, mp->cur_type); -else mp_print(mp, "numeric"); -mp_print_char(mp, xord(')')); -help2("I'm sorry, but I don't know how to make such things equal.", - "(See the two expressions just above the error message.)"); -mp_put_get_error(mp) +mp_disp_err (mp, lhs, ""); +exp_err ("Equation cannot be performed ("); +@.Equation cannot be performed@>; +if (mp_type (lhs) <= mp_pair_type) + mp_print_type (mp, mp_type (lhs)); +else + mp_print (mp, "numeric"); +mp_print_char (mp, xord ('=')); +if (mp->cur_exp.type <= mp_pair_type) + mp_print_type (mp, mp->cur_exp.type); +else + mp_print (mp, "numeric"); +mp_print_char (mp, xord (')')); +help2 ("I'm sorry, but I don't know how to make such things equal.", + "(See the two expressions just above the error message.)"); +mp_put_get_error (mp) + @ @<For each type |t|, make an equation and |goto done| unless...@>= -case mp_boolean_type: case mp_string_type: case mp_pen_type: -case mp_path_type: case mp_picture_type: - if ( mp->cur_type==t+unknown_tag ) { - mp_nonlinear_eq(mp, v,mp->cur_exp,false); - mp_unstash_cur_exp(mp, mp->cur_exp); goto DONE; - } else if ( mp->cur_type==t ) { - @<Report redundant or inconsistent equation and |goto done|@>; - } - break; +case mp_boolean_type: +case mp_string_type: +case mp_pen_type: +case mp_path_type: +case mp_picture_type: +if (mp->cur_exp.type == t + unknown_tag) { + new_expr.data.val = v; + mp_nonlinear_eq (mp, new_expr, cur_exp_node (), false); + mp_unstash_cur_exp (mp, cur_exp_node ()); + goto DONE; +} else if (mp->cur_exp.type == t) { + @<Report redundant or inconsistent equation and |goto done|@>; +} +break; case unknown_types: - if ( mp->cur_type==t-unknown_tag ) { - mp_nonlinear_eq(mp, mp->cur_exp,lhs,true); goto DONE; - } else if ( mp->cur_type==t ) { - mp_ring_merge(mp, lhs,mp->cur_exp); goto DONE; - } else if ( mp->cur_type==mp_pair_type ) { - if ( t==mp_unknown_path ) { - mp_pair_to_path(mp); goto RESTART; - }; - } - break; -case mp_transform_type: case mp_color_type: -case mp_cmykcolor_type: case mp_pair_type: - if ( mp->cur_type==t ) { - @<Do multiple equations and |goto done|@>; +if (mp->cur_exp.type == t - unknown_tag) { + mp_nonlinear_eq (mp, mp->cur_exp, lhs, true); + goto DONE; +} else if (mp->cur_exp.type == t) { + mp_ring_merge (mp, lhs, cur_exp_node ()); + goto DONE; +} else if (mp->cur_exp.type == mp_pair_type) { + if (t == mp_unknown_path) { + mp_pair_to_path (mp); + goto RESTART; } - break; -case mp_known: case mp_dependent: -case mp_proto_dependent: case mp_independent: - if ( mp->cur_type>=mp_known ) { - mp_try_eq(mp, lhs,null); goto DONE; - }; - break; +} +break; +case mp_transform_type: +case mp_color_type: +case mp_cmykcolor_type: +case mp_pair_type: +if (mp->cur_exp.type == t) { + @<Do multiple equations and |goto done|@>; +} +break; +case mp_known: +case mp_dependent: +case mp_proto_dependent: +case mp_independent: +if (mp->cur_exp.type >= mp_known) { + mp_try_eq (mp, lhs, NULL); + goto DONE; +} +break; case mp_vacuous: - break; +break; @ @<Report redundant or inconsistent equation and |goto done|@>= -{ - if ( mp->cur_type<=mp_string_type ) { - if ( mp->cur_type==mp_string_type ) { - if ( mp_str_vs_str(mp, v,mp->cur_exp)!=0 ) { +{ + if (mp->cur_exp.type <= mp_string_type) { + if (mp->cur_exp.type == mp_string_type) { + if (mp_str_vs_str (mp, str_value (lhs), cur_exp_str ()) != 0) { goto NOT_FOUND; } - } else if ( v!=mp->cur_exp ) { + } else if (v != cur_exp_value ()) { goto NOT_FOUND; } - @<Exclaim about a redundant equation@>; goto DONE; + @<Exclaim about a redundant equation@>; + goto DONE; } - print_err("Redundant or inconsistent equation"); -@.Redundant or inconsistent equation@> - help2("An equation between already-known quantities can't help.", - "But don't worry; continue and I'll just ignore it."); - mp_put_get_error(mp); goto DONE; -NOT_FOUND: - print_err("Inconsistent equation"); -@.Inconsistent equation@> - help2("The equation I just read contradicts what was said before.", - "But don't worry; continue and I'll just ignore it."); - mp_put_get_error(mp); goto DONE; + print_err ("Redundant or inconsistent equation"); +@.Redundant or inconsistent equation@>; + help2 ("An equation between already-known quantities can't help.", + "But don't worry; continue and I'll just ignore it."); + mp_put_get_error (mp); + goto DONE; +NOT_FOUND: + print_err ("Inconsistent equation"); +@.Inconsistent equation@>; + help2 ("The equation I just read contradicts what was said before.", + "But don't worry; continue and I'll just ignore it."); + mp_put_get_error (mp); + goto DONE; } + @ @<Do multiple equations and |goto done|@>= -{ - p=v+mp->big_node_size[t]; - q=value(mp->cur_exp)+mp->big_node_size[t]; - do { - p=p-2; q=q-2; mp_try_eq(mp, p,q); - } while (p!=v); +{ + p = value_node (lhs); + switch (t) { + case mp_transform_type: + mp_try_eq (mp, yy_part_loc (p), yy_part_loc (value_node (cur_exp_node ()))); + mp_try_eq (mp, yx_part_loc (p), yx_part_loc (value_node (cur_exp_node ()))); + mp_try_eq (mp, xy_part_loc (p), xy_part_loc (value_node (cur_exp_node ()))); + mp_try_eq (mp, xx_part_loc (p), xx_part_loc (value_node (cur_exp_node ()))); + mp_try_eq (mp, ty_part_loc (p), ty_part_loc (value_node (cur_exp_node ()))); + mp_try_eq (mp, tx_part_loc (p), tx_part_loc (value_node (cur_exp_node ()))); + break; + case mp_color_type: + mp_try_eq (mp, blue_part_loc (p), + blue_part_loc (value_node (cur_exp_node ()))); + mp_try_eq (mp, green_part_loc (p), + green_part_loc (value_node (cur_exp_node ()))); + mp_try_eq (mp, red_part_loc (p), + red_part_loc (value_node (cur_exp_node ()))); + break; + case mp_cmykcolor_type: + mp_try_eq (mp, black_part_loc (p), + black_part_loc (value_node (cur_exp_node ()))); + mp_try_eq (mp, yellow_part_loc (p), + yellow_part_loc (value_node (cur_exp_node ()))); + mp_try_eq (mp, magenta_part_loc (p), + magenta_part_loc (value_node (cur_exp_node ()))); + mp_try_eq (mp, cyan_part_loc (p), + cyan_part_loc (value_node (cur_exp_node ()))); + break; + case mp_pair_type: + mp_try_eq (mp, y_part_loc (p), y_part_loc (value_node (cur_exp_node ()))); + mp_try_eq (mp, x_part_loc (p), x_part_loc (value_node (cur_exp_node ()))); + break; + default: /* there are no other valid cases, but please the compiler */ + break; + } goto DONE; } + @ The first argument to |try_eq| is the location of a value node in a capsule that will soon be recycled. The second argument is either a location within a pair or transform node pointed to by -|cur_exp|, or it is |null| (which means that |cur_exp| itself +|cur_exp|, or it is |NULL| (which means that |cur_exp| itself serves as the second argument). The idea is to leave |cur_exp| unchanged, but to equate the two operands. @<Declarations@>= -static void mp_try_eq (MP mp,pointer l, pointer r) ; +static void mp_try_eq (MP mp, mp_node l, mp_node r); @ -@c void mp_try_eq (MP mp,pointer l, pointer r) { - pointer p; /* dependency list for right operand minus left operand */ - int t; /* the type of list |p| */ - pointer q; /* the constant term of |p| is here */ - pointer pp; /* dependency list for right operand */ - int tt; /* the type of list |pp| */ - boolean copied; /* have we copied a list that ought to be recycled? */ +@c +void mp_try_eq (MP mp, mp_node l, mp_node r) { + mp_value_node p; /* dependency list for right operand minus left operand */ + mp_variable_type t; /* the type of list |p| */ + mp_value_node q; /* the constant term of |p| is here */ + mp_value_node pp; /* dependency list for right operand */ + mp_variable_type tt; /* the type of list |pp| */ + boolean copied; /* have we copied a list that ought to be recycled? */ @<Remove the left operand from its container, negate it, and put it into dependency list~|p| with constant term~|q|@>; @<Add the right operand to list |p|@>; - if ( mp_info(p)==null ) { + if (dep_info (p) == NULL) { @<Deal with redundant or inconsistent equation@>; - } else { - mp_linear_eq(mp, p, (quarterword)t); - if ( r==null ) if ( mp->cur_type!=mp_known ) { - if ( mp_type(mp->cur_exp)==mp_known ) { - pp=mp->cur_exp; mp->cur_exp=value(mp->cur_exp); mp->cur_type=mp_known; - mp_free_node(mp, pp,value_node_size); + } else { + mp_linear_eq (mp, p, (quarterword) t); + if (r == NULL && mp->cur_exp.type != mp_known) { + if (mp_type (cur_exp_node ()) == mp_known) { + mp_node pp = cur_exp_node (); + set_cur_exp_value (value (pp)); + mp->cur_exp.type = mp_known; + mp_free_node (mp, pp, value_node_size); } } } } + @ @<Remove the left operand from its container, negate it, and...@>= -t=mp_type(l); -if ( t==mp_known ) { - t=mp_dependent; p=mp_const_dependency(mp, -value(l)); q=p; -} else if ( t==mp_independent ) { - t=mp_dependent; p=mp_single_dependency(mp, l); negate(value(p)); - q=mp->dep_final; -} else { - p=dep_list(l); q=p; - while (1) { - negate(value(q)); - if ( mp_info(q)==null ) break; - q=mp_link(q); - } - mp_link(prev_dep(l))=mp_link(q); prev_dep(mp_link(q))=prev_dep(l); - mp_type(l)=mp_known; +t = mp_type (l); +if (t == mp_known) { + t = mp_dependent; + p = mp_const_dependency (mp, -value (l)); + q = p; +} else if (t == mp_independent) { + t = mp_dependent; + p = mp_single_dependency (mp, l); + set_dep_value (p, -dep_value (p)); + q = mp->dep_final; +} else { + mp_value_node ll = (mp_value_node) l; + p = (mp_value_node) dep_list (ll); + q = p; + while (1) { + set_dep_value (q, -dep_value (q)); + if (dep_info (q) == NULL) + break; + q = (mp_value_node) mp_link (q); + } + mp_link (prev_dep (ll)) = mp_link (q); + set_prev_dep ((mp_value_node) mp_link (q), prev_dep (ll)); + mp_type (ll) = mp_known; } + @ @<Deal with redundant or inconsistent equation@>= -{ - if ( abs(value(p))>64 ) { /* off by .001 or more */ - print_err("Inconsistent equation"); -@.Inconsistent equation@> - mp_print(mp, " (off by "); mp_print_scaled(mp, value(p)); - mp_print_char(mp, xord(')')); - help2("The equation I just read contradicts what was said before.", - "But don't worry; continue and I'll just ignore it."); - mp_put_get_error(mp); - } else if ( r==null ) { +{ + if (abs (value (p)) > 64) { /* off by .001 or more */ + print_err ("Inconsistent equation"); +@.Inconsistent equation@>; + mp_print (mp, " (off by "); + mp_print_scaled (mp, value (p)); + mp_print_char (mp, xord (')')); + help2 ("The equation I just read contradicts what was said before.", + "But don't worry; continue and I'll just ignore it."); + mp_put_get_error (mp); + } else if (r == NULL) { @<Exclaim about a redundant equation@>; } - mp_free_node(mp, p,dep_node_size); + mp_free_dep_node (mp, p); } + @ @<Add the right operand to list |p|@>= -if ( r==null ) { - if ( mp->cur_type==mp_known ) { - value(q)=value(q)+mp->cur_exp; goto DONE1; - } else { - tt=mp->cur_type; - if ( tt==mp_independent ) pp=mp_single_dependency(mp, mp->cur_exp); - else pp=dep_list(mp->cur_exp); - } +if (r == NULL) { + if (mp->cur_exp.type == mp_known) { + set_value (q, value (q) + cur_exp_value ()); + goto DONE1; + } else { + tt = mp->cur_exp.type; + if (tt == mp_independent) + pp = mp_single_dependency (mp, cur_exp_node ()); + else + pp = (mp_value_node) dep_list ((mp_value_node) cur_exp_node ()); + } } else { - if ( mp_type(r)==mp_known ) { - value(q)=value(q)+value(r); goto DONE1; - } else { - tt=mp_type(r); - if ( tt==mp_independent ) pp=mp_single_dependency(mp, r); - else pp=dep_list(r); + if (mp_type (r) == mp_known) { + set_dep_value (q, dep_value (q) + value (r)); + goto DONE1; + } else { + tt = mp_type (r); + if (tt == mp_independent) + pp = mp_single_dependency (mp, r); + else + pp = (mp_value_node) dep_list ((mp_value_node) r); } } -if ( tt!=mp_independent ) copied=false; -else { copied=true; tt=mp_dependent; }; +if (tt != mp_independent) { + copied = false; +} else { + copied = true; + tt = mp_dependent; +} @<Add dependency list |pp| of type |tt| to dependency list~|p| of type~|t|@>; -if ( copied ) mp_flush_node_list(mp, pp); +if (copied) + mp_flush_node_list (mp, (mp_node) pp); DONE1: @ @<Add dependency list |pp| of type |tt| to dependency list~|p| of type~|t|@>= -mp->watch_coefs=false; -if ( t==tt ) { - p=mp_p_plus_q(mp, p,pp, (quarterword)t); -} else if ( t==mp_proto_dependent ) { - p=mp_p_plus_fq(mp, p,unity,pp,mp_proto_dependent,mp_dependent); -} else { - q=p; - while ( mp_info(q)!=null ) { - value(q)=mp_round_fraction(mp, value(q)); q=mp_link(q); +mp->watch_coefs = false; +if (t == tt) { + p = mp_p_plus_q (mp, p, pp, (quarterword) t); +} else if (t == mp_proto_dependent) { + p = mp_p_plus_fq (mp, p, unity, pp, mp_proto_dependent, mp_dependent); +} else { + q = p; + while (dep_info (q) != NULL) { + set_dep_value (q, mp_round_fraction (mp, dep_value (q))); + q = (mp_value_node) mp_link (q); } - t=mp_proto_dependent; p=mp_p_plus_q(mp, p, pp, (quarterword)t); + t = mp_proto_dependent; + p = mp_p_plus_q (mp, p, pp, (quarterword) t); } -mp->watch_coefs=true; +mp->watch_coefs = true; @ Our next goal is to process type declarations. For this purpose it's convenient to have a procedure that scans a $\langle\,$declared @@ -22288,160 +26029,186 @@ will have been scanned, so it will appear in |cur_cmd|, |cur_mod|, and~|cur_sym|. @<Declarations@>= -static pointer mp_scan_declared_variable (MP mp) ; +static mp_node mp_scan_declared_variable (MP mp); @ @c -pointer mp_scan_declared_variable (MP mp) { - pointer x; /* hash address of the variable's root */ - pointer h,t; /* head and tail of the token list to be returned */ - pointer l; /* hash address of left bracket */ - mp_get_symbol(mp); x=mp->cur_sym; - if ( mp->cur_cmd!=tag_token ) mp_clear_symbol(mp, x,false); - h=mp_get_avail(mp); mp_info(h)=x; t=h; - while (1) { - mp_get_x_next(mp); - if ( mp->cur_sym==0 ) break; - if ( mp->cur_cmd!=tag_token ) if ( mp->cur_cmd!=internal_quantity) { - if ( mp->cur_cmd==left_bracket ) { - @<Descend past a collective subscript@>; - } else { - break; +mp_node mp_scan_declared_variable (MP mp) { + mp_sym x; /* hash address of the variable's root */ + mp_node h, t; /* head and tail of the token list to be returned */ + mp_get_symbol (mp); + x = mp->cur_sym; + if (mp->cur_cmd != tag_token) + mp_clear_symbol (mp, x, false); + h = mp_get_symbolic_node (mp); + set_mp_sym_sym (h, x); + t = h; + while (1) { + mp_get_x_next (mp); + if (mp->cur_sym == NULL) + break; + if (mp->cur_cmd != tag_token) + if (mp->cur_cmd != internal_quantity) { + if (mp->cur_cmd == left_bracket) { + @<Descend past a collective subscript@>; + } else { + break; + } } - } - mp_link(t)=mp_get_avail(mp); t=mp_link(t); mp_info(t)=mp->cur_sym; - } - if ( (eq_type(x)%outer_tag)!=tag_token ) mp_clear_symbol(mp, x,false); - if ( equiv(x)==null ) mp_new_root(mp, x); + mp_link (t) = mp_get_symbolic_node (mp); + t = mp_link (t); + set_mp_sym_sym (t, mp->cur_sym); + mp_name_type (t) = mp->cur_sym_mod; + } + if ((eq_type (x) % outer_tag) != tag_token) + mp_clear_symbol (mp, x, false); + if (equiv_node (x) == NULL) + mp_new_root (mp, x); return h; } + @ If the subscript isn't collective, we don't accept it as part of the declared variable. @<Descend past a collective subscript@>= -{ - l=mp->cur_sym; mp_get_x_next(mp); - if ( mp->cur_cmd!=right_bracket ) { - mp_back_input(mp); mp->cur_sym=l; mp->cur_cmd=left_bracket; break; +{ + mp_sym ll = mp->cur_sym; /* hash address of left bracket */ + mp_get_x_next (mp); + if (mp->cur_cmd != right_bracket) { + mp_back_input (mp); + mp->cur_sym = ll; + mp->cur_cmd = left_bracket; + break; } else { - mp->cur_sym=collective_subscript; + mp->cur_sym = collective_subscript; } } + @ Type declarations are introduced by the following primitive operations. @<Put each...@>= -mp_primitive(mp, "numeric",type_name,mp_numeric_type); -@:numeric_}{\&{numeric} primitive@> -mp_primitive(mp, "string",type_name,mp_string_type); -@:string_}{\&{string} primitive@> -mp_primitive(mp, "boolean",type_name,mp_boolean_type); -@:boolean_}{\&{boolean} primitive@> -mp_primitive(mp, "path",type_name,mp_path_type); -@:path_}{\&{path} primitive@> -mp_primitive(mp, "pen",type_name,mp_pen_type); -@:pen_}{\&{pen} primitive@> -mp_primitive(mp, "picture",type_name,mp_picture_type); -@:picture_}{\&{picture} primitive@> -mp_primitive(mp, "transform",type_name,mp_transform_type); -@:transform_}{\&{transform} primitive@> -mp_primitive(mp, "color",type_name,mp_color_type); -@:color_}{\&{color} primitive@> -mp_primitive(mp, "rgbcolor",type_name,mp_color_type); -@:color_}{\&{rgbcolor} primitive@> -mp_primitive(mp, "cmykcolor",type_name,mp_cmykcolor_type); -@:color_}{\&{cmykcolor} primitive@> -mp_primitive(mp, "pair",type_name,mp_pair_type); +mp_primitive (mp, "numeric", type_name, mp_numeric_type); +@:numeric_}{\&{numeric} primitive@>; +mp_primitive (mp, "string", type_name, mp_string_type); +@:string_}{\&{string} primitive@>; +mp_primitive (mp, "boolean", type_name, mp_boolean_type); +@:boolean_}{\&{boolean} primitive@>; +mp_primitive (mp, "path", type_name, mp_path_type); +@:path_}{\&{path} primitive@>; +mp_primitive (mp, "pen", type_name, mp_pen_type); +@:pen_}{\&{pen} primitive@>; +mp_primitive (mp, "picture", type_name, mp_picture_type); +@:picture_}{\&{picture} primitive@>; +mp_primitive (mp, "transform", type_name, mp_transform_type); +@:transform_}{\&{transform} primitive@>; +mp_primitive (mp, "color", type_name, mp_color_type); +@:color_}{\&{color} primitive@>; +mp_primitive (mp, "rgbcolor", type_name, mp_color_type); +@:color_}{\&{rgbcolor} primitive@>; +mp_primitive (mp, "cmykcolor", type_name, mp_cmykcolor_type); +@:color_}{\&{cmykcolor} primitive@>; +mp_primitive (mp, "pair", type_name, mp_pair_type); @:pair_}{\&{pair} primitive@> + @ @<Cases of |print_cmd...@>= -case type_name: mp_print_type(mp, (quarterword)m); break; +case type_name: +mp_print_type (mp, (quarterword) m); +break; @ Now we are ready to handle type declarations, assuming that a |type_name| has just been scanned. @<Declare action procedures for use by |do_statement|@>= -static void mp_do_type_declaration (MP mp) ; +static void mp_do_type_declaration (MP mp); @ @c void mp_do_type_declaration (MP mp) { - quarterword t; /* the type being declared */ - pointer p; /* token list for a declared variable */ - pointer q; /* value node for the variable */ - if ( mp->cur_mod>=mp_transform_type ) - t=(quarterword)mp->cur_mod; - else - t=(quarterword)(mp->cur_mod+unknown_tag); - do { - p=mp_scan_declared_variable(mp); - mp_flush_variable(mp, equiv(mp_info(p)),mp_link(p),false); - q=mp_find_variable(mp, p); - if ( q!=null ) { - mp_type(q)=t; value(q)=null; - } else { - print_err("Declared variable conflicts with previous vardef"); -@.Declared variable conflicts...@> - help2("You can't use, e.g., `numeric foo[]' after `vardef foo'.", - "Proceed, and I'll ignore the illegal redeclaration."); - mp_put_get_error(mp); + quarterword t; /* the type being declared */ + mp_node p; /* token list for a declared variable */ + mp_node q; /* value node for the variable */ + if (mp->cur_mod >= mp_transform_type) + t = (quarterword) mp->cur_mod; + else + t = (quarterword) (mp->cur_mod + unknown_tag); + do { + p = mp_scan_declared_variable (mp); + mp_flush_variable (mp, equiv_node (mp_sym_sym (p)), mp_link (p), false); + q = mp_find_variable (mp, p); + if (q != NULL) { + mp_type (q) = t; + set_value (q, 0); /* todo: this was |null| */ + } else { + print_err ("Declared variable conflicts with previous vardef"); +@.Declared variable conflicts...@>; + help2 ("You can't use, e.g., `numeric foo[]' after `vardef foo'.", + "Proceed, and I'll ignore the illegal redeclaration."); + mp_put_get_error (mp); } - mp_flush_list(mp, p); - if ( mp->cur_cmd<comma ) { + mp_flush_node_list (mp, p); + if (mp->cur_cmd < comma) { @<Flush spurious symbols after the declared variable@>; } - } while (! end_of_statement); + } while (!end_of_statement); } + @ @<Flush spurious symbols after the declared variable@>= -{ - print_err("Illegal suffix of declared variable will be flushed"); -@.Illegal suffix...flushed@> - help5("Variables in declarations must consist entirely of", - "names and collective subscripts, e.g., `x[]a'.", - "Are you trying to use a reserved word in a variable name?", - "I'm going to discard the junk I found here,", - "up to the next comma or the end of the declaration."); - if ( mp->cur_cmd==numeric_token ) - mp->help_line[2]="Explicit subscripts like `x15a' aren't permitted."; - mp_put_get_error(mp); mp->scanner_status=flushing; - do { - get_t_next; +{ + print_err ("Illegal suffix of declared variable will be flushed"); +@.Illegal suffix...flushed@>; + help5 ("Variables in declarations must consist entirely of", + "names and collective subscripts, e.g., `x[]a'.", + "Are you trying to use a reserved word in a variable name?", + "I'm going to discard the junk I found here,", + "up to the next comma or the end of the declaration."); + if (mp->cur_cmd == numeric_token) + mp->help_line[2] = "Explicit subscripts like `x15a' aren't permitted."; + mp_put_get_error (mp); + mp->scanner_status = flushing; + do { + get_t_next (mp); @<Decrease the string reference count...@>; - } while (mp->cur_cmd<comma); /* either |end_of_statement| or |cur_cmd=comma| */ - mp->scanner_status=normal; + } while (mp->cur_cmd < comma); /* either |end_of_statement| or |cur_cmd=comma| */ + mp->scanner_status = normal; } + @ \MP's |main_control| procedure just calls |do_statement| repeatedly until coming to the end of the user's program. Each execution of |do_statement| concludes with |cur_cmd=semicolon|, |end_group|, or |stop|. -@c -static void mp_main_control (MP mp) { - do { - mp_do_statement(mp); - if ( mp->cur_cmd==end_group ) { - print_err("Extra `endgroup'"); -@.Extra `endgroup'@> - help2("I'm not currently working on a `begingroup',", - "so I had better not try to end anything."); - mp_flush_error(mp, 0); +@c +static void mp_main_control (MP mp) { + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + do { + mp_do_statement (mp); + if (mp->cur_cmd == end_group) { + print_err ("Extra `endgroup'"); +@.Extra `endgroup'@>; + help2 ("I'm not currently working on a `begingroup',", + "so I had better not try to end anything."); + mp_flush_error (mp, new_expr); } - } while (mp->cur_cmd!=stop); + } while (mp->cur_cmd != stop); } int mp_run (MP mp) { - if (mp->history < mp_fatal_error_stop ) { - xfree(mp->jump_buf); - mp->jump_buf = malloc(sizeof(jmp_buf)); - if (mp->jump_buf == NULL || setjmp(*(mp->jump_buf)) != 0) + if (mp->history < mp_fatal_error_stop) { + xfree (mp->jump_buf); + mp->jump_buf = malloc (sizeof (jmp_buf)); + if (mp->jump_buf == NULL || setjmp (*(mp->jump_buf)) != 0) return mp->history; - mp_main_control(mp); /* come to life */ - mp_final_cleanup(mp); /* prepare for death */ - mp_close_files_and_terminate(mp); + mp_main_control (mp); /* come to life */ + mp_final_cleanup (mp); /* prepare for death */ + mp_close_files_and_terminate (mp); } return mp->history; } + @ This function allows setting of internals from an external source (like the command line or a controlling application). @@ -22450,52 +26217,46 @@ it calls |atoi| to get an integer from the start of the string. @c void mp_set_internal (MP mp, char *n, char *v, int isstring) { - size_t l = strlen(n); + size_t l = strlen (n); char err[256]; const char *errid = NULL; - if (l>0) { - integer h = mp_compute_hash(mp, n, (int)l); - pointer p = h+hash_base; /* we start searching here */ - while (true) { - if (text(p)>0 && length(text(p))==(int)l && - mp_str_eq_cstr(mp, text(p),n)) { - if (eq_type(p)==internal_quantity) { - if ((mp->int_type[equiv(p)]==mp_string_type) && (isstring)) { - mp->internal[equiv(p)] = mp_rts(mp,v); - } else if ((mp->int_type[equiv(p)]==mp_known) && (!isstring)) { - scaled test = (scaled)atoi(v); - if (test>16383 ) { - errid = "value is too large"; - } else if (test<-16383) { - errid = "value is too small"; - } else { - mp->internal[equiv(p)] = test*unity; - } + if (l > 0) { + mp_sym p = mp_id_lookup (mp, n, l, false); + if (p == NULL) { + errid = "variable does not exist"; + } else { + if (eq_type (p) == internal_quantity) { + if ((internal_type (equiv (p)) == mp_string_type) && (isstring)) { + internal_string (equiv (p)) = mp_rts (mp, v); + } else if ((internal_type (equiv (p)) == mp_known) && (!isstring)) { + scaled test = (scaled) atoi (v); + if (test > 16383) { + errid = "value is too large"; + } else if (test < -16383) { + errid = "value is too small"; } else { - errid = "value has the wrong type"; + internal_value (equiv (p)) = test * unity; } } else { - errid = "variable is not an internal"; + errid = "value has the wrong type"; } - break; - } - if ( mp_next(p)==0 ) { - errid = "variable does not exist"; - break; + } else { + errid = "variable is not an internal"; } - p=mp_next(p); } } if (errid != NULL) { if (isstring) { - mp_snprintf(err,256,"%s=\"%s\": %s, assignment ignored.",n,v, errid); + mp_snprintf (err, 256, "%s=\"%s\": %s, assignment ignored.", n, v, errid); } else { - mp_snprintf(err,256,"%s=%d: %s, assignment ignored.",n,atoi(v),errid); + mp_snprintf (err, 256, "%s=%d: %s, assignment ignored.", n, atoi (v), + errid); } - mp_warn(mp,err); + mp_warn (mp, err); } } + @ @<Exported function headers@>= void mp_set_internal (MP mp, char *n, char *v, int isstring); @@ -22515,22 +26276,21 @@ definition. There are small differences between input and output: |term_in| never uses |used|, whereas the other four never use |cur|. -@<Exported types@>= +@<Exported types@>= typedef struct { - void * fptr; - char * data; - char * cur; - size_t size; - size_t used; + void *fptr; + char *data; + char *cur; + size_t size; + size_t used; } mp_stream; - typedef struct { - mp_stream term_out; - mp_stream error_out; - mp_stream log_out; - mp_stream ps_out; - mp_stream term_in; - struct mp_edge_object *edges; + mp_stream term_out; + mp_stream error_out; + mp_stream log_out; + mp_stream ps_out; + mp_stream term_in; + struct mp_edge_object *edges; } mp_run_data; @ We need a function to clear an output stream, this is called at the @@ -22538,20 +26298,21 @@ beginning of |mp_execute|. We also need one for destroying an output stream, this is called just before a stream is (re)opened. @c -static void mp_reset_stream(mp_stream *str) { - xfree(str->data); - str->cur = NULL; - str->size = 0; - str->used = 0; +static void mp_reset_stream (mp_stream * str) { + xfree (str->data); + str->cur = NULL; + str->size = 0; + str->used = 0; } -static void mp_free_stream(mp_stream *str) { - xfree(str->fptr); - mp_reset_stream(str); +static void mp_free_stream (mp_stream * str) { + xfree (str->fptr); + mp_reset_stream (str); } + @ @<Declarations@>= -static void mp_reset_stream(mp_stream *str); -static void mp_free_stream(mp_stream *str); +static void mp_reset_stream (mp_stream * str); +static void mp_free_stream (mp_stream * str); @ The global instance contains a pointer instead of the actual structure even though it is essentially static, because that makes it is easier to move @@ -22566,23 +26327,25 @@ indirect object is used that wraps a |FILE *|. @<Types ... @>= typedef struct File { - FILE *f; + FILE *f; } File; @ Here are all of the functions that need to be overloaded for |mp_execute|. @<Declarations@>= -static void *mplib_open_file(MP mp, const char *fname, const char *fmode, int ftype); -static int mplib_get_char(void *f, mp_run_data * mplib_data); -static void mplib_unget_char(void *f, mp_run_data * mplib_data, int c); -static char *mplib_read_ascii_file(MP mp, void *ff, size_t * size); -static void mplib_write_ascii_file(MP mp, void *ff, const char *s); -static void mplib_read_binary_file(MP mp, void *ff, void **data, size_t * size); -static void mplib_write_binary_file(MP mp, void *ff, void *s, size_t size); -static void mplib_close_file(MP mp, void *ff); -static int mplib_eof_file(MP mp, void *ff); -static void mplib_flush_file(MP mp, void *ff); -static void mplib_shipout_backend(MP mp, int h); +static void *mplib_open_file (MP mp, const char *fname, const char *fmode, + int ftype); +static int mplib_get_char (void *f, mp_run_data * mplib_data); +static void mplib_unget_char (void *f, mp_run_data * mplib_data, int c); +static char *mplib_read_ascii_file (MP mp, void *ff, size_t * size); +static void mplib_write_ascii_file (MP mp, void *ff, const char *s); +static void mplib_read_binary_file (MP mp, void *ff, void **data, + size_t * size); +static void mplib_write_binary_file (MP mp, void *ff, void *s, size_t size); +static void mplib_close_file (MP mp, void *ff); +static int mplib_eof_file (MP mp, void *ff); +static void mplib_flush_file (MP mp, void *ff); +static void mplib_shipout_backend (MP mp, void *h); @ The |xmalloc(1,1)| calls make sure the stored indirection values are unique. @@ -22594,399 +26357,394 @@ static void mplib_shipout_backend(MP mp, int h); } } while (0) @c - -static void *mplib_open_file(MP mp, const char *fname, const char *fmode, int ftype) -{ - File *ff = xmalloc(1, sizeof(File)); - mp_run_data *run = mp_rundata(mp); - ff->f = NULL; - if (ftype == mp_filetype_terminal) { - if (fmode[0] == 'r') { - if (!ff->f) { - ff->f = xmalloc(1,1); - run->term_in.fptr = ff->f; - } - } else { - reset_stream(run->term_out); - } - } else if (ftype == mp_filetype_error) { - reset_stream(run->error_out); - } else if (ftype == mp_filetype_log) { - reset_stream(run->log_out); - } else if (ftype == mp_filetype_postscript) { - mp_free_stream(&(run->ps_out)); - ff->f = xmalloc(1,1); - run->ps_out.fptr = ff->f; +static void *mplib_open_file (MP mp, const char *fname, const char *fmode, + int ftype) { + File *ff = xmalloc (1, sizeof (File)); + mp_run_data *run = mp_rundata (mp); + ff->f = NULL; + if (ftype == mp_filetype_terminal) { + if (fmode[0] == 'r') { + if (!ff->f) { + ff->f = xmalloc (1, 1); + run->term_in.fptr = ff->f; + } } else { - char realmode[3]; - char *f = (mp->find_file)(mp, fname, fmode, ftype); - if (f == NULL) - return NULL; - realmode[0] = *fmode; - realmode[1] = 'b'; - realmode[2] = 0; - ff->f = fopen(f, realmode); - free(f); - if ((fmode[0] == 'r') && (ff->f == NULL)) { - free(ff); - return NULL; - } + reset_stream (run->term_out); } - return ff; -} - -static int mplib_get_char(void *f, mp_run_data * run) -{ - int c; - if (f == run->term_in.fptr && run->term_in.data != NULL) { - if (run->term_in.size == 0) { - if (run->term_in.cur != NULL) { - run->term_in.cur = NULL; - } else { - xfree(run->term_in.data); - } - c = EOF; - } else { - run->term_in.size--; - c = *(run->term_in.cur)++; - } - } else { - c = fgetc(f); + } else if (ftype == mp_filetype_error) { + reset_stream (run->error_out); + } else if (ftype == mp_filetype_log) { + reset_stream (run->log_out); + } else if (ftype == mp_filetype_postscript) { + mp_free_stream (&(run->ps_out)); + ff->f = xmalloc (1, 1); + run->ps_out.fptr = ff->f; + } else { + char realmode[3]; + char *f = (mp->find_file) (mp, fname, fmode, ftype); + if (f == NULL) + return NULL; + realmode[0] = *fmode; + realmode[1] = 'b'; + realmode[2] = 0; + ff->f = fopen (f, realmode); + free (f); + if ((fmode[0] == 'r') && (ff->f == NULL)) { + free (ff); + return NULL; } - return c; + } + return ff; } - -static void mplib_unget_char(void *f, mp_run_data * run, int c) -{ - if (f == run->term_in.fptr && run->term_in.cur != NULL) { - run->term_in.size++; - run->term_in.cur--; +static int mplib_get_char (void *f, mp_run_data * run) { + int c; + if (f == run->term_in.fptr && run->term_in.data != NULL) { + if (run->term_in.size == 0) { + if (run->term_in.cur != NULL) { + run->term_in.cur = NULL; + } else { + xfree (run->term_in.data); + } + c = EOF; } else { - ungetc(c, f); + run->term_in.size--; + c = *(run->term_in.cur)++; } + } else { + c = fgetc (f); + } + return c; } - - -static char *mplib_read_ascii_file(MP mp, void *ff, size_t * size) -{ - char *s = NULL; - if (ff != NULL) { - int c; - size_t len = 0, lim = 128; - mp_run_data *run = mp_rundata(mp); - FILE *f = ((File *) ff)->f; - if (f == NULL) - return NULL; - *size = 0; - c = mplib_get_char(f, run); - if (c == EOF) - return NULL; - s = malloc(lim); - if (s == NULL) - return NULL; - while (c != EOF && c != '\n' && c != '\r') { - if (len >= (lim-1)) { - s = xrealloc(s, (lim + (lim >> 2)),1); - if (s == NULL) - return NULL; - lim += (lim >> 2); - } - s[len++] = (char)c; - c = mplib_get_char(f, run); - } - if (c == '\r') { - c = mplib_get_char(f, run); - if (c != EOF && c != '\n') - mplib_unget_char(f, run, c); - } - s[len] = 0; - *size = len; - } - return s; +static void mplib_unget_char (void *f, mp_run_data * run, int c) { + if (f == run->term_in.fptr && run->term_in.cur != NULL) { + run->term_in.size++; + run->term_in.cur--; + } else { + ungetc (c, f); + } } - -static void mp_append_string (MP mp, mp_stream *a,const char *b) { - size_t l = strlen(b); - if ((a->used+l)>=a->size) { - a->size += 256+(a->size)/5+l; - a->data = xrealloc(a->data,a->size,1); +static char *mplib_read_ascii_file (MP mp, void *ff, size_t * size) { + char *s = NULL; + if (ff != NULL) { + int c; + size_t len = 0, lim = 128; + mp_run_data *run = mp_rundata (mp); + FILE *f = ((File *) ff)->f; + if (f == NULL) + return NULL; + *size = 0; + c = mplib_get_char (f, run); + if (c == EOF) + return NULL; + s = malloc (lim); + if (s == NULL) + return NULL; + while (c != EOF && c != '\n' && c != '\r') { + if (len >= (lim - 1)) { + s = xrealloc (s, (lim + (lim >> 2)), 1); + if (s == NULL) + return NULL; + lim += (lim >> 2); + } + s[len++] = (char) c; + c = mplib_get_char (f, run); } - (void)strcpy(a->data+a->used,b); - a->used += l; -} - - -static void mplib_write_ascii_file(MP mp, void *ff, const char *s) -{ - if (ff != NULL) { - void *f = ((File *) ff)->f; - mp_run_data *run = mp_rundata(mp); - if (f != NULL) { - if (f == run->term_out.fptr) { - mp_append_string(mp,&(run->term_out), s); - } else if (f == run->error_out.fptr) { - mp_append_string(mp,&(run->error_out), s); - } else if (f == run->log_out.fptr) { - mp_append_string(mp,&(run->log_out), s); - } else if (f == run->ps_out.fptr) { - mp_append_string(mp,&(run->ps_out), s); - } else { - fprintf((FILE *) f, "%s", s); - } - } + if (c == '\r') { + c = mplib_get_char (f, run); + if (c != EOF && c != '\n') + mplib_unget_char (f, run, c); } + s[len] = 0; + *size = len; + } + return s; } - -static void mplib_read_binary_file(MP mp, void *ff, void **data, size_t * size) -{ - (void) mp; - if (ff != NULL) { - size_t len = 0; - FILE *f = ((File *) ff)->f; - if (f != NULL) - len = fread(*data, 1, *size, f); - *size = len; +static void mp_append_string (MP mp, mp_stream * a, const char *b) { + size_t l = strlen (b); + if ((a->used + l) >= a->size) { + a->size += 256 + (a->size) / 5 + l; + a->data = xrealloc (a->data, a->size, 1); + } + memcpy (a->data + a->used, b, l); + a->used += l; +} +static void mplib_write_ascii_file (MP mp, void *ff, const char *s) { + if (ff != NULL) { + void *f = ((File *) ff)->f; + mp_run_data *run = mp_rundata (mp); + if (f != NULL) { + if (f == run->term_out.fptr) { + mp_append_string (mp, &(run->term_out), s); + } else if (f == run->error_out.fptr) { + mp_append_string (mp, &(run->error_out), s); + } else if (f == run->log_out.fptr) { + mp_append_string (mp, &(run->log_out), s); + } else if (f == run->ps_out.fptr) { + mp_append_string (mp, &(run->ps_out), s); + } else { + fprintf ((FILE *) f, "%s", s); + } } + } } - -static void mplib_write_binary_file(MP mp, void *ff, void *s, size_t size) -{ - (void) mp; - if (ff != NULL) { - FILE *f = ((File *) ff)->f; - if (f != NULL) - (void)fwrite(s, size, 1, f); - } +static void mplib_read_binary_file (MP mp, void *ff, void **data, size_t * size) { + (void) mp; + if (ff != NULL) { + size_t len = 0; + FILE *f = ((File *) ff)->f; + if (f != NULL) + len = fread (*data, 1, *size, f); + *size = len; + } } - -static void mplib_close_file(MP mp, void *ff) -{ - if (ff != NULL) { - mp_run_data *run = mp_rundata(mp); - void *f = ((File *) ff)->f; - if (f != NULL) { - if (f != run->term_out.fptr - && f != run->error_out.fptr - && f != run->log_out.fptr - && f != run->ps_out.fptr - && f != run->term_in.fptr) { - fclose(f); - } - } - free(ff); +static void mplib_write_binary_file (MP mp, void *ff, void *s, size_t size) { + (void) mp; + if (ff != NULL) { + FILE *f = ((File *) ff)->f; + if (f != NULL) + (void) fwrite (s, size, 1, f); + } +} +static void mplib_close_file (MP mp, void *ff) { + if (ff != NULL) { + mp_run_data *run = mp_rundata (mp); + void *f = ((File *) ff)->f; + if (f != NULL) { + if (f != run->term_out.fptr + && f != run->error_out.fptr + && f != run->log_out.fptr + && f != run->ps_out.fptr && f != run->term_in.fptr) { + fclose (f); + } } + free (ff); + } } - -static int mplib_eof_file(MP mp, void *ff) -{ - if (ff != NULL) { - mp_run_data *run = mp_rundata(mp); - FILE *f = ((File *) ff)->f; - if (f == NULL) - return 1; - if (f == run->term_in.fptr && run->term_in.data != NULL) { - return (run->term_in.size == 0); - } - return feof(f); +static int mplib_eof_file (MP mp, void *ff) { + if (ff != NULL) { + mp_run_data *run = mp_rundata (mp); + FILE *f = ((File *) ff)->f; + if (f == NULL) + return 1; + if (f == run->term_in.fptr && run->term_in.data != NULL) { + return (run->term_in.size == 0); } - return 1; + return feof (f); + } + return 1; } - -static void mplib_flush_file(MP mp, void *ff) -{ - (void) mp; - (void) ff; - return; +static void mplib_flush_file (MP mp, void *ff) { + (void) mp; + (void) ff; + return; } - -static void mplib_shipout_backend(MP mp, int h) -{ - mp_edge_object *hh = mp_gr_export(mp, h); - if (hh) { - mp_run_data *run = mp_rundata(mp); - if (run->edges==NULL) { - run->edges = hh; - } else { - mp_edge_object *p = run->edges; - while (p->next!=NULL) { p = p->next; } - p->next = hh; - } +static void mplib_shipout_backend (MP mp, void *voidh) { + mp_node h = (mp_node) voidh; + mp_edge_object *hh = mp_gr_export (mp, h); + if (hh) { + mp_run_data *run = mp_rundata (mp); + if (run->edges == NULL) { + run->edges = hh; + } else { + mp_edge_object *p = run->edges; + while (p->next != NULL) { + p = p->next; + } + p->next = hh; } + } } @ This is where we fill them all in. @<Prepare function pointers for non-interactive use@>= { - mp->open_file = mplib_open_file; - mp->close_file = mplib_close_file; - mp->eof_file = mplib_eof_file; - mp->flush_file = mplib_flush_file; - mp->write_ascii_file = mplib_write_ascii_file; - mp->read_ascii_file = mplib_read_ascii_file; - mp->write_binary_file = mplib_write_binary_file; - mp->read_binary_file = mplib_read_binary_file; - mp->shipout_backend = mplib_shipout_backend; + mp->open_file = mplib_open_file; + mp->close_file = mplib_close_file; + mp->eof_file = mplib_eof_file; + mp->flush_file = mplib_flush_file; + mp->write_ascii_file = mplib_write_ascii_file; + mp->read_ascii_file = mplib_read_ascii_file; + mp->write_binary_file = mplib_write_binary_file; + mp->read_binary_file = mplib_read_binary_file; + mp->shipout_backend = mplib_shipout_backend; } + @ Perhaps this is the most important API function in the library. @<Exported function ...@>= -extern mp_run_data *mp_rundata (MP mp) ; +extern mp_run_data *mp_rundata (MP mp); @ @c -mp_run_data *mp_rundata (MP mp) { +mp_run_data *mp_rundata (MP mp) { return &(mp->run_data); } + @ @<Dealloc ...@>= -mp_free_stream(&(mp->run_data.term_in)); -mp_free_stream(&(mp->run_data.term_out)); -mp_free_stream(&(mp->run_data.log_out)); -mp_free_stream(&(mp->run_data.error_out)); -mp_free_stream(&(mp->run_data.ps_out)); +mp_free_stream (&(mp->run_data.term_in)); +mp_free_stream (&(mp->run_data.term_out)); +mp_free_stream (&(mp->run_data.log_out)); +mp_free_stream (&(mp->run_data.error_out)); +mp_free_stream (&(mp->run_data.ps_out)); @ @<Finish non-interactive use@>= -xfree(mp->term_out); -xfree(mp->term_in); -xfree(mp->err_out); +xfree (mp->term_out); +xfree (mp->term_in); +xfree (mp->err_out); @ @<Start non-interactive work@>= @<Initialize the output routines@>; -mp->input_ptr=0; mp->max_in_stack=0; -mp->in_open=0; mp->open_parens=0; mp->max_buf_stack=0; -mp->param_ptr=0; mp->max_param_stack=0; -start = loc = iindex = 0; mp->first = 0; -line=0; name=is_term; -mp->mpx_name[0]=absent; -mp->force_eof=false; -t_open_in; -mp->scanner_status=normal; -if (mp->mem_ident==NULL) { - if ( ! mp_load_mem_file(mp) ) { - (mp->close_file)(mp, mp->mem_file); - mp->history = mp_fatal_error_stop; - return mp->history; - } - (mp->close_file)(mp, mp->mem_file); -} -mp_fix_date_and_time(mp); -if (mp->random_seed==0) - mp->random_seed = (mp->internal[mp_time] / unity)+mp->internal[mp_day]; -mp_init_randoms(mp, mp->random_seed); +mp->input_ptr = 0; +mp->max_in_stack = file_bottom; +mp->in_open = file_bottom; +mp->open_parens = 0; +mp->max_buf_stack = 0; +mp->param_ptr = 0; +mp->max_param_stack = 0; +start = loc = 0; +iindex = file_bottom; +nloc = nstart = NULL; +mp->first = 0; +line = 0; +name = is_term; +mp->mpx_name[file_bottom] = absent; +mp->force_eof = false; +t_open_in; +mp->scanner_status = normal; +if (mp->mem_ident == NULL && !mp->ini_version) { + if (!mp_load_preload_file (mp)) { + mp->history = mp_fatal_error_stop; + return mp->history; + } +} +mp_fix_date_and_time (mp); +if (mp->random_seed == 0) + mp->random_seed = + (internal_value (mp_time) / unity) + internal_value (mp_day); +mp_init_randoms (mp, mp->random_seed); @<Initialize the print |selector|...@>; -mp_open_log_file(mp); -mp_set_job_id(mp); -mp_init_map_file(mp, mp->troff_mode); -mp->history=mp_spotless; /* ready to go! */ +mp_open_log_file (mp); +mp_set_job_id (mp); +mp_init_map_file (mp, mp->troff_mode); +mp->history = mp_spotless; /* ready to go! */ if (mp->troff_mode) { - mp->internal[mp_gtroffmode]=unity; - mp->internal[mp_prologues]=unity; + internal_value (mp_gtroffmode) = unity; + internal_value (mp_prologues) = unity; } @<Fix up |mp->internal[mp_job_name]|@>; -if ( mp->start_sym>0 ) { /* insert the `\&{everyjob}' symbol */ - mp->cur_sym=mp->start_sym; mp_back_input(mp); +if (mp->start_sym != NULL) { /* insert the `\&{everyjob}' symbol */ + mp->cur_sym = mp->start_sym; + mp_back_input (mp); } @ @c -int mp_execute (MP mp, const char *s, size_t l) { - mp_reset_stream(&(mp->run_data.term_out)); - mp_reset_stream(&(mp->run_data.log_out)); - mp_reset_stream(&(mp->run_data.error_out)); - mp_reset_stream(&(mp->run_data.ps_out)); +int mp_execute (MP mp, char *s, size_t l) { + mp_reset_stream (&(mp->run_data.term_out)); + mp_reset_stream (&(mp->run_data.log_out)); + mp_reset_stream (&(mp->run_data.error_out)); + mp_reset_stream (&(mp->run_data.ps_out)); if (mp->finished) { - return mp->history; + return mp->history; } else if (!mp->noninteractive) { - mp->history = mp_fatal_error_stop ; - return mp->history; + mp->history = mp_fatal_error_stop; + return mp->history; } - if (mp->history < mp_fatal_error_stop ) { - xfree(mp->jump_buf); - mp->jump_buf = malloc(sizeof(jmp_buf)); - if (mp->jump_buf == NULL || setjmp(*(mp->jump_buf)) != 0) { - return mp->history; + if (mp->history < mp_fatal_error_stop) { + xfree (mp->jump_buf); + mp->jump_buf = malloc (sizeof (jmp_buf)); + if (mp->jump_buf == NULL || setjmp (*(mp->jump_buf)) != 0) { + return mp->history; } - if (s==NULL) { /* this signals EOF */ - mp_final_cleanup(mp); /* prepare for death */ - mp_close_files_and_terminate(mp); + if (s == NULL) { /* this signals EOF */ + mp_final_cleanup (mp); /* prepare for death */ + mp_close_files_and_terminate (mp); return mp->history; - } - mp->tally=0; - mp->term_offset=0; mp->file_offset=0; + } + mp->tally = 0; + mp->term_offset = 0; + mp->file_offset = 0; /* Perhaps some sort of warning here when |data| is not * yet exhausted would be nice ... this happens after errors */ if (mp->run_data.term_in.data) - xfree(mp->run_data.term_in.data); - mp->run_data.term_in.data = xstrdup(s); + xfree (mp->run_data.term_in.data); + mp->run_data.term_in.data = xstrdup (s); mp->run_data.term_in.cur = mp->run_data.term_in.data; mp->run_data.term_in.size = l; if (mp->run_state == 0) { - mp->selector=term_only; - @<Start non-interactive work@>; + mp->selector = term_only; + @<Start non-interactive work@>; } - mp->run_state =1; - (void)mp_input_ln(mp,mp->term_in); - mp_firm_up_the_line(mp); - mp->buffer[limit]=xord('%'); - mp->first=(size_t)(limit+1); - loc=start; - do { - mp_do_statement(mp); - } while (mp->cur_cmd!=stop); - mp_final_cleanup(mp); - mp_close_files_and_terminate(mp); + mp->run_state = 1; + (void) mp_input_ln (mp, mp->term_in); + mp_firm_up_the_line (mp); + mp->buffer[limit] = xord ('%'); + mp->first = (size_t) (limit + 1); + loc = start; + do { + mp_do_statement (mp); + } while (mp->cur_cmd != stop); + mp_final_cleanup (mp); + mp_close_files_and_terminate (mp); } return mp->history; } + @ This function cleans up @c int mp_finish (MP mp) { int history = 0; if (mp->finished || mp->history >= mp_fatal_error_stop) { history = mp->history; - mp_free(mp); + mp_free (mp); return history; } - xfree(mp->jump_buf); - mp->jump_buf = malloc(sizeof(jmp_buf)); - if (mp->jump_buf == NULL || setjmp(*(mp->jump_buf)) != 0) { + xfree (mp->jump_buf); + mp->jump_buf = malloc (sizeof (jmp_buf)); + if (mp->jump_buf == NULL || setjmp (*(mp->jump_buf)) != 0) { history = mp->history; } else { history = mp->history; - mp_final_cleanup(mp); /* prepare for death */ + mp_final_cleanup (mp); /* prepare for death */ } - mp_close_files_and_terminate(mp); - mp_free(mp); + mp_close_files_and_terminate (mp); + mp_free (mp); return history; } + @ People may want to know the library version -@c -char * mp_metapost_version (void) { - return mp_strdup(metapost_version); +@c +char *mp_metapost_version (void) { + return mp_strdup (metapost_version); } + @ @<Exported function headers@>= int mp_run (MP mp); -int mp_execute (MP mp, const char *s, size_t l); +int mp_execute (MP mp, char *s, size_t l); int mp_finish (MP mp); -char * mp_metapost_version (void); +char *mp_metapost_version (void); @ @<Put each...@>= -mp_primitive(mp, "end",stop,0); -@:end_}{\&{end} primitive@> -mp_primitive(mp, "dump",stop,1); +mp_primitive (mp, "end", stop, 0); +@:end_}{\&{end} primitive@>; +mp_primitive (mp, "dump", stop, 1); @:dump_}{\&{dump} primitive@> + @ @<Cases of |print_cmd...@>= case stop: - if ( m==0 ) mp_print(mp, "end"); - else mp_print(mp, "dump"); - break; +if (mp->cur_mod == 0) + mp_print (mp, "end"); +else + mp_print (mp, "dump"); +break; -@* \[41] Commands. +@* Commands. Let's turn now to statements that are classified as ``commands'' because of their imperative nature. We'll begin with simple ones, so that it will be clear how to hook command processing into the |do_statement| routine; @@ -22995,109 +26753,138 @@ then we'll tackle the tougher commands. Here's one of the simplest: @<Cases of |do_statement|...@>= -case mp_random_seed: mp_do_random_seed(mp); break; +case mp_random_seed: +mp_do_random_seed (mp); +break; @ @<Declare action procedures for use by |do_statement|@>= -static void mp_do_random_seed (MP mp) ; - -@ @c void mp_do_random_seed (MP mp) { - mp_get_x_next(mp); - if ( mp->cur_cmd!=assignment ) { - mp_missing_err(mp, ":="); -@.Missing `:='@> - help1("Always say `randomseed:=<numeric expression>'."); - mp_back_error(mp); +static void mp_do_random_seed (MP mp); + +@ @c +void mp_do_random_seed (MP mp) { + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + mp_get_x_next (mp); + if (mp->cur_cmd != assignment) { + mp_missing_err (mp, ":="); +@.Missing `:='@>; + help1 ("Always say `randomseed:=<numeric expression>'."); + mp_back_error (mp); }; - mp_get_x_next(mp); mp_scan_expression(mp); - if ( mp->cur_type!=mp_known ) { - exp_err("Unknown value will be ignored"); -@.Unknown value...ignored@> - help2("Your expression was too random for me to handle,", - "so I won't change the random seed just now."); - mp_put_get_flush_error(mp, 0); + mp_get_x_next (mp); + mp_scan_expression (mp); + if (mp->cur_exp.type != mp_known) { + exp_err ("Unknown value will be ignored"); +@.Unknown value...ignored@>; + help2 ("Your expression was too random for me to handle,", + "so I won't change the random seed just now."); + mp_put_get_flush_error (mp, new_expr); } else { - @<Initialize the random seed to |cur_exp|@>; + @<Initialize the random seed to |cur_exp|@>; } } + @ @<Initialize the random seed to |cur_exp|@>= -{ - mp_init_randoms(mp, mp->cur_exp); - if ( mp->selector>=log_only && mp->selector<write_file) { - mp->old_setting=mp->selector; mp->selector=log_only; - mp_print_nl(mp, "{randomseed:="); - mp_print_scaled(mp, mp->cur_exp); - mp_print_char(mp, xord('}')); - mp_print_nl(mp, ""); mp->selector=mp->old_setting; +{ + mp_init_randoms (mp, cur_exp_value ()); + if (mp->selector >= log_only && mp->selector < write_file) { + mp->old_setting = mp->selector; + mp->selector = log_only; + mp_print_nl (mp, "{randomseed:="); + mp_print_scaled (mp, cur_exp_value ()); + mp_print_char (mp, xord ('}')); + mp_print_nl (mp, ""); + mp->selector = mp->old_setting; } } + @ And here's another simple one (somewhat different in flavor): @<Cases of |do_statement|...@>= -case mode_command: - mp_print_ln(mp); mp->interaction=mp->cur_mod; - @<Initialize the print |selector| based on |interaction|@>; - if ( mp->log_opened ) mp->selector=mp->selector+2; - mp_get_x_next(mp); - break; +case mode_command: +mp_print_ln (mp); +mp->interaction = mp->cur_mod; +@<Initialize the print |selector| based on |interaction|@>; +if (mp->log_opened) + mp->selector = mp->selector + 2; +mp_get_x_next (mp); +break; @ @<Put each...@>= -mp_primitive(mp, "batchmode",mode_command,mp_batch_mode); -@:mp_batch_mode_}{\&{batchmode} primitive@> -mp_primitive(mp, "nonstopmode",mode_command,mp_nonstop_mode); -@:mp_nonstop_mode_}{\&{nonstopmode} primitive@> -mp_primitive(mp, "scrollmode",mode_command,mp_scroll_mode); -@:mp_scroll_mode_}{\&{scrollmode} primitive@> -mp_primitive(mp, "errorstopmode",mode_command,mp_error_stop_mode); +mp_primitive (mp, "batchmode", mode_command, mp_batch_mode); +@:mp_batch_mode_}{\&{batchmode} primitive@>; +mp_primitive (mp, "nonstopmode", mode_command, mp_nonstop_mode); +@:mp_nonstop_mode_}{\&{nonstopmode} primitive@>; +mp_primitive (mp, "scrollmode", mode_command, mp_scroll_mode); +@:mp_scroll_mode_}{\&{scrollmode} primitive@>; +mp_primitive (mp, "errorstopmode", mode_command, mp_error_stop_mode); @:mp_error_stop_mode_}{\&{errorstopmode} primitive@> + @ @<Cases of |print_cmd_mod|...@>= -case mode_command: - switch (m) { - case mp_batch_mode: mp_print(mp, "batchmode"); break; - case mp_nonstop_mode: mp_print(mp, "nonstopmode"); break; - case mp_scroll_mode: mp_print(mp, "scrollmode"); break; - default: mp_print(mp, "errorstopmode"); break; - } +case mode_command: +switch (m) { +case mp_batch_mode: + mp_print (mp, "batchmode"); break; +case mp_nonstop_mode: + mp_print (mp, "nonstopmode"); + break; +case mp_scroll_mode: + mp_print (mp, "scrollmode"); + break; +default: + mp_print (mp, "errorstopmode"); + break; +} +break; @ The `\&{inner}' and `\&{outer}' commands are only slightly harder. @<Cases of |do_statement|...@>= -case protection_command: mp_do_protection(mp); break; +case protection_command: +mp_do_protection (mp); +break; @ @<Put each...@>= -mp_primitive(mp, "inner",protection_command,0); -@:inner_}{\&{inner} primitive@> -mp_primitive(mp, "outer",protection_command,1); +mp_primitive (mp, "inner", protection_command, 0); +@:inner_}{\&{inner} primitive@>; +mp_primitive (mp, "outer", protection_command, 1); @:outer_}{\&{outer} primitive@> + @ @<Cases of |print_cmd...@>= -case protection_command: - if ( m==0 ) mp_print(mp, "inner"); - else mp_print(mp, "outer"); - break; +case protection_command: +if (m == 0) + mp_print (mp, "inner"); +else + mp_print (mp, "outer"); +break; @ @<Declare action procedures for use by |do_statement|@>= -static void mp_do_protection (MP mp) ; +static void mp_do_protection (MP mp); -@ @c void mp_do_protection (MP mp) { - int m; /* 0 to unprotect, 1 to protect */ - halfword t; /* the |eq_type| before we change it */ - m=mp->cur_mod; - do { - mp_get_symbol(mp); t=eq_type(mp->cur_sym); - if ( m==0 ) { - if ( t>=outer_tag ) - eq_type(mp->cur_sym)=t-outer_tag; - } else if ( t<outer_tag ) { - eq_type(mp->cur_sym)=t+outer_tag; +@ @c +void mp_do_protection (MP mp) { + int m; /* 0 to unprotect, 1 to protect */ + halfword t; /* the |eq_type| before we change it */ + m = mp->cur_mod; + do { + mp_get_symbol (mp); + t = eq_type (mp->cur_sym); + if (m == 0) { + if (t >= outer_tag) + eq_type (mp->cur_sym) = t - outer_tag; + } else if (t < outer_tag) { + eq_type (mp->cur_sym) = t + outer_tag; } - mp_get_x_next(mp); - } while (mp->cur_cmd==comma); + mp_get_x_next (mp); + } while (mp->cur_cmd == comma); } + @ \MP\ never defines the tokens `\.(' and `\.)' to be primitives, but plain \MP\ begins with the declaration `\&{delimiters} \.{()}'. Such a declaration assigns the command code |left_delimiter| to `\.{(}' and @@ -23105,185 +26892,215 @@ declaration assigns the command code |left_delimiter| to `\.{(}' and hash address of its mate. @<Cases of |do_statement|...@>= -case delimiters: mp_def_delims(mp); break; +case delimiters: +mp_def_delims (mp); +break; @ @<Declare action procedures for use by |do_statement|@>= -static void mp_def_delims (MP mp) ; +static void mp_def_delims (MP mp); -@ @c void mp_def_delims (MP mp) { - pointer l_delim,r_delim; /* the new delimiter pair */ - mp_get_clear_symbol(mp); l_delim=mp->cur_sym; - mp_get_clear_symbol(mp); r_delim=mp->cur_sym; - eq_type(l_delim)=left_delimiter; equiv(l_delim)=r_delim; - eq_type(r_delim)=right_delimiter; equiv(r_delim)=l_delim; - mp_get_x_next(mp); +@ @c +void mp_def_delims (MP mp) { + mp_sym l_delim, r_delim; /* the new delimiter pair */ + mp_get_clear_symbol (mp); + l_delim = mp->cur_sym; + mp_get_clear_symbol (mp); + r_delim = mp->cur_sym; + eq_type (l_delim) = left_delimiter; + equiv_sym (l_delim) = r_delim; + eq_type (r_delim) = right_delimiter; + equiv_sym (r_delim) = l_delim; + mp_get_x_next (mp); } + @ Here is a procedure that is called when \MP\ has reached a point where some right delimiter is mandatory. @<Declarations@>= -static void mp_check_delimiter (MP mp,pointer l_delim, pointer r_delim); +static void mp_check_delimiter (MP mp, mp_sym l_delim, mp_sym r_delim); @ @c -void mp_check_delimiter (MP mp,pointer l_delim, pointer r_delim) { - if ( mp->cur_cmd==right_delimiter ) - if ( mp->cur_mod==l_delim ) +void mp_check_delimiter (MP mp, mp_sym l_delim, mp_sym r_delim) { + if (mp->cur_cmd == right_delimiter) + if (mp->cur_sym2 == l_delim) return; - if ( mp->cur_sym!=r_delim ) { - mp_missing_err(mp, str(text(r_delim))); -@.Missing `)'@> - help2("I found no right delimiter to match a left one. So I've", - "put one in, behind the scenes; this may fix the problem."); - mp_back_error(mp); - } else { - print_err("The token `"); mp_print_text(r_delim); -@.The token...delimiter@> - mp_print(mp, "' is no longer a right delimiter"); - help3("Strange: This token has lost its former meaning!", - "I'll read it as a right delimiter this time;", - "but watch out, I'll probably miss it later."); - mp_error(mp); + if (mp->cur_sym != r_delim) { + mp_missing_err (mp, mp_str (mp, text (r_delim))); +@.Missing `)'@>; + help2 ("I found no right delimiter to match a left one. So I've", + "put one in, behind the scenes; this may fix the problem."); + mp_back_error (mp); + } else { + print_err ("The token `"); + mp_print_text (r_delim); +@.The token...delimiter@>; + mp_print (mp, "' is no longer a right delimiter"); + help3 ("Strange: This token has lost its former meaning!", + "I'll read it as a right delimiter this time;", + "but watch out, I'll probably miss it later."); + mp_error (mp); } } + @ The next four commands save or change the values associated with tokens. @<Cases of |do_statement|...@>= -case save_command: - do { - mp_get_symbol(mp); mp_save_variable(mp, mp->cur_sym); mp_get_x_next(mp); - } while (mp->cur_cmd==comma); - break; -case interim_command: mp_do_interim(mp); break; -case let_command: mp_do_let(mp); break; -case new_internal: mp_do_new_internal(mp); break; +case save_command: +do { + mp_get_symbol (mp); + mp_save_variable (mp, mp->cur_sym); + mp_get_x_next (mp); +} while (mp->cur_cmd == comma); +break; +case interim_command: +mp_do_interim (mp); +break; +case let_command: +mp_do_let (mp); +break; +case new_internal: +mp_do_new_internal (mp); +break; @ @<Declare action procedures for use by |do_statement|@>= static void mp_do_statement (MP mp); static void mp_do_interim (MP mp); -@ @c void mp_do_interim (MP mp) { - mp_get_x_next(mp); - if ( mp->cur_cmd!=internal_quantity ) { - print_err("The token `"); -@.The token...quantity@> - if ( mp->cur_sym==0 ) mp_print(mp, "(%CAPSULE)"); - else mp_print_text(mp->cur_sym); - mp_print(mp, "' isn't an internal quantity"); - help1("Something like `tracingonline' should follow `interim'."); - mp_back_error(mp); - } else { - mp_save_internal(mp, mp->cur_mod); mp_back_input(mp); +@ @c +void mp_do_interim (MP mp) { + mp_get_x_next (mp); + if (mp->cur_cmd != internal_quantity) { + print_err ("The token `"); +@.The token...quantity@>; + if (mp->cur_sym == NULL) + mp_print (mp, "(%CAPSULE)"); + else + mp_print_text (mp->cur_sym); + mp_print (mp, "' isn't an internal quantity"); + help1 ("Something like `tracingonline' should follow `interim'."); + mp_back_error (mp); + } else { + mp_save_internal (mp, mp->cur_mod); + mp_back_input (mp); } - mp_do_statement(mp); + mp_do_statement (mp); } + @ The following procedure is careful not to undefine the left-hand symbol too soon, lest commands like `{\tt let x=x}' have a surprising effect. @<Declare action procedures for use by |do_statement|@>= -static void mp_do_let (MP mp) ; - -@ @c void mp_do_let (MP mp) { - pointer l; /* hash location of the left-hand symbol */ - mp_get_symbol(mp); l=mp->cur_sym; mp_get_x_next(mp); - if ( mp->cur_cmd!=equals ) if ( mp->cur_cmd!=assignment ) { - mp_missing_err(mp, "="); -@.Missing `='@> - help3("You should have said `let symbol = something'.", - "But don't worry; I'll pretend that an equals sign", - "was present. The next token I read will be `something'."); - mp_back_error(mp); - } - mp_get_symbol(mp); +static void mp_do_let (MP mp); + +@ @c +void mp_do_let (MP mp) { + mp_sym l; /* hash location of the left-hand symbol */ + mp_get_symbol (mp); + l = mp->cur_sym; + mp_get_x_next (mp); + if (mp->cur_cmd != equals && mp->cur_cmd != assignment) { + mp_missing_err (mp, "="); +@.Missing `='@>; + help3 ("You should have said `let symbol = something'.", + "But don't worry; I'll pretend that an equals sign", + "was present. The next token I read will be `something'."); + mp_back_error (mp); + } + mp_get_symbol (mp); switch (mp->cur_cmd) { - case defined_macro: case secondary_primary_macro: - case tertiary_secondary_macro: case expression_tertiary_macro: - add_mac_ref(mp->cur_mod); + case defined_macro: + case secondary_primary_macro: + case tertiary_secondary_macro: + case expression_tertiary_macro: + add_mac_ref (mp->cur_mod_node); break; - default: + default: break; } - mp_clear_symbol(mp, l,false); eq_type(l)=mp->cur_cmd; - if ( mp->cur_cmd==tag_token ) equiv(l)=null; - else equiv(l)=mp->cur_mod; - mp_get_x_next(mp); + mp_clear_symbol (mp, l, false); + eq_type (l) = mp->cur_cmd; + if (mp->cur_cmd == tag_token) + equiv (l) = 0; /* todo: this was |null| */ + else if (mp->cur_cmd == defined_macro || + mp->cur_cmd == secondary_primary_macro || + mp->cur_cmd == tertiary_secondary_macro || + mp->cur_cmd == expression_tertiary_macro) + equiv_node (l) = mp->cur_mod_node; + else if (mp->cur_cmd == left_delimiter || + mp->cur_cmd == right_delimiter) + equiv_sym (l) = mp->cur_sym2; + else + equiv (l) = mp->cur_mod; + mp_get_x_next (mp); } + @ @<Declarations@>= -static void mp_do_new_internal (MP mp) ; +static void mp_do_new_internal (MP mp); @ @<Internal library ...@>= void mp_grow_internals (MP mp, int l); @ @c void mp_grow_internals (MP mp, int l) { - scaled *internal; - char * *int_name; - int *int_type; + mp_internal *internal; int k; - if ( hash_end+l>max_halfword ) { - mp_confusion(mp, "out of memory space"); /* can't be reached */ - } - int_name = xmalloc ((l+1),sizeof(char *)); - int_type = xmalloc ((l+1),sizeof(int)); - internal = xmalloc ((l+1),sizeof(scaled)); - for (k=0;k<=l; k++ ) { - if (k<=mp->max_internal) { - internal[k]=mp->internal[k]; - int_name[k]=mp->int_name[k]; - int_type[k]=mp->int_type[k]; + if (l > max_halfword) { + mp_confusion (mp, "out of memory space"); /* can't be reached */ + } + internal = xmalloc ((l + 1), sizeof (mp_internal)); + for (k = 0; k <= l; k++) { + if (k <= mp->max_internal) { + memcpy (internal + k, mp->internal + k, sizeof (mp_internal)); } else { - internal[k]=0; - int_name[k]=NULL; - int_type[k]=0; + memset (internal + k, 0, sizeof (mp_internal)); } } - xfree(mp->internal); xfree(mp->int_name); xfree(mp->int_type); - mp->int_type = int_type; - mp->int_name = int_name; + xfree (mp->internal); mp->internal = internal; mp->max_internal = l; } - -void mp_do_new_internal (MP mp) { +void mp_do_new_internal (MP mp) { int the_type = mp_known; - mp_get_x_next(mp); - if (mp->cur_cmd==type_name && mp->cur_mod==mp_string_type) { - the_type = mp_string_type; + mp_get_x_next (mp); + if (mp->cur_cmd == type_name && mp->cur_mod == mp_string_type) { + the_type = mp_string_type; } else { - if (!(mp->cur_cmd==type_name && mp->cur_mod==mp_known)) { - mp_back_input(mp); - } + if (!(mp->cur_cmd == type_name && mp->cur_mod == mp_numeric_type)) { + mp_back_input (mp); + } } - do { - if ( mp->int_ptr==mp->max_internal ) { - mp_grow_internals(mp, (mp->max_internal + (mp->max_internal/4))); + do { + if (mp->int_ptr == mp->max_internal) { + mp_grow_internals (mp, (mp->max_internal + (mp->max_internal / 4))); } - mp_get_clear_symbol(mp); incr(mp->int_ptr); - eq_type(mp->cur_sym)=internal_quantity; - equiv(mp->cur_sym)=mp->int_ptr; - if(mp->int_name[mp->int_ptr]!=NULL) - xfree(mp->int_name[mp->int_ptr]); - mp->int_name[mp->int_ptr]=str(text(mp->cur_sym)); - if (the_type==mp_string_type) { - mp->internal[mp->int_ptr]=null_str; + mp_get_clear_symbol (mp); + incr (mp->int_ptr); + eq_type (mp->cur_sym) = internal_quantity; + equiv (mp->cur_sym) = mp->int_ptr; + if (internal_name (mp->int_ptr) != NULL) + xfree (internal_name (mp->int_ptr)); + internal_name (mp->int_ptr) = + mp_xstrdup (mp, mp_str (mp, text (mp->cur_sym))); + if (the_type == mp_string_type) { + internal_string (mp->int_ptr) = null_str; } else { - mp->internal[mp->int_ptr]=0; + internal_value (mp->int_ptr) = 0; } - mp->int_type[mp->int_ptr]=the_type; - mp_get_x_next(mp); - } while (mp->cur_cmd==comma); + internal_type (mp->int_ptr) = the_type; + mp_get_x_next (mp); + } while (mp->cur_cmd == comma); } + @ @<Dealloc variables@>= -for (k=0;k<=mp->max_internal;k++) { - xfree(mp->int_name[k]); +for (k = 0; k <= mp->max_internal; k++) { + xfree (internal_name (k)); } -xfree(mp->internal); -xfree(mp->int_name); -xfree(mp->int_type); +xfree (mp->internal); @ The various `\&{show}' commands are distinguished by modifier fields @@ -23296,246 +27113,315 @@ in the usual way. @d show_dependencies_code 4 /* show dependent variables in terms of independents */ @<Put each...@>= -mp_primitive(mp, "showtoken",show_command,show_token_code); -@:show_token_}{\&{showtoken} primitive@> -mp_primitive(mp, "showstats",show_command,show_stats_code); -@:show_stats_}{\&{showstats} primitive@> -mp_primitive(mp, "show",show_command,show_code); -@:show_}{\&{show} primitive@> -mp_primitive(mp, "showvariable",show_command,show_var_code); -@:show_var_}{\&{showvariable} primitive@> -mp_primitive(mp, "showdependencies",show_command,show_dependencies_code); +mp_primitive (mp, "showtoken", show_command, show_token_code); +@:show_token_}{\&{showtoken} primitive@>; +mp_primitive (mp, "showstats", show_command, show_stats_code); +@:show_stats_}{\&{showstats} primitive@>; +mp_primitive (mp, "show", show_command, show_code); +@:show_}{\&{show} primitive@>; +mp_primitive (mp, "showvariable", show_command, show_var_code); +@:show_var_}{\&{showvariable} primitive@>; +mp_primitive (mp, "showdependencies", show_command, show_dependencies_code); @:show_dependencies_}{\&{showdependencies} primitive@> + @ @<Cases of |print_cmd...@>= -case show_command: - switch (m) { - case show_token_code:mp_print(mp, "showtoken"); break; - case show_stats_code:mp_print(mp, "showstats"); break; - case show_code:mp_print(mp, "show"); break; - case show_var_code:mp_print(mp, "showvariable"); break; - default: mp_print(mp, "showdependencies"); break; - } +case show_command: +switch (m) { +case show_token_code: + mp_print (mp, "showtoken"); + break; +case show_stats_code: + mp_print (mp, "showstats"); + break; +case show_code: + mp_print (mp, "show"); break; +case show_var_code: + mp_print (mp, "showvariable"); + break; +default: + mp_print (mp, "showdependencies"); + break; +} +break; @ @<Cases of |do_statement|...@>= -case show_command:mp_do_show_whatever(mp); break; +case show_command: +mp_do_show_whatever (mp); +break; @ The value of |cur_mod| controls the |verbosity| in the |print_exp| routine: if it's |show_code|, complicated structures are abbreviated, otherwise they aren't. @<Declare action procedures for use by |do_statement|@>= -static void mp_do_show (MP mp) ; +static void mp_do_show (MP mp); -@ @c void mp_do_show (MP mp) { - do { - mp_get_x_next(mp); mp_scan_expression(mp); - mp_print_nl(mp, ">> "); -@.>>@> - mp_print_exp(mp, null,2); mp_flush_cur_exp(mp, 0); - } while (mp->cur_cmd==comma); +@ @c +void mp_do_show (MP mp) { + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + do { + mp_get_x_next (mp); + mp_scan_expression (mp); + mp_print_nl (mp, ">> "); +@.>>@>; + mp_print_exp (mp, NULL, 2); + mp_flush_cur_exp (mp, new_expr); + } while (mp->cur_cmd == comma); } + @ @<Declare action procedures for use by |do_statement|@>= -static void mp_disp_token (MP mp) ; +static void mp_disp_token (MP mp); -@ @c void mp_disp_token (MP mp) { - mp_print_nl(mp, "> "); -@.>\relax@> - if ( mp->cur_sym==0 ) { +@ @c +void mp_disp_token (MP mp) { + mp_print_nl (mp, "> "); +@.>\relax@>; + if (mp->cur_sym == NULL) { @<Show a numeric or string or capsule token@>; - } else { - mp_print_text(mp->cur_sym); mp_print_char(mp, xord('=')); - if ( eq_type(mp->cur_sym)>=outer_tag ) mp_print(mp, "(outer) "); - mp_print_cmd_mod(mp, mp->cur_cmd,mp->cur_mod); - if ( mp->cur_cmd==defined_macro ) { - mp_print_ln(mp); mp_show_macro(mp, mp->cur_mod,null,100000); - } /* this avoids recursion between |show_macro| and |print_cmd_mod| */ + } else { + mp_print_text (mp->cur_sym); + mp_print_char (mp, xord ('=')); + if (eq_type (mp->cur_sym) >= outer_tag) + mp_print (mp, "(outer) "); + mp_print_cmd_mod (mp, mp->cur_cmd, mp->cur_mod); + if (mp->cur_cmd == defined_macro) { + mp_print_ln (mp); + mp_show_macro (mp, mp->cur_mod_node, NULL, 100000); + } /* this avoids recursion between |show_macro| and |print_cmd_mod| */ @^recursion@> } } + @ @<Show a numeric or string or capsule token@>= -{ - if ( mp->cur_cmd==numeric_token ) { - mp_print_scaled(mp, mp->cur_mod); - } else if ( mp->cur_cmd==capsule_token ) { - mp_print_capsule(mp,mp->cur_mod); - } else { - mp_print_char(mp, xord('"')); - mp_print_str(mp, mp->cur_mod); mp_print_char(mp, xord('"')); - delete_str_ref(mp->cur_mod); +{ + if (mp->cur_cmd == numeric_token) { + mp_print_scaled (mp, mp->cur_mod); + } else if (mp->cur_cmd == capsule_token) { + mp_print_capsule (mp, mp->cur_mod_node); + } else { + mp_print_char (mp, xord ('"')); + mp_print_str (mp, mp->cur_mod_str); + mp_print_char (mp, xord ('"')); + delete_str_ref (mp->cur_mod_str); } } + @ The following cases of |print_cmd_mod| might arise in connection with |disp_token|, although they don't necessarily correspond to primitive tokens. @<Cases of |print_cmd_...@>= case left_delimiter: -case right_delimiter: - if ( c==left_delimiter ) mp_print(mp, "left"); - else mp_print(mp, "right"); - mp_print(mp, " delimiter that matches "); - mp_print_text(m); - break; +case right_delimiter: +if (c == left_delimiter) + mp_print (mp, "left"); +else + mp_print (mp, "right"); +#if 0 +mp_print (mp, " delimiter that matches "); +mp_print_text (m); +#else +mp_print (mp, " delimiter"); +#endif +break; case tag_token: - if ( m==null ) mp_print(mp, "tag"); - else mp_print(mp, "variable"); - break; -case defined_macro: - mp_print(mp, "macro:"); - break; -case secondary_primary_macro: -case tertiary_secondary_macro: -case expression_tertiary_macro: - mp_print_cmd_mod(mp, macro_def,c); - mp_print(mp, "'d macro:"); - mp_print_ln(mp); mp_show_token_list(mp, mp_link(mp_link(m)),null,1000,0); - break; +if (m == 0) /* todo: this was |null| */ + mp_print (mp, "tag"); +else + mp_print (mp, "variable"); +break; +case defined_macro: +mp_print (mp, "macro:"); +break; case repeat_loop: - mp_print(mp, "[repeat the loop]"); - break; +mp_print (mp, "[repeat the loop]"); +break; case internal_quantity: - mp_print(mp, mp->int_name[m]); - break; +mp_print (mp, internal_name (m)); +break; + @ @<Declare action procedures for use by |do_statement|@>= -static void mp_do_show_token (MP mp) ; +static void mp_do_show_token (MP mp); -@ @c void mp_do_show_token (MP mp) { - do { - get_t_next; mp_disp_token(mp); - mp_get_x_next(mp); - } while (mp->cur_cmd==comma); +@ @c +void mp_do_show_token (MP mp) { + do { + get_t_next (mp); + mp_disp_token (mp); + mp_get_x_next (mp); + } while (mp->cur_cmd == comma); } + @ @<Declare action procedures for use by |do_statement|@>= -static void mp_do_show_stats (MP mp) ; - -@ @c void mp_do_show_stats (MP mp) { - mp_print_nl(mp, "Memory usage "); -@.Memory usage...@> - mp_print_int(mp, mp->var_used); mp_print_char(mp, xord('&')); mp_print_int(mp, mp->dyn_used); - mp_print(mp, " ("); mp_print_int(mp, mp->hi_mem_min-mp->lo_mem_max-1); - mp_print(mp, " still untouched)"); mp_print_ln(mp); - mp_print_nl(mp, "String usage "); - mp_print_int(mp, mp->strs_in_use-mp->init_str_use); - mp_print_char(mp, xord('&')); mp_print_int(mp, mp->pool_in_use-mp->init_pool_ptr); - mp_print(mp, " ("); - mp_print_int(mp, mp->max_strings-1-mp->strs_used_up); mp_print_char(mp, xord('&')); - mp_print_int(mp, mp->pool_size-mp->pool_ptr); - mp_print(mp, " now untouched)"); mp_print_ln(mp); - mp_get_x_next(mp); +static void mp_do_show_stats (MP mp); + +@ @c +void mp_do_show_stats (MP mp) { + mp_print_nl (mp, "Memory usage "); +@.Memory usage...@>; + mp_print_int (mp, (integer) mp->var_used); + mp_print_ln (mp); + mp_print_nl (mp, "String usage "); + mp_print_int (mp, (int) mp->strs_in_use); + mp_print_char (mp, xord ('&')); + mp_print_int (mp, (int) mp->pool_in_use); + mp_print_ln (mp); + mp_get_x_next (mp); } + @ Here's a recursive procedure that gives an abbreviated account of a variable, for use by |do_show_var|. @<Declare action procedures for use by |do_statement|@>= -static void mp_disp_var (MP mp,pointer p) ; +static void mp_disp_var (MP mp, mp_node p); -@ @c void mp_disp_var (MP mp,pointer p) { - pointer q; /* traverses attributes and subscripts */ - int n; /* amount of macro text to show */ - if ( mp_type(p)==mp_structured ) { +@ @c +void mp_disp_var (MP mp, mp_node p) { + mp_node q; /* traverses attributes and subscripts */ + int n; /* amount of macro text to show */ + if (mp_type (p) == mp_structured) { @<Descend the structure@>; - } else if ( mp_type(p)>=mp_unsuffixed_macro ) { + } else if (mp_type (p) >= mp_unsuffixed_macro) { @<Display a variable macro@>; - } else if ( mp_type(p)!=undefined ){ - mp_print_nl(mp, ""); mp_print_variable_name(mp, p); - mp_print_char(mp, xord('=')); - mp_print_exp(mp, p,0); + } else if (mp_type (p) != undefined) { + mp_print_nl (mp, ""); + mp_print_variable_name (mp, p); + mp_print_char (mp, xord ('=')); + mp_print_exp (mp, p, 0); } } + @ @<Descend the structure@>= -{ - q=attr_head(p); - do { mp_disp_var(mp, q); q=mp_link(q); } while (q!=end_attr); - q=subscr_head(p); - while ( mp_name_type(q)==mp_subscr ) { - mp_disp_var(mp, q); q=mp_link(q); +{ + q = attr_head (p); + do { + mp_disp_var (mp, q); + q = mp_link (q); + } while (q != mp->end_attr); + q = subscr_head (p); + while (mp_name_type (q) == mp_subscr) { + mp_disp_var (mp, q); + q = mp_link (q); } } + @ @<Display a variable macro@>= -{ - mp_print_nl(mp, ""); mp_print_variable_name(mp, p); - if ( mp_type(p)>mp_unsuffixed_macro ) - mp_print(mp, "@@#"); /* |suffixed_macro| */ - mp_print(mp, "=macro:"); - if ( (int)mp->file_offset>=mp->max_print_line-20 ) n=5; - else n=mp->max_print_line-(int)mp->file_offset-15; - mp_show_macro(mp, value(p),null,n); +{ + mp_print_nl (mp, ""); + mp_print_variable_name (mp, p); + if (mp_type (p) > mp_unsuffixed_macro) + mp_print (mp, "@@#"); /* |suffixed_macro| */ + mp_print (mp, "=macro:"); + if ((int) mp->file_offset >= mp->max_print_line - 20) + n = 5; + else + n = mp->max_print_line - (int) mp->file_offset - 15; + mp_show_macro (mp, value_node (p), NULL, n); } + @ @<Declare action procedures for use by |do_statement|@>= -static void mp_do_show_var (MP mp) ; +static void mp_do_show_var (MP mp); -@ @c void mp_do_show_var (MP mp) { - do { - get_t_next; - if ( mp->cur_sym>0 ) if ( mp->cur_sym<=hash_end ) - if ( mp->cur_cmd==tag_token ) if ( mp->cur_mod!=null ) { - mp_disp_var(mp, mp->cur_mod); goto DONE; - } - mp_disp_token(mp); +@ @c +void mp_do_show_var (MP mp) { + do { + get_t_next (mp); + if (mp->cur_sym != NULL) + if (mp->cur_sym_mod == 0) + if (mp->cur_cmd == tag_token) + if (mp->cur_mod != 0) { + mp_disp_var (mp, mp->cur_mod_node); + goto DONE; + } + mp_disp_token (mp); DONE: - mp_get_x_next(mp); - } while (mp->cur_cmd==comma); + mp_get_x_next (mp); + } while (mp->cur_cmd == comma); } + @ @<Declare action procedures for use by |do_statement|@>= -static void mp_do_show_dependencies (MP mp) ; - -@ @c void mp_do_show_dependencies (MP mp) { - pointer p; /* link that runs through all dependencies */ - p=mp_link(dep_head); - while ( p!=dep_head ) { - if ( mp_interesting(mp, p) ) { - mp_print_nl(mp, ""); mp_print_variable_name(mp, p); - if ( mp_type(p)==mp_dependent ) mp_print_char(mp, xord('=')); - else mp_print(mp, " = "); /* extra spaces imply proto-dependency */ - mp_print_dependency(mp, dep_list(p),mp_type(p)); +static void mp_do_show_dependencies (MP mp); + +@ @c +void mp_do_show_dependencies (MP mp) { + mp_value_node p; /* link that runs through all dependencies */ + p = (mp_value_node) mp_link (mp->dep_head); + while (p != mp->dep_head) { + if (mp_interesting (mp, (mp_node) p)) { + mp_print_nl (mp, ""); + mp_print_variable_name (mp, (mp_node) p); + if (mp_type (p) == mp_dependent) + mp_print_char (mp, xord ('=')); + else + mp_print (mp, " = "); /* extra spaces imply proto-dependency */ + mp_print_dependency (mp, (mp_value_node) dep_list (p), mp_type (p)); } - p=dep_list(p); - while ( mp_info(p)!=null ) p=mp_link(p); - p=mp_link(p); + p = (mp_value_node) dep_list (p); + while (dep_info (p) != NULL) + p = (mp_value_node) mp_link (p); + p = (mp_value_node) mp_link (p); } - mp_get_x_next(mp); + mp_get_x_next (mp); } + @ Finally we are ready for the procedure that governs all of the show commands. @<Declare action procedures for use by |do_statement|@>= -static void mp_do_show_whatever (MP mp) ; +static void mp_do_show_whatever (MP mp); -@ @c void mp_do_show_whatever (MP mp) { - if ( mp->interaction==mp_error_stop_mode ) wake_up_terminal; +@ @c +void mp_do_show_whatever (MP mp) { + if (mp->interaction == mp_error_stop_mode) + wake_up_terminal; switch (mp->cur_mod) { - case show_token_code:mp_do_show_token(mp); break; - case show_stats_code:mp_do_show_stats(mp); break; - case show_code:mp_do_show(mp); break; - case show_var_code:mp_do_show_var(mp); break; - case show_dependencies_code:mp_do_show_dependencies(mp); break; - } /* there are no other cases */ - if ( mp->internal[mp_showstopping]>0 ){ - print_err("OK"); -@.OK@> - if ( mp->interaction<mp_error_stop_mode ) { - help0; decr(mp->error_count); + case show_token_code: + mp_do_show_token (mp); + break; + case show_stats_code: + mp_do_show_stats (mp); + break; + case show_code: + mp_do_show (mp); + break; + case show_var_code: + mp_do_show_var (mp); + break; + case show_dependencies_code: + mp_do_show_dependencies (mp); + break; + } /* there are no other cases */ + if (internal_value (mp_showstopping) > 0) { + print_err ("OK"); +@.OK@>; + if (mp->interaction < mp_error_stop_mode) { + help0; + decr (mp->error_count); } else { - help1("This isn't an error message; I'm just showing something."); + help1 ("This isn't an error message; I'm just showing something."); } - if ( mp->cur_cmd==semicolon ) mp_error(mp); - else mp_put_get_error(mp); + if (mp->cur_cmd == semicolon) + mp_error (mp); + else + mp_put_get_error (mp); } } + @ The `\&{addto}' command needs the following additional primitives: @d double_path_code 0 /* command modifier for `\&{doublepath}' */ @@ -23548,49 +27434,62 @@ static void mp_do_show_whatever (MP mp) ; @d with_mp_post_script 13 @<Put each...@>= -mp_primitive(mp, "doublepath",thing_to_add,double_path_code); -@:double_path_}{\&{doublepath} primitive@> -mp_primitive(mp, "contour",thing_to_add,contour_code); -@:contour_}{\&{contour} primitive@> -mp_primitive(mp, "also",thing_to_add,also_code); -@:also_}{\&{also} primitive@> -mp_primitive(mp, "withpen",with_option,mp_pen_type); -@:with_pen_}{\&{withpen} primitive@> -mp_primitive(mp, "dashed",with_option,mp_picture_type); -@:dashed_}{\&{dashed} primitive@> -mp_primitive(mp, "withprescript",with_option,with_mp_pre_script); -@:with_mp_pre_script_}{\&{withprescript} primitive@> -mp_primitive(mp, "withpostscript",with_option,with_mp_post_script); -@:with_mp_post_script_}{\&{withpostscript} primitive@> -mp_primitive(mp, "withoutcolor",with_option,mp_no_model); -@:with_color_}{\&{withoutcolor} primitive@> -mp_primitive(mp, "withgreyscale",with_option,mp_grey_model); -@:with_color_}{\&{withgreyscale} primitive@> -mp_primitive(mp, "withcolor",with_option,mp_uninitialized_model); +mp_primitive (mp, "doublepath", thing_to_add, double_path_code); +@:double_path_}{\&{doublepath} primitive@>; +mp_primitive (mp, "contour", thing_to_add, contour_code); +@:contour_}{\&{contour} primitive@>; +mp_primitive (mp, "also", thing_to_add, also_code); +@:also_}{\&{also} primitive@>; +mp_primitive (mp, "withpen", with_option, mp_pen_type); +@:with_pen_}{\&{withpen} primitive@>; +mp_primitive (mp, "dashed", with_option, mp_picture_type); +@:dashed_}{\&{dashed} primitive@>; +mp_primitive (mp, "withprescript", with_option, with_mp_pre_script); +@:with_mp_pre_script_}{\&{withprescript} primitive@>; +mp_primitive (mp, "withpostscript", with_option, with_mp_post_script); +@:with_mp_post_script_}{\&{withpostscript} primitive@>; +mp_primitive (mp, "withoutcolor", with_option, mp_no_model); +@:with_color_}{\&{withoutcolor} primitive@>; +mp_primitive (mp, "withgreyscale", with_option, mp_grey_model); +@:with_color_}{\&{withgreyscale} primitive@>; +mp_primitive (mp, "withcolor", with_option, mp_uninitialized_model); @:with_color_}{\&{withcolor} primitive@> /* \&{withrgbcolor} is an alias for \&{withcolor} */ -mp_primitive(mp, "withrgbcolor",with_option,mp_rgb_model); -@:with_color_}{\&{withrgbcolor} primitive@> -mp_primitive(mp, "withcmykcolor",with_option,mp_cmyk_model); + mp_primitive (mp, "withrgbcolor", with_option, mp_rgb_model); +@:with_color_}{\&{withrgbcolor} primitive@>; +mp_primitive (mp, "withcmykcolor", with_option, mp_cmyk_model); @:with_color_}{\&{withcmykcolor} primitive@> + @ @<Cases of |print_cmd...@>= case thing_to_add: - if ( m==contour_code ) mp_print(mp, "contour"); - else if ( m==double_path_code ) mp_print(mp, "doublepath"); - else mp_print(mp, "also"); - break; +if (m == contour_code) + mp_print (mp, "contour"); +else if (m == double_path_code) + mp_print (mp, "doublepath"); +else + mp_print (mp, "also"); +break; case with_option: - if ( m==mp_pen_type ) mp_print(mp, "withpen"); - else if ( m==with_mp_pre_script ) mp_print(mp, "withprescript"); - else if ( m==with_mp_post_script ) mp_print(mp, "withpostscript"); - else if ( m==mp_no_model ) mp_print(mp, "withoutcolor"); - else if ( m==mp_rgb_model ) mp_print(mp, "withrgbcolor"); - else if ( m==mp_uninitialized_model ) mp_print(mp, "withcolor"); - else if ( m==mp_cmyk_model ) mp_print(mp, "withcmykcolor"); - else if ( m==mp_grey_model ) mp_print(mp, "withgreyscale"); - else mp_print(mp, "dashed"); - break; +if (m == mp_pen_type) + mp_print (mp, "withpen"); +else if (m == with_mp_pre_script) + mp_print (mp, "withprescript"); +else if (m == with_mp_post_script) + mp_print (mp, "withpostscript"); +else if (m == mp_no_model) + mp_print (mp, "withoutcolor"); +else if (m == mp_rgb_model) + mp_print (mp, "withrgbcolor"); +else if (m == mp_uninitialized_model) + mp_print (mp, "withcolor"); +else if (m == mp_cmyk_model) + mp_print (mp, "withcmykcolor"); +else if (m == mp_grey_model) + mp_print (mp, "withgreyscale"); +else + mp_print (mp, "dashed"); +break; @ The |scan_with_list| procedure parses a $\langle$with list$\rangle$ and updates the list of graphical objects starting at |p|. Each $\langle$with @@ -23598,119 +27497,153 @@ clause$\rangle$ updates all graphical objects whose |type| is compatible. Other objects are ignored. @<Declare action procedures for use by |do_statement|@>= -static void mp_scan_with_list (MP mp,pointer p) ; +static void mp_scan_with_list (MP mp, mp_node p); -@ @c void mp_scan_with_list (MP mp,pointer p) { - quarterword t; /* |cur_mod| of the |with_option| (should match |cur_type|) */ - pointer q; /* for list manipulation */ +@ @c +void mp_scan_with_list (MP mp, mp_node p) { + mp_variable_type t; /* |cur_mod| of the |with_option| (should match |cur_type|) */ + mp_node q; /* for list manipulation */ unsigned old_setting; /* saved |selector| setting */ - pointer k; /* for finding the near-last item in a list */ + mp_node k; /* for finding the near-last item in a list */ str_number s; /* for string cleanup after combining */ - pointer cp,pp,dp,ap,bp; - /* objects being updated; |void| initially; |null| to suppress update */ - cp=mp_void; pp=mp_void; dp=mp_void; ap=mp_void; bp=mp_void; - k=0; - while ( mp->cur_cmd==with_option ){ - t=(quarterword)mp->cur_mod; - mp_get_x_next(mp); - if ( t!=mp_no_model ) mp_scan_expression(mp); - if (((t==with_mp_pre_script)&&(mp->cur_type!=mp_string_type))|| - ((t==with_mp_post_script)&&(mp->cur_type!=mp_string_type))|| - ((t==mp_uninitialized_model)&& - ((mp->cur_type!=mp_cmykcolor_type)&&(mp->cur_type!=mp_color_type) - &&(mp->cur_type!=mp_known)&&(mp->cur_type!=mp_boolean_type)))|| - ((t==mp_cmyk_model)&&(mp->cur_type!=mp_cmykcolor_type))|| - ((t==mp_rgb_model)&&(mp->cur_type!=mp_color_type))|| - ((t==mp_grey_model)&&(mp->cur_type!=mp_known))|| - ((t==mp_pen_type)&&(mp->cur_type!=t))|| - ((t==mp_picture_type)&&(mp->cur_type!=t)) ) { + mp_value new_expr; + mp_node cp, pp, dp, ap, bp; + /* objects being updated; |void| initially; |NULL| to suppress update */ + cp = MP_VOID; + pp = MP_VOID; + dp = MP_VOID; + ap = MP_VOID; + bp = MP_VOID; + k = 0; + memset(&new_expr,0,sizeof(mp_value)); + while (mp->cur_cmd == with_option) { + /* todo this is not very nice: the color models have their own enumeration */ + t = (mp_variable_type) mp->cur_mod; + mp_get_x_next (mp); + if (t != (mp_variable_type) mp_no_model) + mp_scan_expression (mp); + if (((t == with_mp_pre_script) && (mp->cur_exp.type != mp_string_type)) || + ((t == with_mp_post_script) && (mp->cur_exp.type != mp_string_type)) || + ((t == (mp_variable_type) mp_uninitialized_model) && + ((mp->cur_exp.type != mp_cmykcolor_type) + && (mp->cur_exp.type != mp_color_type) + && (mp->cur_exp.type != mp_known) + && (mp->cur_exp.type != mp_boolean_type))) || ((t == (mp_variable_type) mp_cmyk_model) + && (mp->cur_exp.type != + mp_cmykcolor_type)) + || ((t == (mp_variable_type) mp_rgb_model) && (mp->cur_exp.type != mp_color_type)) + || ((t == (mp_variable_type) mp_grey_model) && (mp->cur_exp.type != mp_known)) + || ((t == (mp_variable_type) mp_pen_type) && (mp->cur_exp.type != t)) + || ((t == (mp_variable_type) mp_picture_type) && (mp->cur_exp.type != t))) { @<Complain about improper type@>; - } else if ( t==mp_uninitialized_model ) { - if ( cp==mp_void ) @<Make |cp| a colored object in object list~|p|@>; - if ( cp!=null ) + } else if (t == (mp_variable_type) mp_uninitialized_model) { + if (cp == MP_VOID) + @<Make |cp| a colored object in object list~|p|@>; + if (cp != NULL) @<Transfer a color from the current expression to object~|cp|@>; - mp_flush_cur_exp(mp, 0); - } else if ( t==mp_rgb_model ) { - if ( cp==mp_void ) @<Make |cp| a colored object in object list~|p|@>; - if ( cp!=null ) + mp_flush_cur_exp (mp, new_expr); + } else if (t == (mp_variable_type) mp_rgb_model) { + if (cp == MP_VOID) + @<Make |cp| a colored object in object list~|p|@>; + if (cp != NULL) @<Transfer a rgbcolor from the current expression to object~|cp|@>; - mp_flush_cur_exp(mp, 0); - } else if ( t==mp_cmyk_model ) { - if ( cp==mp_void ) @<Make |cp| a colored object in object list~|p|@>; - if ( cp!=null ) + mp_flush_cur_exp (mp, new_expr); + } else if (t == (mp_variable_type) mp_cmyk_model) { + if (cp == MP_VOID) + @<Make |cp| a colored object in object list~|p|@>; + if (cp != NULL) @<Transfer a cmykcolor from the current expression to object~|cp|@>; - mp_flush_cur_exp(mp, 0); - } else if ( t==mp_grey_model ) { - if ( cp==mp_void ) @<Make |cp| a colored object in object list~|p|@>; - if ( cp!=null ) + mp_flush_cur_exp (mp, new_expr); + } else if (t == (mp_variable_type) mp_grey_model) { + if (cp == MP_VOID) + @<Make |cp| a colored object in object list~|p|@>; + if (cp != NULL) @<Transfer a greyscale from the current expression to object~|cp|@>; - mp_flush_cur_exp(mp, 0); - } else if ( t==mp_no_model ) { - if ( cp==mp_void ) @<Make |cp| a colored object in object list~|p|@>; - if ( cp!=null ) + mp_flush_cur_exp (mp, new_expr); + } else if (t == (mp_variable_type) mp_no_model) { + if (cp == MP_VOID) + @<Make |cp| a colored object in object list~|p|@>; + if (cp != NULL) @<Transfer a noncolor from the current expression to object~|cp|@>; - } else if ( t==mp_pen_type ) { - if ( pp==mp_void ) @<Make |pp| an object in list~|p| that needs a pen@>; - if ( pp!=null ) { - if ( mp_pen_p(pp)!=null ) mp_toss_knot_list(mp, mp_pen_p(pp)); - mp_pen_p(pp)=mp->cur_exp; mp->cur_type=mp_vacuous; + } else if (t == mp_pen_type) { + if (pp == MP_VOID) + @<Make |pp| an object in list~|p| that needs a pen@>; + if (pp != NULL) { + switch (mp_type (pp)) { + case mp_fill_node_type: + if (mp_pen_p ((mp_fill_node) pp) != NULL) + mp_toss_knot_list (mp, mp_pen_p ((mp_fill_node) pp)); + mp_pen_p ((mp_fill_node) pp) = cur_exp_knot (); + break; + case mp_stroked_node_type: + if (mp_pen_p ((mp_stroked_node) pp) != NULL) + mp_toss_knot_list (mp, mp_pen_p ((mp_stroked_node) pp)); + mp_pen_p ((mp_stroked_node) pp) = cur_exp_knot (); + break; + default: + assert (0); + break; + } + mp->cur_exp.type = mp_vacuous; } - } else if ( t==with_mp_pre_script ) { - if ( ap==mp_void ) - ap=p; - while ( (ap!=null)&&(! has_color(ap)) ) - ap=mp_link(ap); - if ( ap!=null ) { - if ( mp_pre_script(ap)!=null ) { /* build a new,combined string */ - s=mp_pre_script(ap); - old_setting=mp->selector; - mp->selector=new_string; - str_room(length(mp_pre_script(ap))+length(mp->cur_exp)+2); - mp_print_str(mp, mp->cur_exp); - append_char(13); /* a forced \ps\ newline */ - mp_print_str(mp, mp_pre_script(ap)); - mp_pre_script(ap)=mp_make_string(mp); - delete_str_ref(s); - mp->selector=old_setting; + } else if (t == with_mp_pre_script) { + if (ap == MP_VOID) + ap = p; + while ((ap != NULL) && (!has_color (ap))) + ap = mp_link (ap); + if (ap != NULL) { + if (mp_pre_script (ap) != NULL) { /* build a new,combined string */ + s = mp_pre_script (ap); + old_setting = mp->selector; + mp->selector = new_string; + str_room (length (mp_pre_script (ap)) + length (cur_exp_str ()) + 2); + mp_print_str (mp, cur_exp_str ()); + append_char (13); /* a forced \ps\ newline */ + mp_print_str (mp, mp_pre_script (ap)); + mp_pre_script (ap) = mp_make_string (mp); + delete_str_ref (s); + mp->selector = old_setting; } else { - mp_pre_script(ap)=mp->cur_exp; + mp_pre_script (ap) = cur_exp_str (); } - mp->cur_type=mp_vacuous; + mp->cur_exp.type = mp_vacuous; } - } else if ( t==with_mp_post_script ) { - if ( bp==mp_void ) - k=p; - bp=k; - while ( mp_link(k)!=null ) { - k=mp_link(k); - if ( has_color(k) ) bp=k; + } else if (t == with_mp_post_script) { + if (bp == MP_VOID) + k = p; + bp = k; + while (mp_link (k) != NULL) { + k = mp_link (k); + if (has_color (k)) + bp = k; } - if ( bp!=null ) { - if ( mp_post_script(bp)!=null ) { - s=mp_post_script(bp); - old_setting=mp->selector; - mp->selector=new_string; - str_room(length(mp_post_script(bp))+length(mp->cur_exp)+2); - mp_print_str(mp, mp_post_script(bp)); - append_char(13); /* a forced \ps\ newline */ - mp_print_str(mp, mp->cur_exp); - mp_post_script(bp)=mp_make_string(mp); - delete_str_ref(s); - mp->selector=old_setting; - } else { - mp_post_script(bp)=mp->cur_exp; - } - mp->cur_type=mp_vacuous; - } - } else { - if ( dp==mp_void ) { + if (bp != NULL) { + if (mp_post_script (bp) != NULL) { + s = mp_post_script (bp); + old_setting = mp->selector; + mp->selector = new_string; + str_room (length (mp_post_script (bp)) + length (cur_exp_str ()) + 2); + mp_print_str (mp, mp_post_script (bp)); + append_char (13); /* a forced \ps\ newline */ + mp_print_str (mp, cur_exp_str ()); + mp_post_script (bp) = mp_make_string (mp); + delete_str_ref (s); + mp->selector = old_setting; + } else { + mp_post_script (bp) = cur_exp_str (); + } + mp->cur_exp.type = mp_vacuous; + } + } else { + if (dp == MP_VOID) { @<Make |dp| a stroked node in list~|p|@>; } - if ( dp!=null ) { - if ( mp_dash_p(dp)!=null ) delete_edge_ref(mp_dash_p(dp)); - mp_dash_p(dp)=mp_make_dashes(mp, mp->cur_exp); - dash_scale(dp)=unity; - mp->cur_type=mp_vacuous; + if (dp != NULL) { + if (mp_dash_p (dp) != NULL) + delete_edge_ref (mp_dash_p (dp)); + mp_dash_p (dp) = mp_make_dashes (mp, cur_exp_node ()); + dash_scale (dp) = unity; + mp->cur_exp.type = mp_vacuous; } } } @@ -23718,228 +27651,307 @@ static void mp_scan_with_list (MP mp,pointer p) ; of the list@>; } + @ @<Complain about improper type@>= -{ exp_err("Improper type"); -@.Improper type@> -help2("Next time say `withpen <known pen expression>';", - "I'll ignore the bad `with' clause and look for another."); -if ( t==with_mp_pre_script ) - mp->help_line[1]="Next time say `withprescript <known string expression>';"; -else if ( t==with_mp_post_script ) - mp->help_line[1]="Next time say `withpostscript <known string expression>';"; -else if ( t==mp_picture_type ) - mp->help_line[1]="Next time say `dashed <known picture expression>';"; -else if ( t==mp_uninitialized_model ) - mp->help_line[1]="Next time say `withcolor <known color expression>';"; -else if ( t==mp_rgb_model ) - mp->help_line[1]="Next time say `withrgbcolor <known color expression>';"; -else if ( t==mp_cmyk_model ) - mp->help_line[1]="Next time say `withcmykcolor <known cmykcolor expression>';"; -else if ( t==mp_grey_model ) - mp->help_line[1]="Next time say `withgreyscale <known numeric expression>';";; -mp_put_get_flush_error(mp, 0); +{ + exp_err ("Improper type"); +@.Improper type@>; + help2 ("Next time say `withpen <known pen expression>';", + "I'll ignore the bad `with' clause and look for another."); + if (t == with_mp_pre_script) + mp->help_line[1] = + "Next time say `withprescript <known string expression>';"; + else if (t == with_mp_post_script) + mp->help_line[1] = + "Next time say `withpostscript <known string expression>';"; + else if (t == mp_picture_type) + mp->help_line[1] = "Next time say `dashed <known picture expression>';"; + else if (t == (mp_variable_type) mp_uninitialized_model) + mp->help_line[1] = "Next time say `withcolor <known color expression>';"; + else if (t == (mp_variable_type) mp_rgb_model) + mp->help_line[1] = "Next time say `withrgbcolor <known color expression>';"; + else if (t == (mp_variable_type) mp_cmyk_model) + mp->help_line[1] = + "Next time say `withcmykcolor <known cmykcolor expression>';"; + else if (t == (mp_variable_type) mp_grey_model) + mp->help_line[1] = + "Next time say `withgreyscale <known numeric expression>';";; + mp_put_get_flush_error (mp, new_expr); } + @ Forcing the color to be between |0| and |unity| here guarantees that no picture will ever contain a color outside the legal range for \ps\ graphics. @<Transfer a color from the current expression to object~|cp|@>= -{ if ( mp->cur_type==mp_color_type ) - @<Transfer a rgbcolor from the current expression to object~|cp|@> -else if ( mp->cur_type==mp_cmykcolor_type ) - @<Transfer a cmykcolor from the current expression to object~|cp|@> -else if ( mp->cur_type==mp_known ) - @<Transfer a greyscale from the current expression to object~|cp|@> -else if ( mp->cur_exp==false_code ) - @<Transfer a noncolor from the current expression to object~|cp|@> -else if ( mp->cur_exp==true_code ) - @<Transfer no color from the current expression to object~|cp|@>; +{ + if (mp->cur_exp.type == mp_color_type) { + @<Transfer a rgbcolor from the current expression to object~|cp|@>; + } else if (mp->cur_exp.type == mp_cmykcolor_type) { + @<Transfer a cmykcolor from the current expression to object~|cp|@>; + } else if (mp->cur_exp.type == mp_known) { + @<Transfer a greyscale from the current expression to object~|cp|@>; + } else if (cur_exp_value () == false_code) { + @<Transfer a noncolor from the current expression to object~|cp|@>; + } else if (cur_exp_value () == true_code) { + @<Transfer no color from the current expression to object~|cp|@>; + } } + @ @<Transfer a rgbcolor from the current expression to object~|cp|@>= -{ q=value(mp->cur_exp); -cyan_val(cp)=0; -magenta_val(cp)=0; -yellow_val(cp)=0; -black_val(cp)=0; -red_val(cp)=value(red_part_loc(q)); -green_val(cp)=value(green_part_loc(q)); -blue_val(cp)=value(blue_part_loc(q)); -mp_color_model(cp)=mp_rgb_model; -if ( red_val(cp)<0 ) red_val(cp)=0; -if ( green_val(cp)<0 ) green_val(cp)=0; -if ( blue_val(cp)<0 ) blue_val(cp)=0; -if ( red_val(cp)>unity ) red_val(cp)=unity; -if ( green_val(cp)>unity ) green_val(cp)=unity; -if ( blue_val(cp)>unity ) blue_val(cp)=unity; +{ + q = value_node (cur_exp_node ()); + cyan_val (cp) = 0; + magenta_val (cp) = 0; + yellow_val (cp) = 0; + black_val (cp) = 0; + red_val (cp) = value (red_part_loc (q)); + green_val (cp) = value (green_part_loc (q)); + blue_val (cp) = value (blue_part_loc (q)); + mp_color_model (cp) = mp_rgb_model; + if (red_val (cp) < 0) + red_val (cp) = 0; + if (green_val (cp) < 0) + green_val (cp) = 0; + if (blue_val (cp) < 0) + blue_val (cp) = 0; + if (red_val (cp) > unity) + red_val (cp) = unity; + if (green_val (cp) > unity) + green_val (cp) = unity; + if (blue_val (cp) > unity) + blue_val (cp) = unity; } + @ @<Transfer a cmykcolor from the current expression to object~|cp|@>= -{ q=value(mp->cur_exp); -cyan_val(cp)=value(cyan_part_loc(q)); -magenta_val(cp)=value(magenta_part_loc(q)); -yellow_val(cp)=value(yellow_part_loc(q)); -black_val(cp)=value(black_part_loc(q)); -mp_color_model(cp)=mp_cmyk_model; -if ( cyan_val(cp)<0 ) cyan_val(cp)=0; -if ( magenta_val(cp)<0 ) magenta_val(cp)=0; -if ( yellow_val(cp)<0 ) yellow_val(cp)=0; -if ( black_val(cp)<0 ) black_val(cp)=0; -if ( cyan_val(cp)>unity ) cyan_val(cp)=unity; -if ( magenta_val(cp)>unity ) magenta_val(cp)=unity; -if ( yellow_val(cp)>unity ) yellow_val(cp)=unity; -if ( black_val(cp)>unity ) black_val(cp)=unity; +{ + q = value_node (cur_exp_node ()); + cyan_val (cp) = value (cyan_part_loc (q)); + magenta_val (cp) = value (magenta_part_loc (q)); + yellow_val (cp) = value (yellow_part_loc (q)); + black_val (cp) = value (black_part_loc (q)); + mp_color_model (cp) = mp_cmyk_model; + if (cyan_val (cp) < 0) + cyan_val (cp) = 0; + if (magenta_val (cp) < 0) + magenta_val (cp) = 0; + if (yellow_val (cp) < 0) + yellow_val (cp) = 0; + if (black_val (cp) < 0) + black_val (cp) = 0; + if (cyan_val (cp) > unity) + cyan_val (cp) = unity; + if (magenta_val (cp) > unity) + magenta_val (cp) = unity; + if (yellow_val (cp) > unity) + yellow_val (cp) = unity; + if (black_val (cp) > unity) + black_val (cp) = unity; } + @ @<Transfer a greyscale from the current expression to object~|cp|@>= -{ q=mp->cur_exp; -cyan_val(cp)=0; -magenta_val(cp)=0; -yellow_val(cp)=0; -black_val(cp)=0; -grey_val(cp)=q; -mp_color_model(cp)=mp_grey_model; -if ( grey_val(cp)<0 ) grey_val(cp)=0; -if ( grey_val(cp)>unity ) grey_val(cp)=unity; +{ + scaled qq = cur_exp_value (); + cyan_val (cp) = 0; + magenta_val (cp) = 0; + yellow_val (cp) = 0; + black_val (cp) = 0; + grey_val (cp) = qq; + mp_color_model (cp) = mp_grey_model; + if (grey_val (cp) < 0) + grey_val (cp) = 0; + if (grey_val (cp) > unity) + grey_val (cp) = unity; } + @ @<Transfer a noncolor from the current expression to object~|cp|@>= { -cyan_val(cp)=0; -magenta_val(cp)=0; -yellow_val(cp)=0; -black_val(cp)=0; -grey_val(cp)=0; -mp_color_model(cp)=mp_no_model; + cyan_val (cp) = 0; + magenta_val (cp) = 0; + yellow_val (cp) = 0; + black_val (cp) = 0; + grey_val (cp) = 0; + mp_color_model (cp) = mp_no_model; } + @ @<Transfer no color from the current expression to object~|cp|@>= { -cyan_val(cp)=0; -magenta_val(cp)=0; -yellow_val(cp)=0; -black_val(cp)=0; -grey_val(cp)=0; -mp_color_model(cp)=mp_uninitialized_model; + cyan_val (cp) = 0; + magenta_val (cp) = 0; + yellow_val (cp) = 0; + black_val (cp) = 0; + grey_val (cp) = 0; + mp_color_model (cp) = mp_uninitialized_model; } + @ @<Make |cp| a colored object in object list~|p|@>= -{ cp=p; - while ( cp!=null ){ - if ( has_color(cp) ) break; - cp=mp_link(cp); +{ + cp = p; + while (cp != NULL) { + if (has_color (cp)) + break; + cp = mp_link (cp); } } + @ @<Make |pp| an object in list~|p| that needs a pen@>= -{ pp=p; - while ( pp!=null ) { - if ( has_pen(pp) ) break; - pp=mp_link(pp); +{ + pp = p; + while (pp != NULL) { + if (has_pen (pp)) + break; + pp = mp_link (pp); } } + @ @<Make |dp| a stroked node in list~|p|@>= -{ dp=p; - while ( dp!=null ) { - if ( mp_type(dp)==mp_stroked_code ) break; - dp=mp_link(dp); +{ + dp = p; + while (dp != NULL) { + if (mp_type (dp) == mp_stroked_node_type) + break; + dp = mp_link (dp); } } + @ @<Copy the information from objects |cp|, |pp|, and |dp| into...@>= -@<Copy |cp|'s color into the colored objects linked to~|cp|@>; -if ( pp>mp_void ) { +if (cp > MP_VOID) { + @<Copy |cp|'s color into the colored objects linked to~|cp|@>; +} +if (pp > MP_VOID) { @<Copy |mp_pen_p(pp)| into stroked and filled nodes linked to |pp|@>; } -if ( dp>mp_void ) { +if (dp > MP_VOID) { @<Make stroked nodes linked to |dp| refer to |mp_dash_p(dp)|@>; } - @ @<Copy |cp|'s color into the colored objects linked to~|cp|@>= -{ q=mp_link(cp); - while ( q!=null ) { - if ( has_color(q) ) { - red_val(q)=red_val(cp); - green_val(q)=green_val(cp); - blue_val(q)=blue_val(cp); - black_val(q)=black_val(cp); - mp_color_model(q)=mp_color_model(cp); +{ + q = mp_link (cp); + while (q != NULL) { + if (has_color (q)) { + red_val (q) = red_val (cp); + green_val (q) = green_val (cp); + blue_val (q) = blue_val (cp); + black_val (q) = black_val (cp); + mp_color_model (q) = mp_color_model (cp); } - q=mp_link(q); + q = mp_link (q); } } + @ @<Copy |mp_pen_p(pp)| into stroked and filled nodes linked to |pp|@>= -{ q=mp_link(pp); - while ( q!=null ) { - if ( has_pen(q) ) { - if ( mp_pen_p(q)!=null ) mp_toss_knot_list(mp, mp_pen_p(q)); - mp_pen_p(q)=copy_pen(mp_pen_p(pp)); +{ + q = mp_link (pp); + while (q != NULL) { + if (has_pen (q)) { + switch (mp_type (q)) { + case mp_fill_node_type: + if (mp_pen_p ((mp_fill_node) q) != NULL) + mp_toss_knot_list (mp, mp_pen_p ((mp_fill_node) q)); + mp_pen_p ((mp_fill_node) q) = copy_pen (mp_pen_p ((mp_fill_node) pp)); + break; + case mp_stroked_node_type: + if (mp_pen_p ((mp_stroked_node) q) != NULL) + mp_toss_knot_list (mp, mp_pen_p ((mp_stroked_node) q)); + mp_pen_p ((mp_stroked_node) q) = + copy_pen (mp_pen_p ((mp_stroked_node) pp)); + break; + default: + assert (0); + break; + } } - q=mp_link(q); + q = mp_link (q); } } + @ @<Make stroked nodes linked to |dp| refer to |mp_dash_p(dp)|@>= -{ q=mp_link(dp); - while ( q!=null ) { - if ( mp_type(q)==mp_stroked_code ) { - if ( mp_dash_p(q)!=null ) delete_edge_ref(mp_dash_p(q)); - mp_dash_p(q)=mp_dash_p(dp); - dash_scale(q)=unity; - if ( mp_dash_p(q)!=null ) add_edge_ref(mp_dash_p(q)); +{ + q = mp_link (dp); + while (q != NULL) { + if (mp_type (q) == mp_stroked_node_type) { + if (mp_dash_p (q) != NULL) + delete_edge_ref (mp_dash_p (q)); + mp_dash_p (q) = mp_dash_p (dp); + dash_scale (q) = unity; + if (mp_dash_p (q) != NULL) + add_edge_ref (mp_dash_p (q)); } - q=mp_link(q); + q = mp_link (q); } } + @ One of the things we need to do when we've parsed an \&{addto} or similar command is find the header of a supposed \&{picture} variable, given a token list for that variable. Since the edge structure is about to be updated, we use |private_edges| to make sure that this is possible. @<Declare action procedures for use by |do_statement|@>= -static pointer mp_find_edges_var (MP mp, pointer t) ; - -@ @c pointer mp_find_edges_var (MP mp, pointer t) { - pointer p; - pointer cur_edges; /* the return value */ - p=mp_find_variable(mp, t); cur_edges=null; - if ( p==null ) { - mp_obliterated(mp, t); mp_put_get_error(mp); - } else if ( mp_type(p)!=mp_picture_type ) { - print_err("Variable "); mp_show_token_list(mp, t,null,1000,0); -@.Variable x is the wrong type@> - mp_print(mp, " is the wrong type ("); - mp_print_type(mp, mp_type(p)); mp_print_char(mp, xord(')')); - help2("I was looking for a \"known\" picture variable.", - "So I'll not change anything just now."); - mp_put_get_error(mp); - } else { - value(p)=mp_private_edges(mp, value(p)); - cur_edges=value(p); - } - mp_flush_node_list(mp, t); +static mp_node mp_find_edges_var (MP mp, mp_node t); + +@ @c +mp_node mp_find_edges_var (MP mp, mp_node t) { + mp_node p; + mp_node cur_edges; /* the return value */ + p = mp_find_variable (mp, t); + cur_edges = NULL; + if (p == NULL) { + mp_obliterated (mp, t); + mp_put_get_error (mp); + } else if (mp_type (p) != mp_picture_type) { + print_err ("Variable "); + mp_show_token_list (mp, t, NULL, 1000, 0); +@.Variable x is the wrong type@>; + mp_print (mp, " is the wrong type ("); + mp_print_type (mp, mp_type (p)); + mp_print_char (mp, xord (')')); + help2 ("I was looking for a \"known\" picture variable.", + "So I'll not change anything just now."); + mp_put_get_error (mp); + } else { + set_value_node (p, mp_private_edges (mp, value_node (p))); + cur_edges = value_node (p); + } + mp_flush_node_list (mp, t); return cur_edges; } + @ @<Cases of |do_statement|...@>= -case add_to_command: mp_do_add_to(mp); break; -case bounds_command:mp_do_bounds(mp); break; +case add_to_command: +mp_do_add_to (mp); +break; +case bounds_command: +mp_do_bounds (mp); +break; @ @<Put each...@>= -mp_primitive(mp, "clip",bounds_command,mp_start_clip_code); -@:clip_}{\&{clip} primitive@> -mp_primitive(mp, "setbounds",bounds_command,mp_start_bounds_code); +mp_primitive (mp, "clip", bounds_command, mp_start_clip_node_type); +@:clip_}{\&{clip} primitive@>; +mp_primitive (mp, "setbounds", bounds_command, mp_start_bounds_node_type); @:set_bounds_}{\&{setbounds} primitive@> + @ @<Cases of |print_cmd...@>= -case bounds_command: - if ( m==mp_start_clip_code ) mp_print(mp, "clip"); - else mp_print(mp, "setbounds"); - break; +case bounds_command: +if (m == mp_start_clip_node_type) + mp_print (mp, "clip"); +else + mp_print (mp, "setbounds"); +break; @ The following function parses the beginning of an \&{addto} or \&{clip} command: it expects a variable name followed by a token with |cur_cmd=sep| @@ -23953,55 +27965,72 @@ quarterword last_add_type; /* command modifier that identifies the last \&{addto} command */ @ @<Declare action procedures for use by |do_statement|@>= -static pointer mp_start_draw_cmd (MP mp,quarterword sep) ; - -@ @c pointer mp_start_draw_cmd (MP mp,quarterword sep) { - pointer lhv; /* variable to add to left */ - quarterword add_type=0; /* value to be returned in |last_add_type| */ - lhv=null; - mp_get_x_next(mp); mp->var_flag=sep; mp_scan_primary(mp); - if ( mp->cur_type!=mp_token_list ) { +static mp_node mp_start_draw_cmd (MP mp, quarterword sep); + +@ @c +mp_node mp_start_draw_cmd (MP mp, quarterword sep) { + mp_node lhv; /* variable to add to left */ + mp_value new_expr; + quarterword add_type = 0; /* value to be returned in |last_add_type| */ + lhv = NULL; + memset(&new_expr,0,sizeof(mp_value)); + mp_get_x_next (mp); + mp->var_flag = sep; + mp_scan_primary (mp); + if (mp->cur_exp.type != mp_token_list) { @<Abandon edges command because there's no variable@>; - } else { - lhv=mp->cur_exp; add_type=(quarterword)mp->cur_mod; - mp->cur_type=mp_vacuous; mp_get_x_next(mp); mp_scan_expression(mp); + } else { + lhv = cur_exp_node (); + add_type = (quarterword) mp->cur_mod; + mp->cur_exp.type = mp_vacuous; + mp_get_x_next (mp); + mp_scan_expression (mp); } - mp->last_add_type=add_type; + mp->last_add_type = add_type; return lhv; } + @ @<Abandon edges command because there's no variable@>= -{ exp_err("Not a suitable variable"); -@.Not a suitable variable@> - help4("At this point I needed to see the name of a picture variable.", - "(Or perhaps you have indeed presented me with one; I might", - "have missed it, if it wasn't followed by the proper token.)", - "So I'll not change anything just now."); - mp_put_get_flush_error(mp, 0); +{ + exp_err ("Not a suitable variable"); +@.Not a suitable variable@>; + help4 ("At this point I needed to see the name of a picture variable.", + "(Or perhaps you have indeed presented me with one; I might", + "have missed it, if it wasn't followed by the proper token.)", + "So I'll not change anything just now."); + new_expr.data.val = 0; + mp_put_get_flush_error (mp, new_expr); } + @ Here is an example of how to use |start_draw_cmd|. @<Declare action procedures for use by |do_statement|@>= -static void mp_do_bounds (MP mp) ; - -@ @c void mp_do_bounds (MP mp) { - pointer lhv,lhe; /* variable on left, the corresponding edge structure */ - pointer p; /* for list manipulation */ - integer m; /* initial value of |cur_mod| */ - m=mp->cur_mod; - lhv=mp_start_draw_cmd(mp, to_token); - if ( lhv!=null ) { - lhe=mp_find_edges_var(mp, lhv); - if ( lhe==null ) { - mp_flush_cur_exp(mp, 0); - } else if ( mp->cur_type!=mp_path_type ) { - exp_err("Improper `clip'"); -@.Improper `addto'@> - help2("This expression should have specified a known path.", - "So I'll not change anything just now."); - mp_put_get_flush_error(mp, 0); - } else if ( mp_left_type(mp->cur_exp)==mp_endpoint ) { +static void mp_do_bounds (MP mp); + +@ @c +void mp_do_bounds (MP mp) { + mp_node lhv, lhe; /* variable on left, the corresponding edge structure */ + mp_node p; /* for list manipulation */ + integer m; /* initial value of |cur_mod| */ + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + m = mp->cur_mod; + lhv = mp_start_draw_cmd (mp, to_token); + if (lhv != NULL) { + lhe = mp_find_edges_var (mp, lhv); + if (lhe == NULL) { + new_expr.data.val = 0; + mp_flush_cur_exp (mp, new_expr); + } else if (mp->cur_exp.type != mp_path_type) { + exp_err ("Improper `clip'"); +@.Improper `addto'@>; + help2 ("This expression should have specified a known path.", + "So I'll not change anything just now."); + new_expr.data.val = 0; + mp_put_get_flush_error (mp, new_expr); + } else if (mp_left_type (cur_exp_knot ()) == mp_endpoint) { @<Complain about a non-cycle@>; } else { @<Make |cur_exp| into a \&{setbounds} or clipping path and add it to |lhe|@>; @@ -24009,164 +28038,203 @@ static void mp_do_bounds (MP mp) ; } } + @ @<Complain about a non-cycle@>= -{ print_err("Not a cycle"); -@.Not a cycle@> - help2("That contour should have ended with `..cycle' or `&cycle'.", - "So I'll not change anything just now."); mp_put_get_error(mp); +{ + print_err ("Not a cycle"); +@.Not a cycle@>; + help2 ("That contour should have ended with `..cycle' or `&cycle'.", + "So I'll not change anything just now."); + mp_put_get_error (mp); } + @ @<Make |cur_exp| into a \&{setbounds} or clipping path and add...@>= -{ p=mp_new_bounds_node(mp, mp->cur_exp, (quarterword)m); - mp_link(p)=mp_link(dummy_loc(lhe)); - mp_link(dummy_loc(lhe))=p; - if ( obj_tail(lhe)==dummy_loc(lhe) ) obj_tail(lhe)=p; - p=mp_get_node(mp, mp->gr_object_size[stop_type(m)]); - mp_type(p)=(quarterword)stop_type(m); - mp_link(obj_tail(lhe))=p; - obj_tail(lhe)=p; - mp_init_bbox(mp, lhe); +{ + p = mp_new_bounds_node (mp, cur_exp_knot (), (quarterword) m); + mp_link (p) = mp_link (dummy_loc (lhe)); + mp_link (dummy_loc (lhe)) = p; + if (obj_tail (lhe) == dummy_loc (lhe)) + obj_tail (lhe) = p; + if (m == mp_start_clip_node_type) { + p = mp_new_bounds_node (mp, NULL, mp_stop_clip_node_type); + } else if (m == mp_start_bounds_node_type) { + p = mp_new_bounds_node (mp, NULL, mp_stop_bounds_node_type); + } + mp_link (obj_tail (lhe)) = p; + obj_tail (lhe) = p; + mp_init_bbox (mp, lhe); } + @ The |do_add_to| procedure is a little like |do_clip| but there are a lot more cases to deal with. @<Declare action procedures for use by |do_statement|@>= -static void mp_do_add_to (MP mp) ; +static void mp_do_add_to (MP mp); -@ @c void mp_do_add_to (MP mp) { - pointer lhv,lhe; /* variable on left, the corresponding edge structure */ - pointer p; /* the graphical object or list for |scan_with_list| to update */ - pointer e; /* an edge structure to be merged */ +@ @c +void mp_do_add_to (MP mp) { + mp_node lhv, lhe; /* variable on left, the corresponding edge structure */ + mp_node p; /* the graphical object or list for |scan_with_list| to update */ + mp_node e; /* an edge structure to be merged */ quarterword add_type; /* |also_code|, |contour_code|, or |double_path_code| */ - lhv=mp_start_draw_cmd(mp, thing_to_add); add_type=mp->last_add_type; - if ( lhv!=null ) { - if ( add_type==also_code ) { + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + lhv = mp_start_draw_cmd (mp, thing_to_add); + add_type = mp->last_add_type; + if (lhv != NULL) { + if (add_type == also_code) { @<Make sure the current expression is a suitable picture and set |e| and |p| appropriately@>; } else { @<Create a graphical object |p| based on |add_type| and the current expression@>; } - mp_scan_with_list(mp, p); + mp_scan_with_list (mp, p); @<Use |p|, |e|, and |add_type| to augment |lhv| as requested@>; } } -@ Setting |p:=null| causes the $\langle$with list$\rangle$ to be ignored; -setting |e:=null| prevents anything from being added to |lhe|. + +@ Setting |p:=NULL| causes the $\langle$with list$\rangle$ to be ignored; +setting |e:=NULL| prevents anything from being added to |lhe|. @ @<Make sure the current expression is a suitable picture and set |e|...@>= -{ - p=null; e=null; - if ( mp->cur_type!=mp_picture_type ) { - exp_err("Improper `addto'"); -@.Improper `addto'@> - help2("This expression should have specified a known picture.", - "So I'll not change anything just now."); - mp_put_get_flush_error(mp, 0); - } else { - e=mp_private_edges(mp, mp->cur_exp); mp->cur_type=mp_vacuous; - p=mp_link(dummy_loc(e)); +{ + p = NULL; + e = NULL; + if (mp->cur_exp.type != mp_picture_type) { + exp_err ("Improper `addto'"); +@.Improper `addto'@>; + help2 ("This expression should have specified a known picture.", + "So I'll not change anything just now."); + new_expr.data.val = 0; + mp_put_get_flush_error (mp, new_expr); + } else { + e = mp_private_edges (mp, cur_exp_node ()); + mp->cur_exp.type = mp_vacuous; + p = mp_link (dummy_loc (e)); } } -@ In this case |add_type<>also_code| so setting |p:=null| suppresses future + +@ In this case |add_type<>also_code| so setting |p:=NULL| suppresses future attempts to add to the edge structure. @<Create a graphical object |p| based on |add_type| and the current...@>= -{ e=null; p=null; - if ( mp->cur_type==mp_pair_type ) mp_pair_to_path(mp); - if ( mp->cur_type!=mp_path_type ) { - exp_err("Improper `addto'"); -@.Improper `addto'@> - help2("This expression should have specified a known path.", - "So I'll not change anything just now."); - mp_put_get_flush_error(mp, 0); - } else if ( add_type==contour_code ) { - if ( mp_left_type(mp->cur_exp)==mp_endpoint ) { +{ + e = NULL; + p = NULL; + if (mp->cur_exp.type == mp_pair_type) + mp_pair_to_path (mp); + if (mp->cur_exp.type != mp_path_type) { + exp_err ("Improper `addto'"); +@.Improper `addto'@>; + help2 ("This expression should have specified a known path.", + "So I'll not change anything just now."); + new_expr.data.val = 0; + mp_put_get_flush_error (mp, new_expr); + } else if (add_type == contour_code) { + if (mp_left_type (cur_exp_knot ()) == mp_endpoint) { @<Complain about a non-cycle@>; - } else { - p=mp_new_fill_node(mp, mp->cur_exp); - mp->cur_type=mp_vacuous; + } else { + p = mp_new_fill_node (mp, cur_exp_knot ()); + mp->cur_exp.type = mp_vacuous; } - } else { - p=mp_new_stroked_node(mp, mp->cur_exp); - mp->cur_type=mp_vacuous; + } else { + p = mp_new_stroked_node (mp, cur_exp_knot ()); + mp->cur_exp.type = mp_vacuous; } } + @ @<Use |p|, |e|, and |add_type| to augment |lhv| as requested@>= -lhe=mp_find_edges_var(mp, lhv); -if ( lhe==null ) { - if ( (e==null)&&(p!=null) ) e=mp_toss_gr_object(mp, p); - if ( e!=null ) delete_edge_ref(e); -} else if ( add_type==also_code ) { - if ( e!=null ) { +lhe = mp_find_edges_var (mp, lhv); +if (lhe == NULL) { + if ((e == NULL) && (p != NULL)) + e = mp_toss_gr_object (mp, p); + if (e != NULL) + delete_edge_ref (e); +} else if (add_type == also_code) { + if (e != NULL) { @<Merge |e| into |lhe| and delete |e|@>; - } else { - do_nothing; } -} else if ( p!=null ) { - mp_link(obj_tail(lhe))=p; - obj_tail(lhe)=p; - if ( add_type==double_path_code ) - if ( mp_pen_p(p)==null ) - mp_pen_p(p)=mp_get_pen_circle(mp, 0); +} else if (p != NULL) { + mp_link (obj_tail (lhe)) = p; + obj_tail (lhe) = p; + if (add_type == double_path_code) + if (mp_pen_p ((mp_stroked_node) p) == NULL) + mp_pen_p ((mp_stroked_node) p) = mp_get_pen_circle (mp, 0); } @ @<Merge |e| into |lhe| and delete |e|@>= -{ if ( mp_link(dummy_loc(e))!=null ) { - mp_link(obj_tail(lhe))=mp_link(dummy_loc(e)); - obj_tail(lhe)=obj_tail(e); - obj_tail(e)=dummy_loc(e); - mp_link(dummy_loc(e))=null; - mp_flush_dash_list(mp, lhe); +{ + if (mp_link (dummy_loc (e)) != NULL) { + mp_link (obj_tail (lhe)) = mp_link (dummy_loc (e)); + obj_tail (lhe) = obj_tail (e); + obj_tail (e) = dummy_loc (e); + mp_link (dummy_loc (e)) = NULL; + mp_flush_dash_list (mp, lhe); } - mp_toss_edges(mp, e); + mp_toss_edges (mp, e); } + @ @<Cases of |do_statement|...@>= -case ship_out_command: mp_do_ship_out(mp); break; +case ship_out_command: +mp_do_ship_out (mp); +break; @ @<Declare action procedures for use by |do_statement|@>= -@<Declare the \ps\ output procedures@> -static void mp_do_ship_out (MP mp) ; +@<Declare the \ps\ output procedures@>; +static void mp_do_ship_out (MP mp); -@ @c void mp_do_ship_out (MP mp) { - integer c; /* the character code */ - mp_get_x_next(mp); mp_scan_expression(mp); - if ( mp->cur_type!=mp_picture_type ) { +@ @c +void mp_do_ship_out (MP mp) { + integer c; /* the character code */ + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + mp_get_x_next (mp); + mp_scan_expression (mp); + if (mp->cur_exp.type != mp_picture_type) { @<Complain that it's not a known picture@>; - } else { - c=mp_round_unscaled(mp, mp->internal[mp_char_code]) % 256; - if ( c<0 ) c=c+256; + } else { + c = mp_round_unscaled (mp, internal_value (mp_char_code)) % 256; + if (c < 0) + c = c + 256; @<Store the width information for character code~|c|@>; - mp_ship_out(mp, mp->cur_exp); - mp_flush_cur_exp(mp, 0); + mp_ship_out (mp, cur_exp_node ()); + new_expr.data.val = 0; + mp_flush_cur_exp (mp, new_expr); } } + @ @<Complain that it's not a known picture@>= -{ - exp_err("Not a known picture"); - help1("I can only output known pictures."); - mp_put_get_flush_error(mp, 0); +{ + exp_err ("Not a known picture"); + help1 ("I can only output known pictures."); + new_expr.data.val = 0; + mp_put_get_flush_error (mp, new_expr); } + @ The \&{everyjob} command simply assigns a nonzero value to the global variable |start_sym|. @<Cases of |do_statement|...@>= -case every_job_command: - mp_get_symbol(mp); mp->start_sym=mp->cur_sym; mp_get_x_next(mp); - break; +case every_job_command: +mp_get_symbol (mp); +mp->start_sym = mp->cur_sym; +mp_get_x_next (mp); +break; @ @<Glob...@>= -halfword start_sym; /* a symbolic token to insert at beginning of job */ +mp_sym start_sym; /* a symbolic token to insert at beginning of job */ @ @<Set init...@>= -mp->start_sym=0; +mp->start_sym = NULL; @ Finally, we have only the ``message'' commands remaining. @@ -24175,55 +28243,69 @@ mp->start_sym=0; @d err_help_code 2 @d filename_template_code 3 @d print_with_leading_zeroes(A,B) do { - integer g = mp->pool_ptr; - integer f = (B); - mp_print_int(mp, (A)); g = mp->pool_ptr-g; + size_t g = mp->cur_length; + size_t f = (size_t)(B); + mp_print_int(mp, (A)); + g = mp->cur_length - g; if ( f>g ) { - mp->pool_ptr = mp->pool_ptr - g; + mp->cur_length = mp->cur_length - g; while ( f>g ) { mp_print_char(mp, xord('0')); decr(f); - }; + }; mp_print_int(mp, (A)); }; f = 0; } while (0) @<Put each...@>= -mp_primitive(mp, "message",message_command,message_code); -@:message_}{\&{message} primitive@> -mp_primitive(mp, "errmessage",message_command,err_message_code); -@:err_message_}{\&{errmessage} primitive@> -mp_primitive(mp, "errhelp",message_command,err_help_code); -@:err_help_}{\&{errhelp} primitive@> -mp_primitive(mp, "filenametemplate",message_command,filename_template_code); +mp_primitive (mp, "message", message_command, message_code); +@:message_}{\&{message} primitive@>; +mp_primitive (mp, "errmessage", message_command, err_message_code); +@:err_message_}{\&{errmessage} primitive@>; +mp_primitive (mp, "errhelp", message_command, err_help_code); +@:err_help_}{\&{errhelp} primitive@>; +mp_primitive (mp, "filenametemplate", message_command, filename_template_code); @:filename_template_}{\&{filenametemplate} primitive@> + @ @<Cases of |print_cmd...@>= -case message_command: - if ( m<err_message_code ) mp_print(mp, "message"); - else if ( m==err_message_code ) mp_print(mp, "errmessage"); - else if ( m==filename_template_code ) mp_print(mp, "filenametemplate"); - else mp_print(mp, "errhelp"); - break; +case message_command: +if (m < err_message_code) + mp_print (mp, "message"); +else if (m == err_message_code) + mp_print (mp, "errmessage"); +else if (m == filename_template_code) + mp_print (mp, "filenametemplate"); +else + mp_print (mp, "errhelp"); +break; @ @<Cases of |do_statement|...@>= -case message_command: mp_do_message(mp); break; +case message_command: +mp_do_message (mp); +break; @ @<Declare action procedures for use by |do_statement|@>= -@<Declare a procedure called |no_string_err|@> -static void mp_do_message (MP mp) ; +@<Declare a procedure called |no_string_err|@>; +static void mp_do_message (MP mp); @ -@c void mp_do_message (MP mp) { - int m; /* the type of message */ - m=mp->cur_mod; mp_get_x_next(mp); mp_scan_expression(mp); - if ( mp->cur_type!=mp_string_type ) - mp_no_string_err(mp, "A message should be a known string expression."); +@c +void mp_do_message (MP mp) { + int m; /* the type of message */ + mp_value new_expr; + m = mp->cur_mod; + memset(&new_expr,0,sizeof(mp_value)); + mp_get_x_next (mp); + mp_scan_expression (mp); + if (mp->cur_exp.type != mp_string_type) + mp_no_string_err (mp, "A message should be a known string expression."); else { switch (m) { - case message_code: - mp_print_nl(mp, ""); mp_print_str(mp, mp->cur_exp); + case message_code: + mp_print_nl (mp, ""); + mp_print_str (mp, cur_exp_str ()); break; case err_message_code: @<Print string |cur_exp| as an error message@>; @@ -24234,29 +28316,50 @@ static void mp_do_message (MP mp) ; case filename_template_code: @<Save the filename template@>; break; - } /* there are no other cases */ + } /* there are no other cases */ + } + new_expr.data.val = 0; + mp_flush_cur_exp (mp, new_expr); +} + + +@ @<Save the filename template@>= +{ + delete_str_ref (internal_string (mp_output_template)); + if (length (cur_exp_str ()) == 0) { + internal_string (mp_output_template) = mp_rts (mp, "%j.%c"); + } else { + internal_string (mp_output_template) = cur_exp_str (); + add_str_ref (internal_string (mp_output_template)); } - mp_flush_cur_exp(mp, 0); } + @ @<Declare a procedure called |no_string_err|@>= -static void mp_no_string_err (MP mp, const char *s) { - exp_err("Not a string"); -@.Not a string@> - help1(s); - mp_put_get_error(mp); +static void mp_no_string_err (MP mp, const char *s) { + exp_err ("Not a string"); +@.Not a string@>; + help1 (s); + mp_put_get_error (mp); } + @ The global variable |err_help| is zero when the user has most recently given an empty help string, or if none has ever been given. @<Save string |cur_exp| as the |err_help|@>= -{ - if ( mp->err_help!=0 ) delete_str_ref(mp->err_help); - if ( length(mp->cur_exp)==0 ) mp->err_help=0; - else { mp->err_help=mp->cur_exp; add_str_ref(mp->err_help); } +{ + if (mp->err_help != NULL) + delete_str_ref (mp->err_help); + if (length (cur_exp_str ()) == 0) + mp->err_help = NULL; + else { + mp->err_help = cur_exp_str (); + add_str_ref (mp->err_help); + } } + @ If \&{errmessage} occurs often in |mp_scroll_mode|, without user-defined \&{errhelp}, we don't want to give a long help message each time. So we give a verbose explanation only once. @@ -24264,121 +28367,143 @@ give a verbose explanation only once. @<Glob...@>= boolean long_help_seen; /* has the long \.{\\errmessage} help been used? */ -@ @<Set init...@>=mp->long_help_seen=false; +@ @<Set init...@>= +mp->long_help_seen = false; @ @<Print string |cur_exp| as an error message@>= -{ - print_err(""); mp_print_str(mp, mp->cur_exp); - if ( mp->err_help!=0 ) { - mp->use_err_help=true; - } else if ( mp->long_help_seen ) { - help1("(That was another `errmessage'.)") ; - } else { - if ( mp->interaction<mp_error_stop_mode ) mp->long_help_seen=true; - help4("This error message was generated by an `errmessage'", - "command, so I can\'t give any explicit help.", - "Pretend that you're Miss Marple: Examine all clues,", +{ + print_err (""); + mp_print_str (mp, cur_exp_str ()); + if (mp->err_help != NULL) { + mp->use_err_help = true; + } else if (mp->long_help_seen) { + help1 ("(That was another `errmessage'.)"); + } else { + if (mp->interaction < mp_error_stop_mode) + mp->long_help_seen = true; + help4 ("This error message was generated by an `errmessage'", + "command, so I can\'t give any explicit help.", + "Pretend that you're Miss Marple: Examine all clues,", @^Marple, Jane@> - "and deduce the truth by inspired guesses."); + "and deduce the truth by inspired guesses."); } - mp_put_get_error(mp); mp->use_err_help=false; + mp_put_get_error (mp); + mp->use_err_help = false; } + @ @<Cases of |do_statement|...@>= -case write_command: mp_do_write(mp); break; +case write_command: +mp_do_write (mp); +break; @ @<Declare action procedures for use by |do_statement|@>= -static void mp_do_write (MP mp) ; +static void mp_do_write (MP mp); -@ @c void mp_do_write (MP mp) { +@ @c +void mp_do_write (MP mp) { str_number t; /* the line of text to be written */ - write_index n,n0; /* for searching |wr_fname| and |wr_file| arrays */ + write_index n, n0; /* for searching |wr_fname| and |wr_file| arrays */ unsigned old_setting; /* for saving |selector| during output */ - mp_get_x_next(mp); - mp_scan_expression(mp); - if ( mp->cur_type!=mp_string_type ) { - mp_no_string_err(mp, "The text to be written should be a known string expression"); - } else if ( mp->cur_cmd!=to_token ) { - print_err("Missing `to' clause"); - help1("A write command should end with `to <filename>'"); - mp_put_get_error(mp); - } else { - t=mp->cur_exp; mp->cur_type=mp_vacuous; - mp_get_x_next(mp); - mp_scan_expression(mp); - if ( mp->cur_type!=mp_string_type ) - mp_no_string_err(mp, "I can\'t write to that file name. It isn't a known string"); + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + mp_get_x_next (mp); + mp_scan_expression (mp); + if (mp->cur_exp.type != mp_string_type) { + mp_no_string_err (mp, + "The text to be written should be a known string expression"); + } else if (mp->cur_cmd != to_token) { + print_err ("Missing `to' clause"); + help1 ("A write command should end with `to <filename>'"); + mp_put_get_error (mp); + } else { + t = cur_exp_str (); + mp->cur_exp.type = mp_vacuous; + mp_get_x_next (mp); + mp_scan_expression (mp); + if (mp->cur_exp.type != mp_string_type) + mp_no_string_err (mp, + "I can\'t write to that file name. It isn't a known string"); else { @<Write |t| to the file named by |cur_exp|@>; } - delete_str_ref(t); + /* |delete_str_ref(t);| *//* todo: is this right? */ } - mp_flush_cur_exp(mp, 0); + new_expr.data.val = 0; + mp_flush_cur_exp (mp, new_expr); } + @ @<Write |t| to the file named by |cur_exp|@>= -{ +{ @<Find |n| where |wr_fname[n]=cur_exp| and call |open_write_file| if |cur_exp| must be inserted@>; - if ( mp_str_vs_str(mp, t,mp->eof_line)==0 ) { + if (mp_str_vs_str (mp, t, mp->eof_line) == 0) { @<Record the end of file on |wr_file[n]|@>; - } else { - old_setting=mp->selector; - mp->selector=n+write_file; - mp_print_str(mp, t); mp_print_ln(mp); + } else { + old_setting = mp->selector; + mp->selector = n + write_file; + mp_print_str (mp, t); + mp_print_ln (mp); mp->selector = old_setting; } } + @ @<Find |n| where |wr_fname[n]=cur_exp| and call |open_write_file| if...@>= { - char *fn = str(mp->cur_exp); - n=mp->write_files; - n0=mp->write_files; - while (mp_xstrcmp(fn,mp->wr_fname[n])!=0) { - if ( n==0 ) { /* bottom reached */ - if ( n0==mp->write_files ) { - if ( mp->write_files<mp->max_write_files ) { - incr(mp->write_files); + char *fn = mp_str (mp, cur_exp_str ()); + n = mp->write_files; + n0 = mp->write_files; + while (mp_xstrcmp (fn, mp->wr_fname[n]) != 0) { + if (n == 0) { /* bottom reached */ + if (n0 == mp->write_files) { + if (mp->write_files < mp->max_write_files) { + incr (mp->write_files); } else { void **wr_file; char **wr_fname; - write_index l,k; - l = mp->max_write_files + (mp->max_write_files/4); - wr_file = xmalloc((l+1),sizeof(void *)); - wr_fname = xmalloc((l+1),sizeof(char *)); - for (k=0;k<=l;k++) { - if (k<=mp->max_write_files) { - wr_file[k]=mp->wr_file[k]; - wr_fname[k]=mp->wr_fname[k]; + write_index l, k; + l = mp->max_write_files + (mp->max_write_files / 4); + wr_file = xmalloc ((l + 1), sizeof (void *)); + wr_fname = xmalloc ((l + 1), sizeof (char *)); + for (k = 0; k <= l; k++) { + if (k <= mp->max_write_files) { + wr_file[k] = mp->wr_file[k]; + wr_fname[k] = mp->wr_fname[k]; } else { - wr_file[k]=0; - wr_fname[k]=NULL; + wr_file[k] = 0; + wr_fname[k] = NULL; } } - xfree(mp->wr_file); xfree(mp->wr_fname); + xfree (mp->wr_file); + xfree (mp->wr_fname); mp->max_write_files = l; mp->wr_file = wr_file; mp->wr_fname = wr_fname; } } - n=n0; - mp_open_write_file(mp, fn ,n); - } else { - decr(n); - if ( mp->wr_fname[n]==NULL ) n0=n; + n = n0; + mp_open_write_file (mp, fn, n); + } else { + decr (n); + if (mp->wr_fname[n] == NULL) + n0 = n; } } } + @ @<Record the end of file on |wr_file[n]|@>= -{ (mp->close_file)(mp,mp->wr_file[n]); - xfree(mp->wr_fname[n]); - if ( n==mp->write_files-1 ) mp->write_files=n; +{ + (mp->close_file) (mp, mp->wr_file[n]); + xfree (mp->wr_fname[n]); + if (n == mp->write_files - 1) + mp->write_files = n; } -@* \[42] Writing font metric data. +@* Writing font metric data. \TeX\ gets its knowledge about fonts from font metric files, also called \.{TFM} files; the `\.T' in `\.{TFM}' stands for \TeX, but other programs know about them too. One of \MP's duties is to @@ -24396,8 +28521,8 @@ Lyle Ramshaw in 1980. The intent is to convey a lot of different kinds of information in a compact but useful form. @<Glob...@>= -void * tfm_file; /* the font metric output goes here */ -char * metric_file_name; /* full name of the font metric file */ +void *tfm_file; /* the font metric output goes here */ +char *metric_file_name; /* full name of the font metric file */ @ The first 24 bytes (6 words) of a \.{TFM} file contain twelve 16-bit integers that give the lengths of the various subsequent portions @@ -24657,93 +28782,123 @@ is kept in additional arrays called |header_byte|, |lig_kern|, @<Glob...@>= #define TFM_ITEMS 257 eight_bits bc; -eight_bits ec; /* smallest and largest character codes shipped out */ -scaled tfm_width[TFM_ITEMS]; /* \&{charwd} values */ -scaled tfm_height[TFM_ITEMS]; /* \&{charht} values */ -scaled tfm_depth[TFM_ITEMS]; /* \&{chardp} values */ -scaled tfm_ital_corr[TFM_ITEMS]; /* \&{charic} values */ +eight_bits ec; /* smallest and largest character codes shipped out */ +mp_node tfm_width[TFM_ITEMS]; /* \&{charwd} values */ +mp_node tfm_height[TFM_ITEMS]; /* \&{charht} values */ +mp_node tfm_depth[TFM_ITEMS]; /* \&{chardp} values */ +mp_node tfm_ital_corr[TFM_ITEMS]; /* \&{charic} values */ boolean char_exists[TFM_ITEMS]; /* has this code been shipped out? */ -int char_tag[TFM_ITEMS]; /* |remainder| category */ -int char_remainder[TFM_ITEMS]; /* the |remainder| byte */ -char *header_byte; /* bytes of the \.{TFM} header */ -int header_last; /* last initialized \.{TFM} header byte */ -int header_size; /* size of the \.{TFM} header */ -four_quarters *lig_kern; /* the ligature/kern table */ -short nl; /* the number of ligature/kern steps so far */ -scaled *kern; /* distinct kerning amounts */ -short nk; /* the number of distinct kerns so far */ +int char_tag[TFM_ITEMS]; /* |remainder| category */ +int char_remainder[TFM_ITEMS]; /* the |remainder| byte */ +char *header_byte; /* bytes of the \.{TFM} header */ +int header_last; /* last initialized \.{TFM} header byte */ +int header_size; /* size of the \.{TFM} header */ +four_quarters *lig_kern; /* the ligature/kern table */ +short nl; /* the number of ligature/kern steps so far */ +scaled *kern; /* distinct kerning amounts */ +short nk; /* the number of distinct kerns so far */ four_quarters exten[TFM_ITEMS]; /* extensible character recipes */ -short ne; /* the number of extensible characters so far */ -scaled *param; /* \&{fontinfo} parameters */ -short np; /* the largest \&{fontinfo} parameter specified so far */ -short nw;short nh;short nd;short ni; /* sizes of \.{TFM} subtables */ -short skip_table[TFM_ITEMS]; /* local label status */ -boolean lk_started; /* has there been a lig/kern step in this command yet? */ -integer bchar; /* right boundary character */ -short bch_label; /* left boundary starting location */ -short ll;short lll; /* registers used for lig/kern processing */ -short label_loc[257]; /* lig/kern starting addresses */ -eight_bits label_char[257]; /* characters for |label_loc| */ -short label_ptr; /* highest position occupied in |label_loc| */ +short ne; /* the number of extensible characters so far */ +scaled *param; /* \&{fontinfo} parameters */ +short np; /* the largest \&{fontinfo} parameter specified so far */ +short nw; +short nh; +short nd; +short ni; /* sizes of \.{TFM} subtables */ +short skip_table[TFM_ITEMS]; /* local label status */ +boolean lk_started; /* has there been a lig/kern step in this command yet? */ +integer bchar; /* right boundary character */ +short bch_label; /* left boundary starting location */ +short ll; +short lll; /* registers used for lig/kern processing */ +short label_loc[257]; /* lig/kern starting addresses */ +eight_bits label_char[257]; /* characters for |label_loc| */ +short label_ptr; /* highest position occupied in |label_loc| */ @ @<Allocate or initialize ...@>= -mp->header_last=7; -mp->header_size = 128; /* just for init */ -mp->header_byte = xmalloc(mp->header_size, sizeof(char)); +mp->header_last = 7; +mp->header_size = 128; /* just for init */ +mp->header_byte = xmalloc (mp->header_size, sizeof (char)); @ @<Dealloc variables@>= -xfree(mp->header_byte); -xfree(mp->lig_kern); -xfree(mp->kern); -xfree(mp->param); +xfree (mp->header_byte); +xfree (mp->lig_kern); +xfree (mp->kern); +xfree (mp->param); @ @<Set init...@>= -for (k=0;k<= 255;k++ ) { - mp->tfm_width[k]=0; mp->tfm_height[k]=0; mp->tfm_depth[k]=0; mp->tfm_ital_corr[k]=0; - mp->char_exists[k]=false; mp->char_tag[k]=no_tag; mp->char_remainder[k]=0; - mp->skip_table[k]=undefined_label; -} -memset(mp->header_byte,0,(size_t)mp->header_size); -mp->bc=255; mp->ec=0; mp->nl=0; mp->nk=0; mp->ne=0; mp->np=0; -mp->internal[mp_boundary_char]=-unity; -mp->bch_label=undefined_label; -mp->label_loc[0]=-1; mp->label_ptr=0; +for (k = 0; k <= 255; k++) { + mp->tfm_width[k] = 0; + mp->tfm_height[k] = 0; + mp->tfm_depth[k] = 0; + mp->tfm_ital_corr[k] = 0; + mp->char_exists[k] = false; + mp->char_tag[k] = no_tag; + mp->char_remainder[k] = 0; + mp->skip_table[k] = undefined_label; +} +memset (mp->header_byte, 0, (size_t) mp->header_size); +mp->bc = 255; +mp->ec = 0; +mp->nl = 0; +mp->nk = 0; +mp->ne = 0; +mp->np = 0; +internal_value (mp_boundary_char) = -unity; +mp->bch_label = undefined_label; +mp->label_loc[0] = -1; +mp->label_ptr = 0; @ @<Declarations@>= -static scaled mp_tfm_check (MP mp,quarterword m) ; +static mp_node mp_tfm_check (MP mp, quarterword m); @ @c -static scaled mp_tfm_check (MP mp,quarterword m) { - if ( abs(mp->internal[m])>=fraction_half ) { - print_err("Enormous "); mp_print(mp, mp->int_name[m]); +static mp_node mp_tfm_check (MP mp, quarterword m) { + mp_node p = mp_get_value_node (mp); + if (abs (internal_value (m)) >= fraction_half) { + print_err ("Enormous "); + mp_print (mp, internal_name (m)); @.Enormous charwd...@> @.Enormous chardp...@> @.Enormous charht...@> @.Enormous charic...@> -@.Enormous designsize...@> - mp_print(mp, " has been reduced"); - help1("Font metric dimensions must be less than 2048pt."); - mp_put_get_error(mp); - if ( mp->internal[m]>0 ) return (fraction_half-1); - else return (1-fraction_half); +@.Enormous designsize...@>; + mp_print (mp, " has been reduced"); + help1 ("Font metric dimensions must be less than 2048pt."); + mp_put_get_error (mp); + if (internal_value (m) > 0) + set_value (p, (fraction_half - 1)); + else + set_value (p, (1 - fraction_half)); } else { - return mp->internal[m]; + set_value (p, internal_value (m)); } + return p; } + @ @<Store the width information for character code~|c|@>= -if ( c<mp->bc ) mp->bc=(eight_bits)c; -if ( c>mp->ec ) mp->ec=(eight_bits)c; -mp->char_exists[c]=true; -mp->tfm_width[c]=mp_tfm_check(mp,mp_char_wd); -mp->tfm_height[c]=mp_tfm_check(mp, mp_char_ht); -mp->tfm_depth[c]=mp_tfm_check(mp, mp_char_dp); -mp->tfm_ital_corr[c]=mp_tfm_check(mp, mp_char_ic) +if (c < mp->bc) + mp->bc = (eight_bits) c; +if (c > mp->ec) + mp->ec = (eight_bits) c; +mp->char_exists[c] = true; +mp_xfree (mp->tfm_width[c]); +mp->tfm_width[c] = mp_tfm_check (mp, mp_char_wd); +mp_xfree (mp->tfm_height[c]); +mp->tfm_height[c] = mp_tfm_check (mp, mp_char_ht); +mp_xfree (mp->tfm_depth[c]); +mp->tfm_depth[c] = mp_tfm_check (mp, mp_char_dp); +mp_xfree (mp->tfm_ital_corr[c]); +mp->tfm_ital_corr[c] = mp_tfm_check (mp, mp_char_ic) + @ Now let's consider \MP's special \.{TFM}-oriented commands. @<Cases of |do_statement|...@>= -case tfm_command: mp_do_tfm_command(mp); break; +case tfm_command: +mp_do_tfm_command (mp); +break; @ @d char_list_code 0 @d lig_table_code 1 @@ -24752,202 +28907,274 @@ case tfm_command: mp_do_tfm_command(mp); break; @d font_dimen_code 4 @<Put each...@>= -mp_primitive(mp, "charlist",tfm_command,char_list_code); -@:char_list_}{\&{charlist} primitive@> -mp_primitive(mp, "ligtable",tfm_command,lig_table_code); -@:lig_table_}{\&{ligtable} primitive@> -mp_primitive(mp, "extensible",tfm_command,extensible_code); -@:extensible_}{\&{extensible} primitive@> -mp_primitive(mp, "headerbyte",tfm_command,header_byte_code); -@:header_byte_}{\&{headerbyte} primitive@> -mp_primitive(mp, "fontdimen",tfm_command,font_dimen_code); +mp_primitive (mp, "charlist", tfm_command, char_list_code); +@:char_list_}{\&{charlist} primitive@>; +mp_primitive (mp, "ligtable", tfm_command, lig_table_code); +@:lig_table_}{\&{ligtable} primitive@>; +mp_primitive (mp, "extensible", tfm_command, extensible_code); +@:extensible_}{\&{extensible} primitive@>; +mp_primitive (mp, "headerbyte", tfm_command, header_byte_code); +@:header_byte_}{\&{headerbyte} primitive@>; +mp_primitive (mp, "fontdimen", tfm_command, font_dimen_code); @:font_dimen_}{\&{fontdimen} primitive@> + @ @<Cases of |print_cmd...@>= -case tfm_command: - switch (m) { - case char_list_code:mp_print(mp, "charlist"); break; - case lig_table_code:mp_print(mp, "ligtable"); break; - case extensible_code:mp_print(mp, "extensible"); break; - case header_byte_code:mp_print(mp, "headerbyte"); break; - default: mp_print(mp, "fontdimen"); break; - } +case tfm_command: +switch (m) { +case char_list_code: + mp_print (mp, "charlist"); break; +case lig_table_code: + mp_print (mp, "ligtable"); + break; +case extensible_code: + mp_print (mp, "extensible"); + break; +case header_byte_code: + mp_print (mp, "headerbyte"); + break; +default: + mp_print (mp, "fontdimen"); + break; +} +break; @ @<Declare action procedures for use by |do_statement|@>= -static eight_bits mp_get_code (MP mp) ; - -@ @c eight_bits mp_get_code (MP mp) { /* scans a character code value */ - integer c; /* the code value found */ - mp_get_x_next(mp); mp_scan_expression(mp); - if ( mp->cur_type==mp_known ) { - c=mp_round_unscaled(mp, mp->cur_exp); - if ( c>=0 ) if ( c<256 ) return (eight_bits)c; - } else if ( mp->cur_type==mp_string_type ) { - if ( length(mp->cur_exp)==1 ) { - c=mp->str_pool[mp->str_start[mp->cur_exp]]; - return (eight_bits)c; +static eight_bits mp_get_code (MP mp); + +@ @c +eight_bits mp_get_code (MP mp) { /* scans a character code value */ + integer c; /* the code value found */ + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); + mp_get_x_next (mp); + mp_scan_expression (mp); + if (mp->cur_exp.type == mp_known) { + c = mp_round_unscaled (mp, cur_exp_value ()); + if (c >= 0) + if (c < 256) + return (eight_bits) c; + } else if (mp->cur_exp.type == mp_string_type) { + if (length (cur_exp_str ()) == 1) { + c = (integer) (*(cur_exp_str ()->str)); + return (eight_bits) c; } } - exp_err("Invalid code has been replaced by 0"); -@.Invalid code...@> - help2("I was looking for a number between 0 and 255, or for a", - "string of length 1. Didn't find it; will use 0 instead."); - mp_put_get_flush_error(mp, 0); c=0; - return (eight_bits)c; + exp_err ("Invalid code has been replaced by 0"); +@.Invalid code...@>; + help2 ("I was looking for a number between 0 and 255, or for a", + "string of length 1. Didn't find it; will use 0 instead."); + new_expr.data.val = 0; + mp_put_get_flush_error (mp, new_expr); + c = 0; + return (eight_bits) c; } + @ @<Declare action procedures for use by |do_statement|@>= -static void mp_set_tag (MP mp,halfword c, quarterword t, halfword r) ; - -@ @c void mp_set_tag (MP mp,halfword c, quarterword t, halfword r) { - if ( mp->char_tag[c]==no_tag ) { - mp->char_tag[c]=t; mp->char_remainder[c]=r; - if ( t==lig_tag ){ - mp->label_ptr++; mp->label_loc[mp->label_ptr]=(short)r; - mp->label_char[mp->label_ptr]=(eight_bits)c; +static void mp_set_tag (MP mp, halfword c, quarterword t, halfword r); + +@ @c +void mp_set_tag (MP mp, halfword c, quarterword t, halfword r) { + if (mp->char_tag[c] == no_tag) { + mp->char_tag[c] = t; + mp->char_remainder[c] = r; + if (t == lig_tag) { + mp->label_ptr++; + mp->label_loc[mp->label_ptr] = (short) r; + mp->label_char[mp->label_ptr] = (eight_bits) c; } } else { @<Complain about a character tag conflict@>; } } + @ @<Complain about a character tag conflict@>= -{ - print_err("Character "); - if ( (c>' ')&&(c<127) ) mp_print_char(mp,xord(c)); - else if ( c==256 ) mp_print(mp, "||"); - else { mp_print(mp, "code "); mp_print_int(mp, c); }; - mp_print(mp, " is already "); -@.Character c is already...@> +{ + print_err ("Character "); + if ((c > ' ') && (c < 127)) + mp_print_char (mp, xord (c)); + else if (c == 256) + mp_print (mp, "||"); + else { + mp_print (mp, "code "); + mp_print_int (mp, c); + }; + mp_print (mp, " is already "); +@.Character c is already...@>; switch (mp->char_tag[c]) { - case lig_tag: mp_print(mp, "in a ligtable"); break; - case list_tag: mp_print(mp, "in a charlist"); break; - case ext_tag: mp_print(mp, "extensible"); break; - } /* there are no other cases */ - help2("It's not legal to label a character more than once.", - "So I'll not change anything just now."); - mp_put_get_error(mp); + case lig_tag: + mp_print (mp, "in a ligtable"); + break; + case list_tag: + mp_print (mp, "in a charlist"); + break; + case ext_tag: + mp_print (mp, "extensible"); + break; + } /* there are no other cases */ + help2 ("It's not legal to label a character more than once.", + "So I'll not change anything just now."); + mp_put_get_error (mp); } + @ @<Declare action procedures for use by |do_statement|@>= -static void mp_do_tfm_command (MP mp) ; +static void mp_do_tfm_command (MP mp); -@ @c void mp_do_tfm_command (MP mp) { - int c,cc; /* character codes */ - int k; /* index into the |kern| array */ - int j; /* index into |header_byte| or |param| */ +@ @c +void mp_do_tfm_command (MP mp) { + int c, cc; /* character codes */ + int k; /* index into the |kern| array */ + int j; /* index into |header_byte| or |param| */ + mp_value new_expr; + memset(&new_expr,0,sizeof(mp_value)); switch (mp->cur_mod) { - case char_list_code: - c=mp_get_code(mp); - /* we will store a list of character successors */ - while ( mp->cur_cmd==colon ) { - cc=mp_get_code(mp); mp_set_tag(mp, c,list_tag,cc); c=cc; + case char_list_code: + c = mp_get_code (mp); + /* we will store a list of character successors */ + while (mp->cur_cmd == colon) { + cc = mp_get_code (mp); + mp_set_tag (mp, c, list_tag, cc); + c = cc; }; break; - case lig_table_code: - if (mp->lig_kern==NULL) - mp->lig_kern = xmalloc((max_tfm_int+1),sizeof(four_quarters)); - if (mp->kern==NULL) - mp->kern = xmalloc((max_tfm_int+1),sizeof(scaled)); + case lig_table_code: + if (mp->lig_kern == NULL) + mp->lig_kern = xmalloc ((max_tfm_int + 1), sizeof (four_quarters)); + if (mp->kern == NULL) + mp->kern = xmalloc ((max_tfm_int + 1), sizeof (scaled)); @<Store a list of ligature/kern steps@>; break; - case extensible_code: + case extensible_code: @<Define an extensible recipe@>; break; - case header_byte_code: - case font_dimen_code: - c=mp->cur_mod; mp_get_x_next(mp); - mp_scan_expression(mp); - if ( (mp->cur_type!=mp_known)||(mp->cur_exp<half_unit) ) { - exp_err("Improper location"); -@.Improper location@> - help2("I was looking for a known, positive number.", - "For safety's sake I'll ignore the present command."); - mp_put_get_error(mp); - } else { - j=mp_round_unscaled(mp, mp->cur_exp); - if ( mp->cur_cmd!=colon ) { - mp_missing_err(mp, ":"); -@.Missing `:'@> - help1("A colon should follow a headerbyte or fontinfo location."); - mp_back_error(mp); + case header_byte_code: + case font_dimen_code: + c = mp->cur_mod; + mp_get_x_next (mp); + mp_scan_expression (mp); + if ((mp->cur_exp.type != mp_known) || (cur_exp_value () < half_unit)) { + exp_err ("Improper location"); +@.Improper location@>; + help2 ("I was looking for a known, positive number.", + "For safety's sake I'll ignore the present command."); + mp_put_get_error (mp); + } else { + j = mp_round_unscaled (mp, cur_exp_value ()); + if (mp->cur_cmd != colon) { + mp_missing_err (mp, ":"); +@.Missing `:'@>; + help1 ("A colon should follow a headerbyte or fontinfo location."); + mp_back_error (mp); } - if ( c==header_byte_code ) { - @<Store a list of header bytes@>; - } else { - if (mp->param==NULL) - mp->param = xmalloc((max_tfm_int+1),sizeof(scaled)); + if (c == header_byte_code) { + @<Store a list of header bytes@>; + } else { + if (mp->param == NULL) + mp->param = xmalloc ((max_tfm_int + 1), sizeof (scaled)); @<Store a list of font dimensions@>; } } break; - } /* there are no other cases */ + } /* there are no other cases */ } + @ @<Store a list of ligature/kern steps@>= -{ - mp->lk_started=false; -CONTINUE: - mp_get_x_next(mp); - if ((mp->cur_cmd==skip_to)&& mp->lk_started ) +{ + mp->lk_started = false; +CONTINUE: + mp_get_x_next (mp); + if ((mp->cur_cmd == skip_to) && mp->lk_started) @<Process a |skip_to| command and |goto done|@>; - if ( mp->cur_cmd==bchar_label ) { c=256; mp->cur_cmd=colon; } - else { mp_back_input(mp); c=mp_get_code(mp); }; - if ((mp->cur_cmd==colon)||(mp->cur_cmd==double_colon)) { + if (mp->cur_cmd == bchar_label) { + c = 256; + mp->cur_cmd = colon; + } else { + mp_back_input (mp); + c = mp_get_code (mp); + }; + if ((mp->cur_cmd == colon) || (mp->cur_cmd == double_colon)) { @<Record a label in a lig/kern subprogram and |goto continue|@>; } - if ( mp->cur_cmd==lig_kern_token ) { - @<Compile a ligature/kern command@>; - } else { - print_err("Illegal ligtable step"); -@.Illegal ligtable step@> - help1("I was looking for `=:' or `kern' here."); - mp_back_error(mp); next_char(mp->nl)=qi(0); - op_byte(mp->nl)=qi(0); rem_byte(mp->nl)=qi(0); - skip_byte(mp->nl)=stop_flag+1; /* this specifies an unconditional stop */ - } - if ( mp->nl==max_tfm_int) mp_fatal_error(mp, "ligtable too large"); + if (mp->cur_cmd == lig_kern_token) { + @<Compile a ligature/kern command@>; + } else { + print_err ("Illegal ligtable step"); +@.Illegal ligtable step@>; + help1 ("I was looking for `=:' or `kern' here."); + mp_back_error (mp); + next_char (mp->nl) = qi (0); + op_byte (mp->nl) = qi (0); + rem_byte (mp->nl) = qi (0); + skip_byte (mp->nl) = stop_flag + 1; /* this specifies an unconditional stop */ + } + if (mp->nl == max_tfm_int) + mp_fatal_error (mp, "ligtable too large"); mp->nl++; - if ( mp->cur_cmd==comma ) goto CONTINUE; - if ( skip_byte(mp->nl-1)<stop_flag ) skip_byte(mp->nl-1)=stop_flag; + if (mp->cur_cmd == comma) + goto CONTINUE; + if (skip_byte (mp->nl - 1) < stop_flag) + skip_byte (mp->nl - 1) = stop_flag; } DONE: @ @<Put each...@>= -mp_primitive(mp, "=:",lig_kern_token,0); -@:=:_}{\.{=:} primitive@> -mp_primitive(mp, "=:|",lig_kern_token,1); -@:=:/_}{\.{=:\char'174} primitive@> -mp_primitive(mp, "=:|>",lig_kern_token,5); -@:=:/>_}{\.{=:\char'174>} primitive@> -mp_primitive(mp, "|=:",lig_kern_token,2); -@:=:/_}{\.{\char'174=:} primitive@> -mp_primitive(mp, "|=:>",lig_kern_token,6); -@:=:/>_}{\.{\char'174=:>} primitive@> -mp_primitive(mp, "|=:|",lig_kern_token,3); -@:=:/_}{\.{\char'174=:\char'174} primitive@> -mp_primitive(mp, "|=:|>",lig_kern_token,7); -@:=:/>_}{\.{\char'174=:\char'174>} primitive@> -mp_primitive(mp, "|=:|>>",lig_kern_token,11); -@:=:/>_}{\.{\char'174=:\char'174>>} primitive@> -mp_primitive(mp, "kern",lig_kern_token,128); +mp_primitive (mp, "=:", lig_kern_token, 0); +@:=:_}{\.{=:} primitive@>; +mp_primitive (mp, "=:|", lig_kern_token, 1); +@:=:/_}{\.{=:\char'174} primitive@>; +mp_primitive (mp, "=:|>", lig_kern_token, 5); +@:=:/>_}{\.{=:\char'174>} primitive@>; +mp_primitive (mp, "|=:", lig_kern_token, 2); +@:=:/_}{\.{\char'174=:} primitive@>; +mp_primitive (mp, "|=:>", lig_kern_token, 6); +@:=:/>_}{\.{\char'174=:>} primitive@>; +mp_primitive (mp, "|=:|", lig_kern_token, 3); +@:=:/_}{\.{\char'174=:\char'174} primitive@>; +mp_primitive (mp, "|=:|>", lig_kern_token, 7); +@:=:/>_}{\.{\char'174=:\char'174>} primitive@>; +mp_primitive (mp, "|=:|>>", lig_kern_token, 11); +@:=:/>_}{\.{\char'174=:\char'174>>} primitive@>; +mp_primitive (mp, "kern", lig_kern_token, 128); @:kern_}{\&{kern} primitive@> + @ @<Cases of |print_cmd...@>= -case lig_kern_token: - switch (m) { - case 0:mp_print(mp, "=:"); break; - case 1:mp_print(mp, "=:|"); break; - case 2:mp_print(mp, "|=:"); break; - case 3:mp_print(mp, "|=:|"); break; - case 5:mp_print(mp, "=:|>"); break; - case 6:mp_print(mp, "|=:>"); break; - case 7:mp_print(mp, "|=:|>"); break; - case 11:mp_print(mp, "|=:|>>"); break; - default: mp_print(mp, "kern"); break; - } +case lig_kern_token: +switch (m) { +case 0: + mp_print (mp, "=:"); + break; +case 1: + mp_print (mp, "=:|"); + break; +case 2: + mp_print (mp, "|=:"); break; +case 3: + mp_print (mp, "|=:|"); + break; +case 5: + mp_print (mp, "=:|>"); + break; +case 6: + mp_print (mp, "|=:>"); + break; +case 7: + mp_print (mp, "|=:|>"); + break; +case 11: + mp_print (mp, "|=:|>>"); + break; +default: + mp_print (mp, "kern"); + break; +} +break; @ Local labels are implemented by maintaining the |skip_table| array, where |skip_table[c]| is either |undefined_label| or the address of the @@ -24970,61 +29197,79 @@ We may need to cancel skips that span more than 127 lig/kern steps. } @<Process a |skip_to| command and |goto done|@>= -{ - c=mp_get_code(mp); - if ( mp->nl-mp->skip_table[c]>128 ) { - skip_error(mp->skip_table[c]); mp->skip_table[c]=(short)undefined_label; +{ + c = mp_get_code (mp); + if (mp->nl - mp->skip_table[c] > 128) { + skip_error (mp->skip_table[c]); + mp->skip_table[c] = (short) undefined_label; } - if ( mp->skip_table[c]==undefined_label ) skip_byte(mp->nl-1)=qi(0); - else skip_byte(mp->nl-1)=qi(mp->nl-mp->skip_table[c]-1); - mp->skip_table[c]=(short)(mp->nl-1); goto DONE; + if (mp->skip_table[c] == undefined_label) + skip_byte (mp->nl - 1) = qi (0); + else + skip_byte (mp->nl - 1) = qi (mp->nl - mp->skip_table[c] - 1); + mp->skip_table[c] = (short) (mp->nl - 1); + goto DONE; } + @ @<Record a label in a lig/kern subprogram and |goto continue|@>= -{ - if ( mp->cur_cmd==colon ) { - if ( c==256 ) mp->bch_label=mp->nl; - else mp_set_tag(mp, c,lig_tag,mp->nl); - } else if ( mp->skip_table[c]<undefined_label ) { - mp->ll=mp->skip_table[c]; mp->skip_table[c]=undefined_label; - do { - mp->lll=qo(skip_byte(mp->ll)); - if ( mp->nl-mp->ll>128 ) { - skip_error(mp->ll); goto CONTINUE; +{ + if (mp->cur_cmd == colon) { + if (c == 256) + mp->bch_label = mp->nl; + else + mp_set_tag (mp, c, lig_tag, mp->nl); + } else if (mp->skip_table[c] < undefined_label) { + mp->ll = mp->skip_table[c]; + mp->skip_table[c] = undefined_label; + do { + mp->lll = qo (skip_byte (mp->ll)); + if (mp->nl - mp->ll > 128) { + skip_error (mp->ll); + goto CONTINUE; } - skip_byte(mp->ll)=qi(mp->nl-mp->ll-1); mp->ll=(short)(mp->ll-mp->lll); - } while (mp->lll!=0); + skip_byte (mp->ll) = qi (mp->nl - mp->ll - 1); + mp->ll = (short) (mp->ll - mp->lll); + } while (mp->lll != 0); } goto CONTINUE; } + @ @<Compile a ligature/kern...@>= -{ - next_char(mp->nl)=qi(c); skip_byte(mp->nl)=qi(0); - if ( mp->cur_mod<128 ) { /* ligature op */ - op_byte(mp->nl)=qi(mp->cur_mod); rem_byte(mp->nl)=qi(mp_get_code(mp)); - } else { - mp_get_x_next(mp); mp_scan_expression(mp); - if ( mp->cur_type!=mp_known ) { - exp_err("Improper kern"); -@.Improper kern@> - help2("The amount of kern should be a known numeric value.", - "I'm zeroing this one. Proceed, with fingers crossed."); - mp_put_get_flush_error(mp, 0); +{ + next_char (mp->nl) = qi (c); + skip_byte (mp->nl) = qi (0); + if (mp->cur_mod < 128) { /* ligature op */ + op_byte (mp->nl) = qi (mp->cur_mod); + rem_byte (mp->nl) = qi (mp_get_code (mp)); + } else { + mp_get_x_next (mp); + mp_scan_expression (mp); + if (mp->cur_exp.type != mp_known) { + exp_err ("Improper kern"); +@.Improper kern@>; + help2 ("The amount of kern should be a known numeric value.", + "I'm zeroing this one. Proceed, with fingers crossed."); + new_expr.data.val = 0; + mp_put_get_flush_error (mp, new_expr); } - mp->kern[mp->nk]=mp->cur_exp; - k=0; - while ( mp->kern[k]!=mp->cur_exp ) incr(k); - if ( k==mp->nk ) { - if ( mp->nk==max_tfm_int ) mp_fatal_error(mp, "too many TFM kerns"); + mp->kern[mp->nk] = cur_exp_value (); + k = 0; + while (mp->kern[k] != cur_exp_value ()) + incr (k); + if (k == mp->nk) { + if (mp->nk == max_tfm_int) + mp_fatal_error (mp, "too many TFM kerns"); mp->nk++; } - op_byte(mp->nl)=qi(kern_flag+(k / 256)); - rem_byte(mp->nl)=qi((k % 256)); + op_byte (mp->nl) = qi (kern_flag + (k / 256)); + rem_byte (mp->nl) = qi ((k % 256)); } - mp->lk_started=true; + mp->lk_started = true; } + @ @d missing_extensible_punctuation(A) { mp_missing_err(mp, (A)); @.Missing `\char`\#'@> @@ -25032,51 +29277,66 @@ We may need to cancel skips that span more than 127 lig/kern steps. } @<Define an extensible recipe@>= -{ - if ( mp->ne==256 ) mp_fatal_error(mp, "too many extensible recipies"); - c=mp_get_code(mp); mp_set_tag(mp, c,ext_tag,mp->ne); - if ( mp->cur_cmd!=colon ) missing_extensible_punctuation(":"); - ext_top(mp->ne)=qi(mp_get_code(mp)); - if ( mp->cur_cmd!=comma ) missing_extensible_punctuation(","); - ext_mid(mp->ne)=qi(mp_get_code(mp)); - if ( mp->cur_cmd!=comma ) missing_extensible_punctuation(","); - ext_bot(mp->ne)=qi(mp_get_code(mp)); - if ( mp->cur_cmd!=comma ) missing_extensible_punctuation(","); - ext_rep(mp->ne)=qi(mp_get_code(mp)); +{ + if (mp->ne == 256) + mp_fatal_error (mp, "too many extensible recipies"); + c = mp_get_code (mp); + mp_set_tag (mp, c, ext_tag, mp->ne); + if (mp->cur_cmd != colon) + missing_extensible_punctuation (":"); + ext_top (mp->ne) = qi (mp_get_code (mp)); + if (mp->cur_cmd != comma) + missing_extensible_punctuation (","); + ext_mid (mp->ne) = qi (mp_get_code (mp)); + if (mp->cur_cmd != comma) + missing_extensible_punctuation (","); + ext_bot (mp->ne) = qi (mp_get_code (mp)); + if (mp->cur_cmd != comma) + missing_extensible_punctuation (","); + ext_rep (mp->ne) = qi (mp_get_code (mp)); mp->ne++; } + @ The header could contain ASCII zeroes, so can't use |strdup|. @<Store a list of header bytes@>= j--; -do { - if ( j>=mp->header_size ) { - size_t l = (size_t)(mp->header_size + (mp->header_size/4)); - char *t = xmalloc(l,1); - memset(t,0,l); - (void)memcpy(t,mp->header_byte,(size_t)mp->header_size); +do { + if (j >= mp->header_size) { + size_t l = (size_t) (mp->header_size + (mp->header_size / 4)); + char *t = xmalloc (l, 1); + memset (t, 0, l); + (void) memcpy (t, mp->header_byte, (size_t) mp->header_size); xfree (mp->header_byte); mp->header_byte = t; - mp->header_size = (int)l; + mp->header_size = (int) l; } - mp->header_byte[j]=(char)mp_get_code(mp); - incr(j); incr(mp->header_last); -} while (mp->cur_cmd==comma) + mp->header_byte[j] = (char) mp_get_code (mp); + incr (j); + incr (mp->header_last); +} while (mp->cur_cmd == comma) @ @<Store a list of font dimensions@>= -do { - if ( j>max_tfm_int ) mp_fatal_error(mp, "too many fontdimens"); - while ( j>mp->np ) { mp->np++; mp->param[mp->np]=0; }; - mp_get_x_next(mp); mp_scan_expression(mp); - if ( mp->cur_type!=mp_known ){ - exp_err("Improper font parameter"); -@.Improper font parameter@> - help1("I'm zeroing this one. Proceed, with fingers crossed."); - mp_put_get_flush_error(mp, 0); - } - mp->param[j]=mp->cur_exp; incr(j); -} while (mp->cur_cmd==comma) +do { + if (j > max_tfm_int) + mp_fatal_error (mp, "too many fontdimens"); + while (j > mp->np) { + mp->np++; + mp->param[mp->np] = 0; + }; + mp_get_x_next (mp); + mp_scan_expression (mp); + if (mp->cur_exp.type != mp_known) { + exp_err ("Improper font parameter"); +@.Improper font parameter@>; + help1 ("I'm zeroing this one. Proceed, with fingers crossed."); + new_expr.data.val = 0; + mp_put_get_flush_error (mp, new_expr); + } + mp->param[j] = cur_exp_value (); + incr (j); +} while (mp->cur_cmd == comma) @ OK: We've stored all the data that is needed for the \.{TFM} file. All that remains is to output it in the correct format. @@ -25094,8 +29354,12 @@ then the list of sorted values is perturbed, if necessary. The sorting operation is facilitated by having a special node of essentially infinite |value| at the end of the current list. -@<Initialize table entries...@>= -value(inf_val)=fraction_four; +@<Initialize table entries@>= +mp->inf_val = mp_get_value_node (mp); +set_value (mp->inf_val, fraction_four); + +@ @<Free table entries@>= +mp_free_value_node (mp, mp->inf_val); @ Straight linear insertion is good enough for sorting, since the lists are usually not terribly long. As we work on the data, the current list @@ -25110,23 +29374,28 @@ At the time we need to do these operations, most of \MP's work has been completed, so we will have plenty of memory to play with. The value nodes that are allocated for sorting will never be returned to free storage. -@d clear_the_list mp_link(temp_head)=inf_val +@d clear_the_list mp_link(mp->temp_head)=mp->inf_val -@c -static pointer mp_sort_in (MP mp,scaled v) { - pointer p,q,r; /* list manipulation registers */ - p=temp_head; - while (1) { - q=mp_link(p); - if ( v<=value(q) ) break; - p=q; +@c +static mp_node mp_sort_in (MP mp, scaled v) { + mp_node p, q, r; /* list manipulation registers */ + p = mp->temp_head; + while (1) { + q = mp_link (p); + if (v <= value (q)) + break; + p = q; } - if ( v<value(q) ) { - r=mp_get_node(mp, value_node_size); value(r)=v; mp_link(r)=q; mp_link(p)=r; + if (v < value (q)) { + r = mp_get_value_node (mp); + set_value (r, v); + mp_link (r) = q; + mp_link (p) = r; } - return mp_link(p); + return mp_link (p); } + @ Now we come to the interesting part, where we reduce the list if necessary until it has the required size. The |min_cover| routine is basic to this process; it computes the minimum number~|m| such that the values of the @@ -25138,23 +29407,29 @@ In particular, |min_cover(0)| returns the number of distinct values in the current list and sets |perturbation| to the minimum distance between adjacent values. -@c -static integer mp_min_cover (MP mp,scaled d) { - pointer p; /* runs through the current list */ - scaled l; /* the least element covered by the current interval */ - integer m; /* lower bound on the size of the minimum cover */ - m=0; p=mp_link(temp_head); mp->perturbation=el_gordo; - while ( p!=inf_val ){ - incr(m); l=value(p); - do { p=mp_link(p); } while (value(p)<=l+d); - if ( value(p)-l<mp->perturbation ) - mp->perturbation=value(p)-l; +@c +static integer mp_min_cover (MP mp, scaled d) { + mp_node p; /* runs through the current list */ + scaled l; /* the least element covered by the current interval */ + integer m; /* lower bound on the size of the minimum cover */ + m = 0; + p = mp_link (mp->temp_head); + mp->perturbation = EL_GORDO; + while (p != mp->inf_val) { + incr (m); + l = value (p); + do { + p = mp_link (p); + } while (value (p) <= l + d); + if (value (p) - l < mp->perturbation) + mp->perturbation = value (p) - l; } return m; } + @ @<Glob...@>= -scaled perturbation; /* quantity related to \.{TFM} rounding */ +scaled perturbation; /* quantity related to \.{TFM} rounding */ integer excess; /* the list is this much too long */ @ The smallest |d| such that a given list can be covered with |m| intervals @@ -25163,22 +29438,23 @@ to |min_cover|. The idea is to increase the interval size rapidly until finding the range, then to go sequentially until the exact borderline has been discovered. -@c -static scaled mp_threshold (MP mp,integer m) { - scaled d; /* lower bound on the smallest interval size */ - mp->excess=mp_min_cover(mp, 0)-m; - if ( mp->excess<=0 ) { +@c +static scaled mp_threshold (MP mp, integer m) { + scaled d; /* lower bound on the smallest interval size */ + mp->excess = mp_min_cover (mp, 0) - m; + if (mp->excess <= 0) { return 0; - } else { - do { - d=mp->perturbation; - } while (mp_min_cover(mp, d+d)>m); - while ( mp_min_cover(mp, d)>m ) - d=mp->perturbation; + } else { + do { + d = mp->perturbation; + } while (mp_min_cover (mp, d + d) > m); + while (mp_min_cover (mp, d) > m) + d = mp->perturbation; return d; } } + @ The |skimp| procedure reduces the current list to at most |m| entries, by changing values if necessary. It also sets |mp_info(p):=k| if |value(p)| is the |k|th distinct value on the resulting list, and it sets @@ -25186,55 +29462,69 @@ is the |k|th distinct value on the resulting list, and it sets been changed. The size of the resulting list is returned as the value of |skimp|. -@c -static integer mp_skimp (MP mp,integer m) { - scaled d; /* the size of intervals being coalesced */ - pointer p,q,r; /* list manipulation registers */ - scaled l; /* the least value in the current interval */ - scaled v; /* a compromise value */ - d=mp_threshold(mp, m); mp->perturbation=0; - q=temp_head; m=0; p=mp_link(temp_head); - while ( p!=inf_val ) { - incr(m); l=value(p); mp_info(p)=m; - if ( value(mp_link(p))<=l+d ) { +@c +static integer mp_skimp (MP mp, integer m) { + scaled d; /* the size of intervals being coalesced */ + mp_node p, q, r; /* list manipulation registers */ + scaled l; /* the least value in the current interval */ + scaled v; /* a compromise value */ + d = mp_threshold (mp, m); + mp->perturbation = 0; + q = mp->temp_head; + m = 0; + p = mp_link (mp->temp_head); + while (p != mp->inf_val) { + incr (m); + l = value (p); + set_mp_info (p, m); + if (value (mp_link (p)) <= l + d) { @<Replace an interval of values by its midpoint@>; } - q=p; p=mp_link(p); + q = p; + p = mp_link (p); } return m; } + @ @<Replace an interval...@>= -{ - do { - p=mp_link(p); mp_info(p)=m; - decr(mp->excess); if ( mp->excess==0 ) d=0; - } while (value(mp_link(p))<=l+d); - v=l+halfp(value(p)-l); - if ( value(p)-v>mp->perturbation ) - mp->perturbation=value(p)-v; - r=q; - do { - r=mp_link(r); value(r)=v; - } while (r!=p); - mp_link(q)=p; /* remove duplicate values from the current list */ +{ + do { + p = mp_link (p); + set_mp_info (p, m); + decr (mp->excess); + if (mp->excess == 0) + d = 0; + } while (value (mp_link (p)) <= l + d); + v = l + halfp (value (p) - l); + if (value (p) - v > mp->perturbation) + mp->perturbation = value (p) - v; + r = q; + do { + r = mp_link (r); + set_value (r, v); + } while (r != p); + mp_link (q) = p; /* remove duplicate values from the current list */ } + @ A warning message is issued whenever something is perturbed by more than 1/16\thinspace pt. -@c -static void mp_tfm_warning (MP mp,quarterword m) { - mp_print_nl(mp, "(some "); - mp_print(mp, mp->int_name[m]); +@c +static void mp_tfm_warning (MP mp, quarterword m) { + mp_print_nl (mp, "(some "); + mp_print (mp, internal_name (m)); @.some charwds...@> @.some chardps...@> @.some charhts...@> -@.some charics...@> - mp_print(mp, " values had to be adjusted by as much as "); - mp_print_scaled(mp, mp->perturbation); mp_print(mp, "pt)"); +@.some charics...@>; + mp_print (mp, " values had to be adjusted by as much as "); + mp_print_scaled (mp, mp->perturbation); + mp_print (mp, "pt)"); } + @ Here's an example of how we use these routines. The width data needs to be perturbed only if there are 256 distinct widths, but \MP\ must check for this case even though it is @@ -25246,15 +29536,18 @@ lists of dimensions. @<Massage the \.{TFM} widths@>= clear_the_list; -for (k=mp->bc;k<=mp->ec;k++) { - if ( mp->char_exists[k] ) - mp->tfm_width[k]=mp_sort_in(mp, mp->tfm_width[k]); +for (k = mp->bc; k <= mp->ec; k++) { + if (mp->char_exists[k]) + mp->tfm_width[k] = mp_sort_in (mp, value (mp->tfm_width[k])); } -mp->nw=(short)(mp_skimp(mp, 255)+1); mp->dimen_head[1]=mp_link(temp_head); -if ( mp->perturbation>=010000 ) mp_tfm_warning(mp, mp_char_wd) +mp->nw = (short) (mp_skimp (mp, 255) + 1); +mp->dimen_head[1] = mp_link (mp->temp_head); +if (mp->perturbation >= 010000) + mp_tfm_warning (mp, mp_char_wd) + @ @<Glob...@>= -pointer dimen_head[5]; /* lists of \.{TFM} dimensions */ +mp_node dimen_head[5]; /* lists of \.{TFM} dimensions */ @ Heights, depths, and italic corrections are different from widths not only because their list length is more severely restricted, but @@ -25262,35 +29555,53 @@ also because zero values do not need to be put into the lists. @<Massage the \.{TFM} heights, depths, and italic corrections@>= clear_the_list; -for (k=mp->bc;k<=mp->ec;k++) { - if ( mp->char_exists[k] ) { - if ( mp->tfm_height[k]==0 ) mp->tfm_height[k]=zero_val; - else mp->tfm_height[k]=mp_sort_in(mp, mp->tfm_height[k]); +for (k = mp->bc; k <= mp->ec; k++) { + if (mp->char_exists[k]) { + if (mp->tfm_height[k] == 0) + mp->tfm_height[k] = mp->zero_val; + else + mp->tfm_height[k] = mp_sort_in (mp, value (mp->tfm_height[k])); } } -mp->nh=(short)(mp_skimp(mp, 15)+1); mp->dimen_head[2]=mp_link(temp_head); -if ( mp->perturbation>=010000 ) mp_tfm_warning(mp, mp_char_ht); +mp->nh = (short) (mp_skimp (mp, 15) + 1); +mp->dimen_head[2] = mp_link (mp->temp_head); +if (mp->perturbation >= 010000) + mp_tfm_warning (mp, mp_char_ht); clear_the_list; -for (k=mp->bc;k<=mp->ec;k++) { - if ( mp->char_exists[k] ) { - if ( mp->tfm_depth[k]==0 ) mp->tfm_depth[k]=zero_val; - else mp->tfm_depth[k]=mp_sort_in(mp, mp->tfm_depth[k]); +for (k = mp->bc; k <= mp->ec; k++) { + if (mp->char_exists[k]) { + if (mp->tfm_depth[k] == 0) + mp->tfm_depth[k] = mp->zero_val; + else + mp->tfm_depth[k] = mp_sort_in (mp, value (mp->tfm_depth[k])); } } -mp->nd=(short)(mp_skimp(mp, 15)+1); mp->dimen_head[3]=mp_link(temp_head); -if ( mp->perturbation>=010000 ) mp_tfm_warning(mp, mp_char_dp); +mp->nd = (short) (mp_skimp (mp, 15) + 1); +mp->dimen_head[3] = mp_link (mp->temp_head); +if (mp->perturbation >= 010000) + mp_tfm_warning (mp, mp_char_dp); clear_the_list; -for (k=mp->bc;k<=mp->ec;k++) { - if ( mp->char_exists[k] ) { - if ( mp->tfm_ital_corr[k]==0 ) mp->tfm_ital_corr[k]=zero_val; - else mp->tfm_ital_corr[k]=mp_sort_in(mp, mp->tfm_ital_corr[k]); +for (k = mp->bc; k <= mp->ec; k++) { + if (mp->char_exists[k]) { + if (mp->tfm_ital_corr[k] == 0) + mp->tfm_ital_corr[k] = mp->zero_val; + else + mp->tfm_ital_corr[k] = mp_sort_in (mp, value (mp->tfm_ital_corr[k])); } } -mp->ni=(short)(mp_skimp(mp, 63)+1); mp->dimen_head[4]=mp_link(temp_head); -if ( mp->perturbation>=010000 ) mp_tfm_warning(mp, mp_char_ic) +mp->ni = (short) (mp_skimp (mp, 63) + 1); +mp->dimen_head[4] = mp_link (mp->temp_head); +if (mp->perturbation >= 010000) + mp_tfm_warning (mp, mp_char_ic) + + +@ @<Initialize table entries@>= +mp->zero_val = mp_get_value_node (mp); +set_value (mp->zero_val, 0); +set_mp_info (mp->zero_val, 0); -@ @<Initialize table entries...@>= -value(zero_val)=0; mp_info(zero_val)=0; +@ @<Free table entries@>= +mp_free_value_node (mp, mp->zero_val); @ Bytes 5--8 of the header are set to the design size, unless the user has some crazy reason for specifying them differently. @@ -25300,82 +29611,100 @@ Error messages are not allowed at the time this procedure is called, so a warning is printed instead. The value of |max_tfm_dimen| is calculated so that -$$\hbox{|make_scaled(16*max_tfm_dimen,internal[mp_design_size])|} +$$\hbox{|make_scaled(16*max_tfm_dimen,internal_value(mp_design_size))|} < \\{three\_bytes}.$$ @d three_bytes 0100000000 /* $2^{24}$ */ -@c +@c static void mp_fix_design_size (MP mp) { - scaled d; /* the design size */ - d=mp->internal[mp_design_size]; - if ( (d<unity)||(d>=fraction_half) ) { - if ( d!=0 ) - mp_print_nl(mp, "(illegal design size has been changed to 128pt)"); -@.illegal design size...@> - d=040000000; mp->internal[mp_design_size]=d; - } - if ( mp->header_byte[4]==0 && mp->header_byte[5]==0 && - mp->header_byte[6]==0 && mp->header_byte[7]==0 ) { - mp->header_byte[4]=(char)(d / 04000000); - mp->header_byte[5]=(char)((d / 4096) % 256); - mp->header_byte[6]=(char)((d / 16) % 256); - mp->header_byte[7]=(char)((d % 16)*16); - } - mp->max_tfm_dimen=16*mp->internal[mp_design_size]-1-mp->internal[mp_design_size] / 010000000; - if ( mp->max_tfm_dimen>=fraction_half ) mp->max_tfm_dimen=fraction_half-1; + scaled d; /* the design size */ + d = internal_value (mp_design_size); + if ((d < unity) || (d >= fraction_half)) { + if (d != 0) + mp_print_nl (mp, "(illegal design size has been changed to 128pt)"); +@.illegal design size...@>; + d = 040000000; + internal_value (mp_design_size) = d; + } + if (mp->header_byte[4] == 0 && mp->header_byte[5] == 0 && + mp->header_byte[6] == 0 && mp->header_byte[7] == 0) { + mp->header_byte[4] = (char) (d / 04000000); + mp->header_byte[5] = (char) ((d / 4096) % 256); + mp->header_byte[6] = (char) ((d / 16) % 256); + mp->header_byte[7] = (char) ((d % 16) * 16); + } + mp->max_tfm_dimen = + 16 * internal_value (mp_design_size) - 1 - + internal_value (mp_design_size) / 010000000; + if (mp->max_tfm_dimen >= fraction_half) + mp->max_tfm_dimen = fraction_half - 1; } + @ The |dimen_out| procedure computes a |fix_word| relative to the design size. If the data was out of range, it is corrected and the global variable |tfm_changed| is increased by~one. -@c -static integer mp_dimen_out (MP mp,scaled x) { - if ( abs(x)>mp->max_tfm_dimen ) { - incr(mp->tfm_changed); - if ( x>0 ) x=mp->max_tfm_dimen; else x=-mp->max_tfm_dimen; +@c +static integer mp_dimen_out (MP mp, scaled x) { + if (abs (x) > mp->max_tfm_dimen) { + incr (mp->tfm_changed); + if (x > 0) + x = mp->max_tfm_dimen; + else + x = -mp->max_tfm_dimen; } - x=mp_make_scaled(mp, x*16,mp->internal[mp_design_size]); + x = mp_make_scaled (mp, x * 16, internal_value (mp_design_size)); return x; } + @ @<Glob...@>= -scaled max_tfm_dimen; /* bound on widths, heights, kerns, etc. */ -integer tfm_changed; /* the number of data entries that were out of bounds */ +scaled max_tfm_dimen; /* bound on widths, heights, kerns, etc. */ +integer tfm_changed; /* the number of data entries that were out of bounds */ @ If the user has not specified any of the first four header bytes, the |fix_check_sum| procedure replaces them by a ``check sum'' computed from the |tfm_width| data relative to the design size. @^check sum@> -@c +@c static void mp_fix_check_sum (MP mp) { eight_bits k; /* runs through character codes */ - eight_bits B1,B2,B3,B4; /* bytes of the check sum */ - integer x; /* hash value used in check sum computation */ - if ( mp->header_byte[0]==0 && mp->header_byte[1]==0 && - mp->header_byte[2]==0 && mp->header_byte[3]==0 ) { + eight_bits B1, B2, B3, B4; /* bytes of the check sum */ + integer x; /* hash value used in check sum computation */ + if (mp->header_byte[0] == 0 && mp->header_byte[1] == 0 && + mp->header_byte[2] == 0 && mp->header_byte[3] == 0) { @<Compute a check sum in |(b1,b2,b3,b4)|@>; - mp->header_byte[0]=(char)B1; mp->header_byte[1]=(char)B2; - mp->header_byte[2]=(char)B3; mp->header_byte[3]=(char)B4; + mp->header_byte[0] = (char) B1; + mp->header_byte[1] = (char) B2; + mp->header_byte[2] = (char) B3; + mp->header_byte[3] = (char) B4; return; } } + @ @<Compute a check sum in |(b1,b2,b3,b4)|@>= -B1=mp->bc; B2=mp->ec; B3=mp->bc; B4=mp->ec; mp->tfm_changed=0; -for (k=mp->bc;k<=mp->ec;k++) { - if ( mp->char_exists[k] ) { - x=mp_dimen_out(mp, value(mp->tfm_width[k]))+(k+4)*020000000; /* this is positive */ - B1=(eight_bits)((B1+B1+x) % 255); - B2=(eight_bits)((B2+B2+x) % 253); - B3=(eight_bits)((B3+B3+x) % 251); - B4=(eight_bits)((B4+B4+x) % 247); - } - if (k==mp->ec) break; +B1 = mp->bc; +B2 = mp->ec; +B3 = mp->bc; +B4 = mp->ec; +mp->tfm_changed = 0; +for (k = mp->bc; k <= mp->ec; k++) { + if (mp->char_exists[k]) { + x = mp_dimen_out (mp, value (mp->tfm_width[k])) + (k + 4) * 020000000; /* this is positive */ + B1 = (eight_bits) ((B1 + B1 + x) % 255); + B2 = (eight_bits) ((B2 + B2 + x) % 253); + B3 = (eight_bits) ((B3 + B3 + x) % 251); + B4 = (eight_bits) ((B4 + B4 + x) % 247); + } + if (k == mp->ec) + break; } + @ Finally we're ready to actually write the \.{TFM} information. Here are some utility routines for this purpose. @@ -25384,81 +29713,102 @@ Here are some utility routines for this purpose. (mp->write_binary_file)(mp,mp->tfm_file,(void *)&s,1); } while (0) -@c -static void mp_tfm_two (MP mp,integer x) { /* output two bytes to |tfm_file| */ - tfm_out(x / 256); tfm_out(x % 256); +@c +static void mp_tfm_two (MP mp, integer x) { /* output two bytes to |tfm_file| */ + tfm_out (x / 256); + tfm_out (x % 256); } -static void mp_tfm_four (MP mp,integer x) { /* output four bytes to |tfm_file| */ - if ( x>=0 ) tfm_out(x / three_bytes); - else { - x=x+010000000000; /* use two's complement for negative values */ - x=x+010000000000; - tfm_out((x / three_bytes) + 128); +static void mp_tfm_four (MP mp, integer x) { /* output four bytes to |tfm_file| */ + if (x >= 0) + tfm_out (x / three_bytes); + else { + x = x + 010000000000; /* use two's complement for negative values */ + x = x + 010000000000; + tfm_out ((x / three_bytes) + 128); }; - x=x % three_bytes; tfm_out(x / unity); - x=x % unity; tfm_out(x / 0400); - tfm_out(x % 0400); + x = x % three_bytes; + tfm_out (x / unity); + x = x % unity; + tfm_out (x / 0400); + tfm_out (x % 0400); } -static void mp_tfm_qqqq (MP mp,four_quarters x) { /* output four quarterwords to |tfm_file| */ - tfm_out(qo(x.b0)); tfm_out(qo(x.b1)); - tfm_out(qo(x.b2)); tfm_out(qo(x.b3)); +static void mp_tfm_qqqq (MP mp, four_quarters x) { /* output four quarterwords to |tfm_file| */ + tfm_out (qo (x.b0)); + tfm_out (qo (x.b1)); + tfm_out (qo (x.b2)); + tfm_out (qo (x.b3)); } + @ @<Finish the \.{TFM} file@>= -if ( mp->job_name==NULL ) mp_open_log_file(mp); -mp_pack_job_name(mp, ".tfm"); -while ( ! mp_b_open_out(mp, &mp->tfm_file, mp_filetype_metrics) ) - mp_prompt_file_name(mp, "file name for font metrics",".tfm"); -mp->metric_file_name=xstrdup(mp->name_of_file); +if (mp->job_name == NULL) + mp_open_log_file (mp); +mp_pack_job_name (mp, ".tfm"); +while (!mp_b_open_out (mp, &mp->tfm_file, mp_filetype_metrics)) + mp_prompt_file_name (mp, "file name for font metrics", ".tfm"); +mp->metric_file_name = xstrdup (mp->name_of_file); @<Output the subfile sizes and header bytes@>; @<Output the character information bytes, then output the dimensions themselves@>; @<Output the ligature/kern program@>; @<Output the extensible character recipes and the font metric parameters@>; - if ( mp->internal[mp_tracing_stats]>0 ) +if (internal_value (mp_tracing_stats) > 0) @<Log the subfile sizes of the \.{TFM} file@>; -mp_print_nl(mp, "Font metrics written on "); -mp_print(mp, mp->metric_file_name); mp_print_char(mp, xord('.')); -@.Font metrics written...@> -(mp->close_file)(mp,mp->tfm_file) +mp_print_nl (mp, "Font metrics written on "); +mp_print (mp, mp->metric_file_name); +mp_print_char (mp, xord ('.')); +@.Font metrics written...@>; +(mp->close_file) (mp, mp->tfm_file) + @ Integer variables |lh|, |k|, and |lk_offset| will be defined when we use this code. @<Output the subfile sizes and header bytes@>= -k=mp->header_last; -LH=(k+4) / 4; /* this is the number of header words */ -if ( mp->bc>mp->ec ) mp->bc=1; /* if there are no characters, |ec=0| and |bc=1| */ +k = mp->header_last; +LH = (k + 4) / 4; /* this is the number of header words */ +if (mp->bc > mp->ec) + mp->bc = 1; /* if there are no characters, |ec=0| and |bc=1| */ @<Compute the ligature/kern program offset and implant the left boundary label@>; -mp_tfm_two(mp,6+LH+(mp->ec-mp->bc+1)+mp->nw+mp->nh+mp->nd+mp->ni+mp->nl - +lk_offset+mp->nk+mp->ne+mp->np); +mp_tfm_two (mp, + 6 + LH + (mp->ec - mp->bc + 1) + mp->nw + mp->nh + mp->nd + mp->ni + + mp->nl + lk_offset + mp->nk + mp->ne + mp->np); /* this is the total number of file words that will be output */ -mp_tfm_two(mp, LH); mp_tfm_two(mp, mp->bc); mp_tfm_two(mp, mp->ec); -mp_tfm_two(mp, mp->nw); mp_tfm_two(mp, mp->nh); -mp_tfm_two(mp, mp->nd); mp_tfm_two(mp, mp->ni); mp_tfm_two(mp, mp->nl+lk_offset); -mp_tfm_two(mp, mp->nk); mp_tfm_two(mp, mp->ne); -mp_tfm_two(mp, mp->np); -for (k=0;k< 4*LH;k++) { - tfm_out(mp->header_byte[k]); +mp_tfm_two (mp, LH); +mp_tfm_two (mp, mp->bc); +mp_tfm_two (mp, mp->ec); +mp_tfm_two (mp, mp->nw); +mp_tfm_two (mp, mp->nh); +mp_tfm_two (mp, mp->nd); +mp_tfm_two (mp, mp->ni); +mp_tfm_two (mp, mp->nl + lk_offset); +mp_tfm_two (mp, mp->nk); +mp_tfm_two (mp, mp->ne); +mp_tfm_two (mp, mp->np); +for (k = 0; k < 4 * LH; k++) { + tfm_out (mp->header_byte[k]); } + @ @<Output the character information bytes...@>= -for (k=mp->bc;k<=mp->ec;k++) { - if ( ! mp->char_exists[k] ) { - mp_tfm_four(mp, 0); - } else { - tfm_out(mp_info(mp->tfm_width[k])); /* the width index */ - tfm_out((mp_info(mp->tfm_height[k]))*16+mp_info(mp->tfm_depth[k])); - tfm_out((mp_info(mp->tfm_ital_corr[k]))*4+mp->char_tag[k]); - tfm_out(mp->char_remainder[k]); +for (k = mp->bc; k <= mp->ec; k++) { + if (!mp->char_exists[k]) { + mp_tfm_four (mp, 0); + } else { + tfm_out (mp_info (mp->tfm_width[k])); /* the width index */ + tfm_out ((mp_info (mp->tfm_height[k])) * 16 + mp_info (mp->tfm_depth[k])); + tfm_out ((mp_info (mp->tfm_ital_corr[k])) * 4 + mp->char_tag[k]); + tfm_out (mp->char_remainder[k]); }; } -mp->tfm_changed=0; -for (k=1;k<=4;k++) { - mp_tfm_four(mp, 0); p=mp->dimen_head[k]; - while ( p!=inf_val ) { - mp_tfm_four(mp, mp_dimen_out(mp, value(p))); p=mp_link(p); +mp->tfm_changed = 0; +for (k = 1; k <= 4; k++) { + mp_tfm_four (mp, 0); + p = mp->dimen_head[k]; + while (p != mp->inf_val) { + mp_tfm_four (mp, mp_dimen_out (mp, value (p))); + p = mp_link (p); } } @@ -25471,196 +29821,229 @@ starting addresses; we have $-1=|label_loc|[0]<|label_loc|[1]\le\cdots \le|label_loc|[|label_ptr]|$. @<Compute the ligature/kern program offset...@>= -mp->bchar=mp_round_unscaled(mp, mp->internal[mp_boundary_char]); -if ((mp->bchar<0)||(mp->bchar>255)) { - mp->bchar=-1; mp->lk_started=false; lk_offset=0; -} else { - mp->lk_started=true; lk_offset=1; +mp->bchar = mp_round_unscaled (mp, internal_value (mp_boundary_char)); +if ((mp->bchar < 0) || (mp->bchar > 255)) { + mp->bchar = -1; + mp->lk_started = false; + lk_offset = 0; +} else { + mp->lk_started = true; + lk_offset = 1; } @<Find the minimum |lk_offset| and adjust all remainders@>; -if ( mp->bch_label<undefined_label ) - { skip_byte(mp->nl)=qi(255); next_char(mp->nl)=qi(0); - op_byte(mp->nl)=qi(((mp->bch_label+lk_offset)/ 256)); - rem_byte(mp->nl)=qi(((mp->bch_label+lk_offset)% 256)); - mp->nl++; /* possibly |nl=lig_table_size+1| */ - } +if (mp->bch_label < undefined_label) { + skip_byte (mp->nl) = qi (255); + next_char (mp->nl) = qi (0); + op_byte (mp->nl) = qi (((mp->bch_label + lk_offset) / 256)); + rem_byte (mp->nl) = qi (((mp->bch_label + lk_offset) % 256)); + mp->nl++; /* possibly |nl=lig_table_size+1| */ +} @ @<Find the minimum |lk_offset|...@>= -k=mp->label_ptr; /* pointer to the largest unallocated label */ -if ( mp->label_loc[k]+lk_offset>255 ) { - lk_offset=0; mp->lk_started=false; /* location 0 can do double duty */ - do { - mp->char_remainder[mp->label_char[k]]=lk_offset; - while ( mp->label_loc[k-1]==mp->label_loc[k] ) { - decr(k); mp->char_remainder[mp->label_char[k]]=lk_offset; +k = mp->label_ptr; /* pointer to the largest unallocated label */ +if (mp->label_loc[k] + lk_offset > 255) { + lk_offset = 0; + mp->lk_started = false; /* location 0 can do double duty */ + do { + mp->char_remainder[mp->label_char[k]] = lk_offset; + while (mp->label_loc[k - 1] == mp->label_loc[k]) { + decr (k); + mp->char_remainder[mp->label_char[k]] = lk_offset; } - incr(lk_offset); decr(k); - } while (! (lk_offset+mp->label_loc[k]<256)); - /* N.B.: |lk_offset=256| satisfies this when |k=0| */ + incr (lk_offset); + decr (k); + } while (!(lk_offset + mp->label_loc[k] < 256)); + /* N.B.: |lk_offset=256| satisfies this when |k=0| */ } -if ( lk_offset>0 ) { - while ( k>0 ) { +if (lk_offset > 0) { + while (k > 0) { mp->char_remainder[mp->label_char[k]] - =mp->char_remainder[mp->label_char[k]]+lk_offset; - decr(k); + = mp->char_remainder[mp->label_char[k]] + lk_offset; + decr (k); } } @ @<Output the ligature/kern program@>= -for (k=0;k<= 255;k++ ) { - if ( mp->skip_table[k]<undefined_label ) { - mp_print_nl(mp, "(local label "); mp_print_int(mp, k); mp_print(mp, ":: was missing)"); -@.local label l:: was missing@> - cancel_skips(mp->skip_table[k]); - } -} -if ( mp->lk_started ) { /* |lk_offset=1| for the special |bchar| */ - tfm_out(255); tfm_out(mp->bchar); mp_tfm_two(mp, 0); +for (k = 0; k <= 255; k++) { + if (mp->skip_table[k] < undefined_label) { + mp_print_nl (mp, "(local label "); + mp_print_int (mp, k); + mp_print (mp, ":: was missing)"); +@.local label l:: was missing@>; + cancel_skips (mp->skip_table[k]); + } +} +if (mp->lk_started) { /* |lk_offset=1| for the special |bchar| */ + tfm_out (255); + tfm_out (mp->bchar); + mp_tfm_two (mp, 0); } else { - for (k=1;k<=lk_offset;k++) {/* output the redirection specs */ - mp->ll=mp->label_loc[mp->label_ptr]; - if ( mp->bchar<0 ) { tfm_out(254); tfm_out(0); } - else { tfm_out(255); tfm_out(mp->bchar); }; - mp_tfm_two(mp, mp->ll+lk_offset); - do { + for (k = 1; k <= lk_offset; k++) { /* output the redirection specs */ + mp->ll = mp->label_loc[mp->label_ptr]; + if (mp->bchar < 0) { + tfm_out (254); + tfm_out (0); + } else { + tfm_out (255); + tfm_out (mp->bchar); + }; + mp_tfm_two (mp, mp->ll + lk_offset); + do { mp->label_ptr--; - } while (! (mp->label_loc[mp->label_ptr]<mp->ll)); + } while (!(mp->label_loc[mp->label_ptr] < mp->ll)); } } -for (k=0;k<mp->nl;k++) mp_tfm_qqqq(mp, mp->lig_kern[k]); -for (k=0;k<mp->nk;k++) mp_tfm_four(mp, mp_dimen_out(mp, mp->kern[k])) +for (k = 0; k < mp->nl; k++) + mp_tfm_qqqq (mp, mp->lig_kern[k]); +for (k = 0; k < mp->nk; k++) + mp_tfm_four (mp, mp_dimen_out (mp, mp->kern[k])) + @ @<Output the extensible character recipes...@>= -for (k=0;k<mp->ne;k++) - mp_tfm_qqqq(mp, mp->exten[k]); -for (k=1;k<=mp->np;k++) { - if ( k==1 ) { - if ( abs(mp->param[1])<fraction_half ) { - mp_tfm_four(mp, mp->param[1]*16); - } else { - incr(mp->tfm_changed); - if ( mp->param[1]>0 ) mp_tfm_four(mp, el_gordo); - else mp_tfm_four(mp, -el_gordo); +for (k = 0; k < mp->ne; k++) + mp_tfm_qqqq (mp, mp->exten[k]); +for (k = 1; k <= mp->np; k++) { + if (k == 1) { + if (abs (mp->param[1]) < fraction_half) { + mp_tfm_four (mp, mp->param[1] * 16); + } else { + incr (mp->tfm_changed); + if (mp->param[1] > 0) + mp_tfm_four (mp, max_integer); + else + mp_tfm_four (mp, -max_integer); } } else { - mp_tfm_four(mp, mp_dimen_out(mp, mp->param[k])); + mp_tfm_four (mp, mp_dimen_out (mp, mp->param[k])); } } -if ( mp->tfm_changed>0 ) { - if ( mp->tfm_changed==1 ) mp_print_nl(mp, "(a font metric dimension"); +if (mp->tfm_changed > 0) { + if (mp->tfm_changed == 1) { + mp_print_nl (mp, "(a font metric dimension"); @.a font metric dimension...@> - else { - mp_print_nl(mp, "("); mp_print_int(mp, mp->tfm_changed); -@.font metric dimensions...@> - mp_print(mp, " font metric dimensions"); + } else { + mp_print_nl (mp, "("); + mp_print_int (mp, mp->tfm_changed); +@.font metric dimensions...@>; + mp_print (mp, " font metric dimensions"); } - mp_print(mp, " had to be decreased)"); + mp_print (mp, " had to be decreased)"); } @ @<Log the subfile sizes of the \.{TFM} file@>= -{ +{ char s[200]; - wlog_ln(" "); - if ( mp->bch_label<undefined_label ) mp->nl--; - mp_snprintf(s,128,"(You used %iw,%ih,%id,%ii,%il,%ik,%ie,%ip metric file positions)", - mp->nw, mp->nh, mp->nd, mp->ni, mp->nl, mp->nk, mp->ne, mp->np); - wlog_ln(s); + wlog_ln (" "); + if (mp->bch_label < undefined_label) + mp->nl--; + mp_snprintf (s, 128, + "(You used %iw,%ih,%id,%ii,%il,%ik,%ie,%ip metric file positions)", + mp->nw, mp->nh, mp->nd, mp->ni, mp->nl, mp->nk, mp->ne, mp->np); + wlog_ln (s); } -@* \[43] Reading font metric data. + +@* Reading font metric data. \MP\ isn't a typesetting program but it does need to find the bounding box of a sequence of typeset characters. Thus it needs to read \.{TFM} files as well as write them. @<Glob...@>= -void * tfm_infile; +void *tfm_infile; @ All the width, height, and depth information is stored in an array called |font_info|. This array is allocated sequentially and each font is stored as a series of |char_info| words followed by the width, height, and depth tables. Since |font_name| entries are permanent, their |str_ref| values are -set to |max_str_ref|. +set to |MAX_STR_REF|. @<Types...@>= -typedef unsigned int font_number; /* |0..font_max| */ +typedef unsigned int font_number; /* |0..font_max| */ @ The |font_info| array is indexed via a group directory arrays. For example, the |char_info| data for character~|c| in font~|f| will be in |font_info[char_base[f]+c].qqqq|. @<Glob...@>= -font_number font_max; /* maximum font number for included text fonts */ -size_t font_mem_size; /* number of words for \.{TFM} information for text fonts */ -memory_word *font_info; /* height, width, and depth data */ -char **font_enc_name; /* encoding names, if any */ -boolean *font_ps_name_fixed; /* are the postscript names fixed already? */ -size_t next_fmem; /* next unused entry in |font_info| */ -font_number last_fnum; /* last font number used so far */ -scaled *font_dsize; /* 16 times the ``design'' size in \ps\ points */ -char **font_name; /* name as specified in the \&{infont} command */ -char **font_ps_name; /* PostScript name for use when |internal[mp_prologues]>0| */ -font_number last_ps_fnum; /* last valid |font_ps_name| index */ -eight_bits *font_bc; -eight_bits *font_ec; /* first and last character code */ -int *char_base; /* base address for |char_info| */ -int *width_base; /* index for zeroth character width */ -int *height_base; /* index for zeroth character height */ -int *depth_base; /* index for zeroth character depth */ -pointer *font_sizes; +font_number font_max; /* maximum font number for included text fonts */ +size_t font_mem_size; /* number of words for \.{TFM} information for text fonts */ +font_data *font_info; /* height, width, and depth data */ +char **font_enc_name; /* encoding names, if any */ +boolean *font_ps_name_fixed; /* are the postscript names fixed already? */ +size_t next_fmem; /* next unused entry in |font_info| */ +font_number last_fnum; /* last font number used so far */ +integer *font_dsize; /* 16 times the ``design'' size in \ps\ points */ +char **font_name; /* name as specified in the \&{infont} command */ +char **font_ps_name; /* PostScript name for use when |internal[mp_prologues]>0| */ +font_number last_ps_fnum; /* last valid |font_ps_name| index */ +eight_bits *font_bc; +eight_bits *font_ec; /* first and last character code */ +int *char_base; /* base address for |char_info| */ +int *width_base; /* index for zeroth character width */ +int *height_base; /* index for zeroth character height */ +int *depth_base; /* index for zeroth character depth */ +mp_node *font_sizes; @ @<Allocate or initialize ...@>= -mp->font_mem_size = 10000; -mp->font_info = xmalloc ((mp->font_mem_size+1),sizeof(memory_word)); -memset (mp->font_info,0,sizeof(memory_word)*(mp->font_mem_size+1)); +mp->font_mem_size = 10000; +mp->font_info = xmalloc ((mp->font_mem_size + 1), sizeof (font_data)); +memset (mp->font_info, 0, sizeof (font_data) * (mp->font_mem_size + 1)); mp->last_fnum = null_font; @ @<Dealloc variables@>= -for (k=1;k<=(int)mp->last_fnum;k++) { - xfree(mp->font_enc_name[k]); - xfree(mp->font_name[k]); - xfree(mp->font_ps_name[k]); -} -xfree(mp->font_info); -xfree(mp->font_enc_name); -xfree(mp->font_ps_name_fixed); -xfree(mp->font_dsize); -xfree(mp->font_name); -xfree(mp->font_ps_name); -xfree(mp->font_bc); -xfree(mp->font_ec); -xfree(mp->char_base); -xfree(mp->width_base); -xfree(mp->height_base); -xfree(mp->depth_base); -xfree(mp->font_sizes); +for (k = 1; k <= (int) mp->last_fnum; k++) { + xfree (mp->font_enc_name[k]); + xfree (mp->font_name[k]); + xfree (mp->font_ps_name[k]); +} +for (k = 0; k < TFM_ITEMS; k++) { + mp_xfree (mp->tfm_width[k]); + mp_xfree (mp->tfm_height[k]); + mp_xfree (mp->tfm_depth[k]); + mp_xfree (mp->tfm_ital_corr[k]); +} +xfree (mp->font_info); +xfree (mp->font_enc_name); +xfree (mp->font_ps_name_fixed); +xfree (mp->font_dsize); +xfree (mp->font_name); +xfree (mp->font_ps_name); +xfree (mp->font_bc); +xfree (mp->font_ec); +xfree (mp->char_base); +xfree (mp->width_base); +xfree (mp->height_base); +xfree (mp->depth_base); +xfree (mp->font_sizes); @ -@c +@c void mp_reallocate_fonts (MP mp, font_number l) { font_number f; - XREALLOC(mp->font_enc_name, l, char *); - XREALLOC(mp->font_ps_name_fixed, l, boolean); - XREALLOC(mp->font_dsize, l, scaled); - XREALLOC(mp->font_name, l, char *); - XREALLOC(mp->font_ps_name, l, char *); - XREALLOC(mp->font_bc, l, eight_bits); - XREALLOC(mp->font_ec, l, eight_bits); - XREALLOC(mp->char_base, l, int); - XREALLOC(mp->width_base, l, int); - XREALLOC(mp->height_base, l, int); - XREALLOC(mp->depth_base, l, int); - XREALLOC(mp->font_sizes, l, pointer); - for (f=(mp->last_fnum+1);f<=l;f++) { - mp->font_enc_name[f]=NULL; + XREALLOC (mp->font_enc_name, l, char *); + XREALLOC (mp->font_ps_name_fixed, l, boolean); + XREALLOC (mp->font_dsize, l, integer); + XREALLOC (mp->font_name, l, char *); + XREALLOC (mp->font_ps_name, l, char *); + XREALLOC (mp->font_bc, l, eight_bits); + XREALLOC (mp->font_ec, l, eight_bits); + XREALLOC (mp->char_base, l, int); + XREALLOC (mp->width_base, l, int); + XREALLOC (mp->height_base, l, int); + XREALLOC (mp->depth_base, l, int); + XREALLOC (mp->font_sizes, l, mp_node); + for (f = (mp->last_fnum + 1); f <= l; f++) { + mp->font_enc_name[f] = NULL; mp->font_ps_name_fixed[f] = false; - mp->font_name[f]=NULL; - mp->font_ps_name[f]=NULL; - mp->font_sizes[f]=null; + mp->font_name[f] = NULL; + mp->font_ps_name[f] = NULL; + mp->font_sizes[f] = NULL; } mp->font_max = l; } + @ @<Internal library declarations@>= void mp_reallocate_fonts (MP mp, font_number l); @@ -25673,21 +30056,25 @@ wasting a lot of space. @d null_font 0 /* the |font_number| for an empty font */ @<Set initial...@>= -mp->font_dsize[null_font]=0; -mp->font_bc[null_font]=1; -mp->font_ec[null_font]=0; -mp->char_base[null_font]=0; -mp->width_base[null_font]=0; -mp->height_base[null_font]=0; -mp->depth_base[null_font]=0; -mp->next_fmem=0; -mp->last_fnum=null_font; -mp->last_ps_fnum=null_font; -mp->font_name[null_font]=mp_xstrdup(mp,"nullfont"); -mp->font_ps_name[null_font]=mp_xstrdup(mp,""); +mp->font_dsize[null_font] = 0; +mp->font_bc[null_font] = 1; +mp->font_ec[null_font] = 0; +mp->char_base[null_font] = 0; +mp->width_base[null_font] = 0; +mp->height_base[null_font] = 0; +mp->depth_base[null_font] = 0; +mp->next_fmem = 0; +mp->last_fnum = null_font; +mp->last_ps_fnum = null_font; +{ + static char nullfont_name[] = "nullfont"; + static char nullfont_psname[] = ""; + mp->font_name[null_font] = nullfont_name; + mp->font_ps_name[null_font] = nullfont_psname; +} mp->font_ps_name_fixed[null_font] = false; -mp->font_enc_name[null_font]=NULL; -mp->font_sizes[null_font]=null; +mp->font_enc_name[null_font] = NULL; +mp->font_sizes[null_font] = NULL; @ Each |char_info| word is of type |four_quarters|. The |b0| field contains the |width index|; the |b1| field contains the height @@ -25711,216 +30098,202 @@ $$\hbox{|char_width(f,char_mp_info(f,c)).sc|.}$$ we scan the |font_name| array before calling |read_font_info|. @<Declarations@>= -static font_number mp_find_font (MP mp, char *f) ; +static font_number mp_find_font (MP mp, char *f); @ @c font_number mp_find_font (MP mp, char *f) { font_number n; - for (n=0;n<=mp->last_fnum;n++) { - if (mp_xstrcmp(f,mp->font_name[n])==0 ) { - mp_xfree(f); + for (n = 0; n <= mp->last_fnum; n++) { + if (mp_xstrcmp (f, mp->font_name[n]) == 0) { return n; } } - n = mp_read_font_info(mp, f); - mp_xfree(f); + n = mp_read_font_info (mp, f); return n; } + @ This is an interface function for getting the width of character, as a double in ps units -@c double mp_get_char_dimension (MP mp, const char *fname, int c, int t) { +@c +double mp_get_char_dimension (MP mp, char *fname, int c, int t) { unsigned n; four_quarters cc; font_number f = 0; double w = -1.0; - for (n=0;n<=mp->last_fnum;n++) { - if (mp_xstrcmp(fname,mp->font_name[n])==0 ) { + for (n = 0; n <= mp->last_fnum; n++) { + if (mp_xstrcmp (fname, mp->font_name[n]) == 0) { f = n; break; } } - if (f==0) + if (f == 0) return 0.0; - cc = char_mp_info(f,c); - if (! ichar_exists(cc) ) + cc = char_mp_info (f, c); + if (!ichar_exists (cc)) return 0.0; - if (t=='w') - w = (double)char_width(f,cc); - else if (t=='h') - w = (double)char_height(f,cc); - else if (t=='d') - w = (double)char_depth(f,cc); - return w/655.35*(72.27/72); + if (t == 'w') + w = (double) char_width (f, cc); + else if (t == 'h') + w = (double) char_height (f, cc); + else if (t == 'd') + w = (double) char_depth (f, cc); + return w / 655.35 * (72.27 / 72); } + @ @<Exported function ...@>= -double mp_get_char_dimension (MP mp, const char *fname, int n, int t); +double mp_get_char_dimension (MP mp, char *fname, int n, int t); @ One simple application of |find_font| is the implementation of the |font_size| operator that gets the design size for a given font name. @<Find the design size of the font whose name is |cur_exp|@>= -mp_flush_cur_exp(mp, (mp->font_dsize[mp_find_font(mp, str(mp->cur_exp))]+8) / 16) +{ + new_expr.data.val = + (mp->font_dsize[mp_find_font (mp, mp_str (mp, cur_exp_str ()))] + 8) / 16; + mp_flush_cur_exp (mp, new_expr); +} + @ If we discover that the font doesn't have a requested character, we omit it from the bounding box computation and expect the \ps\ interpreter to drop it. This routine issues a warning message if the user has asked for it. @<Declarations@>= -static void mp_lost_warning (MP mp,font_number f, pool_pointer k); +static void mp_lost_warning (MP mp, font_number f, int k); -@ @c -void mp_lost_warning (MP mp,font_number f, pool_pointer k) { - if ( mp->internal[mp_tracing_lost_chars]>0 ) { - mp_begin_diagnostic(mp); - if ( mp->selector==log_only ) incr(mp->selector); - mp_print_nl(mp, "Missing character: There is no "); -@.Missing character@> - mp_print_str(mp, mp->str_pool[k]); - mp_print(mp, " in font "); - mp_print(mp, mp->font_name[f]); mp_print_char(mp, xord('!')); - mp_end_diagnostic(mp, false); +@ @c +void mp_lost_warning (MP mp, font_number f, int k) { + if (internal_value (mp_tracing_lost_chars) > 0) { + mp_begin_diagnostic (mp); + if (mp->selector == log_only) + incr (mp->selector); + mp_print_nl (mp, "Missing character: There is no "); +@.Missing character@>; + mp_print_int (mp, k); + mp_print (mp, " in font "); + mp_print (mp, mp->font_name[f]); + mp_print_char (mp, xord ('!')); + mp_end_diagnostic (mp, false); } } + @ The whole purpose of saving the height, width, and depth information is to be able to find the bounding box of an item of text in an edge structure. The |set_text_box| procedure takes a text node and adds this information. @<Declarations@>= -static void mp_set_text_box (MP mp,pointer p); - -@ @c -void mp_set_text_box (MP mp,pointer p) { - font_number f; /* |mp_font_n(p)| */ - ASCII_code bc,ec; /* range of valid characters for font |f| */ - pool_pointer k,kk; /* current character and character to stop at */ - four_quarters cc; /* the |char_info| for the current character */ - scaled h,d; /* dimensions of the current character */ - width_val(p)=0; - height_val(p)=-el_gordo; - depth_val(p)=-el_gordo; - f=(font_number)mp_font_n(p); - bc=mp->font_bc[f]; - ec=mp->font_ec[f]; - kk=str_stop(mp_text_p(p)); - k=mp->str_start[mp_text_p(p)]; - while ( k<kk ) { +static void mp_set_text_box (MP mp, mp_node p); + +@ @c +void mp_set_text_box (MP mp, mp_node p) { + font_number f; /* |mp_font_n(p)| */ + ASCII_code bc, ec; /* range of valid characters for font |f| */ + size_t k, kk; /* current character and character to stop at */ + four_quarters cc; /* the |char_info| for the current character */ + scaled h, d; /* dimensions of the current character */ + width_val (p) = 0; + height_val (p) = -EL_GORDO; + depth_val (p) = -EL_GORDO; + f = (font_number) mp_font_n (p); + bc = mp->font_bc[f]; + ec = mp->font_ec[f]; + kk = length (mp_text_p (p)); + k = 0; + while (k < kk) { @<Adjust |p|'s bounding box to contain |str_pool[k]|; advance |k|@>; } @<Set the height and depth to zero if the bounding box is empty@>; } + @ @<Adjust |p|'s bounding box to contain |str_pool[k]|; advance |k|@>= -{ - if ( (mp->str_pool[k]<bc)||(mp->str_pool[k]>ec) ) { - mp_lost_warning(mp, f,k); - } else { - cc=char_mp_info(f,mp->str_pool[k]); - if ( ! ichar_exists(cc) ) { - mp_lost_warning(mp, f,k); - } else { - width_val(p)=width_val(p)+char_width(f,cc); - h=char_height(f,cc); - d=char_depth(f,cc); - if ( h>height_val(p) ) height_val(p)=h; - if ( d>depth_val(p) ) depth_val(p)=d; +{ + if ((*(mp_text_p (p)->str + k) < bc) || (*(mp_text_p (p)->str + k) > ec)) { + mp_lost_warning (mp, f, *(mp_text_p (p)->str + k)); + } else { + cc = char_mp_info (f, *(mp_text_p (p)->str + k)); + if (!ichar_exists (cc)) { + mp_lost_warning (mp, f, *(mp_text_p (p)->str + k)); + } else { + width_val (p) = width_val (p) + char_width (f, cc); + h = char_height (f, cc); + d = char_depth (f, cc); + if (h > height_val (p)) + height_val (p) = h; + if (d > depth_val (p)) + depth_val (p) = d; } } - incr(k); + incr (k); } + @ Let's hope modern compilers do comparisons correctly when the difference would overflow. @<Set the height and depth to zero if the bounding box is empty@>= -if ( height_val(p)<-depth_val(p) ) { - height_val(p)=0; - depth_val(p)=0; +if (height_val (p) < -depth_val (p)) { + height_val (p) = 0; + depth_val (p) = 0; } @ The new primitives fontmapfile and fontmapline. @<Declare action procedures for use by |do_statement|@>= -static void mp_do_mapfile (MP mp) ; -static void mp_do_mapline (MP mp) ; +static void mp_do_mapfile (MP mp); +static void mp_do_mapline (MP mp); -@ @c -static void mp_do_mapfile (MP mp) { - mp_get_x_next(mp); mp_scan_expression(mp); - if ( mp->cur_type!=mp_string_type ) { +@ @c +static void mp_do_mapfile (MP mp) { + mp_get_x_next (mp); + mp_scan_expression (mp); + if (mp->cur_exp.type != mp_string_type) { @<Complain about improper map operation@>; } else { - mp_map_file(mp,mp->cur_exp); + mp_map_file (mp, cur_exp_str ()); } } -static void mp_do_mapline (MP mp) { - mp_get_x_next(mp); mp_scan_expression(mp); - if ( mp->cur_type!=mp_string_type ) { - @<Complain about improper map operation@>; - } else { - mp_map_line(mp,mp->cur_exp); +static void mp_do_mapline (MP mp) { + mp_get_x_next (mp); + mp_scan_expression (mp); + if (mp->cur_exp.type != mp_string_type) { + @<Complain about improper map operation@>; + } else { + mp_map_line (mp, cur_exp_str ()); } } + @ @<Complain about improper map operation@>= -{ - exp_err("Unsuitable expression"); - help1("Only known strings can be map files or map lines."); - mp_put_get_error(mp); +{ + exp_err ("Unsuitable expression"); + help1 ("Only known strings can be map files or map lines."); + mp_put_get_error (mp); } + @ To print |scaled| value to PDF output we need some subroutines to ensure accurary. @d max_integer 0x7FFFFFFF /* $2^{31}-1$ */ @<Glob...@>= -scaled one_bp; /* scaled value corresponds to 1bp */ -scaled one_hundred_bp; /* scaled value corresponds to 100bp */ -scaled one_hundred_inch; /* scaled value corresponds to 100in */ -integer ten_pow[10]; /* $10^0..10^9$ */ -integer scaled_out; /* amount of |scaled| that was taken out in |divide_scaled| */ +integer ten_pow[10]; /* $10^0..10^9$ */ +integer scaled_out; /* amount of |scaled| that was taken out in |divide_scaled| */ @ @<Set init...@>= -mp->one_bp = 65782; /* 65781.76 */ -mp->one_hundred_bp = 6578176; -mp->one_hundred_inch = 473628672; mp->ten_pow[0] = 1; -for (i = 1;i<= 9; i++ ) { - mp->ten_pow[i] = 10*mp->ten_pow[i - 1]; +for (i = 1; i <= 9; i++) { + mp->ten_pow[i] = 10 * mp->ten_pow[i - 1]; } -@ The following function divides |s| by |m|. |dd| is number of decimal digits. - -@c -#if 0 -scaled mp_divide_scaled (MP mp,scaled s, scaled m, integer dd) { - scaled q,r; - integer sign,i; - sign = 1; - if ( s < 0 ) { sign = -sign; s = -s; } - if ( m < 0 ) { sign = -sign; m = -m; } - if ( m == 0 ) - mp_confusion(mp, "arithmetic: divided by zero"); - else if ( m >= (max_integer / 10) ) - mp_confusion(mp, "arithmetic: number too big"); - q = s / m; - r = s % m; - for (i = 1;i<=dd;i++) { - q = 10*q + (10*r) / m; - r = (10*r) % m; - } - if ( 2*r >= m ) { incr(q); r = r - m; } - mp->scaled_out = sign*(s - (r / mp->ten_pow[dd])); - return (sign*q); -} -#endif -@* \[44] Shipping pictures out. +@* Shipping pictures out. The |ship_out| procedure, to be described below, is given a pointer to an edge structure. Its mission is to output a file containing the \ps\ description of an edge structure. @@ -25934,212 +30307,221 @@ There are just too many variable accesses needed for status reporting etcetera to make it worthwile to move the code to |psout.w|. @<Internal library declarations@>= -void mp_open_output_file (MP mp) ; +void mp_open_output_file (MP mp); -@ @c -static void mp_append_to_template (MP mp, integer ff, integer c) { - if (mp->int_type[c]==mp_string_type) { - char *ss = str(mp->internal[c]); - mp_print(mp,ss); - mp_xfree(ss); - } else if (mp->int_type[c]==mp_known) { - integer cc = mp_round_unscaled(mp, mp->internal[c]); - print_with_leading_zeroes(cc, ff); +@ @c +static void mp_append_to_template (MP mp, integer ff, integer c, boolean rounding) { + if (internal_type (c) == mp_string_type) { + char *ss = mp_str (mp, internal_string (c)); + mp_print (mp, ss); + } else if (internal_type (c) == mp_known) { + if (rounding) { + integer cc = mp_round_unscaled (mp, internal_value (c)); + print_with_leading_zeroes (cc, ff); + } else { + mp_print_scaled (mp, internal_value (c)); + } } } static char *mp_set_output_file_name (MP mp, integer c) { - char *ss = NULL; /* filename extension proposal */ - char *nn = NULL; /* temp string for str() */ + char *ss = NULL; /* filename extension proposal */ + char *nn = NULL; /* temp string for str() */ unsigned old_setting; /* previous |selector| setting */ - pool_pointer i; /* indexes into |filename_template| */ - integer f; /* field width */ - str_room(1024); /* have to prevent reallocation of the - string pool during this template processing */ - if ( mp->job_name==NULL ) mp_open_log_file(mp); - if ( mp->internal[mp_output_template]==0) { - char *s; /* a file extension derived from |c| */ - if ( c<0 ) - s=xstrdup(".ps"); - else + size_t i; /* indexes into |filename_template| */ + integer f; /* field width */ + str_room (1024); + if (mp->job_name == NULL) + mp_open_log_file (mp); + if (internal_string (mp_output_template) == NULL) { + char *s; /* a file extension derived from |c| */ + if (c < 0) + s = xstrdup (".ps"); + else @<Use |c| to compute the file extension |s|@>; - mp_pack_job_name(mp, s); - free(s); - ss = xstrdup(mp->name_of_file); - } else { /* initializations */ - str_number s, n; /* a file extension derived from |c| */ - scaled saved_char_code = mp->internal[mp_char_code]; - mp->internal[mp_char_code] = (c*unity); - if (mp->internal[mp_job_name]==0) { - if ( mp->job_name==NULL ) { - mp->job_name=xstrdup("mpout"); + mp_pack_job_name (mp, s); + free (s); + ss = xstrdup (mp->name_of_file); + } else { /* initializations */ + str_number s, n, template; /* a file extension derived from |c| */ + scaled saved_char_code = internal_value (mp_char_code); + internal_value (mp_char_code) = (c * unity); + if (internal_string (mp_job_name) == NULL) { + if (mp->job_name == NULL) { + mp->job_name = xstrdup ("mpout"); } @<Fix up |mp->internal[mp_job_name]|@>; } - old_setting=mp->selector; - mp->selector=new_string; - i = mp->str_start[mp->internal[mp_output_template]]; - n = null_str; /* initialize */ - while ( i<str_stop(mp->internal[mp_output_template]) ) { - f=0; - if ( mp->str_pool[i]=='%' ) { + old_setting = mp->selector; + mp->selector = new_string; + i = 0; + n = null_str; /* initialize */ + template = internal_string (mp_output_template); + while (i < length (template)) { + f = 0; + if (*(template->str + i) == '%') { CONTINUE: - incr(i); - if ( i<str_stop(mp->internal[mp_output_template]) ) { - switch (mp->str_pool[i]) { - case 'j': - mp_append_to_template(mp,f,mp_job_name); - break; - case 'c': - if (mp->internal[mp_char_code]<0) { - mp_print(mp,"ps"); + incr (i); + if (i < length (template)) { + switch (*(template->str + i)) { + case 'j': + mp_append_to_template (mp, f, mp_job_name, true); + break; + case 'c': + if (internal_value (mp_char_code) < 0) { + mp_print (mp, "ps"); } else { - mp_append_to_template(mp,f,mp_char_code); + mp_append_to_template (mp, f, mp_char_code, true); } break; - case 'o': - mp_append_to_template(mp,f,mp_output_format); + case 'o': + mp_append_to_template (mp, f, mp_output_format, true); break; - case 'd': - mp_append_to_template(mp,f,mp_day); + case 'd': + mp_append_to_template (mp, f, mp_day, true); break; - case 'm': - mp_append_to_template(mp,f,mp_month); + case 'm': + mp_append_to_template (mp, f, mp_month, true); break; - case 'y': - mp_append_to_template(mp,f,mp_year); + case 'y': + mp_append_to_template (mp, f, mp_year, true); break; - case 'H': - mp_append_to_template(mp,f,mp_hour); + case 'H': + mp_append_to_template (mp, f, mp_hour, true); break; - case 'M': - mp_append_to_template(mp,f,mp_minute); + case 'M': + mp_append_to_template (mp, f, mp_minute, true); break; case '{': - { - /* look up a name */ - integer l=0; - integer frst = i+1; - while ( i<str_stop(mp->internal[mp_output_template]) ) { + { + /* look up a name */ + size_t l = 0; + size_t frst = i + 1; + while (i < length (template)) { i++; - if (mp->str_pool[i] == '}') + if (*(template->str + i) == '}') break; l++; } - if (l>0) { - integer h = mp_compute_hash(mp, (char *)(mp->str_pool+frst),l); - pointer p=h+hash_base; /* we start searching here */ - char *id = xmalloc(mp, (size_t)(l+1)); - (void)memcpy(id,(char *)(mp->str_pool+frst),(size_t)l); - *(id+l)=0; - while (true) { - if (text(p)>0 && length(text(p))==l && - mp_str_eq_cstr(mp, text(p),id)) { - if (eq_type(p)==internal_quantity) { - if (equiv(p)==mp_output_template) { - char err[256]; - mp_snprintf(err,256, - "The appearance of outputtemplate inside outputtemplate is ignored."); - mp_warn(mp,err); - } else { - mp_append_to_template(mp,f,equiv(p)); - } + if (l > 0) { + mp_sym p = + mp_id_lookup (mp, (char *) (template->str + frst), l, false); + char *id = xmalloc (mp, (size_t) (l + 1)); + (void) memcpy (id, (char *) (template->str + frst), (size_t) l); + *(id + l) = '\0'; + if (p == NULL) { + char err[256]; + mp_snprintf (err, 256, + "requested identifier (%s) in outputtemplate not found.", + id); + mp_warn (mp, err); + } else { + if (eq_type (p) == internal_quantity) { + if (equiv (p) == mp_output_template) { + char err[256]; + mp_snprintf (err, 256, + "The appearance of outputtemplate inside outputtemplate is ignored."); + mp_warn (mp, err); } else { - char err[256]; - mp_snprintf(err,256, - "requested identifier (%s) in outputtemplate is not an internal.",id); - mp_warn(mp,err); + mp_append_to_template (mp, f, equiv (p), false); } - break; - } - if ( mp_next(p)==0 ) { + } else { char err[256]; - mp_snprintf(err,256, - "requested identifier (%s) in outputtemplate not found.",id); - mp_warn(mp,err); - break; + mp_snprintf (err, 256, + "requested identifier (%s) in outputtemplate is not an internal.", + id); + mp_warn (mp, err); } - p=mp_next(p); } - free(id); + free (id); } } - break; - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - if ( (f<10) ) - f = (f*10) + mp->str_pool[i]-'0'; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if ((f < 10)) + f = (f * 10) + template->str[i] - '0'; goto CONTINUE; - break; + break; case '%': - mp_print_str(mp, mp->str_pool[i]); - break; + mp_print_char (mp, '%'); + break; default: { char err[256]; - mp_snprintf(err,256, - "requested format (%c) in outputtemplate is unknown.",mp->str_pool[i]); - mp_warn(mp,err); + mp_snprintf (err, 256, + "requested format (%c) in outputtemplate is unknown.", + *(template->str + i)); + mp_warn (mp, err); } - mp_print_str(mp, mp->str_pool[i]); + mp_print_char (mp, *(template->str + i)); } } } else { - if ( mp->str_pool[i]=='.' ) - if (length(n)==0) - n = mp_make_string(mp); - mp_print_str(mp, mp->str_pool[i]); + if (*(template->str + i) == '.') + if (length (n) == 0) + n = mp_make_string (mp); + mp_print_char (mp, *(template->str + i)); }; - incr(i); + incr (i); } - s = mp_make_string(mp); - mp->internal[mp_char_code] = saved_char_code; - mp->selector= old_setting; - if (length(n)==0) { - n=s; - s=null_str; + s = mp_make_string (mp); + internal_value (mp_char_code) = saved_char_code; + mp->selector = old_setting; + if (length (n) == 0) { + n = s; + s = null_str; } - ss = str(s); - nn = str(n); - mp_pack_file_name(mp, nn,"",ss); - mp_xfree(nn); - delete_str_ref(n); - delete_str_ref(s); + ss = mp_str (mp, s); + nn = mp_str (mp, n); + mp_pack_file_name (mp, nn, "", ss); + delete_str_ref (n); + delete_str_ref (s); } return ss; } - -static char * mp_get_output_file_name (MP mp) { +static char *mp_get_output_file_name (MP mp) { char *f; - char *saved_name; /* saved |name_of_file| */ - saved_name = xstrdup(mp->name_of_file); - (void)mp_set_output_file_name(mp, mp_round_unscaled(mp, mp->internal[mp_char_code])); - f = xstrdup(mp->name_of_file); - mp_pack_file_name(mp, saved_name,NULL,NULL); - free(saved_name); + char *saved_name; /* saved |name_of_file| */ + saved_name = xstrdup (mp->name_of_file); + (void) mp_set_output_file_name (mp, + mp_round_unscaled (mp, + internal_value + (mp_char_code))); + f = xstrdup (mp->name_of_file); + mp_pack_file_name (mp, saved_name, NULL, NULL); + free (saved_name); return f; } - void mp_open_output_file (MP mp) { - char *ss; /* filename extension proposal */ - integer c; /* \&{charcode} rounded to the nearest integer */ - c=mp_round_unscaled(mp, mp->internal[mp_char_code]); - ss = mp_set_output_file_name(mp, c); - while ( ! mp_a_open_out(mp, (void *)&mp->output_file, mp_filetype_postscript) ) - mp_prompt_file_name(mp, "file name for output",ss); - xfree(ss); + char *ss; /* filename extension proposal */ + integer c; /* \&{charcode} rounded to the nearest integer */ + c = mp_round_unscaled (mp, internal_value (mp_char_code)); + ss = mp_set_output_file_name (mp, c); + while (!mp_a_open_out (mp, (void *) &mp->output_file, mp_filetype_postscript)) + mp_prompt_file_name (mp, "file name for output", ss); @<Store the true output file name if appropriate@>; } + @ The file extension created here could be up to five characters long in extreme cases so it may have to be shortened on some systems. @^system dependencies@> @<Use |c| to compute the file extension |s|@>= -{ - s = xmalloc(7,1); - mp_snprintf(s,7,".%i",(int)c); +{ + s = xmalloc (7, 1); + mp_snprintf (s, 7, ".%i", (int) c); } + @ The user won't want to see all the output file names so we only save the first and last ones and a count of how many there were. For this purpose files are ordered primarily by \&{charcode} and secondarily by order of @@ -26147,145 +30529,161 @@ creation. @:char_code_}{\&{charcode} primitive@> @<Store the true output file name if appropriate@>= -if ((c<mp->first_output_code)&&(mp->first_output_code>=0)) { - mp->first_output_code=c; - xfree(mp->first_file_name); - mp->first_file_name=xstrdup(mp->name_of_file); +if ((c < mp->first_output_code) && (mp->first_output_code >= 0)) { + mp->first_output_code = c; + xfree (mp->first_file_name); + mp->first_file_name = xstrdup (mp->name_of_file); } -if ( c>=mp->last_output_code ) { - mp->last_output_code=c; - xfree(mp->last_file_name); - mp->last_file_name=xstrdup(mp->name_of_file); +if (c >= mp->last_output_code) { + mp->last_output_code = c; + xfree (mp->last_file_name); + mp->last_file_name = xstrdup (mp->name_of_file); } @ @<Glob...@>= -char * first_file_name; -char * last_file_name; /* full file names */ -integer first_output_code;integer last_output_code; /* rounded \&{charcode} values */ +char *first_file_name; +char *last_file_name; /* full file names */ +integer first_output_code; +integer last_output_code; /* rounded \&{charcode} values */ @:char_code_}{\&{charcode} primitive@> -integer total_shipped; /* total number of |ship_out| operations completed */ +integer total_shipped; /* total number of |ship_out| operations completed */ @ @<Set init...@>= -mp->first_file_name=xstrdup(""); -mp->last_file_name=xstrdup(""); -mp->first_output_code=32768; -mp->last_output_code=-32768; -mp->total_shipped=0; +mp->first_file_name = xstrdup (""); +mp->last_file_name = xstrdup (""); +mp->first_output_code = 32768; +mp->last_output_code = -32768; +mp->total_shipped = 0; @ @<Dealloc variables@>= -xfree(mp->first_file_name); -xfree(mp->last_file_name); +xfree (mp->first_file_name); +xfree (mp->last_file_name); @ @<Begin the progress report for the output of picture~|c|@>= -if ( (int)mp->term_offset>mp->max_print_line-6 ) mp_print_ln(mp); -else if ( (mp->term_offset>0)||(mp->file_offset>0) ) mp_print_char(mp, xord(' ')); -mp_print_char(mp, xord('[')); -if ( c>=0 ) mp_print_int(mp, c) +if ((int) mp->term_offset > mp->max_print_line - 6) + mp_print_ln (mp); +else if ((mp->term_offset > 0) || (mp->file_offset > 0)) + mp_print_char (mp, xord (' ')); +mp_print_char (mp, xord ('[')); +if (c >= 0) + mp_print_int (mp, c) + @ @<End progress report@>= -mp_print_char(mp, xord(']')); +mp_print_char (mp, xord (']')); update_terminal; -incr(mp->total_shipped) +incr (mp->total_shipped) + @ @<Explain what output files were written@>= -if ( mp->total_shipped>0 ) { - mp_print_nl(mp, ""); - mp_print_int(mp, mp->total_shipped); +if (mp->total_shipped > 0) { + mp_print_nl (mp, ""); + mp_print_int (mp, mp->total_shipped); if (mp->noninteractive) { - mp_print(mp, " figure"); - if ( mp->total_shipped>1 ) mp_print_char(mp, xord('s')); - mp_print(mp, " created."); + mp_print (mp, " figure"); + if (mp->total_shipped > 1) + mp_print_char (mp, xord ('s')); + mp_print (mp, " created."); } else { - mp_print(mp, " output file"); - if ( mp->total_shipped>1 ) mp_print_char(mp, xord('s')); - mp_print(mp, " written: "); - mp_print(mp, mp->first_file_name); - if ( mp->total_shipped>1 ) { - if ( 31+strlen(mp->first_file_name)+ - strlen(mp->last_file_name)> (unsigned)mp->max_print_line) - mp_print_ln(mp); - mp_print(mp, " .. "); - mp_print(mp, mp->last_file_name); + mp_print (mp, " output file"); + if (mp->total_shipped > 1) + mp_print_char (mp, xord ('s')); + mp_print (mp, " written: "); + mp_print (mp, mp->first_file_name); + if (mp->total_shipped > 1) { + if (31 + strlen (mp->first_file_name) + + strlen (mp->last_file_name) > (unsigned) mp->max_print_line) + mp_print_ln (mp); + mp_print (mp, " .. "); + mp_print (mp, mp->last_file_name); } + mp_print_nl (mp, ""); } } @ @<Internal library declarations@>= -boolean mp_has_font_size(MP mp, font_number f ); +boolean mp_has_font_size (MP mp, font_number f); -@ @c -boolean mp_has_font_size(MP mp, font_number f ) { - return (mp->font_sizes[f]!=null); +@ @c +boolean mp_has_font_size (MP mp, font_number f) { + return (mp->font_sizes[f] != NULL); } + @ The \&{special} command saves up lines of text to be printed during the next |ship_out| operation. The saved items are stored as a list of capsule tokens. @<Glob...@>= -pointer last_pending; /* the last token in a list of pending specials */ - -@ @<Set init...@>= -mp->last_pending=spec_head; +mp_node last_pending; /* the last token in a list of pending specials */ @ @<Cases of |do_statement|...@>= -case special_command: - if ( mp->cur_mod==0 ) mp_do_special(mp); else - if ( mp->cur_mod==1 ) mp_do_mapfile(mp); else - mp_do_mapline(mp); - break; +case special_command: +if (mp->cur_mod == 0) + mp_do_special (mp); +else if (mp->cur_mod == 1) + mp_do_mapfile (mp); +else + mp_do_mapline (mp); +break; @ @<Declare action procedures for use by |do_statement|@>= -static void mp_do_special (MP mp) ; +static void mp_do_special (MP mp); -@ @c void mp_do_special (MP mp) { - mp_get_x_next(mp); mp_scan_expression(mp); - if ( mp->cur_type!=mp_string_type ) { +@ @c +void mp_do_special (MP mp) { + mp_get_x_next (mp); + mp_scan_expression (mp); + if (mp->cur_exp.type != mp_string_type) { @<Complain about improper special operation@>; - } else { - mp_link(mp->last_pending)=mp_stash_cur_exp(mp); - mp->last_pending=mp_link(mp->last_pending); - mp_link(mp->last_pending)=null; + } else { + mp_link (mp->last_pending) = mp_stash_cur_exp (mp); + mp->last_pending = mp_link (mp->last_pending); + mp_link (mp->last_pending) = NULL; } } + @ @<Complain about improper special operation@>= -{ - exp_err("Unsuitable expression"); - help1("Only known strings are allowed for output as specials."); - mp_put_get_error(mp); +{ + exp_err ("Unsuitable expression"); + help1 ("Only known strings are allowed for output as specials."); + mp_put_get_error (mp); } + @ On the export side, we need an extra object type for special strings. @<Graphical object codes@>= -mp_special_code=8, +mp_special_code = 8, @ @<Export pending specials@>= -p=mp_link(spec_head); -while ( p!=null ) { +p = mp_link (mp->spec_head); +while (p != NULL) { mp_special_object *tp; - tp = (mp_special_object *)mp_new_graphic_object(mp,mp_special_code); - gr_pre_script(tp) = str(value(p)); - if (hh->body==NULL) hh->body = (mp_graphic_object *)tp; - else gr_link(hp) = (mp_graphic_object *)tp; - hp = (mp_graphic_object *)tp; - p=mp_link(p); + tp = (mp_special_object *) mp_new_graphic_object (mp, mp_special_code); + gr_pre_script (tp) = mp_xstrdup(mp,mp_str (mp, str_value (p))); + if (hh->body == NULL) + hh->body = (mp_graphic_object *) tp; + else + gr_link (hp) = (mp_graphic_object *) tp; + hp = (mp_graphic_object *) tp; + p = mp_link (p); } -mp_flush_token_list(mp, mp_link(spec_head)); -mp_link(spec_head)=null; -mp->last_pending=spec_head +mp_flush_token_list (mp, mp_link (mp->spec_head)); +mp_link (mp->spec_head) = NULL; +mp->last_pending = mp->spec_head @ We are now ready for the main output procedure. Note that the |selector| setting is saved in a global variable so that |begin_diagnostic| can access it. @<Declare the \ps\ output procedures@>= -static void mp_ship_out (MP mp, pointer h) ; +static void mp_ship_out (MP mp, mp_node h); @ Once again, the |gr_XXXX| macros are defined in |mppsout.h| @d export_color(q,p) if ( mp_color_model(p)==mp_uninitialized_model ) { - gr_color_model(q) = (unsigned char)(mp->internal[mp_default_color_model]/65536); + gr_color_model(q) = (unsigned char)(internal_value(mp_default_color_model)/65536); gr_cyan_val(q) = 0; gr_magenta_val(q) = 0; gr_yellow_val(q) = 0; @@ -26299,230 +30697,250 @@ static void mp_ship_out (MP mp, pointer h) ; } @d export_scripts(q,p) - if (mp_pre_script(p)!=null) gr_pre_script(q) = str(mp_pre_script(p)); - if (mp_post_script(p)!=null) gr_post_script(q) = str(mp_post_script(p)); + if (mp_pre_script(p)!=NULL) gr_pre_script(q) = mp_xstrdup(mp, mp_str(mp,mp_pre_script(p))); + if (mp_post_script(p)!=NULL) gr_post_script(q) = mp_xstrdup(mp, mp_str(mp,mp_post_script(p))); @c -struct mp_edge_object *mp_gr_export(MP mp, pointer h) { - pointer p; /* the current graphical object */ - integer t; /* a temporary value */ - integer c; /* a rounded charcode */ - scaled d_width; /* the current pen width */ - mp_edge_object *hh; /* the first graphical object */ - mp_graphic_object *hq; /* something |hp| points to */ - mp_text_object *tt; - mp_fill_object *tf; +struct mp_edge_object *mp_gr_export (MP mp, mp_node h) { + mp_node p; /* the current graphical object */ + integer t; /* a temporary value */ + integer c; /* a rounded charcode */ + scaled d_width; /* the current pen width */ + mp_edge_object *hh; /* the first graphical object */ + mp_graphic_object *hq; /* something |hp| points to */ + mp_text_object *tt; + mp_fill_object *tf; mp_stroked_object *ts; - mp_clip_object *tc; - mp_bounds_object *tb; + mp_clip_object *tc; + mp_bounds_object *tb; mp_graphic_object *hp = NULL; /* the current graphical object */ - mp_set_bbox(mp, h, true); - hh = xmalloc(1,sizeof(mp_edge_object)); + mp_set_bbox (mp, h, true); + hh = xmalloc (1, sizeof (mp_edge_object)); hh->body = NULL; hh->next = NULL; hh->parent = mp; - hh->minx = minx_val(h); - hh->miny = miny_val(h); - hh->maxx = maxx_val(h); - hh->maxy = maxy_val(h); - hh->filename = mp_get_output_file_name(mp); - c = mp_round_unscaled(mp,mp->internal[mp_char_code]); + hh->minx = minx_val (h); + hh->miny = miny_val (h); + hh->maxx = maxx_val (h); + hh->maxy = maxy_val (h); + hh->filename = mp_xstrdup (mp, mp_get_output_file_name (mp)); + c = mp_round_unscaled (mp, internal_value (mp_char_code)); hh->charcode = c; - hh->width = mp->internal[mp_char_wd]; - hh->height = mp->internal[mp_char_ht]; - hh->depth = mp->internal[mp_char_dp]; - hh->ital_corr = mp->internal[mp_char_ic]; + hh->width = internal_value (mp_char_wd); + hh->height = internal_value (mp_char_ht); + hh->depth = internal_value (mp_char_dp); + hh->ital_corr = internal_value (mp_char_ic); @<Export pending specials@>; - p=mp_link(dummy_loc(h)); - while ( p!=null ) { - hq = mp_new_graphic_object(mp,mp_type(p)); - switch (mp_type(p)) { - case mp_fill_code: - tf = (mp_fill_object *)hq; - gr_pen_p(tf) = mp_export_knot_list(mp,mp_pen_p(p)); - d_width = mp_get_pen_scale(mp, mp_pen_p(p)); - if ((mp_pen_p(p)==null) || pen_is_elliptical(mp_pen_p(p))) { - gr_path_p(tf) = mp_export_knot_list(mp,mp_path_p(p)); + p = mp_link (dummy_loc (h)); + while (p != NULL) { + hq = + mp_new_graphic_object (mp, (int) ((mp_type (p) - mp_fill_node_type) + 1)); + switch (mp_type (p)) { + case mp_fill_node_type: + tf = (mp_fill_object *) hq; + gr_pen_p (tf) = mp_export_knot_list (mp, mp_pen_p ((mp_fill_node) p)); + d_width = mp_get_pen_scale (mp, mp_pen_p ((mp_fill_node) p)); + if ((mp_pen_p ((mp_fill_node) p) == NULL) + || pen_is_elliptical (mp_pen_p ((mp_fill_node) p))) { + gr_path_p (tf) = mp_export_knot_list (mp, mp_path_p ((mp_fill_node) p)); } else { - pointer pc, pp; - pc = mp_copy_path(mp, mp_path_p(p)); - pp = mp_make_envelope(mp, pc, mp_pen_p(p),ljoin_val(p),0,miterlim_val(p)); - gr_path_p(tf) = mp_export_knot_list(mp,pp); - mp_toss_knot_list(mp, pp); - pc = mp_htap_ypoc(mp, mp_path_p(p)); - pp = mp_make_envelope(mp, pc, mp_pen_p(p),ljoin_val(p),0,miterlim_val(p)); - gr_htap_p(tf) = mp_export_knot_list(mp,pp); - mp_toss_knot_list(mp, pp); + mp_knot pc, pp; + pc = mp_copy_path (mp, mp_path_p ((mp_fill_node) p)); + pp = + mp_make_envelope (mp, pc, mp_pen_p ((mp_fill_node) p), ljoin_val (p), + 0, miterlim_val (p)); + gr_path_p (tf) = mp_export_knot_list (mp, pp); + mp_toss_knot_list (mp, pp); + pc = mp_htap_ypoc (mp, mp_path_p ((mp_fill_node) p)); + pp = + mp_make_envelope (mp, pc, mp_pen_p ((mp_fill_node) p), ljoin_val (p), + 0, miterlim_val (p)); + gr_htap_p (tf) = mp_export_knot_list (mp, pp); + mp_toss_knot_list (mp, pp); } - export_color(tf,p) ; - export_scripts(tf,p); - gr_ljoin_val(tf) = (unsigned char)ljoin_val(p); - gr_miterlim_val(tf) = miterlim_val(p); + export_color (tf, p); + export_scripts (tf, p); + gr_ljoin_val (tf) = (unsigned char) ljoin_val (p); + gr_miterlim_val (tf) = miterlim_val (p); break; - case mp_stroked_code: - ts = (mp_stroked_object *)hq; - gr_pen_p(ts) = mp_export_knot_list(mp,mp_pen_p(p)); - d_width = mp_get_pen_scale(mp, mp_pen_p(p)); - if (pen_is_elliptical(mp_pen_p(p))) { - gr_path_p(ts) = mp_export_knot_list(mp,mp_path_p(p)); + case mp_stroked_node_type: + ts = (mp_stroked_object *) hq; + gr_pen_p (ts) = mp_export_knot_list (mp, mp_pen_p ((mp_stroked_node) p)); + d_width = mp_get_pen_scale (mp, mp_pen_p ((mp_stroked_node) p)); + if (pen_is_elliptical (mp_pen_p ((mp_stroked_node) p))) { + gr_path_p (ts) = + mp_export_knot_list (mp, mp_path_p ((mp_stroked_node) p)); } else { - pointer pc; - pc=mp_copy_path(mp, mp_path_p(p)); - t=lcap_val(p); - if ( mp_left_type(pc)!=mp_endpoint ) { - mp_left_type(mp_insert_knot(mp, pc,mp_x_coord(pc),mp_y_coord(pc)))=mp_endpoint; - mp_right_type(pc)=mp_endpoint; - pc=mp_link(pc); - t=1; + mp_knot pc; + pc = mp_copy_path (mp, mp_path_p ((mp_stroked_node) p)); + t = lcap_val (p); + if (mp_left_type (pc) != mp_endpoint) { + mp_left_type (mp_insert_knot + (mp, pc, mp_x_coord (pc), mp_y_coord (pc))) = + mp_endpoint; + mp_right_type (pc) = mp_endpoint; + pc = mp_next_knot (pc); + t = 1; } - pc=mp_make_envelope(mp,pc,mp_pen_p(p),ljoin_val(p),(quarterword)t,miterlim_val(p)); - gr_path_p(ts) = mp_export_knot_list(mp,pc); - mp_toss_knot_list(mp, pc); + pc = + mp_make_envelope (mp, pc, mp_pen_p ((mp_stroked_node) p), + ljoin_val (p), (quarterword) t, miterlim_val (p)); + gr_path_p (ts) = mp_export_knot_list (mp, pc); + mp_toss_knot_list (mp, pc); } - export_color(ts,p) ; - export_scripts(ts,p); - gr_ljoin_val(ts) = (unsigned char)ljoin_val(p); - gr_miterlim_val(ts) = miterlim_val(p); - gr_lcap_val(ts) = (unsigned char)lcap_val(p); - gr_dash_p(ts) = mp_export_dashes(mp,p,&d_width); + export_color (ts, p); + export_scripts (ts, p); + gr_ljoin_val (ts) = (unsigned char) ljoin_val (p); + gr_miterlim_val (ts) = miterlim_val (p); + gr_lcap_val (ts) = (unsigned char) lcap_val (p); + gr_dash_p (ts) = mp_export_dashes (mp, (mp_stroked_node) p, &d_width); break; - case mp_text_code: - tt = (mp_text_object *)hq; - gr_text_p(tt) = str(mp_text_p(p)); - gr_text_l(tt) = (size_t)length(mp_text_p(p)); - gr_font_n(tt) = (unsigned int)mp_font_n(p); - gr_font_name(tt) = mp_xstrdup(mp,mp->font_name[mp_font_n(p)]); - gr_font_dsize(tt) = (unsigned int)mp->font_dsize[mp_font_n(p)]; - export_color(tt,p) ; - export_scripts(tt,p); - gr_width_val(tt) = width_val(p); - gr_height_val(tt) = height_val(p); - gr_depth_val(tt) = depth_val(p); - gr_tx_val(tt) = tx_val(p); - gr_ty_val(tt) = ty_val(p); - gr_txx_val(tt) = txx_val(p); - gr_txy_val(tt) = txy_val(p); - gr_tyx_val(tt) = tyx_val(p); - gr_tyy_val(tt) = tyy_val(p); + case mp_text_node_type: + tt = (mp_text_object *) hq; + gr_text_p (tt) = mp_xstrdup (mp, mp_str (mp, mp_text_p (p))); + gr_text_l (tt) = (size_t) length (mp_text_p (p)); + gr_font_n (tt) = (unsigned int) mp_font_n (p); + gr_font_name (tt) = mp_xstrdup (mp, mp->font_name[mp_font_n (p)]); + gr_font_dsize (tt) = (unsigned int) mp->font_dsize[mp_font_n (p)]; + export_color (tt, p); + export_scripts (tt, p); + gr_width_val (tt) = width_val (p); + gr_height_val (tt) = height_val (p); + gr_depth_val (tt) = depth_val (p); + gr_tx_val (tt) = tx_val (p); + gr_ty_val (tt) = ty_val (p); + gr_txx_val (tt) = txx_val (p); + gr_txy_val (tt) = txy_val (p); + gr_tyx_val (tt) = tyx_val (p); + gr_tyy_val (tt) = tyy_val (p); break; - case mp_start_clip_code: - tc = (mp_clip_object *)hq; - gr_path_p(tc) = mp_export_knot_list(mp,mp_path_p(p)); + case mp_start_clip_node_type: + tc = (mp_clip_object *) hq; + gr_path_p (tc) = + mp_export_knot_list (mp, mp_path_p ((mp_start_clip_node) p)); break; - case mp_start_bounds_code: - tb = (mp_bounds_object *)hq; - gr_path_p(tb) = mp_export_knot_list(mp,mp_path_p(p)); + case mp_start_bounds_node_type: + tb = (mp_bounds_object *) hq; + gr_path_p (tb) = + mp_export_knot_list (mp, mp_path_p ((mp_start_bounds_node) p)); break; - case mp_stop_clip_code: - case mp_stop_bounds_code: + case mp_stop_clip_node_type: + case mp_stop_bounds_node_type: /* nothing to do here */ break; - } - if (hh->body==NULL) hh->body=hq; else gr_link(hp) = hq; + default: /* there are no other valid cases, but please the compiler */ + break; + } + if (hh->body == NULL) + hh->body = hq; + else + gr_link (hp) = hq; hp = hq; - p=mp_link(p); + p = mp_link (p); } return hh; } + @ This function is only used for the |glyph| operator, so it takes quite a few shortcuts for cases that cannot appear in the output of |mp_ps_font_charstring|. @c -pointer mp_gr_unexport(MP mp, struct mp_edge_object *hh) { - pointer h; /* the edge object */ - pointer ph, pn, pt; /* for adding items */ +mp_node mp_gr_unexport (MP mp, struct mp_edge_object *hh) { + mp_node h; /* the edge object */ + mp_node ph, pn, pt; /* for adding items */ mp_graphic_object *p; /* the current graphical object */ - h = mp_get_node(mp, edge_header_size); - mp_init_edges(mp, h); - ph = dummy_loc(h); + h = mp_get_edge_header_node (mp); + mp_init_edges (mp, h); + ph = dummy_loc (h); pt = ph; p = hh->body; - minx_val(h) = hh->minx; - miny_val(h) = hh->miny; - maxx_val(h) = hh->maxx; - maxy_val(h) = hh->maxy; - while ( p!=NULL ) { - switch (gr_type(p)) { - case mp_fill_code: - if ( gr_pen_p((mp_fill_object *)p)==NULL ) { - pn = mp_new_fill_node (mp, null); - mp_path_p(pn) = mp_import_knot_list(mp,gr_path_p((mp_fill_object *)p)); - mp_color_model(pn)=mp_grey_model; - if (mp_new_turn_cycles(mp, mp_path_p(pn))<0) { - grey_val(pn) = unity; - mp_link(pt) = pn; - pt = mp_link(pt); + minx_val (h) = hh->minx; + miny_val (h) = hh->miny; + maxx_val (h) = hh->maxx; + maxy_val (h) = hh->maxy; + while (p != NULL) { + switch (gr_type (p)) { + case mp_fill_code: + if (gr_pen_p ((mp_fill_object *) p) == NULL) { + pn = mp_new_fill_node (mp, NULL); + mp_path_p ((mp_fill_node) pn) = + mp_import_knot_list (mp, gr_path_p ((mp_fill_object *) p)); + mp_color_model (pn) = mp_grey_model; + if (mp_new_turn_cycles (mp, mp_path_p ((mp_fill_node) pn)) < 0) { + grey_val (pn) = unity; + mp_link (pt) = pn; + pt = mp_link (pt); } else { - mp_link(pn) = mp_link(ph); - mp_link(ph) = pn; - if (ph==pt) - pt=pn; + mp_link (pn) = mp_link (ph); + mp_link (ph) = pn; + if (ph == pt) + pt = pn; } } break; case mp_stroked_code: - case mp_text_code: - case mp_start_clip_code: - case mp_stop_clip_code: + case mp_text_code: + case mp_start_clip_code: + case mp_stop_clip_code: case mp_start_bounds_code: case mp_stop_bounds_code: - case mp_special_code: + case mp_special_code: break; - } /* all cases are enumerated */ - p=p->next; + } /* all cases are enumerated */ + p = p->next; } - mp_gr_toss_objects(hh); + mp_gr_toss_objects (hh); return h; } @ @<Declarations@>= -static struct mp_edge_object *mp_gr_export(MP mp, pointer h); -static pointer mp_gr_unexport(MP mp, struct mp_edge_object *h); +static struct mp_edge_object *mp_gr_export (MP mp, mp_node h); +static mp_node mp_gr_unexport (MP mp, struct mp_edge_object *h); @ This function is now nearly trivial. @c -void mp_ship_out (MP mp, pointer h) { /* output edge structure |h| */ - integer c; /* \&{charcode} rounded to the nearest integer */ - c=mp_round_unscaled(mp, mp->internal[mp_char_code]); +void mp_ship_out (MP mp, mp_node h) { /* output edge structure |h| */ + integer c; /* \&{charcode} rounded to the nearest integer */ + c = mp_round_unscaled (mp, internal_value (mp_char_code)); @<Begin the progress report for the output of picture~|c|@>; (mp->shipout_backend) (mp, h); @<End progress report@>; - if ( mp->internal[mp_tracing_output]>0 ) - mp_print_edges(mp, h," (just shipped out)",true); + if (internal_value (mp_tracing_output) > 0) + mp_print_edges (mp, h, " (just shipped out)", true); } + @ @<Declarations@>= -static void mp_shipout_backend (MP mp, pointer h); +static void mp_shipout_backend (MP mp, void *h); @ @c -void mp_shipout_backend (MP mp, pointer h) { +void mp_shipout_backend (MP mp, void *voidh) { char *s; - mp_edge_object *hh; /* the first graphical object */ - hh = mp_gr_export(mp,h); + mp_edge_object *hh; /* the first graphical object */ + mp_node h = (mp_node) voidh; + hh = mp_gr_export (mp, h); s = NULL; - if (mp->internal[mp_output_format]>0) - s = str(mp->internal[mp_output_format]); - if (s && strcmp(s,"svg")==0) { - (void)mp_svg_gr_ship_out (hh, - (mp->internal[mp_prologues]/65536), - false); + if (internal_string (mp_output_format) != NULL) + s = mp_str (mp, internal_string (mp_output_format)); + if (s && strcmp (s, "svg") == 0) { + (void) mp_svg_gr_ship_out (hh, + (internal_value (mp_prologues) / 65536), false); } else { - (void)mp_gr_ship_out (hh, - (mp->internal[mp_prologues]/65536), - (mp->internal[mp_procset]/65536), - false); + (void) mp_gr_ship_out (hh, + (internal_value (mp_prologues) / 65536), + (internal_value (mp_procset) / 65536), false); } - mp_xfree(s); - mp_gr_toss_objects(hh); + mp_gr_toss_objects (hh); } + @ @<Exported types@>= -typedef void (*mp_backend_writer)(MP, int); +typedef void (*mp_backend_writer) (MP, void *); @ @<Option variables@>= mp_backend_writer shipout_backend; @@ -26531,58 +30949,112 @@ mp_backend_writer shipout_backend; by which a user can send things to the \.{GF} file. @ @<Determine if a character has been shipped out@>= -{ - mp->cur_exp=mp_round_unscaled(mp, mp->cur_exp) % 256; - if ( mp->cur_exp<0 ) mp->cur_exp=mp->cur_exp+256; - boolean_reset(mp->char_exists[mp->cur_exp]); - mp->cur_type=mp_boolean_type; +{ + halfword vv = cur_exp_value (); + set_cur_exp_value (mp_round_unscaled (mp, vv) % 256); + if (cur_exp_value () < 0) { + vv = cur_exp_value (); + set_cur_exp_value (vv + 256); + } + boolean_reset (mp->char_exists[cur_exp_value ()]); + mp->cur_exp.type = mp_boolean_type; } + @ @<Glob...@>= psout_data ps; svgout_data svg; @ @<Allocate or initialize ...@>= -mp_ps_backend_initialize(mp); -mp_svg_backend_initialize(mp); +mp_ps_backend_initialize (mp); +mp_svg_backend_initialize (mp); @ @<Dealloc...@>= -mp_ps_backend_free(mp); -mp_svg_backend_free(mp); - - -@* \[45] Dumping and undumping the tables. -After \.{INIMP} has seen a collection of macros, it -can write all the necessary information on an auxiliary file so -that production versions of \MP\ are able to initialize their -memory at high speed. The present section of the program takes -care of such output and input. We shall consider simultaneously -the processes of storing and restoring, -so that the inverse relation between them is clear. -@.INIMP@> - -The global variable |mem_ident| is a string that is printed right -after the |banner| line when \MP\ is ready to start. For \.{INIMP} this -string says simply `\.{(INIMP)}'; for other versions of \MP\ it says, -for example, `\.{(mem=plain 1990.4.14)}', showing the year, -month, and day that the mem file was created. We have |mem_ident=0| -before \MP's tables are loaded. +mp_ps_backend_free (mp); +mp_svg_backend_free (mp); + + +@* Dumping and undumping the tables. + +When \.{MP} is started, it is possible to preload a macro file +containing definitions that will be usable in the main input +file. This action even takes place automatically, based on the +name of the executable (\.{mpost} will attempt to preload the +macros in the file \.{mpost.mp}). If such a preload is not +desired, the option variable |ini_version| has to be set |true|. + +The global variable |mem_ident| will be set to `\.{ (INIMP)}' +when |ini_version| is true; otherwise it will contain the filename +of the macro file being preloaded. + +The variable |mem_file| holds the open file pointer. @<Glob...@>= -char * mem_ident; -void * mem_file; /* for input or output of mem information */ +char *mem_ident; +void *mem_file; /* file for input or preloaded macros */ @ @<Set init...@>= -mp->mem_ident=NULL; - -@ @<Initialize table entries...@>= -mp->mem_ident=xstrdup(" (INIMP)"); +mp->mem_ident = NULL; @ @<Dealloc variables@>= -xfree(mp->mem_ident); +xfree (mp->mem_ident); + +@ @<Declarations@>= +extern boolean mp_load_preload_file (MP mp); + +@ Preloading a file is a lot like |mp_run| itself, except that +\MP\ should not exit and that a bit of trickery is needed with +the input buffer to make sure that the preloading does not +interfere with the actual job. + +@c +boolean mp_load_preload_file (MP mp) { + size_t k; + in_state_record old_state = mp->cur_input; + char *fname = xstrdup (mp->name_of_file); + size_t l = strlen (fname); + str_room (l); + for (k = 0; k < l; k++) { + append_char (*(fname + k)); + } + name = mp_make_string (mp); + if (!mp->log_opened) { + mp_open_log_file (mp); + } /* |open_log_file| doesn't |show_context|, so |limit| + and |loc| needn't be set to meaningful values yet */ + if (((int) mp->term_offset + (int) strlen (fname)) > (mp->max_print_line - 2)) + mp_print_ln (mp); + else if ((mp->term_offset > 0) || (mp->file_offset > 0)) + mp_print_char (mp, xord (' ')); + mp_print_char (mp, xord ('(')); + incr (mp->open_parens); + mp_print (mp, fname); + mp->mem_ident = fname; + update_terminal; + { + line = 1; + start = loc = limit + 1; + cur_file = mp->mem_file; + (void) mp_input_ln (mp, cur_file); + mp_firm_up_the_line (mp); + mp->buffer[limit] = xord ('%'); + mp->first = (size_t) (limit + 1); + loc = start; + } + do { + mp_do_statement (mp); + } while (!(mp->cur_cmd == stop)); /* "dump" or "end" or EOF */ + mp_primitive (mp, "dump", relax, 0); /* reset |dump| */ + mp_print_char (mp, xord (')')); + decr (mp->open_parens); + (mp->close_file) (mp, mp->mem_file); + cur_file = NULL; + mp->cur_input = old_state; + return true; +} -@* \[46] The main program. +@* The main program. This is it: the part of \MP\ that executes all those procedures we have written. @@ -26590,94 +31062,10 @@ Well---almost. We haven't put the parsing subroutines into the program yet; and we'd better leave space for a few more routines that may have been forgotten. -@c @<Declare the basic parsing subroutines@> +@c +@<Declare the basic parsing subroutines@>; @<Declare miscellaneous procedures that were declared |forward|@> - -@ We've noted that there are two versions of \MP. One, called \.{INIMP}, -@.INIMP@> -has to be run first; it initializes everything from scratch, without -reading a mem file, and it has the capability of dumping a mem file. -The other one is called `\.{VIRMP}'; it is a ``virgin'' program that needs -@.VIRMP@> -to input a mem file in order to get started. \.{VIRMP} typically has -a bit more memory capacity than \.{INIMP}, because it does not need the -space consumed by the dumping/undumping routines and the numerous calls on -|primitive|, etc. - -The \.{VIRMP} program cannot read a mem file instantaneously, of course; -the best implementations therefore allow for production versions of \MP\ that -not only avoid the loading routine for object code, they also have -a mem file pre-loaded. - -@ @<Option variables@>= -int ini_version; /* are we iniMP? */ - -@ @<Set |ini_version|@>= -mp->ini_version = (opt->ini_version ? true : false); - -@ The code below make the final chosen hash size the next larger -multiple of 2 from the requested size, and this array is a list of -suitable prime numbers to go with such values. - -The top limit is chosen such that it is definately lower than -|max_halfword-3*param_size|, because |param_size| cannot be larger -than |max_halfword/sizeof(pointer)|. - -@<Declarations@>= -static int mp_prime_choices[] = - { 12289, 24593, 49157, 98317, - 196613, 393241, 786433, 1572869, - 3145739, 6291469, 12582917, 25165843, - 50331653, 100663319 }; - -@ @<Find constant sizes@>= -if (mp->ini_version) { - unsigned i = 14; - set_value(mp->mem_top,opt->main_memory,5000); - mp->mem_max = mp->mem_top; - set_value(mp->param_size,opt->param_size,150); - set_value(mp->max_in_open,opt->max_in_open,10); - if (opt->hash_size>0x8000000) - opt->hash_size=0x8000000; - if (opt->hash_size>0) { - set_value(mp->hash_size,(2*opt->hash_size-1),16384); - } else { - mp->hash_size = 16384; - } - mp->hash_size = mp->hash_size>>i; - while (mp->hash_size>=2) { - mp->hash_size /= 2; - i++; - } - mp->hash_size = mp->hash_size << i; - if (mp->hash_size>0x8000000) - mp->hash_size=0x8000000; - mp->hash_prime=mp_prime_choices[(i-14)]; -} else { - int i = -1; - if (mp->mem_name == NULL) { - mp->mem_name = mp_xstrdup(mp,"plain"); - } - if (mp_open_mem_file(mp)) { - i = mp_undump_constants(mp); - if (i != metapost_magic) - goto OFF_BASE; - set_value(mp->mem_max,opt->main_memory,mp->mem_top); - goto DONE; -OFF_BASE: - wterm_ln("(Fatal mem file error; "); - wterm((mp->find_file)(mp, mp->mem_name, "r", mp_filetype_memfile)); - if (i>metapost_old_magic && i<metapost_magic) { - wterm(" was written by an older version)\n"); - } else { - wterm(" appears not to be a mem file)\n"); - } - } - mp->history = mp_fatal_error_stop; - mp_jump_out(mp); -} -DONE: - + @ Here we do whatever is needed to complete \MP's job gracefully on the local operating system. The code here might come into play after a fatal @@ -26687,74 +31075,74 @@ cannot produce error messages. For example, it would be a mistake to call might lead to an infinite loop. @^system dependencies@> -This program doesn't bother to close the input files that may still be open. - @ @c void mp_close_files_and_terminate (MP mp) { - integer k; /* all-purpose index */ - integer LH; /* the length of the \.{TFM} header, in words */ - int lk_offset; /* extra words inserted at beginning of |lig_kern| array */ - pointer p; /* runs through a list of \.{TFM} dimensions */ - if (mp->finished) + integer k; /* all-purpose index */ + integer LH; /* the length of the \.{TFM} header, in words */ + int lk_offset; /* extra words inserted at beginning of |lig_kern| array */ + mp_node p; /* runs through a list of \.{TFM} dimensions */ + if (mp->finished) return; @<Close all open files in the |rd_file| and |wr_file| arrays@>; - if ( mp->internal[mp_tracing_stats]>0 ) + if (internal_value (mp_tracing_stats) > 0) @<Output statistics about this job@>; - wake_up_terminal; + wake_up_terminal; @<Do all the finishing work on the \.{TFM} file@>; @<Explain what output files were written@>; - if ( mp->log_opened && ! mp->noninteractive ){ + if (mp->log_opened && !mp->noninteractive) { wlog_cr; - (mp->close_file)(mp,mp->log_file); - mp->selector=mp->selector-2; - if ( mp->selector==term_only ) { - mp_print_nl(mp, "Transcript written on "); -@.Transcript written...@> - mp_print(mp, mp->log_name); mp_print_char(mp, xord('.')); + (mp->close_file) (mp, mp->log_file); + mp->selector = mp->selector - 2; + if (mp->selector == term_only) { + mp_print_nl (mp, "Transcript written on "); +@.Transcript written...@>; + mp_print (mp, mp->log_name); + mp_print_char (mp, xord ('.')); } } - mp_print_ln(mp); + mp_print_ln (mp); mp->finished = true; } + @ @<Declarations@>= -static void mp_close_files_and_terminate (MP mp) ; +static void mp_close_files_and_terminate (MP mp); @ @<Close all open files in the |rd_file| and |wr_file| arrays@>= -if (mp->rd_fname!=NULL) { - for (k=0;k<(int)mp->read_files;k++ ) { - if ( mp->rd_fname[k]!=NULL ) { - (mp->close_file)(mp,mp->rd_file[k]); - xfree(mp->rd_fname[k]); - } - } +if (mp->rd_fname != NULL) { + for (k = 0; k < (int) mp->read_files; k++) { + if (mp->rd_fname[k] != NULL) { + (mp->close_file) (mp, mp->rd_file[k]); + xfree (mp->rd_fname[k]); + } + } } -if (mp->wr_fname!=NULL) { - for (k=0;k<(int)mp->write_files;k++) { - if ( mp->wr_fname[k]!=NULL ) { - (mp->close_file)(mp,mp->wr_file[k]); - xfree(mp->wr_fname[k]); +if (mp->wr_fname != NULL) { + for (k = 0; k < (int) mp->write_files; k++) { + if (mp->wr_fname[k] != NULL) { + (mp->close_file) (mp, mp->wr_file[k]); + xfree (mp->wr_fname[k]); } } } @ @<Dealloc ...@>= -for (k=0;k<(int)mp->max_read_files;k++ ) { - if ( mp->rd_fname[k]!=NULL ) { - (mp->close_file)(mp,mp->rd_file[k]); - xfree(mp->rd_fname[k]); +for (k = 0; k < (int) mp->max_read_files; k++) { + if (mp->rd_fname[k] != NULL) { + (mp->close_file) (mp, mp->rd_file[k]); + xfree (mp->rd_fname[k]); } } -xfree(mp->rd_file); -xfree(mp->rd_fname); -for (k=0;k<(int)mp->max_write_files;k++) { - if ( mp->wr_fname[k]!=NULL ) { - (mp->close_file)(mp,mp->wr_file[k]); - xfree(mp->wr_fname[k]); +xfree (mp->rd_file); +xfree (mp->rd_fname); +for (k = 0; k < (int) mp->max_write_files; k++) { + if (mp->wr_fname[k] != NULL) { + (mp->close_file) (mp, mp->wr_file[k]); + xfree (mp->wr_fname[k]); } } -xfree(mp->wr_file); -xfree(mp->wr_fname); +xfree (mp->wr_file); +xfree (mp->wr_fname); @ We want to produce a \.{TFM} file if and only if |mp_fontmaking| is positive. @@ -26764,137 +31152,126 @@ there is no chance of another memory overflow after the memory capacity has already been exceeded. @<Do all the finishing work on the \.{TFM} file@>= -if ( mp->internal[mp_fontmaking]>0 ) { - @<Make the dynamic memory into one big available node@>; +if (internal_value (mp_fontmaking) > 0) { @<Massage the \.{TFM} widths@>; - mp_fix_design_size(mp); mp_fix_check_sum(mp); + mp_fix_design_size (mp); + mp_fix_check_sum (mp); @<Massage the \.{TFM} heights, depths, and italic corrections@>; - mp->internal[mp_fontmaking]=0; /* avoid loop in case of fatal error */ + internal_value (mp_fontmaking) = 0; /* avoid loop in case of fatal error */ @<Finish the \.{TFM} file@>; } -@ @<Make the dynamic memory into one big available node@>= -mp->rover=lo_mem_stat_max+1; mp_link(mp->rover)=empty_flag; mp->lo_mem_max=mp->hi_mem_min-1; -if ( mp->lo_mem_max-mp->rover>max_halfword ) mp->lo_mem_max=max_halfword+mp->rover; -node_size(mp->rover)=mp->lo_mem_max-mp->rover; -lmp_link(mp->rover)=mp->rover; rmp_link(mp->rover)=mp->rover; -mp_link(mp->lo_mem_max)=null; mp_info(mp->lo_mem_max)=null - @ The present section goes directly to the log file instead of using |print| commands, because there's no need for these strings to take up |str_pool| memory when a non-{\bf stat} version of \MP\ is being used. @<Output statistics...@>= -if ( mp->log_opened ) { +if (mp->log_opened) { char s[128]; - wlog_ln(" "); - wlog_ln("Here is how much of MetaPost's memory you used:"); -@.Here is how much...@> - mp_snprintf(s,128," %i string%s out of %i",(int)mp->max_strs_used-mp->init_str_use, - (mp->max_strs_used!=mp->init_str_use+1 ? "s" : ""), - (int)(mp->max_strings-1-mp->init_str_use)); - wlog_ln(s); - mp_snprintf(s,128," %i string characters out of %i", - (int)mp->max_pl_used-mp->init_pool_ptr, - (int)mp->pool_size-mp->init_pool_ptr); - wlog_ln(s); - mp_snprintf(s,128," %i words of memory out of %i", - (int)mp->lo_mem_max+mp->mem_end-mp->hi_mem_min+2, - (int)mp->mem_end); - wlog_ln(s); - mp_snprintf(s,128," %i symbolic tokens out of %i", (int)mp->st_count, (int)mp->hash_size); - wlog_ln(s); - mp_snprintf(s,128," %ii,%in,%ip,%ib,%if stack positions out of %ii,%in,%ip,%ib,%if", - (int)mp->max_in_stack,(int)mp->int_ptr, - (int)mp->max_param_stack,(int)mp->max_buf_stack+1,(int)mp->in_open_max, - (int)mp->stack_size,(int)mp->max_internal,(int)mp->param_size, - (int)mp->buf_size,(int)mp->max_in_open); - wlog_ln(s); - mp_snprintf(s,128," %i string compactions (moved %i characters, %i strings)", - (int)mp->pact_count,(int)mp->pact_chars,(int)mp->pact_strs); - wlog_ln(s); + wlog_ln (" "); + wlog_ln ("Here is how much of MetaPost's memory you used:"); +@.Here is how much...@>; + mp_snprintf (s, 128, " %i string%s using %i character%s", + (int) mp->max_strs_used, (mp->max_strs_used != 1 ? "s" : ""), + (int) mp->max_pl_used, (mp->max_pl_used != 1 ? "s" : "")); + wlog_ln (s); + mp_snprintf (s, 128, " %i bytes of node memory", (int) mp->var_used_max); + wlog_ln (s); + mp_snprintf (s, 128, " %i symbolic tokens", (int) mp->st_count); + wlog_ln (s); + mp_snprintf (s, 128, + " %ii,%in,%ip,%ib,%if stack positions out of %ii,%in,%ip,%ib,%if", + (int) mp->max_in_stack, (int) mp->int_ptr, + (int) mp->max_param_stack, (int) mp->max_buf_stack + 1, + (int) mp->in_open_max - file_bottom, (int) mp->stack_size, + (int) mp->max_internal, (int) mp->param_size, (int) mp->buf_size, + (int) mp->max_in_open - file_bottom); + wlog_ln (s); } @ It is nice to have have some of the stats available from the API. @<Exported function ...@>= -int mp_memory_usage (MP mp ); -int mp_hash_usage (MP mp ); -int mp_param_usage (MP mp ); -int mp_open_usage (MP mp ); +int mp_memory_usage (MP mp); +int mp_hash_usage (MP mp); +int mp_param_usage (MP mp); +int mp_open_usage (MP mp); @ @c -int mp_memory_usage (MP mp ) { - return (int)mp->lo_mem_max+mp->mem_end-mp->hi_mem_min+2; +int mp_memory_usage (MP mp) { + return (int) mp->var_used; } -int mp_hash_usage (MP mp ) { - return (int)mp->st_count; +int mp_hash_usage (MP mp) { + return (int) mp->st_count; } -int mp_param_usage (MP mp ) { - return (int)mp->max_param_stack; +int mp_param_usage (MP mp) { + return (int) mp->max_param_stack; } -int mp_open_usage (MP mp ) { - return (int)mp->max_in_stack; +int mp_open_usage (MP mp) { + return (int) mp->max_in_stack; } + @ We get to the |final_cleanup| routine when \&{end} or \&{dump} has been scanned. @c void mp_final_cleanup (MP mp) { - integer c; /* 0 for \&{end}, 1 for \&{dump} */ - c=mp->cur_mod; - if ( mp->job_name==NULL ) mp_open_log_file(mp); - while ( mp->input_ptr>0 ) { - if ( token_state ) mp_end_token_list(mp); - else mp_end_file_reading(mp); - } - while ( mp->loop_ptr!=null ) mp_stop_iteration(mp); - while ( mp->open_parens>0 ) { - mp_print(mp, " )"); decr(mp->open_parens); + integer c; /* 0 for \&{end}, 1 for \&{dump} */ + c = mp->cur_mod; + if (mp->job_name == NULL) + mp_open_log_file (mp); + while (mp->input_ptr > 0) { + if (token_state) + mp_end_token_list (mp); + else + mp_end_file_reading (mp); + } + while (mp->loop_ptr != NULL) + mp_stop_iteration (mp); + while (mp->open_parens > 0) { + mp_print (mp, " )"); + decr (mp->open_parens); }; - while ( mp->cond_ptr!=null ) { - mp_print_nl(mp, "(end occurred when "); -@.end occurred...@> - mp_print_cmd_mod(mp, fi_or_else,mp->cur_if); + while (mp->cond_ptr != NULL) { + mp_print_nl (mp, "(end occurred when "); +@.end occurred...@>; + mp_print_cmd_mod (mp, fi_or_else, mp->cur_if); /* `\.{if}' or `\.{elseif}' or `\.{else}' */ - if ( mp->if_line!=0 ) { - mp_print(mp, " on line "); mp_print_int(mp, mp->if_line); - } - mp_print(mp, " was incomplete)"); - mp->if_line=if_line_field(mp->cond_ptr); - mp->cur_if=mp_name_type(mp->cond_ptr); mp->cond_ptr=mp_link(mp->cond_ptr); - } - if ( mp->history!=mp_spotless ) - if ( ((mp->history==mp_warning_issued)||(mp->interaction<mp_error_stop_mode)) ) - if ( mp->selector==term_and_log ) { - mp->selector=term_only; - mp_print_nl(mp, "(see the transcript file for additional information)"); -@.see the transcript file...@> - mp->selector=term_and_log; - } - if ( c==1 ) { - if (mp->ini_version) { - mp_store_mem_file(mp); return; + if (mp->if_line != 0) { + mp_print (mp, " on line "); + mp_print_int (mp, mp->if_line); } - mp_print_nl(mp, "(dump is performed only by INIMP)"); return; -@.dump...only by INIMP@> - } + mp_print (mp, " was incomplete)"); + mp->if_line = if_line_field (mp->cond_ptr); + mp->cur_if = mp_name_type (mp->cond_ptr); + mp->cond_ptr = mp_link (mp->cond_ptr); + } + if (mp->history != mp_spotless) + if (((mp->history == mp_warning_issued) + || (mp->interaction < mp_error_stop_mode))) + if (mp->selector == term_and_log) { + mp->selector = term_only; + mp_print_nl (mp, + "(see the transcript file for additional information)"); +@.see the transcript file...@>; + mp->selector = term_and_log; + } } + @ @<Declarations@>= -static void mp_final_cleanup (MP mp) ; -static void mp_init_prim (MP mp) ; -static void mp_init_tab (MP mp) ; +static void mp_final_cleanup (MP mp); +static void mp_init_prim (MP mp); +static void mp_init_tab (MP mp); @ @c -void mp_init_prim (MP mp) { /* initialize all the primitives */ +void mp_init_prim (MP mp) { /* initialize all the primitives */ @<Put each...@>; } @# -void mp_init_tab (MP mp) { /* initialize other tables */ - integer k; /* all-purpose index */ - @<Initialize table entries (done by \.{INIMP} only)@>; +void mp_init_tab (MP mp) { /* initialize other tables */ + @<Initialize table entries@>; } @@ -26905,56 +31282,39 @@ But when we finish this part of the program, \MP\ is ready to call on the |main_control| routine to do its work. @<Get the first line...@>= -{ +{ @<Initialize the input routines@>; - if (mp->mem_ident==NULL) { - if ( ! mp_load_mem_file(mp) ) { - (mp->close_file)(mp, mp->mem_file); - mp->history = mp_fatal_error_stop; - return mp; + if (mp->mem_ident == NULL) { + if (!mp->ini_version) { + if (!mp_load_preload_file (mp)) { + mp->history = mp_fatal_error_stop; + return mp; + } + } else { + mp->mem_ident = mp_xstrdup (mp, " (INIMP)"); } - (mp->close_file)(mp, mp->mem_file); } @<Initializations following first line@>; } -@ @<Initializations following first line@>= - mp->buffer[limit]=(ASCII_code)'%'; - mp_fix_date_and_time(mp); - if (mp->random_seed==0) - mp->random_seed = (mp->internal[mp_time] / unity)+mp->internal[mp_day]; - mp_init_randoms(mp, mp->random_seed); - @<Initialize the print |selector|...@>; - if ( loc<limit ) if ( mp->buffer[loc]!='\\' ) - mp_start_input(mp); /* \&{input} assumed */ - -@ @<Run inimpost commands@>= -{ - mp_get_strings_started(mp); - mp_init_tab(mp); /* initialize the tables */ - mp_init_prim(mp); /* call |primitive| for each primitive */ - mp->init_str_use=mp->max_str_ptr=mp->str_ptr; - mp->init_pool_ptr=mp->max_pool_ptr=mp->pool_ptr; - mp_fix_date_and_time(mp); -} - -@ Saving the filename template -@<Save the filename template@>= -{ - delete_str_ref(mp->internal[mp_output_template]); - if ( length(mp->cur_exp)==0 ) { - mp->internal[mp_output_template] = rts("%j.%c"); - } else { - mp->internal[mp_output_template]=mp->cur_exp; - add_str_ref(mp->internal[mp_output_template]); - } -} +@ @<Initializations following first line@>= +mp->buffer[limit] = (ASCII_code) '%'; +mp_fix_date_and_time (mp); +if (mp->random_seed == 0) + mp->random_seed = + (internal_value (mp_time) / unity) + internal_value (mp_day); +mp_init_randoms (mp, mp->random_seed); +@<Initialize the print |selector|...@>; +mp_normalize_selector (mp); +if (loc < limit) + if (mp->buffer[loc] != '\\') + mp_start_input (mp); /* \&{input} assumed */ -@* \[47] Debugging. +@* Debugging. -@* \[48] System-dependent changes. +@* System-dependent changes. This section should be replaced, if necessary, by any special modification of the program that are necessary to make \MP\ work at a particular installation. @@ -26965,7 +31325,7 @@ which introduce new sections, can be inserted here; then only the index itself will get a new section number. @^system dependencies@> -@* \[49] Index. +@* Index. Here is where you can find all uses of each identifier in the program, with underlined entries pointing to where the identifier was defined. If the identifier is only one letter long, however, you get to see only diff --git a/Build/source/texk/web2c/mplibdir/mpmath.w b/Build/source/texk/web2c/mplibdir/mpmath.w new file mode 100644 index 00000000000..92f6f4a75a0 --- /dev/null +++ b/Build/source/texk/web2c/mplibdir/mpmath.w @@ -0,0 +1,1226 @@ +% $Id $ +% +% Copyright 2008-2010 Taco Hoekwater. +% +% This program is free software: you can redistribute it and/or modify +% it under the terms of the GNU Lesser General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU Lesser General Public License for more details. +% +% You should have received a copy of the GNU Lesser General Public License +% along with this program. If not, see <http://www.gnu.org/licenses/>. +% +% TeX is a trademark of the American Mathematical Society. +% METAFONT is a trademark of Addison-Wesley Publishing Company. +% PostScript is a trademark of Adobe Systems Incorporated. + +% Here is TeX material that gets inserted after \input webmac + +\font\tenlogo=logo10 % font used for the METAFONT logo +\font\logos=logosl10 +\def\MF{{\tenlogo META}\-{\tenlogo FONT}} +\def\MP{{\tenlogo META}\-{\tenlogo POST}} + +\def\title{Reading TEX metrics files} +\pdfoutput=1 + +@ Introduction. + +@ +@d hlp1(A) mp->help_line[0]=A; } +@d hlp2(A,B) mp->help_line[1]=A; hlp1(B) +@d help1 { mp->help_ptr=1; hlp1 /* use this with one help line */ +@d help2 { mp->help_ptr=2; hlp2 /* use this with two help lines */ + +@ @c +#include <w2c/config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "mplib.h" +#include "mpmp.h" /* internal header */ +#include "mpmath.h" /* internal header */ +@h + +@ @c +@<Declarations@>; + +@ @(mpmath.h@>= +@<Types@>; +@<Internal library declarations@>; + +@ Currently empty +@<Declarations@>= + +@* Math initialization. + +@<Types@>= +typedef struct math_data { + scaled max_scaled_; + scaled one_third_max_scaled_; + scaled unity_; + scaled two_; + scaled three_; + scaled half_unit_; + scaled three_quarter_unit_; + fraction fraction_one_; + fraction fraction_half_; + fraction fraction_two_; + fraction fraction_three_; + fraction fraction_four_; + angle ninety_deg_; + angle one_eighty_deg_; + angle three_sixty_deg_; +} math_data; + +@ @<Internal library declarations@>= +void * mp_initialize_math (MP mp); +void mp_free_math (MP mp); + +@ @c +void * mp_initialize_math (MP mp) { + math_data *math = (math_data *)mp_xmalloc(mp,1,sizeof(math_data)); + /* here are the constants for |scaled| objects */ + math->max_scaled_ = EL_GORDO; + math->one_third_max_scaled_ = one_third_EL_GORDO; + math->unity_ = unity; + math->two_ = two; + math->three_ = three; + math->half_unit_ = half_unit; + math->three_quarter_unit_ = three_quarter_unit; + /* |fractions| */ + math->fraction_one_ = fraction_one; + math->fraction_half_ = fraction_half; + math->fraction_two_ = fraction_two; + math->fraction_three_ = fraction_three; + math->fraction_four_ = fraction_four; + /* |angles| */ + math->ninety_deg_ = ninety_deg; + math->one_eighty_deg_ = one_eighty_deg; + math->three_sixty_deg_ = three_sixty_deg; + return (void *)math; +} + +void mp_free_math (MP mp) { + free(mp->math); +} + +@ Fixed-point arithmetic is done on {\sl scaled integers\/} that are multiples +of $2^{-16}$. In other words, a binary point is assumed to be sixteen bit +positions from the right end of a binary computer word. + +@d unity 0x10000 /* $2^{16}$, represents 1.00000 */ +@d two (2*unity) /* $2^{17}$, represents 2.00000 */ +@d three (3*unity) /* $2^{17}+2^{16}$, represents 3.00000 */ +@d half_unit (unity/2) /* $2^{15}$, represents 0.50000 */ +@d three_quarter_unit (3*(unity/4)) /* $3\cdot2^{14}$, represents 0.75000 */ + +@d EL_GORDO 0x7fffffff /* $2^{31}-1$, the largest value that \MP\ likes */ +@d one_third_EL_GORDO 05252525252 + +@ One of \MP's most common operations is the calculation of +$\lfloor{a+b\over2}\rfloor$, +the midpoint of two given integers |a| and~|b|. The most decent way to do +this is to write `|(a+b)/2|'; but on many machines it is more efficient +to calculate `|(a+b)>>1|'. + +Therefore the midpoint operation will always be denoted by `|half(a+b)|' +in this program. If \MP\ is being implemented with languages that permit +binary shifting, the |half| macro should be changed to make this operation +as efficient as possible. Since some systems have shift operators that can +only be trusted to work on positive numbers, there is also a macro |halfp| +that is used only when the quantity being halved is known to be positive +or zero. + +@<Internal library declarations@>= +#define half(A) ((A) / 2) +#define halfp(A) (integer)((unsigned)(A) >> 1) + +@ Todo: Here are some compilation tricks for problems to be sorted out later + +@<Internal library declarations@>= +#define integer_as_fraction(A) (fraction)(A) + + +@ Here is a procedure analogous to |print_int|. If the output +of this procedure is subsequently read by \MP\ and converted by the +|round_decimals| routine above, it turns out that the original value will +be reproduced exactly. A decimal point is printed only if the value is +not an integer. If there is more than one way to print the result with +the optimum number of digits following the decimal point, the closest +possible value is given. + +The invariant relation in the \&{repeat} loop is that a sequence of +decimal digits yet to be printed will yield the original number if and only if +they form a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$. +We can stop if and only if $f=0$ satisfies this condition; the loop will +terminate before $s$ can possibly become zero. + +@<Internal library declarations@>= +void mp_print_scaled (MP mp, scaled s); + +@ @c +void mp_print_scaled (MP mp, scaled s) { /* prints scaled real, rounded to five digits */ + scaled delta; /* amount of allowable inaccuracy */ + if (s < 0) { + mp_print_char (mp, xord ('-')); + s = -s; /* print the sign, if negative */ + } + mp_print_int (mp, s / unity); /* print the integer part */ + s = 10 * (s % unity) + 5; + if (s != 5) { + delta = 10; + mp_print_char (mp, xord ('.')); + do { + if (delta > unity) + s = s + 0100000 - (delta / 2); /* round the final digit */ + mp_print_char (mp, xord ('0' + (s / unity))); + s = 10 * (s % unity); + delta = delta * 10; + } while (s > delta); + } +} + +@ Addition is not always checked to make sure that it doesn't overflow, +but in places where overflow isn't too unlikely the |slow_add| routine +is used. + +@<Internal library declarations@>= +integer mp_slow_add (MP mp, integer x, integer y); + +@ @c +integer mp_slow_add (MP mp, integer x, integer y) { + if (x >= 0) { + if (y <= EL_GORDO - x) { + return x + y; + } else { + mp->arith_error = true; + return EL_GORDO; + } + } else if (-y <= EL_GORDO + x) { + return x + y; + } else { + mp->arith_error = true; + return -EL_GORDO; + } +} + +@ The |make_fraction| routine produces the |fraction| equivalent of +|p/q|, given integers |p| and~|q|; it computes the integer +$f=\lfloor2^{28}p/q+{1\over2}\rfloor$, when $p$ and $q$ are +positive. If |p| and |q| are both of the same scaled type |t|, +the ``type relation'' |make_fraction(t,t)=fraction| is valid; +and it's also possible to use the subroutine ``backwards,'' using +the relation |make_fraction(t,fraction)=t| between scaled types. + +If the result would have magnitude $2^{31}$ or more, |make_fraction| +sets |arith_error:=true|. Most of \MP's internal computations have +been designed to avoid this sort of error. + +If this subroutine were programmed in assembly language on a typical +machine, we could simply compute |(@t$2^{28}$@>*p)div q|, since a +double-precision product can often be input to a fixed-point division +instruction. But when we are restricted to int-eger arithmetic it +is necessary either to resort to multiple-precision maneuvering +or to use a simple but slow iteration. The multiple-precision technique +would be about three times faster than the code adopted here, but it +would be comparatively long and tricky, involving about sixteen +additional multiplications and divisions. + +This operation is part of \MP's ``inner loop''; indeed, it will +consume nearly 10\pct! of the running time (exclusive of input and output) +if the code below is left unchanged. A machine-dependent recoding +will therefore make \MP\ run faster. The present implementation +is highly portable, but slow; it avoids multiplication and division +except in the initial stage. System wizards should be careful to +replace it with a routine that is guaranteed to produce identical +results in all cases. +@^system dependencies@> + +As noted below, a few more routines should also be replaced by machine-dependent +code, for efficiency. But when a procedure is not part of the ``inner loop,'' +such changes aren't advisable; simplicity and robustness are +preferable to trickery, unless the cost is too high. +@^inner loop@> + +@<Internal library declarations@>= +fraction mp_make_fraction (MP mp, integer p, integer q); + +@ We need these preprocessor values + +@d TWEXP31 2147483648.0 +@d TWEXP28 268435456.0 +@d TWEXP16 65536.0 +@d TWEXP_16 (1.0/65536.0) +@d TWEXP_28 (1.0/268435456.0) + + +@c +fraction mp_make_fraction (MP mp, integer p, integer q) { + fraction i; + if (q == 0) + mp_confusion (mp, "/"); +@:this can't happen /}{\quad \./@> { + register double d; + d = TWEXP28 * (double) p / (double) q; + if ((p ^ q) >= 0) { + d += 0.5; + if (d >= TWEXP31) { + mp->arith_error = true; + return EL_GORDO; + } + i = (integer) d; + if (d == (double) i && (((q > 0 ? -q : q) & 077777) + * (((i & 037777) << 1) - 1) & 04000) != 0) + --i; + } else { + d -= 0.5; + if (d <= -TWEXP31) { + mp->arith_error = true; + return -EL_GORDO; + } + i = (integer) d; + if (d == (double) i && (((q > 0 ? q : -q) & 077777) + * (((i & 037777) << 1) + 1) & 04000) != 0) + ++i; + } + } + return i; +} + + +@ The dual of |make_fraction| is |take_fraction|, which multiplies a +given integer~|q| by a fraction~|f|. When the operands are positive, it +computes $p=\lfloor qf/2^{28}+{1\over2}\rfloor$, a symmetric function +of |q| and~|f|. + +This routine is even more ``inner loopy'' than |make_fraction|; +the present implementation consumes almost 20\pct! of \MP's computation +time during typical jobs, so a machine-language substitute is advisable. +@^inner loop@> @^system dependencies@> + +@<Internal library declarations@>= +integer mp_take_fraction (MP mp, integer q, fraction f); + +@ @c +integer mp_take_fraction (MP mp, integer p, fraction q) { + register double d; + register integer i; + d = (double) p *(double) q *TWEXP_28; + if ((p ^ q) >= 0) { + d += 0.5; + if (d >= TWEXP31) { + if (d != TWEXP31 || (((p & 077777) * (q & 077777)) & 040000) == 0) + mp->arith_error = true; + return EL_GORDO; + } + i = (integer) d; + if (d == (double) i && (((p & 077777) * (q & 077777)) & 040000) != 0) + --i; + } else { + d -= 0.5; + if (d <= -TWEXP31) { + if (d != -TWEXP31 || ((-(p & 077777) * (q & 077777)) & 040000) == 0) + mp->arith_error = true; + return -EL_GORDO; + } + i = (integer) d; + if (d == (double) i && ((-(p & 077777) * (q & 077777)) & 040000) != 0) + ++i; + } + return i; +} + + +@ When we want to multiply something by a |scaled| quantity, we use a scheme +analogous to |take_fraction| but with a different scaling. +Given positive operands, |take_scaled| +computes the quantity $p=\lfloor qf/2^{16}+{1\over2}\rfloor$. + +Once again it is a good idea to use a machine-language replacement if +possible; otherwise |take_scaled| will use more than 2\pct! of the running time +when the Computer Modern fonts are being generated. +@^inner loop@> + +@<Internal library declarations@>= +integer mp_take_scaled (MP mp, integer q, scaled f); + +@ @c +integer mp_take_scaled (MP mp, integer p, scaled q) { + register double d; + register integer i; + d = (double) p *(double) q *TWEXP_16; + if ((p ^ q) >= 0) { + d += 0.5; + if (d >= TWEXP31) { + if (d != TWEXP31 || (((p & 077777) * (q & 077777)) & 040000) == 0) + mp->arith_error = true; + return EL_GORDO; + } + i = (integer) d; + if (d == (double) i && (((p & 077777) * (q & 077777)) & 040000) != 0) + --i; + } else { + d -= 0.5; + if (d <= -TWEXP31) { + if (d != -TWEXP31 || ((-(p & 077777) * (q & 077777)) & 040000) == 0) + mp->arith_error = true; + return -EL_GORDO; + } + i = (integer) d; + if (d == (double) i && ((-(p & 077777) * (q & 077777)) & 040000) != 0) + ++i; + } + return i; +} + + +@ For completeness, there's also |make_scaled|, which computes a +quotient as a |scaled| number instead of as a |fraction|. +In other words, the result is $\lfloor2^{16}p/q+{1\over2}\rfloor$, if the +operands are positive. \ (This procedure is not used especially often, +so it is not part of \MP's inner loop.) + +@<Internal library ...@>= +scaled mp_make_scaled (MP mp, integer p, integer q); + +@ @c +scaled mp_make_scaled (MP mp, integer p, integer q) { + register integer i; + if (q == 0) + mp_confusion (mp, "/"); +@:this can't happen /}{\quad \./@> { + register double d; + d = TWEXP16 * (double) p / (double) q; + if ((p ^ q) >= 0) { + d += 0.5; + if (d >= TWEXP31) { + mp->arith_error = true; + return EL_GORDO; + } + i = (integer) d; + if (d == (double) i && (((q > 0 ? -q : q) & 077777) + * (((i & 037777) << 1) - 1) & 04000) != 0) + --i; + } else { + d -= 0.5; + if (d <= -TWEXP31) { + mp->arith_error = true; + return -EL_GORDO; + } + i = (integer) d; + if (d == (double) i && (((q > 0 ? q : -q) & 077777) + * (((i & 037777) << 1) + 1) & 04000) != 0) + ++i; + } + } + return i; +} + +@ The following function divides |s| by |m|. |dd| is number of decimal digits. + +@<Internal library ...@>= +scaled mp_divide_scaled (MP mp, scaled s, scaled m, integer dd); + +@ @c +scaled mp_divide_scaled (MP mp, scaled s, scaled m, integer dd) { + scaled q, r; + integer sign, i; + sign = 1; + if (s < 0) { + sign = -sign; + s = -s; + } + if (m < 0) { + sign = -sign; + m = -m; + } + if (m == 0) + mp_confusion (mp, "arithmetic: divided by zero"); + else if (m >= (EL_GORDO / 10)) + mp_confusion (mp, "arithmetic: number too big"); + q = s / m; + r = s % m; + for (i = 1; i <= dd; i++) { + q = 10 * q + (10 * r) / m; + r = (10 * r) % m; + } + if (2 * r >= m) { + q++; + r = r - m; + } + mp->scaled_out = sign * (s - (r / mp->ten_pow[dd])); + return (sign * q); +} + + +@ The following function is used to create a scaled integer from a given decimal +fraction $(.d_0d_1\ldots d_{k-1})$, where |0<=k<=17|. + +@<Internal library declarations@>= +scaled mp_round_decimals (MP mp, unsigned char *b, quarterword k); + +@ @c +scaled mp_round_decimals (MP mp, unsigned char *b, quarterword k) { + /* converts a decimal fraction */ + unsigned a = 0; /* the accumulator */ + int l = 0; + for ( l = k-1; l >= 0; l-- ) { + if (l<16) /* digits for |k>=17| cannot affect the result */ + a = (a + (unsigned) (*(b+l) - '0') * two) / 10; + } + return (scaled) halfp (a + 1); +} + +@ The |scaled| quantities in \MP\ programs are generally supposed to be +less than $2^{12}$ in absolute value, so \MP\ does much of its internal +arithmetic with 28~significant bits of precision. A |fraction| denotes +a scaled integer whose binary point is assumed to be 28 bit positions +from the right. + +@d fraction_half 01000000000 /* $2^{27}$, represents 0.50000000 */ +@d fraction_one 02000000000 /* $2^{28}$, represents 1.00000000 */ +@d fraction_two 04000000000 /* $2^{29}$, represents 2.00000000 */ +@d fraction_three 06000000000 /* $3\cdot2^{28}$, represents 3.00000000 */ +@d fraction_four 010000000000 /* $2^{30}$, represents 4.00000000 */ + +@ Here is a typical example of how the routines above can be used. +It computes the function +$${1\over3\tau}f(\theta,\phi)= +{\tau^{-1}\bigl(2+\sqrt2\,(\sin\theta-{1\over16}\sin\phi) + (\sin\phi-{1\over16}\sin\theta)(\cos\theta-\cos\phi)\bigr)\over +3\,\bigl(1+{1\over2}(\sqrt5-1)\cos\theta+{1\over2}(3-\sqrt5\,)\cos\phi\bigr)},$$ +where $\tau$ is a |scaled| ``tension'' parameter. This is \MP's magic +fudge factor for placing the first control point of a curve that starts +at an angle $\theta$ and ends at an angle $\phi$ from the straight path. +(Actually, if the stated quantity exceeds 4, \MP\ reduces it to~4.) + +The trigonometric quantity to be multiplied by $\sqrt2$ is less than $\sqrt2$. +(It's a sum of eight terms whose absolute values can be bounded using +relations such as $\sin\theta\cos\theta\L{1\over2}$.) Thus the numerator +is positive; and since the tension $\tau$ is constrained to be at least +$3\over4$, the numerator is less than $16\over3$. The denominator is +nonnegative and at most~6. Hence the fixed-point calculations below +are guaranteed to stay within the bounds of a 32-bit computer word. + +The angles $\theta$ and $\phi$ are given implicitly in terms of |fraction| +arguments |st|, |ct|, |sf|, and |cf|, representing $\sin\theta$, $\cos\theta$, +$\sin\phi$, and $\cos\phi$, respectively. + +@<Internal library declarations@>= +fraction mp_velocity (MP mp, fraction st, fraction ct, fraction sf, + fraction cf, scaled t); + +@ @c +fraction mp_velocity (MP mp, fraction st, fraction ct, fraction sf, + fraction cf, scaled t) { + integer acc, num, denom; /* registers for intermediate calculations */ + acc = mp_take_fraction (mp, st - (sf / 16), sf - (st / 16)); + acc = mp_take_fraction (mp, acc, ct - cf); + num = fraction_two + mp_take_fraction (mp, acc, 379625062); + /* $2^{28}\sqrt2\approx379625062.497$ */ + denom = + fraction_three + mp_take_fraction (mp, ct, + 497706707) + mp_take_fraction (mp, cf, + 307599661); + /* $3\cdot2^{27}\cdot(\sqrt5-1)\approx497706706.78$ and + $3\cdot2^{27}\cdot(3-\sqrt5\,)\approx307599661.22$ */ + if (t != unity) + num = mp_make_scaled (mp, num, t); + /* |make_scaled(fraction,scaled)=fraction| */ + if (num / 4 >= denom) + return fraction_four; + else + return mp_make_fraction (mp, num, denom); +} + + +@ The following somewhat different subroutine tests rigorously if $ab$ is +greater than, equal to, or less than~$cd$, +given integers $(a,b,c,d)$. In most cases a quick decision is reached. +The result is $+1$, 0, or~$-1$ in the three respective cases. + +@<Internal library declarations@>= +integer mp_ab_vs_cd (MP mp, integer a, integer b, integer c, integer d); + +@ @c +integer mp_ab_vs_cd (MP mp, integer a, integer b, integer c, integer d) { + integer q, r; /* temporary registers */ + (void)mp; + @<Reduce to the case that |a,c>=0|, |b,d>0|@>; + while (1) { + q = a / d; + r = c / b; + if (q != r) + return (q > r ? 1 : -1); + q = a % d; + r = c % b; + if (r == 0) + return (q ? 1 : 0); + if (q == 0) + return -1; + a = b; + b = q; + c = d; + d = r; + } /* now |a>d>0| and |c>b>0| */ +} + + +@ @<Reduce to the case that |a...@>= +if (a < 0) { + a = -a; + b = -b; +}; +if (c < 0) { + c = -c; + d = -d; +}; +if (d <= 0) { + if (b >= 0) { + if ((a == 0 || b == 0) && (c == 0 || d == 0)) + return 0; + else + return 1; + } + if (d == 0) + return (a == 0 ? 0 : -1); + q = a; + a = c; + c = q; + q = -b; + b = -d; + d = q; +} else if (b <= 0) { + if (b < 0) + if (a > 0) + return -1; + return (c == 0 ? 0 : -1); +} + +@ We conclude this set of elementary routines with some simple rounding +and truncation operations. + +@ |floor_scaled| floors a |scaled| +@<Internal library declarations@>= +#define mp_floor_scaled(M,i) ((i)&(-65536)) + +@ |round_unscaled| rounds a |scaled| and converts it to |int| +@<Internal library declarations@>= +#define mp_round_unscaled(M,x) (x>=0100000 ? 1+((x-0100000) / 0200000) \ + : ( x>=-0100000 ? 0 : -(1+((-(x+1)-0100000) / 0200000)))) + +@ |round_fraction| rounds a |fraction| and converts it to |scaled| +@<Internal library declarations@>= +#define mp_round_fraction(M,x) (x>=2048 ? 1+((x-2048) / 4096) \ + : ( x>=-2048 ? 0 : -(1+((-(x+1)-2048) / 4096)))) + + + +@* Algebraic and transcendental functions. +\MP\ computes all of the necessary special functions from scratch, without +relying on |real| arithmetic or system subroutines for sines, cosines, etc. + +@ To get the square root of a |scaled| number |x|, we want to calculate +$s=\lfloor 2^8\!\sqrt x +{1\over2}\rfloor$. If $x>0$, this is the unique +integer such that $2^{16}x-s\L s^2<2^{16}x+s$. The following subroutine +determines $s$ by an iterative method that maintains the invariant +relations $x=2^{46-2k}x_0\bmod 2^{30}$, $0<y=\lfloor 2^{16-2k}x_0\rfloor +-s^2+s\L q=2s$, where $x_0$ is the initial value of $x$. The value of~$y$ +might, however, be zero at the start of the first iteration. + +@<Internal library declarations@>= +scaled mp_square_rt (MP mp, scaled x); + +@ @c +scaled mp_square_rt (MP mp, scaled x) { + quarterword k; /* iteration control counter */ + integer y; /* register for intermediate calculations */ + integer q; /* register for intermediate calculations */ + if (x <= 0) { + @<Handle square root of zero or negative argument@>; + } else { + k = 23; + q = 2; + while (x < fraction_two) { /* i.e., |while x<@t$2^{29}$@>|\unskip */ + k--; + x = x + x + x + x; + } + if (x < fraction_four) + y = 0; + else { + x = x - fraction_four; + y = 1; + }; + do { + @<Decrease |k| by 1, maintaining the invariant + relations between |x|, |y|, and~|q|@>; + } while (k != 0); + return (scaled) (halfp (q)); + } +} + + +@ @<Handle square root of zero...@>= +{ + if (x < 0) { + mp_print_err (mp, "Square root of "); +@.Square root...replaced by 0@>; + mp_print_scaled (mp, x); + mp_print (mp, " has been replaced by 0"); + help2 ("Since I don't take square roots of negative numbers,", + "I'm zeroing this one. Proceed, with fingers crossed."); + mp_error (mp); + }; + return 0; +} + + +@ @<Decrease |k| by 1, maintaining...@>= +x += x; +y += y; +if (x >= fraction_four) { /* note that |fraction_four=@t$2^{30}$@>| */ + x = x - fraction_four; + y++; +}; +x += x; +y = y + y - q; +q += q; +if (x >= fraction_four) { + x = x - fraction_four; + y++; +}; +if (y > (int) q) { + y -= q; + q += 2; +} else if (y <= 0) { + q -= 2; + y += q; +}; +k-- + +@ Pythagorean addition $\psqrt{a^2+b^2}$ is implemented by an elegant +iterative scheme due to Cleve Moler and Donald Morrison [{\sl IBM Journal +@^Moler, Cleve Barry@> +@^Morrison, Donald Ross@> +of Research and Development\/ \bf27} (1983), 577--581]. It modifies |a| and~|b| +in such a way that their Pythagorean sum remains invariant, while the +smaller argument decreases. + +@<Internal library ...@>= +integer mp_pyth_add (MP mp, integer a, integer b); + + +@ @c +integer mp_pyth_add (MP mp, integer a, integer b) { + fraction r; /* register used to transform |a| and |b| */ + boolean big; /* is the result dangerously near $2^{31}$? */ + a = abs (a); + b = abs (b); + if (a < b) { + r = b; + b = a; + a = r; + }; /* now |0<=b<=a| */ + if (b > 0) { + if (a < fraction_two) { + big = false; + } else { + a = a / 4; + b = b / 4; + big = true; + }; /* we reduced the precision to avoid arithmetic overflow */ + @<Replace |a| by an approximation to $\psqrt{a^2+b^2}$@>; + if (big) { + if (a < fraction_two) { + a = a + a + a + a; + } else { + mp->arith_error = true; + a = EL_GORDO; + }; + } + } + return a; +} + + +@ The key idea here is to reflect the vector $(a,b)$ about the +line through $(a,b/2)$. + +@<Replace |a| by an approximation to $\psqrt{a^2+b^2}$@>= +while (1) { + r = mp_make_fraction (mp, b, a); + r = mp_take_fraction (mp, r, r); /* now $r\approx b^2/a^2$ */ + if (r == 0) + break; + r = mp_make_fraction (mp, r, fraction_four + r); + a = a + mp_take_fraction (mp, a + a, r); + b = mp_take_fraction (mp, b, r); +} + + +@ Here is a similar algorithm for $\psqrt{a^2-b^2}$. +It converges slowly when $b$ is near $a$, but otherwise it works fine. + +@<Internal library declarations@>= +integer mp_pyth_sub (MP mp, integer a, integer b); + +@ @c +integer mp_pyth_sub (MP mp, integer a, integer b) { + fraction r; /* register used to transform |a| and |b| */ + boolean big; /* is the input dangerously near $2^{31}$? */ + a = abs (a); + b = abs (b); + if (a <= b) { + @<Handle erroneous |pyth_sub| and set |a:=0|@>; + } else { + if (a < fraction_four) { + big = false; + } else { + a = (integer) halfp (a); + b = (integer) halfp (b); + big = true; + } + @<Replace |a| by an approximation to $\psqrt{a^2-b^2}$@>; + if (big) + a *= 2; + } + return a; +} + + +@ @<Replace |a| by an approximation to $\psqrt{a^2-b^2}$@>= +while (1) { + r = mp_make_fraction (mp, b, a); + r = mp_take_fraction (mp, r, r); /* now $r\approx b^2/a^2$ */ + if (r == 0) + break; + r = mp_make_fraction (mp, r, fraction_four - r); + a = a - mp_take_fraction (mp, a + a, r); + b = mp_take_fraction (mp, b, r); +} + + +@ @<Handle erroneous |pyth_sub| and set |a:=0|@>= +{ + if (a < b) { + mp_print_err (mp, "Pythagorean subtraction "); + mp_print_scaled (mp, a); + mp_print (mp, "+-+"); + mp_print_scaled (mp, b); + mp_print (mp, " has been replaced by 0"); +@.Pythagorean...@>; + help2 ("Since I don't take square roots of negative numbers,", + "I'm zeroing this one. Proceed, with fingers crossed."); + mp_error (mp); + } + a = 0; +} + + +@ The subroutines for logarithm and exponential involve two tables. +The first is simple: |two_to_the[k]| equals $2^k$. The second involves +a bit more calculation, which the author claims to have done correctly: +|spec_log[k]| is $2^{27}$ times $\ln\bigl(1/(1-2^{-k})\bigr)= +2^{-k}+{1\over2}2^{-2k}+{1\over3}2^{-3k}+\cdots\,$, rounded to the +nearest integer. + +@d two_to_the(A) (1<<(unsigned)(A)) + +@<Declarations@>= +static const integer spec_log[29] = { 0, /* special logarithms */ + 93032640, 38612034, 17922280, 8662214, 4261238, 2113709, + 1052693, 525315, 262400, 131136, 65552, 32772, 16385, + 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1, 1 +}; + + +@ Here is the routine that calculates $2^8$ times the natural logarithm +of a |scaled| quantity; it is an integer approximation to $2^{24}\ln(x/2^{16})$, +when |x| is a given positive integer. + +The method is based on exercise 1.2.2--25 in {\sl The Art of Computer +Programming\/}: During the main iteration we have $1\L 2^{-30}x<1/(1-2^{1-k})$, +and the logarithm of $2^{30}x$ remains to be added to an accumulator +register called~$y$. Three auxiliary bits of accuracy are retained in~$y$ +during the calculation, and sixteen auxiliary bits to extend |y| are +kept in~|z| during the initial argument reduction. (We add +$100\cdot2^{16}=6553600$ to~|z| and subtract 100 from~|y| so that |z| will +not become negative; also, the actual amount subtracted from~|y| is~96, +not~100, because we want to add~4 for rounding before the final division by~8.) + +@<Internal library declarations@>= +scaled mp_m_log (MP mp, scaled x); + +@ @c +scaled mp_m_log (MP mp, scaled x) { + integer y, z; /* auxiliary registers */ + integer k; /* iteration counter */ + if (x <= 0) { + @<Handle non-positive logarithm@>; + } else { + y = 1302456956 + 4 - 100; /* $14\times2^{27}\ln2\approx1302456956.421063$ */ + z = 27595 + 6553600; /* and $2^{16}\times .421063\approx 27595$ */ + while (x < fraction_four) { + x = 2*x; + y -= 93032639; + z -= 48782; + } /* $2^{27}\ln2\approx 93032639.74436163$ and $2^{16}\times.74436163\approx 48782$ */ + y = y + (z / unity); + k = 2; + while (x > fraction_four + 4) { + @<Increase |k| until |x| can be multiplied by a + factor of $2^{-k}$, and adjust $y$ accordingly@>; + } + return (y / 8); + } +} + + +@ @<Increase |k| until |x| can...@>= +{ + z = ((x - 1) / two_to_the (k)) + 1; /* $z=\lceil x/2^k\rceil$ */ + while (x < fraction_four + z) { + z = halfp (z + 1); + k++; + }; + y += spec_log[k]; + x -= z; +} + + +@ @<Handle non-positive logarithm@>= +{ + mp_print_err (mp, "Logarithm of "); +@.Logarithm...replaced by 0@>; + mp_print_scaled (mp, x); + mp_print (mp, " has been replaced by 0"); + help2 ("Since I don't take logs of non-positive numbers,", + "I'm zeroing this one. Proceed, with fingers crossed."); + mp_error (mp); + return 0; +} + + +@ Conversely, the exponential routine calculates $\exp(x/2^8)$, +when |x| is |scaled|. The result is an integer approximation to +$2^{16}\exp(x/2^{24})$, when |x| is regarded as an integer. + +@<Internal library declarations@>= +scaled mp_m_exp (MP mp, scaled x); + +@ @c +scaled mp_m_exp (MP mp, scaled x) { + quarterword k; /* loop control index */ + integer y, z; /* auxiliary registers */ + if (x > 174436200) { + /* $2^{24}\ln((2^{31}-1)/2^{16})\approx 174436199.51$ */ + mp->arith_error = true; + return EL_GORDO; + } else if (x < -197694359) { + /* $2^{24}\ln(2^{-1}/2^{16})\approx-197694359.45$ */ + return 0; + } else { + if (x <= 0) { + z = -8 * x; + y = 04000000; /* $y=2^{20}$ */ + } else { + if (x <= 127919879) { + z = 1023359037 - 8 * x; + /* $2^{27}\ln((2^{31}-1)/2^{20})\approx 1023359037.125$ */ + } else { + z = 8 * (174436200 - x); /* |z| is always nonnegative */ + } + y = EL_GORDO; + }; + @<Multiply |y| by $\exp(-z/2^{27})$@>; + if (x <= 127919879) + return ((y + 8) / 16); + else + return y; + } +} + + +@ The idea here is that subtracting |spec_log[k]| from |z| corresponds +to multiplying |y| by $1-2^{-k}$. + +A subtle point (which had to be checked) was that if $x=127919879$, the +value of~|y| will decrease so that |y+8| doesn't overflow. In fact, +$z$ will be 5 in this case, and |y| will decrease by~64 when |k=25| +and by~16 when |k=27|. + +@<Multiply |y| by...@>= +k = 1; +while (z > 0) { + while (z >= spec_log[k]) { + z -= spec_log[k]; + y = y - 1 - ((y - two_to_the (k - 1)) / two_to_the (k)); + } + k++; +} + +@ The trigonometric subroutines use an auxiliary table such that +|spec_atan[k]| contains an approximation to the |angle| whose tangent +is~$1/2^k$. $\arctan2^{-k}$ times $2^{20}\cdot180/\pi$ + +@<Declarations@>= +static const angle spec_atan[27] = { 0, 27855475, 14718068, 7471121, 3750058, + 1876857, 938658, 469357, 234682, 117342, 58671, 29335, 14668, 7334, 3667, + 1833, 917, 458, 229, 115, 57, 29, 14, 7, 4, 2, 1 +}; + + +@ Given integers |x| and |y|, not both zero, the |n_arg| function +returns the |angle| whose tangent points in the direction $(x,y)$. +This subroutine first determines the correct octant, then solves the +problem for |0<=y<=x|, then converts the result appropriately to +return an answer in the range |-one_eighty_deg<=@t$\theta$@><=one_eighty_deg|. +(The answer is |+one_eighty_deg| if |y=0| and |x<0|, but an answer of +|-one_eighty_deg| is possible if, for example, |y=-1| and $x=-2^{30}$.) + +The octants are represented in a ``Gray code,'' since that turns out +to be computationally simplest. + +@d negate_x 1 +@d negate_y 2 +@d switch_x_and_y 4 +@d first_octant 1 +@d second_octant (first_octant+switch_x_and_y) +@d third_octant (first_octant+switch_x_and_y+negate_x) +@d fourth_octant (first_octant+negate_x) +@d fifth_octant (first_octant+negate_x+negate_y) +@d sixth_octant (first_octant+switch_x_and_y+negate_x+negate_y) +@d seventh_octant (first_octant+switch_x_and_y+negate_y) +@d eighth_octant (first_octant+negate_y) + +@<Internal library declarations@>= +angle mp_n_arg (MP mp, integer x, integer y); + +@ @c +angle mp_n_arg (MP mp, integer x, integer y) { + angle z; /* auxiliary register */ + integer t; /* temporary storage */ + quarterword k; /* loop counter */ + int octant; /* octant code */ + if (x >= 0) { + octant = first_octant; + } else { + x = -x; + octant = first_octant + negate_x; + } + if (y < 0) { + y = -y; + octant = octant + negate_y; + } + if (x < y) { + t = y; + y = x; + x = t; + octant = octant + switch_x_and_y; + } + if (x == 0) { + @<Handle undefined arg@>; + } else { + @<Set variable |z| to the arg of $(x,y)$@>; + @<Return an appropriate answer based on |z| and |octant|@>; + } +} + + +@ @<Handle undefined arg@>= +{ + mp_print_err (mp, "angle(0,0) is taken as zero"); +@.angle(0,0)...zero@>; + help2 ("The `angle' between two identical points is undefined.", + "I'm zeroing this one. Proceed, with fingers crossed."); + mp_error (mp); + return 0; +} + + +@ @<Return an appropriate answer...@>= +switch (octant) { +case first_octant: + return z; +case second_octant: + return (ninety_deg - z); +case third_octant: + return (ninety_deg + z); +case fourth_octant: + return (one_eighty_deg - z); +case fifth_octant: + return (z - one_eighty_deg); +case sixth_octant: + return (-z - ninety_deg); +case seventh_octant: + return (z - ninety_deg); +case eighth_octant: + return (-z); +}; /* there are no other cases */ +return 0 + +@ At this point we have |x>=y>=0|, and |x>0|. The numbers are scaled up +or down until $2^{28}\L x<2^{29}$, so that accurate fixed-point calculations +will be made. + +@<Set variable |z| to the arg...@>= +while (x >= fraction_two) { + x = halfp (x); + y = halfp (y); +} +z = 0; +if (y > 0) { + while (x < fraction_one) { + x += x; + y += y; + }; + @<Increase |z| to the arg of $(x,y)$@>; +} + +@ During the calculations of this section, variables |x| and~|y| +represent actual coordinates $(x,2^{-k}y)$. We will maintain the +condition |x>=y|, so that the tangent will be at most $2^{-k}$. +If $x<2y$, the tangent is greater than $2^{-k-1}$. The transformation +$(a,b)\mapsto(a+b\tan\phi,b-a\tan\phi)$ replaces $(a,b)$ by +coordinates whose angle has decreased by~$\phi$; in the special case +$a=x$, $b=2^{-k}y$, and $\tan\phi=2^{-k-1}$, this operation reduces +to the particularly simple iteration shown here. [Cf.~John E. Meggitt, +@^Meggitt, John E.@> +{\sl IBM Journal of Research and Development\/ \bf6} (1962), 210--226.] + +The initial value of |x| will be multiplied by at most +$(1+{1\over2})(1+{1\over8})(1+{1\over32})\cdots\approx 1.7584$; hence +there is no chance of integer overflow. + +@<Increase |z|...@>= +k = 0; +do { + y += y; + k++; + if (y > x) { + z = z + spec_atan[k]; + t = x; + x = x + (y / two_to_the (k + k)); + y = y - t; + }; +} while (k != 15); +do { + y += y; + k++; + if (y > x) { + z = z + spec_atan[k]; + y = y - x; + }; +} while (k != 26) + +@ Conversely, the |n_sin_cos| routine takes an |angle| and produces the sine +and cosine of that angle. The results of this routine are +stored in global integer variables |n_sin| and |n_cos|. + +@ Given an integer |z| that is $2^{20}$ times an angle $\theta$ in degrees, +the purpose of |n_sin_cos(z)| is to set +|x=@t$r\cos\theta$@>| and |y=@t$r\sin\theta$@>| (approximately), +for some rather large number~|r|. The maximum of |x| and |y| +will be between $2^{28}$ and $2^{30}$, so that there will be hardly +any loss of accuracy. Then |x| and~|y| are divided by~|r|. + +@d forty_five_deg 0264000000 /* $45\cdot2^{20}$, represents $45^\circ$ */ +@d ninety_deg 0550000000 /* $90\cdot2^{20}$, represents $90^\circ$ */ +@d one_eighty_deg 01320000000 /* $180\cdot2^{20}$, represents $180^\circ$ */ +@d three_sixty_deg 02640000000 /* $360\cdot2^{20}$, represents $360^\circ$ */ + +@d odd(A) ((A)%2==1) + +@<Internal library declarations@>= +void mp_n_sin_cos (MP mp, angle z, fraction *n_cos, fraction *n_sin); + +@ Compute a multiple of the sine and cosine + +@c +void mp_n_sin_cos (MP mp, angle z, fraction *n_cos, fraction *n_sin) { + quarterword k; /* loop control variable */ + int q; /* specifies the quadrant */ + fraction r; /* magnitude of |(x,y)| */ + integer x, y, t; /* temporary registers */ + while (z < 0) + z = z + three_sixty_deg; + z = z % three_sixty_deg; /* now |0<=z<three_sixty_deg| */ + q = z / forty_five_deg; + z = z % forty_five_deg; + x = fraction_one; + y = x; + if (!odd (q)) + z = forty_five_deg - z; + @<Subtract angle |z| from |(x,y)|@>; + @<Convert |(x,y)| to the octant determined by~|q|@>; + r = mp_pyth_add (mp, x, y); + *n_cos = mp_make_fraction (mp, x, r); + *n_sin = mp_make_fraction (mp, y, r); +} + + +@ In this case the octants are numbered sequentially. + +@<Convert |(x,...@>= +switch (q) { +case 0: + break; +case 1: + t = x; + x = y; + y = t; + break; +case 2: + t = x; + x = -y; + y = t; + break; +case 3: + x = -x; + break; +case 4: + x = -x; + y = -y; + break; +case 5: + t = x; + x = -y; + y = -t; + break; +case 6: + t = x; + x = y; + y = -t; + break; +case 7: + y = -y; + break; +} /* there are no other cases */ + + +@ The main iteration of |n_sin_cos| is similar to that of |n_arg| but +applied in reverse. The values of |spec_atan[k]| decrease slowly enough +that this loop is guaranteed to terminate before the (nonexistent) value +|spec_atan[27]| would be required. + +@<Subtract angle |z|...@>= +k = 1; +while (z > 0) { + if (z >= spec_atan[k]) { + z = z - spec_atan[k]; + t = x; + x = t + y / two_to_the (k); + y = y - t / two_to_the (k); + } + k++; +} +if (y < 0) + y = 0 /* this precaution may never be needed */ + + diff --git a/Build/source/texk/web2c/mplibdir/mpost.w b/Build/source/texk/web2c/mplibdir/mpost.w index d4066e84192..54147b24ec6 100644 --- a/Build/source/texk/web2c/mplibdir/mpost.w +++ b/Build/source/texk/web2c/mplibdir/mpost.w @@ -1,4 +1,4 @@ -% $Id: mpost.w 1219 2010-04-01 09:05:51Z taco $ +% $Id: mpost.w 1422 2010-10-19 07:26:52Z taco $ % % Copyright 2008-2009 Taco Hoekwater. % @@ -69,6 +69,7 @@ static char *job_area = NULL; static int dvitomp_only = 0; static int ini_version_test = false; @<getopt structures@>; +@<Declarations@>; @ Allocating a bit of memory, with error detection: @@ -209,8 +210,11 @@ static string normalize_quotes (const char *name, const char *mesg) { @ Helpers for the filename recorder. -@c -static void recorder_start(char *jobname) { +@<Declarations@>= +void recorder_start(char *jobname); + +@ @c +void recorder_start(char *jobname) { char cwd[1024]; if (jobname==NULL) { recorder_name = mpost_xstrdup("mpout.fls"); @@ -235,6 +239,9 @@ static void recorder_start(char *jobname) { int fmt; boolean req; (void) mpx; + if ((mode[0]=='r' && !kpse_in_name_ok(nam)) || + (mode[0]=='w' && !kpse_out_name_ok(nam))) + return NULL; /* disallowed filename */ if (mode[0] != 'r') { return strdup(nam); } @@ -282,6 +289,8 @@ static int mpost_run_make_mpx (MP mp, char *mpname, char *mpxname) { } else { tmp = normalize_quotes(mpname, "mpname"); } + if (!kpse_in_name_ok(tmp)) + return 0; /* disallowed filename */ qmpname = kpse_find_file (tmp,kpse_mp_format, true); mpost_xfree(tmp); if (qmpname != NULL && job_area != NULL) { @@ -297,13 +306,13 @@ static int mpost_run_make_mpx (MP mp, char *mpname, char *mpxname) { int nothingtodo = 0; if ((stat(qmpxname, &target_stat) >= 0) && (stat(qmpname, &source_stat) >= 0)) { -#if HAVE_STRUCT_STAT_ST_MTIM - if (source_stat.st_mtim.tv_sec <= target_stat.st_mtim.tv_sec || +#if HAVE_ST_MTIM + if (source_stat.st_mtim.tv_sec < target_stat.st_mtim.tv_sec || (source_stat.st_mtim.tv_sec == target_stat.st_mtim.tv_sec && - source_stat.st_mtim.tv_nsec <= target_stat.st_mtim.tv_nsec)) + source_stat.st_mtim.tv_nsec < target_stat.st_mtim.tv_nsec)) nothingtodo = 1; #else - if (source_stat.st_mtime <= target_stat.st_mtime) + if (source_stat.st_mtime < target_stat.st_mtime) nothingtodo = 1; #endif } @@ -423,6 +432,8 @@ static int mpost_run_dvitomp (char *dviname, char *mpxname) { mpost_xfree (m); m = s ; } + if (!(kpse_in_name_ok(d) && kpse_out_name_ok(m))) + return EXIT_FAILURE; /* disallowed filename */ mpxopt->mpname = d; mpxopt->mpxname = m; @@ -454,7 +465,7 @@ static int get_random_seed (void) { #if defined (HAVE_GETTIMEOFDAY) struct timeval tv; gettimeofday(&tv, NULL); - ret = (int)(tv.tv_usec + 1000000 * tv.tv_usec); + ret = (tv.tv_usec + 1000000 * tv.tv_usec); #elif defined (HAVE_FTIME) struct timeb tb; ftime(&tb); @@ -477,6 +488,9 @@ static char *mpost_find_file(MP mp, const char *fname, const char *fmode, int ft char *s; (void)mp; s = NULL; + if ((fmode[0]=='r' && !kpse_in_name_ok(fname)) || + (fmode[0]=='w' && !kpse_out_name_ok(fname))) + return NULL; /* disallowed filename */ if (fmode[0]=='r') { if ((job_area != NULL) && (ftype>=mp_filetype_text || ftype==mp_filetype_program )) { @@ -494,10 +508,10 @@ static char *mpost_find_file(MP mp, const char *fname, const char *fmode, int ft struct stat source_stat, target_stat; char *mpname = mpost_xstrdup(f); *(mpname + strlen(mpname) -1 ) = '\0'; - printf("statting %s and %s\n", mpname, f); + /* printf("statting %s and %s\n", mpname, f); */ if ((stat(f, &target_stat) >= 0) && (stat(mpname, &source_stat) >= 0)) { -#if HAVE_STRUCT_STAT_ST_MTIM +#if HAVE_ST_MTIM if (source_stat.st_mtim.tv_sec <= target_stat.st_mtim.tv_sec || (source_stat.st_mtim.tv_sec == target_stat.st_mtim.tv_sec && source_stat.st_mtim.tv_nsec <= target_stat.st_mtim.tv_nsec)) @@ -588,8 +602,11 @@ processing takes place. As a special hidden feature, a missing right hand side is treated as if it was the integer value |1|. -@c -static void internal_set_option(const char *opt) { +@<Declarations@>= +void internal_set_option(const char *opt); + +@ @c +void internal_set_option(const char *opt) { struct set_list_item *itm; char *s, *v; int isstring = 0; @@ -628,8 +645,11 @@ static void internal_set_option(const char *opt) { runs thourgh the list of options and feeds them to the MPlib function |mp_set_internal|. -@c -static void run_set_list (MP mp) { +@<Declarations@>= +void run_set_list (MP mp); + +@ @c +void run_set_list (MP mp) { struct set_list_item *itm; itm = set_list; while (itm!=NULL) { @@ -727,7 +747,7 @@ static struct option mpost_options[] } if (ARGUMENT_IS ("kpathsea-debug")) { - kpathsea_debug |= atoi (optarg); + kpathsea_debug |= (unsigned)atoi (optarg); } else if (ARGUMENT_IS("jobname")) { if (optarg!=NULL) { @@ -830,7 +850,7 @@ static struct option dvitomp_options[] } if (option_is ("kpathsea-debug")) { if (optarg!=NULL) - kpathsea_debug |= atoi (optarg); + kpathsea_debug |= (unsigned)atoi (optarg); } else if (option_is ("progname")) { user_progname = optarg; } else if (option_is("help")) { @@ -994,13 +1014,11 @@ static int setup_var (int def, const char *var_name, boolean nokpse) { mpost_xfree(options->banner); options->banner = mpost_xmalloc(strlen(banner)+ strlen(mpversion)+ - strlen(WEB2CVERSION)+ strlen(kpsebanner_start)+ strlen(kpathsea_version_string)+ strlen(kpsebanner_stop)+1); strcpy (options->banner, banner); strcat (options->banner, mpversion); - strcat (options->banner, WEB2CVERSION); strcat (options->banner, kpsebanner_start); strcat (options->banner, kpathsea_version_string); strcat (options->banner, kpsebanner_stop); @@ -1110,9 +1128,10 @@ so we have to fix up |job_name| and |job_area|. If there was a \.{--jobname} on the command line, we have to reset the options structure as well. -@d IS_DIR_SEP(c) (c=='/' || c=='\\') - @<Discover the job name@>= +#ifndef IS_DIR_SEP +#define IS_DIR_SEP(c) (c=='/' || c=='\\') +#endif { char *tmp_job = NULL; if (options->job_name != NULL) { @@ -1232,18 +1251,12 @@ int main (int argc, char **argv) { /* |start_here| */ @= /*@@=nullpass@@*/ @> if(putenv(xstrdup("engine=metapost"))) fprintf(stdout,"warning: could not set up $engine\n"); - options->main_memory = setup_var (50000,"main_memory",nokpse); - options->hash_size = (unsigned)setup_var (16384,"hash_size",nokpse); - options->max_in_open = setup_var (25,"max_in_open",nokpse); - options->param_size = setup_var (1500,"param_size",nokpse); options->error_line = setup_var (79,"error_line",nokpse); options->half_error_line = setup_var (50,"half_error_line",nokpse); options->max_print_line = setup_var (100,"max_print_line",nokpse); @<Set up the banner line@>; @<Copy the rest of the command line@>; - if (options->ini_version!=(int)true) { - @<Discover the mem name@>; - } + @<Discover the mem name@>; @<Discover the job name@>; @<Register the callback routines@>; mp = mp_initialize(options); diff --git a/Build/source/texk/web2c/mplibdir/mptrap.test b/Build/source/texk/web2c/mplibdir/mptrap.test index e87ebda8a1c..e1eb9b00f92 100755 --- a/Build/source/texk/web2c/mplibdir/mptrap.test +++ b/Build/source/texk/web2c/mplibdir/mptrap.test @@ -26,7 +26,7 @@ set -x # get same filename in log $LN_S $testdir/mtrap.mp . || exit 1 -./mpost --progname=inimpost --ini mtrap +./mpost --ini mtrap diff $testdir/mtrap.log mtrap.log diff $testdir/mtrap.0 mtrap.0 diff $testdir/mtrap.1 mtrap.1 @@ -37,31 +37,9 @@ $LN_S $testdir/trap.mp . || exit 1 $LN_S $testdir/trap.mpx . || exit 1 ## Why is --jobname required? And why --ini? -./mpost --progname=inimpost --jobname=trap --ini <$testdir/mptrap1.in >mptrapin.fot +./mpost --ini --jobname=trap <$testdir/mptrap1.in >mptrapin.fot mv trap.log mptrapin.log || exit 1 diff $testdir/mptrapin.log mptrapin.log -# Must run inimpost or font_name[null_font] is not initialized, leading to diffs. -./mpost --progname=inimpost --jobname=trap --mem=trap \ - <$testdir/mptrap2.in >mptrap.fot - -## Here the test fails, but we just exit or 'make distcheck' would fail - -mv trap.log mptrap.log || exit 1 -mv trap.tfm mptrap.tfm || exit 1 -diff $testdir/mptrap.fot mptrap.fot -diff $testdir/mptrap.log mptrap.log -diff $testdir/trap.5 trap.5 -diff $testdir/trap.6 trap.6 -diff $testdir/trap.148 trap.148 -diff $testdir/trap.149 trap.149 -diff $testdir/trap.150 trap.150 -diff $testdir/trap.151 trap.151 -diff $testdir/trap.197 trap.197 -diff $testdir/trap.200 trap.200 - -./tftopl ./mptrap.tfm mptrap.pl || exit 1 -diff $testdir/mptrap.pl mptrap.pl - exit 0 diff --git a/Build/source/texk/web2c/mplibdir/mpxout.w b/Build/source/texk/web2c/mplibdir/mpxout.w index d0cfeddf33c..0cd99d7409b 100644 --- a/Build/source/texk/web2c/mplibdir/mpxout.w +++ b/Build/source/texk/web2c/mplibdir/mpxout.w @@ -1,4 +1,4 @@ -% $Id: mpxout.w 1219 2010-04-01 09:05:51Z taco $ +% $Id: mpxout.w 1330 2010-07-27 16:53:56Z taco $ % % Copyright 2008-2009 Taco Hoekwater. % @@ -456,6 +456,7 @@ whitespace corrections applied). @d VERBATIM_TEX 1 @d B_TEX 2 +@d FIRST_VERBATIM_TEX 3 @c static int mpx_getbta(MPX mpx, char *s) { @@ -515,6 +516,7 @@ static void mpx_copy_mpto (MPX mpx, FILE *outfile, int textype) { char *t; /* for finding start of last line */ char c; char *res = NULL; + t = NULL; do { if (mpx->aa == NULL || *mpx->aa == 0) { if ((mpx->aa = mpx_getline(mpx,mpx->mpfile)) == NULL) { @@ -551,12 +553,16 @@ static void mpx_copy_mpto (MPX mpx, FILE *outfile, int textype) { *s = c; } while (*(mpx->tt) != 'e'); s = res; - if (textype != VERBATIM_TEX) { + if (textype == B_TEX) { /* whitespace at the end */ for (s = res + strlen(res) - 1; s >= res && (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n'); s--); t = s; *(++s) = '\0'; + } else { + t =s; + } + if (textype == B_TEX || textype == FIRST_VERBATIM_TEX) { /* whitespace at the start */ for (s = res; s < (res + strlen(res)) && (*s == ' ' || *s == '\t' || *s == '\r' @@ -564,7 +570,7 @@ static void mpx_copy_mpto (MPX mpx, FILE *outfile, int textype) { for (; *t != '\n' && t > s; t--); } fprintf(outfile,"%s", s); - if (textype != VERBATIM_TEX) { + if (textype == B_TEX) { /* put no |%| at end if it's only 1 line total, starting with |%|; * this covers the special case |%&format| in a single line. */ if (t != s || *t != '%') @@ -601,6 +607,7 @@ static const char *mpx_postverb[] = { "\n", "\n" } ; @ @c static void mpx_mpto(MPX mpx, char *tmpname, char *mptexpre) { FILE *outfile; + int verbatim_written = 0; int mode = mpx->mode; char *mpname = mpx->mpname; if (mode==mpx_tex_mode) { @@ -654,11 +661,15 @@ static void mpx_mpto(MPX mpx, char *tmpname, char *mptexpre) { fprintf(outfile,mpx_preverb1[mode], mpx->lnno, mpname); else fprintf(outfile,mpx_preverb[mode], mpx->lnno, mpname); - mpx_copy_mpto(mpx, outfile, VERBATIM_TEX); + if (!verbatim_written) + mpx_copy_mpto(mpx, outfile, FIRST_VERBATIM_TEX); + else + mpx_copy_mpto(mpx, outfile, VERBATIM_TEX); fprintf(outfile,"%s", mpx_postverb[mode]); } else { mpx_error(mpx,"unmatched etex"); } + verbatim_written = 1; } } diff --git a/Build/source/texk/web2c/mplibdir/psout.w b/Build/source/texk/web2c/mplibdir/psout.w index fd638ab20d7..4a3300c4877 100644 --- a/Build/source/texk/web2c/mplibdir/psout.w +++ b/Build/source/texk/web2c/mplibdir/psout.w @@ -1,4 +1,4 @@ -% $Id: psout.w 1219 2010-04-01 09:05:51Z taco $ + $Id: psout.w 1400 2010-10-13 11:26:37Z taco $ % % Copyright 2008-2009 Taco Hoekwater. % @@ -64,7 +64,6 @@ @d decr(A) (A)=(A)-1 /* decrease a variable by unity */ @d negate(A) (A)=-(A) /* change the sign of a variable */ @d odd(A) ((A)%2==1) -@d half(A) ((A)/2) @d print_err(A) mp_print_err(mp,(A)) @d max_quarterword 0x3FFF /* largest allowable value in a |quarterword| */ @@ -80,6 +79,7 @@ #include "mplibps.h" /* external header */ #include "mpmp.h" /* internal header */ #include "mppsout.h" /* internal header */ +#include "mpmath.h" /* internal header */ @h @<Declarations@> @<Static variables in the outer block@> @@ -180,8 +180,23 @@ static void mp_ps_print_char (MP mp, int s) { /* prints a single character */ @ @c static void mp_ps_do_print (MP mp, const char *ss, size_t len) { /* prints string |s| */ size_t j = 0; - while ( j<len ){ - mp_ps_print_char(mp, ss[j]); incr(j); + if (len>255) { + while ( j<len ){ + mp_ps_print_char(mp, ss[j]); incr(j); + } + } else { + static char outbuf[256]; + strncpy(outbuf, ss, len+1); + while ( j<len ){ + if ( *(outbuf+j) == 13 ) { + *(outbuf+j) = '\n'; + mp->ps->ps_offset=0; + } else { + mp->ps->ps_offset++; + } + j++; + } + (mp->write_ascii_file)(mp,mp->output_file,outbuf); } } @@ -215,16 +230,6 @@ static void mp_ps_print_nl (MP mp, const char *s) { /* prints string |s| at begi mp_ps_print(mp, s); } -@ An array of digits in the range |0..9| is printed by |print_the_digs|. - -@c -static void mp_ps_print_the_digs (MP mp, int k) { - /* prints |dig[k-1]|$\,\ldots\,$|dig[0]| */ - while ( k-->0 ){ - mp_ps_print_char(mp, '0'+mp->dig[k]); - } -} - @ The following procedure, which prints out the decimal representation of a given integer |n|, has been written carefully so that it works properly if |n=0| or if |(-n)| would cause overflow. It does not apply |mod| or |div| @@ -234,7 +239,10 @@ by all \PASCAL\ compilers. @c static void mp_ps_print_int (MP mp,integer n) { /* prints an integer in decimal form */ integer m; /* used to negate |n| in possibly dangerous cases */ + char outbuf [24]; /* dig[23], plus terminating \0 */ + unsigned char dig[23]; /* digits in a number, for rounding */ int k = 0; /* index to current digit; we assume that $|n|<10^{23}$ */ + int l = 0; if ( n<0 ) { mp_ps_print_char(mp, '-'); if ( n>-100000000 ) { @@ -242,16 +250,21 @@ static void mp_ps_print_int (MP mp,integer n) { /* prints an integer in decimal } else { m=-1-n; n=m / 10; m=(m % 10)+1; k=1; if ( m<10 ) { - mp->dig[0]=(unsigned char)m; + dig[0]=(unsigned char)m; } else { - mp->dig[0]=0; incr(n); + dig[0]=0; incr(n); } } } do { - mp->dig[k]=(unsigned char)(n % 10); n=n / 10; incr(k); + dig[k]=(unsigned char)(n % 10); n=n / 10; incr(k); } while (n!=0); - mp_ps_print_the_digs(mp, k); + /* print the digits */ + while ( k-->0 ){ + outbuf[l++] = (char)('0'+dig[k]); + } + outbuf[l] = '\0'; + (mp->write_ascii_file)(mp,mp->output_file,outbuf); } @ \MP\ also makes use of a trivial procedure to print two digits. The @@ -527,7 +540,7 @@ avl_tree enc_tree; @ @<Static variables in the outer block@>= -static const char notdef[] = ".notdef"; +static char notdef[] = ".notdef"; @ @<Set initial...@>= mp->ps->enc_tree = NULL; @@ -680,23 +693,44 @@ static void mp_font_encodings (MP mp, font_number lastfnum, boolean encodings_on @<Glob...@>= void * fm_file; +size_t fm_byte_waiting; +size_t fm_byte_length; +unsigned char *fm_bytes; + +@ This is comparable to t1 font loading (see below) but because the first +thing done is not calling |fm_getchar()| but |fm_eof()|, the initial value +of length has to be one more than waiting. + +@<Set initial ...@>= +mp->ps->fm_byte_waiting=0; +mp->ps->fm_byte_length=1; +mp->ps->fm_bytes=NULL; @ -@d fm_close() (mp->close_file)(mp,mp->ps->fm_file) -@d fm_eof() (mp->eof_file)(mp,mp->ps->fm_file) +@d fm_eof() (mp->ps->fm_byte_waiting>=mp->ps->fm_byte_length) +@d fm_close() do { + (mp->close_file)(mp,mp->ps->fm_file); + mp_xfree(mp->ps->fm_bytes); + mp->ps->fm_bytes = NULL; + mp->ps->fm_byte_waiting=0; + mp->ps->fm_byte_length=1; +} while (0) +@d valid_code(c) (c >= 0 && c < 256) @c -static int fm_getchar(MP mp) { - size_t len = 1; - unsigned char abyte=0; - void *byte_ptr = &abyte; - (mp->read_binary_file)(mp,mp->ps->fm_file,&byte_ptr,&len); - if (len==0) - return EOF; - return abyte; +static int fm_getchar (MP mp) { + if (mp->ps->fm_bytes == NULL) { + void *byte_ptr ; + (void)fseek(mp->ps->fm_file,0,SEEK_END); + mp->ps->fm_byte_length = (size_t)ftell(mp->ps->fm_file); + (void)fseek(mp->ps->fm_file,0,SEEK_SET); + mp->ps->fm_bytes = mp_xmalloc(mp, mp->ps->fm_byte_length, 1); + byte_ptr = (void *)mp->ps->fm_bytes; + (mp->read_binary_file)(mp,mp->ps->fm_file,&byte_ptr,&mp->ps->fm_byte_length); + } + return *(mp->ps->fm_bytes+mp->ps->fm_byte_waiting++); } - @ @<Types...@>= enum _mode { FM_DUPIGNORE, FM_REPLACE, FM_DELETE }; enum _ltype { MAPFILE, MAPLINE }; @@ -1466,13 +1500,11 @@ void mp_map_file (MP mp, str_number t) { char *ss = mp_str (mp,t); char *s = mp_xstrdup(mp, ss); mp_process_map_item (mp, s, MAPFILE); - mp_xfree(ss); } void mp_map_line (MP mp, str_number t) { char *ss = mp_str (mp,t); char *s = mp_xstrdup(mp,ss); mp_process_map_item (mp, s, MAPLINE); - mp_xfree(ss); mp_xfree(s); } @@ -1583,18 +1615,20 @@ void mp_set_job_id (MP mp) { if ( mp->job_name==NULL ) mp->job_name = mp_xstrdup(mp,"mpout"); name_string = mp_xstrdup (mp,mp->job_name); - format_string = mp_xstrdup (mp,mp->mem_ident); + format_string = mp_xstrdup (mp, mp->mem_ident); + if (format_string == NULL) + format_string = mp_xstrdup (mp, ""); slen = SMALL_BUF_SIZE + strlen (name_string) + strlen (format_string); s = mp_xmalloc (mp,slen, sizeof (char)); @= /*@@-bufferoverflowhigh@@*/ @> sprintf (s,"%.4u/%.2u/%.2u %.2u:%.2u %s %s", - ((unsigned)mp->internal[mp_year]>>16), - ((unsigned)mp->internal[mp_month]>>16), - ((unsigned)mp->internal[mp_day]>>16), - ((unsigned)mp->internal[mp_time]>>16) / 60, - ((unsigned)mp->internal[mp_time]>>16) % 60, + ((unsigned)internal_value(mp_year)>>16), + ((unsigned)internal_value(mp_month)>>16), + ((unsigned)internal_value(mp_day)>>16), + ((unsigned)internal_value(mp_time)>>16) / 60, + ((unsigned)internal_value(mp_time)>>16) % 60, name_string, format_string); @= /*@@=bufferoverflowhigh@@*/ @> mp->ps->job_id_string = mp_xstrdup (mp,s); @@ -1703,7 +1737,7 @@ static void make_subset_tag (MP mp, fm_entry * fm_cur, char **glyph_names, font_ mp->ps->hexline_length = HEXLINE_WIDTH; end_hexline(mp); mp->ps->t1_eexec_encrypt = false -@d t1_log(s) mp_print(mp,(const char *)s) +@d t1_log(s) mp_print(mp,s) @d t1_putchar(c) wps_chr(c) @d embed_all_glyphs(tex_font) false @d t1_char(c) c @@ -1718,30 +1752,40 @@ char *cur_enc_name; unsigned char *grid; char *ext_glyph_names[256]; char print_buf[PRINTF_BUF_SIZE]; -int t1_byte_waiting; +size_t t1_byte_waiting; +size_t t1_byte_length; +unsigned char *t1_bytes; @ @<Set initial ...@>= mp->ps->dvips_extra_charset=NULL; mp->ps->t1_byte_waiting=0; +mp->ps->t1_byte_length=0; +mp->ps->t1_bytes=NULL; @ -@d t1_ungetchar(A) mp->ps->t1_byte_waiting=(int)(A) -@d t1_eof() (mp->eof_file)(mp,mp->ps->t1_file) -@d t1_close() (mp->close_file)(mp,mp->ps->t1_file) +@d t1_ungetchar() mp->ps->t1_byte_waiting-- +@d t1_eof() (mp->ps->t1_byte_waiting>=mp->ps->t1_byte_length) +@d t1_close() do { + (mp->close_file)(mp,mp->ps->t1_file); + mp_xfree(mp->ps->t1_bytes); + mp->ps->t1_bytes = NULL; + mp->ps->t1_byte_waiting=0; + mp->ps->t1_byte_length=0; +} while (0) @d valid_code(c) (c >= 0 && c < 256) @c static int t1_getchar (MP mp) { - size_t len = 1; - unsigned char abyte=0; - void *byte_ptr = &abyte; - if (mp->ps->t1_byte_waiting) { - abyte = (unsigned char)mp->ps->t1_byte_waiting; - mp->ps->t1_byte_waiting = 0; - } else { - (mp->read_binary_file)(mp,mp->ps->t1_file,&byte_ptr,&len); - } - return (int)abyte; + if (mp->ps->t1_bytes == NULL) { + void *byte_ptr ; + (void)fseek(mp->ps->t1_file,0,SEEK_END); + mp->ps->t1_byte_length = (size_t)ftell(mp->ps->t1_file); + (void)fseek(mp->ps->t1_file,0,SEEK_SET); + mp->ps->t1_bytes = mp_xmalloc(mp, mp->ps->t1_byte_length, 1); + byte_ptr = (void *)mp->ps->t1_bytes; + (mp->read_binary_file)(mp,mp->ps->t1_file,&byte_ptr,&mp->ps->t1_byte_length); + } + return *(mp->ps->t1_bytes+mp->ps->t1_byte_waiting++); } @ @<Static variables in the outer block@>= @@ -1788,11 +1832,12 @@ static const char charstringname[] = "/CharStrings"; @ @<Glob...@>= char **t1_glyph_names; char *t1_builtin_glyph_names[256]; +char charsetstr[0x4000]; boolean read_encoding_only; int t1_encoding; @ @c -#define T1_BUF_SIZE 0x10 +#define T1_BUF_SIZE 0x100 #define CS_HSTEM 1 #define CS_VSTEM 3 @@ -1920,7 +1965,7 @@ static void end_hexline (MP mp) { static void t1_check_pfa (MP mp) { const int c = t1_getchar (mp); mp->ps->t1_pfa = (c != 128) ? true : false; - t1_ungetchar (c); + t1_ungetchar (); } static int t1_getbyte (MP mp) { @@ -2049,17 +2094,7 @@ static boolean str_suffix (const char *begin_buf, const char *end_buf, } } while (0) -@d out_eexec_char(A) t1_outhex(mp,(A)) - @c -static void t1_outhex (MP mp, byte b) -{ - static const char *hexdigits = "0123456789ABCDEF"; - t1_putchar (hexdigits[b / 16]); - t1_putchar (hexdigits[b % 16]); - mp->ps->hexline_length += 2; - end_hexline (mp); -} static void t1_getline (MP mp) { int c, l, eexec_scan; char *p; @@ -2120,16 +2155,40 @@ static void t1_getline (MP mp) { static void t1_putline (MP mp) { + char ss[256]; + int ss_cur = 0; + static const char *hexdigits = "0123456789ABCDEF"; char *p = mp->ps->t1_line_array; if (mp->ps->t1_line_ptr - mp->ps->t1_line_array <= 1) return; if (mp->ps->t1_eexec_encrypt) { - while (p < mp->ps->t1_line_ptr) - out_eexec_char (eencrypt (mp,(byte)*p++)); + while (p < mp->ps->t1_line_ptr) { + byte b = eencrypt (mp,(byte)*p++); + if (ss_cur>=253) { + ss[ss_cur] = '\0'; + (mp->write_ascii_file)(mp,mp->output_file,(char *)ss); + ss_cur = 0; + } + ss[ss_cur++] = hexdigits[b / 16]; + ss[ss_cur++] = hexdigits[b % 16]; + mp->ps->hexline_length += 2; + if (mp->ps->hexline_length >= HEXLINE_WIDTH) { + ss[ss_cur++] = '\n'; + mp->ps->hexline_length = 0; + } + } } else { - while (p < mp->ps->t1_line_ptr) - t1_putchar (*p++); + while (p < mp->ps->t1_line_ptr) { + if (ss_cur>=255) { + ss[ss_cur] = '\0'; + (mp->write_ascii_file)(mp,mp->output_file,(char *)ss); + ss_cur = 0; + } + ss[ss_cur++] = (char)(*p++); } + } + ss[ss_cur] = '\0'; + (mp->write_ascii_file)(mp,mp->output_file,(char *)ss); } static void t1_puts (MP mp, const char *s) @@ -2876,8 +2935,38 @@ static void t1_subset_ascii_part (MP mp, font_number tex_font, fm_entry *fm_cur) int i, j; t1_getline (mp); while (!t1_prefix ("/Encoding")) { - t1_scan_param (mp,tex_font, fm_cur); - t1_putline (mp); + t1_scan_param (mp,tex_font, fm_cur); + /* Patch the initial font directory cacheing mechanism found in some + * pfb fonts. + * + * Even though the T1 spec does not explicitly state that 'FontDirectory' + * should appear at the start of a line, luckily this is standard practise. + */ + if (t1_prefix ("FontDirectory")) { + char *endloc, *p; + char new_line[T1_BUF_SIZE] = {0}; + p = mp->ps->t1_line_array; + while ((endloc = strstr(p,fm_cur->ps_name)) != NULL) { + int n = (endloc-mp->ps->t1_line_array) + strlen(fm_cur->subset_tag) + 2 + strlen(fm_cur->ps_name); + if (n >= T1_BUF_SIZE) { + mp_fatal_error (mp, "t1_subset_ascii_part: buffer overrun detected."); + } + strncat(new_line,p,(endloc-p)); + strcat(new_line,fm_cur->subset_tag); + strcat(new_line,"-"); + strcat(new_line,fm_cur->ps_name); + p = endloc + strlen(fm_cur->ps_name); + } + if (strlen(new_line) + strlen(p) + 1 >= T1_BUF_SIZE ) { + mp_fatal_error (mp, "t1_subset_ascii_part: buffer overrun detected."); + } + strcat(new_line, p); + strcpy(mp->ps->t1_line_array,new_line); + mp->ps->t1_line_ptr = mp->ps->t1_line_array + strlen(mp->ps->t1_line_array); + t1_putline (mp); + } else { + t1_putline (mp); + } t1_getline (mp); } t1_builtin_enc (mp); @@ -2885,12 +2974,6 @@ static void t1_subset_ascii_part (MP mp, font_number tex_font, fm_entry *fm_cur) mp->ps->t1_glyph_names = external_enc (); else mp->ps->t1_glyph_names = mp->ps->t1_builtin_glyph_names; - /* - |if (is_included (fm_cur) && is_subsetted (fm_cur)) { - make_subset_tag (fm_cur, t1_glyph_names, tex_font); - update_subset_tag (); - }| - */ if ((!is_subsetted (fm_cur)) && mp->ps->t1_encoding == ENC_STANDARD) t1_puts (mp,"/Encoding StandardEncoding def\n"); else { @@ -2900,7 +2983,7 @@ static void t1_subset_ascii_part (MP mp, font_number tex_font, fm_entry *fm_cur) if (is_used_char (i) && mp->ps->t1_glyph_names[i] != notdef && strcmp(mp->ps->t1_glyph_names[i],notdef) != 0) { j++; - mp_snprintf (mp->ps->t1_line_array, mp->ps->t1_line_limit, + mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit, "dup %i /%s put\n", (int) t1_char (i), mp->ps->t1_glyph_names[i]); t1_puts(mp,mp->ps->t1_line_array); @@ -3048,7 +3131,7 @@ static void t1_flush_cs (MP mp, boolean is_subr) *mp->ps->t1_line_ptr++ = *p++; while (mp_isdigit (*p)) p++; - mp_snprintf (mp->ps->t1_line_ptr, mp->ps->t1_line_limit, "%u", (unsigned)count); + mp_snprintf (mp->ps->t1_line_ptr, (int)mp->ps->t1_line_limit, "%u", (unsigned)count); strcat (mp->ps->t1_line_ptr, p); mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array); t1_putline (mp); @@ -3072,10 +3155,10 @@ static void t1_flush_cs (MP mp, boolean is_subr) for (ptr = tab; ptr < end_tab; ptr++) { if (ptr->is_used) { if (is_subr) - mp_snprintf (mp->ps->t1_line_array, mp->ps->t1_line_limit, + mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit, "dup %i %u", (int) (ptr - tab), ptr->cslen); else - mp_snprintf (mp->ps->t1_line_array, mp->ps->t1_line_limit, + mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit, "/%s %u", ptr->glyph_name, ptr->cslen); p = strend (mp->ps->t1_line_array); memcpy (p, ptr->data, (size_t)ptr->len); @@ -3084,14 +3167,14 @@ static void t1_flush_cs (MP mp, boolean is_subr) } else { /* replace unsused subr's by |return_cs| */ if (is_subr) { - mp_snprintf (mp->ps->t1_line_array, mp->ps->t1_line_limit, + mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit, "dup %i %u%s ", (int) (ptr - tab), cs_len, mp->ps->cs_token_pair[0]); p = strend (mp->ps->t1_line_array); memcpy (p, return_cs, (size_t)cs_len); mp->ps->t1_line_ptr = p + cs_len; t1_putline (mp); - mp_snprintf (mp->ps->t1_line_array, mp->ps->t1_line_limit, + mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit, " %s", mp->ps->cs_token_pair[1]); mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array); t1_putline (mp); @@ -3101,7 +3184,7 @@ static void t1_flush_cs (MP mp, boolean is_subr) if (ptr->glyph_name != notdef) mp_xfree (ptr->glyph_name); } - mp_snprintf (mp->ps->t1_line_array, mp->ps->t1_line_limit, "%s", line_end); + mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit, "%s", line_end); mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array); t1_putline (mp); if (is_subr) @@ -3313,7 +3396,7 @@ static void t1_free (MP mp) { for (k=0;k<=255;k++) { if (mp->ps->t1_builtin_glyph_names[k] != notdef) mp_xfree(mp->ps->t1_builtin_glyph_names[k]); - mp->ps->t1_builtin_glyph_names[k] = mp_xstrdup(mp, notdef); + mp->ps->t1_builtin_glyph_names[k] = notdef; } } @@ -3444,7 +3527,7 @@ double cur_x, cur_y; /* current point */ double orig_x, orig_y; /* origin (for seac) */ mp_edge_object *h; /* the whole picture */ mp_graphic_object *p; /* the current subpath in the picture */ -mp_knot *pp; /* the last known knot in the subpath */ +mp_knot pp; /* the last known knot in the subpath */ @ @c @@ -3497,9 +3580,9 @@ boolean cs_parse (MP mp, mp_ps_font *f, const char *cs_name, int subr); @d start_subpath(f,dx,dy) do { assert(f->pp == NULL); assert(f->p == NULL); - f->pp = mp_xmalloc(mp, 1, sizeof (mp_knot)); - f->pp->left_type = mp_explicit; - f->pp->right_type = mp_explicit; + f->pp = mp_xmalloc(mp, 1, sizeof (struct mp_knot_data)); + f->pp->data.types.left_type = mp_explicit; + f->pp->data.types.right_type = mp_explicit; f->pp->x_coord = scaled_from_double(f->cur_x + dx); f->pp->y_coord = scaled_from_double(f->cur_y + dy); f->pp->left_x = f->pp->right_x = f->pp->x_coord; @@ -3523,7 +3606,7 @@ boolean cs_parse (MP mp, mp_ps_font *f, const char *cs_name, int subr); } } if (f->p!=NULL) { - mp_knot *r, *rr; + mp_knot r, rr; r = gr_path_p((mp_fill_object *)f->p); rr = r; if (r && r->x_coord == f->pp->x_coord && r->y_coord == f->pp->y_coord ) { @@ -3541,9 +3624,9 @@ boolean cs_parse (MP mp, mp_ps_font *f, const char *cs_name, int subr); @d add_line_segment(f,dx,dy) do { assert(f->pp != NULL); - n = mp_xmalloc(mp,1, sizeof (mp_knot)); - n->left_type = mp_explicit; - n->right_type = mp_explicit; + n = mp_xmalloc(mp,1, sizeof (struct mp_knot_data)); + n->data.types.left_type = mp_explicit; + n->data.types.right_type = mp_explicit; n->next = gr_path_p((mp_fill_object *)f->p); /* loop */ n->x_coord = scaled_from_double(f->cur_x + dx); n->y_coord = scaled_from_double(f->cur_y + dy); @@ -3558,9 +3641,9 @@ boolean cs_parse (MP mp, mp_ps_font *f, const char *cs_name, int subr); } while (0) @d add_curve_segment(f,dx1,dy1,dx2,dy2,dx3,dy3) do { - n = mp_xmalloc(mp, 1, sizeof (mp_knot)); - n->left_type = mp_explicit; - n->right_type = mp_explicit; + n = mp_xmalloc(mp, 1, sizeof (struct mp_knot_data)); + n->data.types.left_type = mp_explicit; + n->data.types.right_type = mp_explicit; n->next = gr_path_p((mp_fill_object *)f->p); /* loop */ n->x_coord = scaled_from_double(f->cur_x + dx1 + dx2 + dx3); n->y_coord = scaled_from_double(f->cur_y + dy1 + dy2 + dy3); @@ -3579,8 +3662,10 @@ boolean cs_parse (MP mp, mp_ps_font *f, const char *cs_name, int subr); @d cs_no_debug(A) cs_do_debug(mp,f,A,#A) @d cs_debug(A) -@c -#if 0 +@<Declarations@>= +void cs_do_debug (MP mp, mp_ps_font *f, int i, char *s); + +@ @c void cs_do_debug (MP mp, mp_ps_font *f, int i, char *s) { int n = cc_tab[i].nargs; (void)mp; /* for -Wall */ @@ -3591,7 +3676,6 @@ void cs_do_debug (MP mp, mp_ps_font *f, int i, char *s) { } fprintf (stdout,"%s\n", s); } -#endif boolean cs_parse (MP mp, mp_ps_font *f, const char *cs_name, int subr) { @@ -3603,7 +3687,7 @@ boolean cs_parse (MP mp, mp_ps_font *f, const char *cs_name, int subr) cs_entry *ptr; cc_entry *cc; - mp_knot *n; + mp_knot n; if (cs_name == NULL) { ptr = f->subr_tab + subr; @@ -4008,7 +4092,7 @@ static char * mp_fm_font_subset_name (MP mp, font_number f) { if (fm != NULL && (fm->ps_name != NULL)) { if (is_subsetted(fm)) { char *s = mp_xmalloc(mp,strlen(fm->ps_name)+8,1); - mp_snprintf(s, strlen(fm->ps_name)+8,"%s-%s",fm->subset_tag,fm->ps_name); + mp_snprintf(s,(int)strlen(fm->ps_name)+8,"%s-%s",fm->subset_tag,fm->ps_name); return s; } else { return mp_xstrdup(mp,fm->ps_name); @@ -4376,7 +4460,10 @@ do { } } } - cur_fsize[f]=link(cur_fsize[f]); + if (cur_fsize[f]==mp_void) + cur_fsize[f]=null; + else + cur_fsize[f]=mp_link(cur_fsize[f]); if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; } } } @@ -4439,10 +4526,10 @@ static void mp_print_improved_prologue (MP mp, mp_edge_object *h, int p1, int pr @ @c void mp_print_improved_prologue (MP mp, mp_edge_object *h, int prologues, int procset) { quarterword next_size; /* the size index for fonts being listed */ - pointer *cur_fsize; /* current positions in |font_sizes| */ + mp_node *cur_fsize; /* current positions in |font_sizes| */ boolean done_fonts; /* have we finished listing the fonts in the header? */ font_number f; /* a font number for loops */ - cur_fsize = mp_xmalloc(mp,(size_t)(mp->font_max+1),sizeof(pointer)); + cur_fsize = mp_xmalloc(mp,(size_t)(mp->font_max+1),sizeof(mp_node)); mp_list_used_resources(mp, prologues, procset); mp_list_supplied_resources(mp, prologues, procset); mp_list_needed_resources(mp, prologues); @@ -4502,13 +4589,13 @@ static font_number mp_print_font_comments (MP mp , mp_edge_object *h, int prolog @c static font_number mp_print_font_comments (MP mp , mp_edge_object *h, int prologues) { quarterword next_size; /* the size index for fonts being listed */ - pointer *cur_fsize; /* current positions in |font_sizes| */ + mp_node *cur_fsize; /* current positions in |font_sizes| */ int ff; /* a loop counter */ boolean done_fonts; /* have we finished listing the fonts in the header? */ font_number f; /* a font number for loops */ scaled ds; /* design size and scale factor for a text node */ int ldf=0; /* the last \.{DocumentFont} listed (otherwise |null_font|) */ - cur_fsize = mp_xmalloc(mp,(size_t)(mp->font_max+1),sizeof(pointer)); + cur_fsize = mp_xmalloc(mp,(size_t)(mp->font_max+1),sizeof(mp_node)); if ( prologues>0 ) { @<Give a \.{DocumentFonts} comment listing all fonts with non-null |font_sizes| and eliminate duplicates@>; @@ -4651,8 +4738,8 @@ static boolean mp_check_ps_marks (MP mp,font_number f) { it was believed that processing programs might not know how to deal with long lines. Nowadays (1.204), we trust backends to do the right thing. -@d link(A) mp->mem[(A)].hh.rh /* the |link| field of a memory word */ -@d sc_factor(A) mp->mem[(A)+1].cint /* the scale factor stored in a font size node */ +@d mp_link(A) (A)->link /* the |link| field of a memory word */ +@d sc_factor(A) ((mp_font_size_node)(A))->sc_factor_ /* the scale factor stored in a font size node */ @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>= { @@ -4666,7 +4753,7 @@ long lines. Nowadays (1.204), we trust backends to do the right thing. mp_ps_print_scaled(mp, ds); mp_ps_marks_out(mp, f ); } - cur_fsize[f]=link(cur_fsize[f]); + cur_fsize[f]=mp_link(cur_fsize[f]); } @ @<Print the procset@>= @@ -4699,7 +4786,7 @@ void mp_print_prologue (MP mp, mp_edge_object *h, int prologues, int procset) { font_number ldf ; ldf = mp_print_font_comments (mp, h, prologues); mp_ps_print_ln(mp); - if ( (prologues==1) && mp->ps->tfm_tree == NULL && (mp->last_ps_fnum<mp->last_fnum) ) + if ( (prologues==1) && (mp->last_ps_fnum<mp->last_fnum) ) mp_read_psname_table(mp); mp_ps_print(mp, "%%BeginProlog"); mp_ps_print_ln(mp); if ( (prologues>0)||(procset>0) ) { @@ -4761,7 +4848,7 @@ static void mp_ps_pair_out (MP mp,scaled x, scaled y) ; @ @c void mp_ps_print_cmd (MP mp, const char *l, const char *s) { - if ( mp->internal[mp_procset]>0 ) { ps_room(strlen(s)); mp_ps_print(mp,s); } + if ( internal_value(mp_procset)>0 ) { ps_room(strlen(s)); mp_ps_print(mp,s); } else { ps_room(strlen(l)); mp_ps_print(mp, l); }; } @@ -4872,13 +4959,13 @@ void mp_print_initial_comment(MP mp,mp_edge_object *hh, int prologues) { mp_ps_print(mp, s); mp_xfree(s); mp_ps_print_nl(mp, "%%CreationDate: "); - mp_ps_print_int(mp, mp_round_unscaled(mp, mp->internal[mp_year])); + mp_ps_print_int(mp, mp_round_unscaled(mp, internal_value(mp_year))); mp_ps_print_char(mp, '.'); - mp_ps_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_month])); + mp_ps_print_dd(mp, mp_round_unscaled(mp, internal_value(mp_month))); mp_ps_print_char(mp, '.'); - mp_ps_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_day])); + mp_ps_print_dd(mp, mp_round_unscaled(mp, internal_value(mp_day))); mp_ps_print_char(mp, ':'); - t=mp_round_unscaled(mp, mp->internal[mp_time]); + t=mp_round_unscaled(mp, internal_value(mp_time)); mp_ps_print_dd(mp, t / 60); mp_ps_print_dd(mp, t % 60); mp_ps_print_nl(mp, "%%Pages: 1"); @@ -4888,22 +4975,10 @@ void mp_print_initial_comment(MP mp,mp_edge_object *hh, int prologues) { a \MP\ path. @(mplibps.h@>= -typedef struct mp_knot { - unsigned short left_type; - unsigned short right_type; - signed int x_coord; - signed int y_coord; - signed int left_x; - signed int left_y; - signed int right_x; - signed int right_y; - struct mp_knot * next; - unsigned char originator; -} mp_knot; @ @<Types...@>= -#define gr_left_type(A) (A)->left_type -#define gr_right_type(A) (A)->right_type +#define gr_left_type(A) (A)->data.types.left_type +#define gr_right_type(A) (A)->data.types.right_type #define gr_x_coord(A) (A)->x_coord #define gr_y_coord(A) (A)->y_coord #define gr_left_x(A) (A)->left_x @@ -4916,10 +4991,10 @@ typedef struct mp_knot { @ If we want to duplicate a knot node, we can say |copy_knot|: @c -static mp_knot *mp_gr_copy_knot (MP mp, mp_knot *p) { - mp_knot *q; /* the copy */ - q = mp_xmalloc(mp, 1, sizeof (mp_knot)); - memcpy(q,p,sizeof (mp_knot)); +static mp_knot mp_gr_copy_knot (MP mp, mp_knot p) { + mp_knot q; /* the copy */ + q = mp_xmalloc(mp, 1, sizeof (struct mp_knot_data)); + memcpy(q,p,sizeof (struct mp_knot_data)); gr_next_knot(q)=NULL; return q; } @@ -4927,8 +5002,8 @@ static mp_knot *mp_gr_copy_knot (MP mp, mp_knot *p) { @ The |copy_path| routine makes a clone of a given path. @c -static mp_knot *mp_gr_copy_path (MP mp, mp_knot *p) { - mp_knot *q, *pp, *qq; /* for list manipulation */ +static mp_knot mp_gr_copy_path (MP mp, mp_knot p) { + mp_knot q, pp, qq; /* for list manipulation */ if (p==NULL) return NULL; q=mp_gr_copy_knot(mp, p); @@ -4943,58 +5018,19 @@ static mp_knot *mp_gr_copy_path (MP mp, mp_knot *p) { return q; } -@ Similarly, there's a way to copy the {\sl reverse\/} of a path. This procedure -returns a pointer to the first node of the copy, if the path is a cycle, -but to the final node of a non-cyclic copy. The global -variable |path_tail| will point to the final node of the original path; -this trick makes it easier to implement `\&{doublepath}'. - -All node types are assumed to be |endpoint| or |explicit| only. - -This function is currenly unused. - -@c -#if 0 -mp_knot * mp_gr_htap_ypoc (MP mp, mp_knot *p) { - mp_knot *q, *pp, *qq, *rr; /* for list manipulation */ - q=mp_xmalloc(mp, 1, sizeof (mp_knot)); /* this will correspond to |p| */ - qq=q; pp=p; - while (1) { - gr_right_type(qq)=gr_left_type(pp); - gr_left_type(qq)=gr_right_type(pp); - gr_x_coord(qq)=gr_x_coord(pp); - gr_y_coord(qq)=gr_y_coord(pp); - gr_right_x(qq)=gr_left_x(pp); - gr_right_y(qq)=gr_left_y(pp); - gr_left_x(qq)=gr_right_x(pp); - gr_left_y(qq)=gr_right_y(pp); - gr_originator(qq)=gr_originator(pp); - if ( gr_next_knot(pp)==p ) { - gr_next_knot(q)=qq; - /* |mp->path_tail=pp;| */ /* ? */ - return q; - } - rr=mp_xmalloc(mp, 1, sizeof (mp_knot)); - gr_next_knot(rr)=qq; - qq=rr; - pp=gr_next_knot(pp); - } -} -#endif - @ When a cyclic list of knot nodes is no longer needed, it can be recycled by calling the following subroutine. @<Declarations@>= -static void mp_do_gr_toss_knot_list (mp_knot *p) ; +static void mp_do_gr_toss_knot_list (mp_knot p) ; @ @d mp_gr_toss_knot_list(B,A) mp_do_gr_toss_knot_list(A) @c -void mp_do_gr_toss_knot_list (mp_knot * p) { - mp_knot *q; /* the node being freed */ - mp_knot *r; /* the next node */ +void mp_do_gr_toss_knot_list (mp_knot p) { + mp_knot q; /* the node being freed */ + mp_knot r; /* the next node */ if (p==NULL) return; q=p; @@ -5007,8 +5043,8 @@ void mp_do_gr_toss_knot_list (mp_knot * p) { @ @c -static void mp_gr_ps_path_out (MP mp, mp_knot *h) { - mp_knot *p, *q; /* for scanning the path */ +static void mp_gr_ps_path_out (MP mp, mp_knot h) { + mp_knot p, q; /* for scanning the path */ scaled d; /* a temporary value */ boolean curved; /* |true| unless the cubic is almost straight */ ps_room(40); @@ -5196,9 +5232,9 @@ typedef struct mp_fill_object { mp_color color; unsigned char color_model; unsigned char ljoin ; - mp_knot * path_p; - mp_knot * htap_p; - mp_knot * pen_p; + mp_knot path_p; + mp_knot htap_p; + mp_knot pen_p; int miterlim ; } mp_fill_object; @@ -5210,20 +5246,20 @@ typedef struct mp_stroked_object { unsigned char color_model; unsigned char ljoin ; unsigned char lcap ; - mp_knot * path_p; - mp_knot * pen_p; + mp_knot path_p; + mp_knot pen_p; int miterlim ; mp_dash_object *dash_p; } mp_stroked_object; typedef struct mp_clip_object { GRAPHIC_BODY; - mp_knot * path_p; + mp_knot path_p; } mp_clip_object; typedef struct mp_bounds_object { GRAPHIC_BODY; - mp_knot * path_p; + mp_knot path_p; } mp_bounds_object; typedef struct mp_special_object { @@ -5325,7 +5361,7 @@ should be black so that the translation of an all-black picture will have no Hence we use |c=0| when initializing the graphics state and we use |c<0| to recover from a situation where we have lost track of the graphics state. -@d mp_void (null+1) /* a null pointer different from |null| */ +@d mp_void (mp_node)(null+1) /* a null pointer different from |null| */ @c static void mp_gs_unknown_graphics_state (MP mp,scaled c) { struct _gs_state *p; /* to shift graphic states around */ @@ -5370,7 +5406,7 @@ static void mp_gr_fix_graphics_state (MP mp, mp_graphic_object *p) ; @ @c void mp_gr_fix_graphics_state (MP mp, mp_graphic_object *p) { /* get ready to output graphical object |p| */ - mp_knot *pp, *path_p; /* for list manipulation */ + mp_knot pp, path_p; /* for list manipulation */ mp_dash_object *hh; scaled wx,wy,ww; /* dimensions of pen bounding box */ quarterword adj_wx; /* whether pixel rounding should be based on |wx| or |wy| */ @@ -5543,7 +5579,7 @@ if ( (ww!=gs_width) || (adj_wx!=gs_adj_wx) ) { mp_ps_print_cmd(mp, " 0 dtransform exch truncate exch idtransform pop setlinewidth"," hlw"); } else { - if ( mp->internal[mp_procset]>0 ) { + if ( internal_value(mp_procset)>0 ) { ps_room(13); mp_ps_print_char(mp, ' '); mp_ps_print_scaled(mp, ww); @@ -5591,13 +5627,13 @@ allowable range for $x$ or~$y$. We do not need and cannot afford a full bounding-box computation. @<Declarations@>= -static boolean mp_gr_coord_rangeOK (mp_knot *h, +static boolean mp_gr_coord_rangeOK (mp_knot h, quarterword zoff, scaled dz); @ @c -boolean mp_gr_coord_rangeOK (mp_knot *h, +boolean mp_gr_coord_rangeOK (mp_knot h, quarterword zoff, scaled dz) { - mp_knot *p; /* for scanning the path form |h| */ + mp_knot p; /* for scanning the path form |h| */ scaled zlo,zhi; /* coordinate range so far */ scaled z; /* coordinate currently being tested */ if (zoff==do_x_loc) { @@ -5726,7 +5762,7 @@ static void mp_gr_stroke_ellipse (MP mp, mp_graphic_object *h, boolean fill_als @c void mp_gr_stroke_ellipse (MP mp, mp_graphic_object *h, boolean fill_also) { /* generate an elliptical pen stroke from object |h| */ scaled txx,txy,tyx,tyy; /* transformation parameters */ - mp_knot *p; /* the pen to stroke with */ + mp_knot p; /* the pen to stroke with */ scaled d1,det; /* for tweaking transformation parameters */ integer s; /* also for tweaking transformation paramters */ boolean transformed; /* keeps track of whether gsave/grestore are needed */ @@ -5739,7 +5775,7 @@ static void mp_gr_stroke_ellipse (MP mp, mp_graphic_object *h, boolean fill_als } else { mp_gr_ps_path_out(mp, gr_path_p((mp_stroked_object *)h)); } - if ( mp->internal[mp_procset]==0 ) { + if ( internal_value(mp_procset)==0 ) { if ( fill_also ) mp_ps_print_nl(mp, "gsave fill grestore"); @<Issue \ps\ commands to transform the coordinate system@>; mp_ps_print(mp, " stroke"); @@ -5848,10 +5884,10 @@ if ( abs(det)<d1 ) { @ Here is a simple routine that just fills a cycle. @<Declarations@>= -static void mp_gr_ps_fill_out (MP mp, mp_knot *p); +static void mp_gr_ps_fill_out (MP mp, mp_knot p); @ @c -void mp_gr_ps_fill_out (MP mp, mp_knot *p) { /* fill cyclic path~|p| */ +void mp_gr_ps_fill_out (MP mp, mp_knot p) { /* fill cyclic path~|p| */ mp_gr_ps_path_out(mp, p); mp_ps_print_cmd(mp, " fill"," F"); mp_ps_print_ln(mp); @@ -5896,22 +5932,24 @@ static quarterword mp_size_index (MP mp, font_number f, scaled s) ; @ @c quarterword mp_size_index (MP mp, font_number f, scaled s) { - pointer p,q; /* the previous and current font size nodes */ + mp_node p,q; /* the previous and current font size nodes */ int i; /* the size index for |q| */ + p=NULL; q=mp->font_sizes[f]; i=0; while ( q!=null ) { if ( abs(s-sc_factor(q))<=fscale_tolerance ) return (quarterword)i; else - { p=q; q=link(q); incr(i); }; + { p=q; q=mp_link(q); incr(i); }; if ( i==max_quarterword ) mp_overflow(mp, "sizes per font",max_quarterword); @:MetaPost capacity exceeded sizes per font}{\quad sizes per font@> } - q=mp_get_node(mp, font_size_size); + q=(mp_node)mp_xmalloc(mp, 1, font_size_size); + mp_link(q) = NULL; sc_factor(q)=s; - if ( i==0 ) mp->font_sizes[f]=q; else link(p)=q; + if ( i==0 ) mp->font_sizes[f]=q; else mp_link(p)=q; return (quarterword)i; } @@ -5920,13 +5958,13 @@ static scaled mp_indexed_size (MP mp,font_number f, quarterword j); @ @c scaled mp_indexed_size (MP mp,font_number f, quarterword j) { - pointer p; /* a font size node */ + mp_node p; /* a font size node */ int i; /* the size index for |p| */ p=mp->font_sizes[f]; i=0; if ( p==null ) mp_confusion(mp, "size"); while ( (i!=j) ) { - incr(i); p=link(p); + incr(i); p=mp_link(p); if ( p==null ) mp_confusion(mp, "size"); } return sc_factor(p); @@ -5937,11 +5975,11 @@ static void mp_clear_sizes (MP mp) ; @ @c void mp_clear_sizes (MP mp) { font_number f; /* the font whose size list is being cleared */ - pointer p; /* current font size nodes */ + mp_node p; /* current font size nodes */ for (f=null_font+1;f<=mp->last_fnum;f++) { while ( mp->font_sizes[f]!=null ) { p=mp->font_sizes[f]; - mp->font_sizes[f]=link(p); + mp->font_sizes[f]=mp_link(p); mp_free_node(mp, p,font_size_size); } } @@ -5979,7 +6017,14 @@ characters used for each size. This is done by keeping a linked list of sizes for each font with a counter in each text node giving the appropriate position in the size list for its font. -@d font_size_size 2 /* size of a font size node */ +@d font_size_size sizeof(struct mp_font_size_node_data) /* size of a font size node */ + +@<Types...@>= +typedef struct mp_font_size_node_data { + NODE_BODY; + scaled sc_factor_; +} mp_font_size_node_data; +typedef struct mp_font_size_node_data* mp_font_size_node; @ @<Declarations@>= @@ -6061,11 +6106,11 @@ int mp_gr_ship_out (mp_edge_object *hh, int qprologues, int qprocset,int standal } if (mp->history >= mp_fatal_error_stop ) return 1; if (qprologues<0) - prologues = (int)((unsigned)mp->internal[mp_prologues]>>16); + prologues = (int)((unsigned)internal_value(mp_prologues)>>16); else prologues=qprologues; if (qprocset<0) - procset = (int)((unsigned)mp->internal[mp_procset]>>16); + procset = (int)((unsigned)internal_value(mp_procset)>>16); else procset=qprocset; mp_open_output_file(mp); @@ -6137,13 +6182,13 @@ int mp_gr_ship_out (mp_edge_object *hh, int qprologues, int qprocset,int standal mp_gr_ps_path_out(mp, gr_path_p((mp_clip_object *)p)); mp_ps_print_cmd(mp, " clip"," W"); mp_ps_print_ln(mp); - if ( mp->internal[mp_restore_clip_color]>0 ) + if ( internal_value(mp_restore_clip_color)>0 ) mp_gs_unknown_graphics_state(mp, 1); break; case mp_stop_clip_code: mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "grestore","Q"); mp_ps_print_ln(mp); - if ( mp->internal[mp_restore_clip_color]>0 ) + if ( internal_value(mp_restore_clip_color)>0 ) mp_gs_unknown_graphics_state(mp, 2); else mp_gs_unknown_graphics_state(mp, -1); @@ -6268,7 +6313,7 @@ void mp_gr_toss_object (mp_graphic_object *p) { case mp_stop_bounds_code: break; case mp_special_code: - mp_xfree(gr_pre_script((mp_special_object *)p)); + mp_xfree(gr_pre_script((mp_special_object *)p)); break; } /* all cases are enumerated */ mp_xfree(p); diff --git a/Build/source/texk/web2c/mplibdir/svgout.w b/Build/source/texk/web2c/mplibdir/svgout.w index 3ed6f825590..9c171c30f1d 100644 --- a/Build/source/texk/web2c/mplibdir/svgout.w +++ b/Build/source/texk/web2c/mplibdir/svgout.w @@ -61,6 +61,7 @@ #include "mpmp.h" /* internal header */ #include "mppsout.h" /* internal header */ #include "mpsvgout.h" /* internal header */ +#include "mpmath.h" /* internal header */ @h @<Types in the outer block@> @<Declarations@> @@ -214,6 +215,7 @@ it works properly if |n=0| or if |(-n)| would cause overflow. @c static void mp_svg_store_int (MP mp,integer n) { + unsigned char dig[23]; /* digits in a number, for rounding */ integer m; /* used to negate |n| in possibly dangerous cases */ int k = 0; /* index to current digit; we assume that $|n|<10^{23}$ */ if ( n<0 ) { @@ -223,18 +225,18 @@ static void mp_svg_store_int (MP mp,integer n) { } else { m=-1-n; n=m / 10; m=(m % 10)+1; k=1; if ( m<10 ) { - mp->dig[0]=(unsigned char)m; + dig[0]=(unsigned char)m; } else { - mp->dig[0]=0; incr(n); + dig[0]=0; incr(n); } } } do { - mp->dig[k]=(unsigned char)(n % 10); n=n / 10; incr(k); + dig[k]=(unsigned char)(n % 10); n=n / 10; incr(k); } while (n!=0); /* print the digits */ while ( k-->0 ){ - append_char((char)('0'+mp->dig[k])); + append_char((char)('0'+dig[k])); } } @@ -388,8 +390,11 @@ void mp_svg_pair_out (MP mp,scaled x, scaled y) { mp_svg_store_scaled(mp, (-(y+mp->svg->dy))); } +@ @<Declarations@>= +void mp_svg_font_pair_out (MP mp,scaled x, scaled y) ; + @ @c -static void mp_svg_font_pair_out (MP mp,scaled x, scaled y) { +void mp_svg_font_pair_out (MP mp,scaled x, scaled y) { mp_svg_store_scaled(mp, (x)); append_char(' '); mp_svg_store_scaled(mp, -(y)); @@ -406,8 +411,11 @@ have a specific helper to write such distorted pairs of coordinates out. @d scaled_from_double(a) (scaled)((a)*65536.0) @d double_from_scaled(a) (double)((a)/65536.0) -@c -static void mp_svg_trans_pair_out (MP mp, mp_pen_info *pen, scaled x, scaled y) { +@<Declarations@>= +void mp_svg_trans_pair_out (MP mp, mp_pen_info *pen, scaled x, scaled y) ; + +@ @c +void mp_svg_trans_pair_out (MP mp, mp_pen_info *pen, scaled x, scaled y) { double sx,sy, rx,ry, px, py, retval, divider; sx = double_from_scaled(pen->sx); sy = double_from_scaled(pen->sy); @@ -479,13 +487,13 @@ void mp_svg_print_initial_comment(MP mp,mp_edge_object *hh) { mp_svg_print(mp, s); mp_xfree(s); mp_svg_print(mp, " on "); - mp_svg_store_int(mp, mp_round_unscaled(mp, mp->internal[mp_year])); + mp_svg_store_int(mp, mp_round_unscaled(mp, internal_value(mp_year))); append_char('.'); - mp_svg_store_dd(mp, mp_round_unscaled(mp, mp->internal[mp_month])); + mp_svg_store_dd(mp, mp_round_unscaled(mp, internal_value(mp_month))); append_char('.'); - mp_svg_store_dd(mp, mp_round_unscaled(mp, mp->internal[mp_day])); + mp_svg_store_dd(mp, mp_round_unscaled(mp, internal_value(mp_day))); append_char(':'); - t=mp_round_unscaled(mp, mp->internal[mp_time]); + t=mp_round_unscaled(mp, internal_value(mp_time)); mp_svg_store_dd(mp, t / 60); mp_svg_store_dd(mp, t % 60); mp_svg_print_buf(mp); @@ -561,7 +569,7 @@ typedef struct mp_pen_info { @ (Re)discover the characteristics of an elliptical pen @<Declarations@>= -mp_pen_info *mp_svg_pen_info(MP mp, mp_knot *pp, mp_knot *p); +mp_pen_info *mp_svg_pen_info(MP mp, mp_knot pp, mp_knot p); @ The next two constants come from the original web source. Together with the two helper functions, they will tell whether @@ -571,10 +579,10 @@ the |x| or the |y| direction of the path is the most important @d aspect_default 1 @c -static scaled coord_range_x (mp_knot *h, scaled dz) { +static scaled coord_range_x (mp_knot h, scaled dz) { scaled z; scaled zlo = 0, zhi = 0; - mp_knot *f = h; + mp_knot f = h; while (h != NULL) { z = gr_x_coord(h); if (z < zlo) zlo = z; else if (z > zhi) zhi = z; @@ -588,10 +596,10 @@ static scaled coord_range_x (mp_knot *h, scaled dz) { } return (zhi - zlo <= dz ? aspect_bound : aspect_default); } -static scaled coord_range_y (mp_knot *h, scaled dz) { +static scaled coord_range_y (mp_knot h, scaled dz) { scaled z; scaled zlo = 0, zhi = 0; - mp_knot *f = h; + mp_knot f = h; while (h != NULL) { z = gr_y_coord(h); if (z < zlo) zlo = z; else if (z > zhi) zhi = z; @@ -608,7 +616,7 @@ static scaled coord_range_y (mp_knot *h, scaled dz) { @ @c -mp_pen_info *mp_svg_pen_info(MP mp, mp_knot *pp, mp_knot *p) { +mp_pen_info *mp_svg_pen_info(MP mp, mp_knot pp, mp_knot p) { scaled wx, wy; /* temporary pen widths, in either direction */ struct mp_pen_info *pen; /* return structure */ if (p == NULL) @@ -658,14 +666,14 @@ cubics with zero initial and final velocity as created by |make_path| or as created by |make_choices|. @<Declarations@>= -static boolean mp_is_curved(mp_knot *p, mp_knot *q) ; +static boolean mp_is_curved(mp_knot p, mp_knot q) ; @ @d bend_tolerance 131 /* allow rounding error of $2\cdot10^{-3}$ */ @c -boolean mp_is_curved(mp_knot *p, mp_knot *q) { +boolean mp_is_curved(mp_knot p, mp_knot q) { scaled d; /* a temporary value */ if ( gr_right_x(p)==gr_x_coord(p) ) if ( gr_right_y(p)==gr_y_coord(p) ) @@ -685,8 +693,8 @@ boolean mp_is_curved(mp_knot *p, mp_knot *q) { @ @c -static void mp_svg_path_out (MP mp, mp_knot *h) { - mp_knot *p, *q; /* for scanning the path */ +static void mp_svg_path_out (MP mp, mp_knot h) { + mp_knot p, q; /* for scanning the path */ append_char('M'); mp_svg_pair_out(mp, gr_x_coord(h),gr_y_coord(h)); p=h; @@ -716,8 +724,8 @@ static void mp_svg_path_out (MP mp, mp_knot *h) { } @ @c -static void mp_svg_path_trans_out (MP mp, mp_knot *h, mp_pen_info *pen) { - mp_knot *p, *q; /* for scanning the path */ +static void mp_svg_path_trans_out (MP mp, mp_knot h, mp_pen_info *pen) { + mp_knot p, q; /* for scanning the path */ append_char('M'); mp_svg_trans_pair_out(mp, pen, gr_x_coord(h),gr_y_coord(h)); p=h; @@ -748,8 +756,8 @@ static void mp_svg_path_trans_out (MP mp, mp_knot *h, mp_pen_info *pen) { @ @c -static void mp_svg_font_path_out (MP mp, mp_knot *h) { - mp_knot *p, *q; /* for scanning the path */ +static void mp_svg_font_path_out (MP mp, mp_knot h) { + mp_knot p, q; /* for scanning the path */ append_char('M'); mp_svg_font_pair_out(mp, gr_x_coord(h),gr_y_coord(h)); p=h; @@ -793,8 +801,11 @@ static void mp_svg_font_path_out (MP mp, mp_knot *h) { mp_chars[(A)][(int)(B)] = 1; } while (0) -@c -static void mp_svg_print_glyph_defs (MP mp, mp_edge_object *h) { +@<Declarations@>= +void mp_svg_print_glyph_defs (MP mp, mp_edge_object *h); + +@ @c +void mp_svg_print_glyph_defs (MP mp, mp_edge_object *h) { mp_graphic_object *p; /* object index */ int k; /* general purpose index */ size_t l; /* a string length */ @@ -1119,10 +1130,10 @@ void mp_svg_stroke_out (MP mp, mp_graphic_object *h, @ Here is a simple routine that just fills a cycle. @<Declarations@>= -static void mp_svg_fill_out (MP mp, mp_knot *p, mp_graphic_object *h); +static void mp_svg_fill_out (MP mp, mp_knot p, mp_graphic_object *h); @ @c -void mp_svg_fill_out (MP mp, mp_knot *p, mp_graphic_object *h) { +void mp_svg_fill_out (MP mp, mp_knot p, mp_graphic_object *h) { mp_svg_open_starttag(mp, "path"); mp_svg_path_out(mp, p); mp_svg_attribute(mp, "d", mp->svg->buf); @@ -1203,7 +1214,7 @@ int mp_svg_gr_ship_out (mp_edge_object *hh, int qprologues, int standalone) { } if (mp->history >= mp_fatal_error_stop ) return 1; mp_open_output_file(mp); - if ( (qprologues>=1) && mp->ps->tfm_tree == NULL && (mp->last_ps_fnum<mp->last_fnum) ) + if ( (qprologues>=1) && (mp->last_ps_fnum<mp->last_fnum) ) mp_read_psname_table(mp); /* The next seems counterintuitive, but calls from |mp_svg_ship_out| * set standalone to true, and because embedded use is likely, it is diff --git a/Build/source/texk/web2c/mplibdir/tfmin.w b/Build/source/texk/web2c/mplibdir/tfmin.w index 8df443db664..56216fb4591 100644 --- a/Build/source/texk/web2c/mplibdir/tfmin.w +++ b/Build/source/texk/web2c/mplibdir/tfmin.w @@ -49,15 +49,16 @@ #include <string.h> #include "mplib.h" #include "mpmp.h" /* internal header */ -#include "mptfmin.h" /* internal header */ +#include "mpmath.h" /* internal header */ +@<Declarations@>; @h @ The |font_ps_name| for a built-in font should be what PostScript expects. A preliminary name is obtained here from the \.{TFM} name as given in the |fname| argument. This gets updated later from an external table if necessary. -@(mptfmin.h@>= -extern font_number mp_read_font_info (MP mp, char *fname); +@<Declarations@>= +font_number mp_read_font_info (MP mp, char *fname); @ @c font_number mp_read_font_info (MP mp, char *fname) { @@ -67,7 +68,7 @@ font_number mp_read_font_info (MP mp, char *fname) { size_t whd_size; /* words needed for heights, widths, and depths */ int i,ii; /* |font_info| indices */ int jj; /* counts bytes to be ignored */ - scaled z; /* used to compute the design size */ + int z; /* used to compute the design size */ fraction d; /* height, width, or depth as a fraction of design size times $2^{-8}$ */ int h_and_d; /* height and depth indices being unpacked */ int tfbyte = 0; /* a byte read from the file */ @@ -158,10 +159,10 @@ if (mp->last_fnum==mp->font_max) mp_reallocate_fonts(mp,(mp->font_max+(mp->font_max/4))); while (mp->next_fmem+whd_size>=mp->font_mem_size) { size_t l = mp->font_mem_size+(mp->font_mem_size/4); - memory_word *font_info; - font_info = mp_xmalloc (mp,(l+1),sizeof(memory_word)); - memset (font_info,0,sizeof(memory_word)*(l+1)); - memcpy (font_info,mp->font_info,sizeof(memory_word)*(mp->font_mem_size+1)); + font_data *font_info; + font_info = mp_xmalloc (mp,(l+1),sizeof(font_data)); + memset (font_info,0,sizeof(font_data)*(l+1)); + memcpy (font_info,mp->font_info,sizeof(font_data)*(mp->font_mem_size+1)); mp_xfree(mp->font_info); mp->font_info = font_info; mp->font_mem_size = l; @@ -183,7 +184,7 @@ tf_ignore(4); tfget; read_two(z); tfget; z=z*0400+tfbyte; tfget; z=z*0400+tfbyte; /* now |z| is 16 times the design size */ -mp->font_dsize[n]=mp_take_fraction(mp, z,267432584); +mp->font_dsize[n]=mp_take_fraction(mp, z,integer_as_fraction(267432584)); /* times ${72\over72.27}2^{28}$ to convert from \TeX\ points */ tf_ignore(4*(tfm_lh-2)) @@ -217,7 +218,7 @@ if ( d>=0200 ) d=d-0400; tfget; d=d*0400+tfbyte; tfget; d=d*0400+tfbyte; tfget; d=d*0400+tfbyte; -mp->font_info[i].cint=mp_take_fraction(mp, d*16,mp->font_dsize[n]); +mp->font_info[i].sc=mp_take_fraction(mp, d*16,integer_as_fraction(mp->font_dsize[n])); i++; } diff --git a/Build/source/texk/web2c/web2c/configure b/Build/source/texk/web2c/web2c/configure index 05aec2cf02b..beab03821fa 100755 --- a/Build/source/texk/web2c/web2c/configure +++ b/Build/source/texk/web2c/web2c/configure @@ -757,7 +757,6 @@ LDFLAGS LIBS CPPFLAGS CPP -CPPFLAGS YACC YFLAGS' @@ -13519,9 +13518,9 @@ esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. -config_files="$ac_config_files" -config_headers="$ac_config_headers" -config_commands="$ac_config_commands" +config_files="`echo $ac_config_files`" +config_headers="`echo $ac_config_headers`" +config_commands="`echo $ac_config_commands`" _ACEOF diff --git a/Master/texmf-dist/doc/metapost/base/mpboxes.pdf b/Master/texmf-dist/doc/metapost/base/mpboxes.pdf Binary files differindex 3a0182c4e20..f06e8847a64 100644 --- a/Master/texmf-dist/doc/metapost/base/mpboxes.pdf +++ b/Master/texmf-dist/doc/metapost/base/mpboxes.pdf diff --git a/Master/texmf-dist/doc/metapost/base/mpgraph.pdf b/Master/texmf-dist/doc/metapost/base/mpgraph.pdf Binary files differindex d49c39a1dcd..b8d5591247f 100644 --- a/Master/texmf-dist/doc/metapost/base/mpgraph.pdf +++ b/Master/texmf-dist/doc/metapost/base/mpgraph.pdf diff --git a/Master/texmf-dist/doc/metapost/base/mpintro.pdf b/Master/texmf-dist/doc/metapost/base/mpintro.pdf Binary files differindex e845c115287..534f2891b67 100644 --- a/Master/texmf-dist/doc/metapost/base/mpintro.pdf +++ b/Master/texmf-dist/doc/metapost/base/mpintro.pdf diff --git a/Master/texmf-dist/doc/metapost/base/mplibapi.pdf b/Master/texmf-dist/doc/metapost/base/mplibapi.pdf Binary files differnew file mode 100644 index 00000000000..d9b491c144a --- /dev/null +++ b/Master/texmf-dist/doc/metapost/base/mplibapi.pdf diff --git a/Master/texmf-dist/doc/metapost/base/mpman.pdf b/Master/texmf-dist/doc/metapost/base/mpman.pdf Binary files differindex 1e2e4bef1e6..4e7fa152f66 100644 --- a/Master/texmf-dist/doc/metapost/base/mpman.pdf +++ b/Master/texmf-dist/doc/metapost/base/mpman.pdf diff --git a/Master/texmf-dist/doc/metapost/base/source-manual/Makefile b/Master/texmf-dist/doc/metapost/base/source-manual/Makefile index 21eaebfcf74..de95dc82304 100644 --- a/Master/texmf-dist/doc/metapost/base/source-manual/Makefile +++ b/Master/texmf-dist/doc/metapost/base/source-manual/Makefile @@ -2,16 +2,24 @@ # Rules for the groff/troff docs are in TeX Live, # texmf-dist/doc/metapost/base/Makefile. +RM = -rm -f latex = latex pdflatex = pdflatex +contextpdf = texexec +contextdvi = texexec --dvi dvips = dvips bibtex = bibtex mpost = mpost -tex=tex mkindex = makeindex -c -s mpman.ist all: pdf -pdf: mpman.pdf mpgraph.pdf mpboxes.pdf -ps: mpman.ps mpgraph.ps mpboxes.ps +pdf: mpman.pdf mpgraph.pdf mpboxes.pdf mplibapi.pdf +ps: mpman.ps mpgraph.ps mpboxes.ps mplibapi.ps + +mplibapi.pdf: mplibapi.tex + mv -f mpgraph.mp mpgraph.saved + $(contextpdf) mplibapi + mv -f mpgraph.saved mpgraph.mp mpboxes.pdf: mpboxes.tex mpboxes.bib mpboxes.mp $(mpost) mpboxes @@ -28,8 +36,9 @@ mpgraph.pdf: mpgraph.tex mpgraph.bib mpgraph.mp $(pdflatex) mpgraph $(pdflatex) mpgraph -mpman.pdf: mpman.tex mpman-app-legacy.tex mpman-app-optab.tex mpman-app-refman.tex mpman.bib mpman.ist mpman.mp +mpman.pdf: mpman.tex mpman-app-legacy.tex mpman-app-optab.tex mpman-app-refman.tex mpman.bib mpman.ist mpman.mp charts.mp mpman-charts.mp $(mpost) mpman + $(mpost) mpman-charts $(pdflatex) mpman $(bibtex) mpman $(pdflatex) mpman @@ -37,6 +46,12 @@ mpman.pdf: mpman.tex mpman-app-legacy.tex mpman-app-optab.tex mpman-app-refman.t $(mkindex) mpman $(pdflatex) mpman +mplibapi.ps: mplibapi.tex + mv -f mpgraph.mp mpgraph.saved + $(contextdvi) mplibapi + mv -f mpgraph.saved mpgraph.mp + $(dvips) mplibapi + mpboxes.ps: mpboxes.tex mpboxes.bib mpboxes.mp $(mpost) mpboxes $(latex) mpboxes @@ -55,8 +70,9 @@ mpgraph.ps: mpgraph.tex mpgraph.bib mpgraph.mp $(latex) mpgraph $(dvips) mpgraph -mpman.ps: mpman.tex mpman-app-legacy.tex mpman-app-optab.tex mpman-app-refman.tex mpman.bib mpman.ist mpman.mp +mpman.ps: mpman.tex mpman-app-legacy.tex mpman-app-optab.tex mpman-app-refman.tex mpman.bib mpman.ist mpman.mp charts.mp mpman-charts.mp $(mpost) mpman + $(mpost) mpman-charts $(latex) mpman $(bibtex) mpman $(latex) mpman @@ -66,8 +82,8 @@ mpman.ps: mpman.tex mpman-app-legacy.tex mpman-app-optab.tex mpman-app-refman.te $(dvips) mpman clean: - rm -f *.aux *.bbl *.blg *.dvi *.log *.mpx *.mps - rm -f *.idx *.ilg *.ind *.toc *.out + $(RM) *.aux *.bbl *.blg *.dvi *.idx *.ilg *.ind *.log *.mps *.mpx *.out *.toc mpxerr.* + $(RM) *.tui *.tuo *.tmp *-mpgraph.mp distclean: clean - rm -f mp*.pdf mp*.ps + $(RM) mp*.pdf mp*.ps diff --git a/Master/texmf-dist/doc/metapost/base/source-manual/README b/Master/texmf-dist/doc/metapost/base/source-manual/README index 61cdd102fc8..bbff9fb2c19 100644 --- a/Master/texmf-dist/doc/metapost/base/source-manual/README +++ b/Master/texmf-dist/doc/metapost/base/source-manual/README @@ -24,6 +24,7 @@ README TODO agepop91.d agepopm.d +charts.mp cm2lm.map countries.d ctabbing.sty @@ -50,7 +51,10 @@ mpgraph.tex mpintro.bib mpintro.mp mpintro.tex -mpman-optab.tex +mpman-app-legacy.tex +mpman-app-optab.tex +mpman-app-refman.tex +mpman-charts.mp mpman.bib mpman.ist mpman.mp diff --git a/Master/texmf-dist/doc/metapost/base/source-manual/TODO b/Master/texmf-dist/doc/metapost/base/source-manual/TODO index 4aea80f9d41..618b973ebdd 100644 --- a/Master/texmf-dist/doc/metapost/base/source-manual/TODO +++ b/Master/texmf-dist/doc/metapost/base/source-manual/TODO @@ -31,10 +31,8 @@ mp-implementors list or alternatively the metapost list. *** Wanted For Release ***
*****************************
-mpman.tex
-* Describe environment variables.
-* Add note on postcontrol and precontrol when discussing control points.
-* Add description of interaction mode commands.
+Notes on the MetaPost Workflow:
+* (SH) Describe variables jobname, charcode.
@@ -43,19 +41,34 @@ mpman.tex *** Mid Term Issues ***
*****************************
+Introduction:
* Revise introduction.
-* Move prologues discussion to section 3.
+
+Integrating Text and Graphics:
+* Completely revise this section and consolidate information
+ scattered around mpman.
* More information and examples on alternatives for calling TeX
or LaTeX from within MetaPost.
* (SH) Revise (and add more) flow charts for MetaPost workflow.
-* Information on using MetaPost from within LaTeX (emp), ConTeXt
- and LuaTeX.
+* Describe whitespace handling of verbatimtex/btex/etex.
* Why is verbatimtex\end{document}etex "safer", cf. sec. 13.1?
* Discuss difference between "draw <pic> withpen" and
"label(<pic>, ...) withpen" (when label bug is fixed).
-* Add a sub-section discussing colours in section 9.
-* (SH) Colour cars and change code example in section 9.9.
-* Describe mpware in section "Legacy information".
+* Fix bad description of prologues in table ivartab.
+* Move discussion of prologues to sec. workflow2?
+
+Advanced Graphics:
+* Add a sub-section discussing colours.
+* (SH) Colour cars and change code example in section piccomp.
+
+Reference Manual:
+* Describe environment variables and configuration files.
+* Describe missing variables, constants, and operators.
+* Add description of interaction mode.
+
+Legacy Information:
+* Cite original Hobby papers.
+* Describe mpware (currently scattered around in mpman).
@@ -64,14 +77,18 @@ mpman.tex *** Long Term Issues ***
*****************************
+Title page:
* Add decent title graphics.
+
+Reference Manual:
* Review presentation of primitives and plain macros in appendix A.
* Build reference manual automatically from mp.web (as a separate manual?).
+
+General:
+* Make mpman self-contained (no need to look-up sth. in 'The MetaFontbook').
* Make manual sources compatible with preview mode (Emacs).
* Make manual sources LaTeX2e conform.
* Make manual sources look nice in Emacs (AUCTeX font-locking).
* Review overall layout of the manuals (type area, float placement,
listing presentation etc.)
-
-
diff --git a/Master/texmf-dist/doc/metapost/base/source-manual/charts.mp b/Master/texmf-dist/doc/metapost/base/source-manual/charts.mp new file mode 100644 index 00000000000..f7ba689a509 --- /dev/null +++ b/Master/texmf-dist/doc/metapost/base/source-manual/charts.mp @@ -0,0 +1,120 @@ +% Support package for drawing charts. + +def _drawNode = drawboxed enddef; + +%%% These paramaters determine the size of nodes and the grid distance. +newinternal applwidth; applwidth := 30bp; +newinternal applheight; applheight := 14bp; +newinternal extwidth; extwidth := 30bp; +newinternal extheight; extheight := 14bp; +newinternal rowscale; rowscale := 100bp; +newinternal colscale; colscale := 100bp; + +%%% This macro builds and draws a rectangular node at a given location +%%% with text indicating an application. +%% @param i Node suffix. +%% @param row Row. +%% @param col Column. +%% @param text The text to render in the box node. +def appl(suffix i)(expr coor)(text t) = + boxit.i(t); + i.c = coor; + i.e = i.c + .5applwidth*right; + i.n = i.c + .5applheight*up; + _drawNode(i); +enddef; + + +%%% This macro builds and draws an oval node at a given location with +%%% text indicating a file name extension. +%% @param i Node suffix. +%% @param row Row. +%% @param col Column. +%% @param text The text to render in the oval node. +def ext(suffix i)(expr coor)(text t) = + circleit.i(t); + i.c = coor; + i.e = i.c + .5extwidth*right; + i.n = i.c + .5extheight*up; + _drawNode(i); +enddef; + + +%%% This macro builds and draws a rectangular node at a given location +%%% with text indicating an application. +%% @param i Node suffix. +%% @param row Row. +%% @param col Column. +%% @param text The text to render in the box node. +def appl_rc(suffix i)(expr coor)(text t) = + boxit.i(t); + i.c = (colscale * ypart coor, -rowscale * xpart coor); + i.e = i.c + .5applwidth*right; + i.n = i.c + .5applheight*up; + _drawNode(i); +enddef; + + +%%% This macro builds and draws an oval node at a given location with +%%% text indicating a file name extension. +%% @param i Node suffix. +%% @param row Row. +%% @param col Column. +%% @param text The text to render in the oval node. +def ext_rc(suffix i)(expr coor)(text t) = + circleit.i(t); + i.c = (colscale * ypart coor, -rowscale * xpart coor); + i.e = i.c + .5extwidth*right; + i.n = i.c + .5extheight*up; + _drawNode(i); +enddef; + + +%%% This macro draws a straight arrow between two nodes. Arrow length +%%% is corrected for the pen width, so that the arrow doesn't sink into +%%% the frame of the target node. If pen width is changed, the code +%%% "0.5bp" has to be replaced by half of the new pen width. +%% @param a Source node. +%% @param b Target node. +def line(suffix a,b) = + begingroup + save p;path p; + p := a.c--b.c cutbefore bpath(a) cutafter bpath(b); + drawarrow subpath (0, arctime (arclength p - 0.5bp) of p) of p; + endgroup +enddef; + + +%%% This macro draws a curved arrow between two nodes with determined +%%% leaving and entering angles. Arrow length is corrected for the pen +%%% width so that the arrow doesn't sink into the frame of the target +%%% node. If pen width is changed, the code "0.5bp" has to be replaced +%%% by half of the new pen width. +%% @param a Source node. +%% @param b Target node. +%% @param anga Leaving angle at (center of) node a. +%% @param angb Entering angle at (center of) node b. +def curve(suffix a,b)(expr anga,angb) = + begingroup + save p;path p; + p := a.c{dir anga}..{dir angb}b.c cutbefore bpath(a) cutafter bpath(b); + drawarrow subpath (0, arctime (arclength p - 0.5bp) of p) of p; + endgroup +enddef; + + +%%% This macro draws a curved arrow between two nodes with determined +%%% leaving and entering angles offset. Arrow length is corrected for +%%% the pen width so that the arrow doesn't sink into the frame of the +%%% target node. If pen width is changed, the code "0.5bp" has to be +%%% replaced by half of the new pen width. +%% @param a Source node. +%% @param b Target node. +%% @param angr Offset to entering and leaving angles at nodes a and b. +def curve_rel(suffix a,b)(expr angr) = + begingroup + save alpha; + alpha = angle(b.c-a.c); + curve(a, b, alpha+angr, alpha-angr); + endgroup +enddef; diff --git a/Master/texmf-dist/doc/metapost/base/source-manual/mp.1 b/Master/texmf-dist/doc/metapost/base/source-manual/mp.1 deleted file mode 100644 index 76df137ca79..00000000000 --- a/Master/texmf-dist/doc/metapost/base/source-manual/mp.1 +++ /dev/null @@ -1,289 +0,0 @@ -.\" MetaPost man page, by John Hobby. Public domain. -.TH MP 1 -.CT 1 writing_other -.SH NAME -mp, MetaPost \- system for drawing pictures -.SH SYNOPSIS -.B mp -[ -.B -I -] -[ -.B -T -] -[ -.I first-line -] -.SH DESCRIPTION -.I Mp -interprets the MetaPost language and produces -.I PostScript -pictures. The MetaPost language is similar to Knuth's -.I METAFONT -with additional features for including -.IR tex (1) -or -.IR troff (1) -commands and accessing features of PostScript not found in METAFONT. -The -.B -T -flag selects -.I troff -instead of -.IR tex . -.PP -An argument given on the command line -behaves as the first input line. -That can be either a (possibly truncated) file name -or a sequence MetaPost commands starting with -.L \e -and including an -.L input -command. Thus -.L mp figs -processes the file -.LR figs.mp . -The basename of -.L figs -becomes the -.IR jobname , -and is used in forming output file names. If no file is named, the -jobname becomes -.LR mpout . -The default -.L .mp -extension can be overridden by specifying an extension explicitly. -.PP -There is normally one output file for each picture generated, -and the output files are named -.IR jobname.nnn , -where -.I nnn -is a number passed to the -.L beginfig -macro. The output file name -can also be -.IB jobname .ps -if this number is negative. -.PP -The output files can be used as figures in a TeX document by including -.ce -.BI "\especial{psfile=" jobname.nnn } -in the TeX document. Alternatively, one can -.L \einput epsf.tex -and then use the macro -.BI "\eepsfbox{" jobname.nnn } -to produce a box of the appropriate size containing the figure. -.TP -.BI btex " TeX commands " etex -This causes -.I mp -to generate a MetaPost picture expression that corresponds to the -TeX commands. If the TeX commands generate -more than one line of text, it must be in a -.L \evbox -or a minipage environment. -.TP -.BI verbatimtex " TeX commands " etex -This is ignored by -.I mp -except that the TeX commands are passed on to TeX. When using LaTeX -instead of TeX the input file must start with a -.L verbatimtex -block that gives the -.L \edocumentstyle -and -.L "\ebegin{document}" -commands. -.PP -Since most TeX fonts have to be downloaded as bitmaps, the -.L btex -feature works best when the output of -.L mp -is to be included in a TeX document so that -.IR dvips (1) -can download the fonts. For self-contained PostScript output that can be -used directly or included in a -.I troff -document, start your MetaPost input file with the command -.L "prologues:=1" -and stick to standard PostScript fonts. TeX and MetaPost use the names in the -third column of the file -.LR /usr/lib/mp/trfonts.map . -.PP -MetaPost output can be included in a -.I troff -document via the -.L "-mpictures" -macro package. In this case -.I mp -should be invoked with the -.B -T -flag so that the commands between -.L btex -and -.L etex -or between -.L verbatimtex -and -.L etex -are interpreted as -.I troff -instead of TeX. (This automatically sets -.L prologues:=1 -). -.PP -Here is a list of the environment variables affect the behavior of -.IR mp : -.TF MPXCOMMAND -.TP -.B MPINPUTS -Search path for -.L \einput -files. -It should be colon-separated, -and start with dot. -Default: -.L .:/usr/lib/mp -.TP -.B MFINPUTS -Auxiliary search path for -.L \einput -files with -.L .mf -extensions. -.TP -.B TEXFONTS -Search path for font metric files. -.TP -.B MPXCOMMAND -The name of a shell script that converts embedded typesetting commands -to a form that -.I mp -understands. -Defaults: -.L /usr/lib/mp/bin/makempx -for -.I tex -and -.L /usr/lib/mp/bin/troffmpx -for -.I troff -.TP -.B TEX -The version of TeX to use when processing -.L btex -and -.L verbatimtex -commands. Default: -.L tex -.TP -.B TROFF -The -.I troff -pipeline for -.L btex -and -.L verbatimtex -commands. Default: -.L eqn -d\e$\e$ | troff -.TP -.B MPMEMS -Search path for -.L .mem -files. Default: -.L .:/usr/lib/mp -.TP -.B MPPOOL -Search path for strings. -Default: -.L .:/usr/lib/mp -.TP -.B MPEDITOR -A command for invoking an editor with -.L %s -in place of the file name and -.L %d -in place of the line number. -.TP -.B TEXVFONTS -Search path for virtual fonts. -.TP -.B MPTEXPRE -Name of a file that is prepended before running tex. -If this file exists, -.I mp -behaves as if the file were inside of a -.L verbatimtex -block at the beginning of each input file. Default: -.L mptexpre.tex -.PD -.PP -A -.L .mem -file is a binary file that permits fast loading of fonts and macro packages. -.I Mp -reads the default -.F plain.mem -unless another -.L .mem -file is specified at the start of the first line with an -.L & -just before it. There is also an -.F mfplain.mem -that simulates plain METAFONT so that -.I mp -can read -.L .mf -fonts. (Plain METAFONT is described in -.I The -.IR METAFONTbook ). -.PP -Experts can create -.L .mem -files be invoking -.I mp -with the -.B -I -switch and giving macro definitions followed by a -.L dump -command. -.PP -The MetaPost language is similar to METAFONT, but the manual -.I A User's Manual for MetaPost -assumes no knowledge of METAFONT. MetaPost does not have bitmap -output commands or METAFONT's online display mechanism. -.SH FILES -.TF /usr/lib/mp/trfonts.map -.TP -.F /usr/lib/mp/* -macros, -.L .mem -files, and tables for handling included -.I tex -and -.I troff -.TP -.F /usr/lib/mp/bin -Directory for programs that handle included -.I tex -and -.IR troff . -.F /usr/lib/mp/trfonts.map -table of corresponding font names for -.IR troff , -PostScript, and TeX -.TP -.F /usr/lib/mp/examples.mp -The source file for a few sample figures. -.IR troff . -.SH "SEE ALSO" -.IR tex (1), -.br -Donald E. Knuth, -.I The METAFONTbook, -Addison Wesley, 1986, -.br -John D. Hobby, -.I A User's Manual for MetaPost -CSTR 162. diff --git a/Master/texmf-dist/doc/metapost/base/source-manual/mpboxes.bib b/Master/texmf-dist/doc/metapost/base/source-manual/mpboxes.bib index 6adf4cd1c7a..6b4042b1a8f 100644 --- a/Master/texmf-dist/doc/metapost/base/source-manual/mpboxes.bib +++ b/Master/texmf-dist/doc/metapost/base/source-manual/mpboxes.bib @@ -17,4 +17,3 @@ pages = "53--77", year = 1990 } -} diff --git a/Master/texmf-dist/doc/metapost/base/source-manual/mpman-app-legacy.tex b/Master/texmf-dist/doc/metapost/base/source-manual/mpman-app-legacy.tex index e73cd05ea45..3af2449244a 100644 --- a/Master/texmf-dist/doc/metapost/base/source-manual/mpman-app-legacy.tex +++ b/Master/texmf-dist/doc/metapost/base/source-manual/mpman-app-legacy.tex @@ -1,343 +1,343 @@ -\svnInfo $Id: mpman-app-legacy.tex 1072 2009-06-01 16:18:44Z stephanhennig $
-\section{Legacy information}
-\label{legacy}
-
-\subsection{MetaPost Versus \MF}
-\label{MPvsMF}
-
-Since the \MF\index{metafont?\MF} and MetaPost languages have so much in
-common, expert users of \MF\ will want to skip most of the explanations
-in this document and concentrate on concepts that are unique to
-MetaPost. The comparisons in this appendix are intended to help experts
-that are familiar with {\sl The\ \MF book} as well as other users that
-want to benefit from Knuth's more detailed explanations \cite{kn:c}.
-
-Since \MF\ is intended for making \TeX\ fonts, it has a number of
-primitives for generating the {\tt tfm}\index{tfm file?{\tt tfm}
-file}\index{files!tfm?{\tt tfm}} files that \TeX\ needs for character
-dimensions, spacing information, ligatures\index{ligatures} and
-kerning\index{kerning}. MetaPost can also be used for generating fonts,
-and it also has \MF's primitives for making {\tt tfm} files. These are
-listed in Table~\ref{tfmprim}. Explanations can be found in the \MF\
-documentation \cite{kn:c,kn:mf3}.
-
-\begin{table}[htp]
-$$\begin{tabular}{|l|l|} \hline
-commands& {\tt charlist}, {\tt extensible},
- {\tt fontdimen}, {\tt headerbyte} \\
- & {\tt kern}, {\tt ligtable} \\ \hline
-ligtable operators& \verb!::!, \verb!=:!, \verb!=:|!, \verb!=:|>!,
- \verb!|=:!, \verb!|=:>!, \\
- & \verb!|=:|!, \verb!|=:|>!, \verb!|=:|>>!,
- \verb!||:! \\ \hline
-internal variables\index{internal variables}\index{variables!internal}&
- {\tt boundarychar}, {\tt chardp},
- {\tt charext}, {\tt charht}, \\
- & {\tt charic}, {\tt charwd},
- {\tt designsize}, {\tt fontmaking} \\ \hline
-other operators& {\tt charexists} \\ \hline
-\end{tabular}
-$$
-\caption{MetaPost primitives for making {\tt tfm} files.}
-\label{tfmprim}
-\end{table}
-
-Even though MetaPost has the primitives for generating fonts, many of
-the font-making primitives and internal variables that are part of Plain
-\MF\index{metafont?\MF} are not defined in Plain MetaPost\index{Plain
-macros}. Instead, there is a separate macro package called {\tt
-mfplain}\index{mfplain?\texttt{mfplain}} that defines the macros
-required to allow MetaPost to process Knuth's Computer Modern fonts as
-shown in Table~\ref{mfponly} \cite{kn:e}. To load these macros, put
-``\verb|&mfplain|'' before the name of the input file. This can be done
-at the {\tt **} prompt after invoking the MetaPost interpreter with no
-arguments, or on a command line that looks something like
-this:\footnote{Command line syntax is system dependent. Quotes are
-needed on most Unix systems to protect special characters like
-{\tt\&}.}
-$$ \hbox{\verb|mpost '&mfplain' cmr10|} $$
-The analog of a \MF\ command line like
-$$ \hbox{\verb|mf '\mode=lowres; mag=1.2; input cmr10'|} $$
-is
-$$ \hbox{\verb|mpost '&mfplain \mode=lowres; mag=1.2; input cmr10'|} $$
-The result is a set of PostScript\index{PostScript} files, one for each
-character in the font. Some editing would be required in order to
-merge them into a downloadable Type~3 PostScript font~\cite{ad:red2}.
-
-\begin{table}[htp]
-$$
-\renewcommand{\FancyVerbFormatLine}[1]{\hbox{#1}\strut}
-\begin{tabular}{|l|} \hline
-\multicolumn 1{|c|}
-{Defined in the {\tt mfplain} package} \\ \hline
-\begin{verbatim}
-beginchar font_identifier
-blacker font_normal_shrink
-capsule_def font_normal_space
-change_width font_normal_stretch
-define_blacker_pixels font_quad
-define_corrected_pixels font_size
-define_good_x_pixels font_slant
-define_good_y_pixels font_x_height
-define_horizontal_corrected_pixels italcorr
-define_pixels labelfont
-define_whole_blacker_pixels makebox
-define_whole_pixels makegrid
-define_whole_vertical_blacker_pixels maketicks
-define_whole_vertical_pixels mode_def
-endchar mode_setup
-extra_beginchar o_correction
-extra_endchar proofrule
-extra_setup proofrulethickness
-font_coding_scheme rulepen
-font_extra_space smode
-\end{verbatim}
- \\ \hline
-\multicolumn 1{|c|}
-{Defined as no-ops in the {\tt mfplain} package}\\ \hline
-\begin{verbatim}
-cullit proofoffset
-currenttransform screenchars
-gfcorners screenrule
-grayfont screenstrokes
-hround showit
-imagerules slantfont
-lowres_fix titlefont
-nodisplays unitpixel
-notransforms vround
-openit
-\end{verbatim}
- \\ \hline
-\end{tabular}
-\renewcommand{\FancyVerbFormatLine}[1]{#1}
-$$
-\caption{Macros and internal variables defined only in the {\tt mfplain}
-package.}
-\label{mfponly}
-\end{table}
-
-
-\begin{table}
-\centering
-\begin{tabular}{|c|}\hline
-MetaPost primitives not found in \MF\\\hline
-\begin{minipage}[b]{.75\linewidth}\ttfamily
-\begin{multicols}{3}
-blackpart\\
-bluepart\\
-bounded\\
-btex\\
-clip\\
-clipped\\
-closefrom\\
-cmykcolor\\
-color\\
-colormodel\\
-cyanpart\\
-dashed\\
-dashpart\\
-defaultcolormodel\\
-etex\\
-filenametemplate\\
-filled\\
-fontmapfile\\
-fontmapline\\
-fontpart\\
-fontsize\\
-for within\\
-glyph of\\
-greenpart\\
-greypart\\
-hour\\
-infont\\
-jobname\\
-linecap\\
-linejoin\\
-llcorner\\
-lrcorner\\
-magentapart\\
-minute\\
-miterlimit\\
-mpprocset\\
-mpxbreak\\
-outputformat\\
-outputtemplate\\
-pathpart\\
-penpart\\
-prologues\\
-readfrom\\
-redpart\\
-restoreclipcolor\\
-rgbcolor\\
-setbounds\\
-stroked\\
-textpart\\
-textual\\
-tracinglostchars\\
-troffmode\\
-truecorners\\
-ulcorner\\
-urcorner\\
-verbatimtex\\
-withcmykcolor\\
-withcolor\\
-withgreyscale\\
-withoutcolor\\
-withpostscript\\
-withprescript\\
-withrgbcolor\\
-write to\\
-yellowpart
-\end{multicols}
-\end{minipage}\\\hline
-
-Variables and Macros defined only in Plain MetaPost\\\hline
-\begin{minipage}[b]{.75\linewidth}\ttfamily
-\begin{multicols}{3}
-ahangle\\
-ahlength\\
-background\\
-bbox\\
-bboxmargin\\
-beginfig\\
-beveled\\
-black\\
-blue\\
-buildcycle\\
-butt\\
-center\\
-colorpart\\
-cutafter\\
-cutbefore\\
-cuttings\\
-dashpattern\\
-defaultfont\\
-defaultpen\\
-defaultscale\\
-dotlabel\\
-dotlabels\\
-drawarrow\\
-drawdblarrow\\
-drawoptions\\
-endfig\\
-EOF\\
-evenly\\
-extra\_beginfig\\
-extra\_endfig\\
-green\\
-image\\
-label\\
-labeloffset\\
-mitered\\
-red\\
-rounded\\
-squared\\
-thelabel\\
-white
-\end{multicols}
-\end{minipage}\\\hline
-\end{tabular}
-\caption{Macros and internal variables defined in MetaPost but not \MF.}
-\label{mponly}
-\end{table}
-
-Another limitation of the {\tt mfplain} package is that certain internal
-variables from Plain \MF\index{metafont?\MF} cannot be given reasonable
-MetaPost definitions. These include {\tt displaying}, {\tt
-currentwindow}, \verb|screen_rows|, and \verb|screen_cols| which depend
-on \MF's ability to display images on the computer screen. In addition,
-\verb|pixels_per_inch| is irrelevant since MetaPost uses fixed units of
-PostScript\index{PostScript!point}\index{point!PostScript} points.
-
-The reason why some macros and internal variables\index{internal
-variables}\index{variables!internal} are not meaningful in MetaPost is
-that \MF\ primitive commands {\tt cull}, {\tt display}, {\tt
-openwindow}, {\tt numspecial} and {\tt totalweight} are not implemented
-in MetaPost. Also not implemented are a number of internal variables as
-well as the \tdescr{drawing option} {\tt withweight}. Here is a
-complete listing of the internal variables whose primitive meanings in
-\MF\ do not make sense in MetaPost:
-$$\begin{verbatim}
-autorounding fillin proofing tracingpens xoffset
-chardx granularity smoothing turningcheck yoffset
-chardy hppp tracingedges vppp
-\end{verbatim}
-$$
-
-There is also one \MF\ primitive that has a slightly different meaning in
-MetaPost. Both languages allow statements of the
-form\index{special?\texttt{special}}\label{Dspecl}
-$$ {\tt special}\, \descr{string expression} \hbox{\tt;} $$
-but \MF\ copies the string into its ``generic font'' output file, while
-MetaPost interprets the string as a sequence of PostScript\index{PostScript}
-commands that are to be placed at the beginning of the next output file.
-
-In this regard, it is worth mentioning that rules in \TeX\ material
-included via {\tt btex..etex} in MetaPost are rounded to the correct
-number of pixels according to PostScript\index{PostScript!conversion rules}
-conversion rules~\cite{ad:red2}. In \MF, rules are not generated
-directly, but simply included in specials and interpreted later by
-other programs, such as {\tt gftodvi}\index{gftodvi?\ttt{gftodvi}},
-so there is no special conversion.
-
-All the other differences between \MF\ and MetaPost are features found
-only in MetaPost. These are listed in Table~\ref{mponly}. The only
-commands listed in this table that the preceding sections do not discuss
-are
-\verb|extra_beginfig|\index{extra_beginfig?\texttt{extra\_beginfig}}\label{Dxbfig},
-\verb|extra_endfig|\index{extra_endfig?\texttt{extra\_endfig}}\label{Dxefig},
-and {\tt mpxbreak}. The first two are strings that contain extra
-commands to be processed by {\tt
-beginfig}\index{beginfig?\texttt{beginfig}} and {\tt
-endfig}\index{endfig?\texttt{endfig}} just as \verb|extra_beginchar| and
-\verb|extra_endchar| are processed by {\tt beginchar} and {\tt endchar}.
-(The file {\tt boxes.mp}\index{boxes.mp?\texttt{boxes.mp}} uses these
-features).
-
-The other new feature listed in Table~\ref{mponly} not listed in the
-index is {\tt mpxbreak}\index{mpxbreak?\texttt{mpxbreak}}. This is used
-to separate blocks of translated \TeX\index{TeX?\TeX} or
-troff\index{troff} commands in {\tt mpx}\index{mpx file?{\tt mpx}
- file}\index{files!mpx?{\tt mpx}} files. It should be of no concern to
-users since {\tt mpx} files are generated automatically.
-
-\subsection{File name templates}
-\label{Dfilenametemplate}
-
-The output file naming template mechanism introduced in MetaPost
-version~1.000 originally used a primitive called
-\ttindex{filenametemplate}, as opposed to the internal string variable
-\ttt{outputtemplate}\index{outputtemplate?\texttt{outputtemplate}}
-described in section~\ref{Doutputtemplate}. This primitive took a
-string argument with the same syntax as \ttt{outputtemplate}, except
-that it didn't know about the \verb|%{...}| escape sequence for
-evaluating internal variables, e.g.,:
-
-$$\begin{verbatim}
-filenametemplate "%j-%c.mps";
-\end{verbatim}
-$$
-
-The \verb|filenametemplate| primitive has been deprecated since the
-introduction of \verb|outputtemplate| (version~1.200), but is still
-supported. If you happen to need writing future-proof source files,
-that at the same time are backwards compatible to MetaPost versions
-between 1.000 and~1.200, this output filename template declaration might
-help:
-
-$$\begin{verbatim}
-if scantokens(mpversion) < 1.200:
- filenametemplate
-else:
- outputtemplate :=
-fi
-"%j-%c.mps";
-\end{verbatim}
-$$
-
-
-%%% Local Variables:
-%%% mode: latex
-%%% TeX-PDF-mode: t
-%%% TeX-master: "mpman"
-%%% End:
+\svnInfo $Id: mpman-app-legacy.tex 1339 2010-08-15 13:17:48Z stephanhennig $ +\section{Legacy Information} +\label{legacy} + +\subsection{MetaPost Versus \MF} +\label{MPvsMF} + +Since the \MF\index{metafont?\MF} and MetaPost languages have so much in +common, expert users of \MF\ will want to skip most of the explanations +in this document and concentrate on concepts that are unique to +MetaPost. The comparisons in this appendix are intended to help experts +that are familiar with {\sl The\ \MF book} as well as other users that +want to benefit from Knuth's more detailed explanations \cite{kn:c}. + +Since \MF\ is intended for making \TeX\ fonts, it has a number of +primitives for generating the {\tt tfm}\index{tfm file?{\tt tfm} +file}\index{files!tfm?{\tt tfm}} files that \TeX\ needs for character +dimensions, spacing information, ligatures\index{ligatures} and +kerning\index{kerning}. MetaPost can also be used for generating fonts, +and it also has \MF's primitives for making {\tt tfm} files. These are +listed in Table~\ref{tfmprim}. Explanations can be found in the \MF\ +documentation \cite{kn:c,kn:mf3}. + +\begin{table}[htp] +$$\begin{tabular}{|l|l|} \hline +commands& {\tt charlist}, {\tt extensible}, + {\tt fontdimen}, {\tt headerbyte} \\ + & {\tt kern}, {\tt ligtable} \\ \hline +ligtable operators& \verb!::!, \verb!=:!, \verb!=:|!, \verb!=:|>!, + \verb!|=:!, \verb!|=:>!, \\ + & \verb!|=:|!, \verb!|=:|>!, \verb!|=:|>>!, + \verb!||:! \\ \hline +internal variables\index{internal variables}\index{variables!internal}& + {\tt boundarychar}, {\tt chardp}, + {\tt charext}, {\tt charht}, \\ + & {\tt charic}, {\tt charwd}, + {\tt designsize}, {\tt fontmaking} \\ \hline +other operators& {\tt charexists} \\ \hline +\end{tabular} +$$ +\caption{MetaPost primitives for making {\tt tfm} files.} +\label{tfmprim} +\end{table} + +Even though MetaPost has the primitives for generating fonts, many of +the font-making primitives and internal variables that are part of Plain +\MF\index{metafont?\MF} are not defined in Plain MetaPost\index{Plain +macros}. Instead, there is a separate macro package called {\tt +mfplain}\index{mfplain?\texttt{mfplain}} that defines the macros +required to allow MetaPost to process Knuth's Computer Modern fonts as +shown in Table~\ref{mfponly} \cite{kn:e}. To load these macros, put +``\verb|&mfplain|'' before the name of the input file. This can be done +at the {\tt **} prompt after invoking the MetaPost interpreter with no +arguments, or on a command line that looks something like +this:\footnote{Command line syntax is system dependent. Quotes are +needed on most Unix systems to protect special characters like +{\tt\&}.} +$$ \hbox{\verb|mpost '&mfplain' cmr10|} $$ +The analog of a \MF\ command line like +$$ \hbox{\verb|mf '\mode=lowres; mag=1.2; input cmr10'|} $$ +is +$$ \hbox{\verb|mpost '&mfplain \mode=lowres; mag=1.2; input cmr10'|} $$ +The result is a set of PostScript\index{PostScript} files, one for each +character in the font. Some editing would be required in order to +merge them into a downloadable Type~3 PostScript font~\cite{ad:red2}. + +\begin{table}[htp] +$$ +\renewcommand{\FancyVerbFormatLine}[1]{\hbox{#1}\strut} +\begin{tabular}{|l|} \hline +\multicolumn 1{|c|} +{Defined in the {\tt mfplain} package} \\ \hline +\begin{verbatim} +beginchar font_identifier +blacker font_normal_shrink +capsule_def font_normal_space +change_width font_normal_stretch +define_blacker_pixels font_quad +define_corrected_pixels font_size +define_good_x_pixels font_slant +define_good_y_pixels font_x_height +define_horizontal_corrected_pixels italcorr +define_pixels labelfont +define_whole_blacker_pixels makebox +define_whole_pixels makegrid +define_whole_vertical_blacker_pixels maketicks +define_whole_vertical_pixels mode_def +endchar mode_setup +extra_beginchar o_correction +extra_endchar proofrule +extra_setup proofrulethickness +font_coding_scheme rulepen +font_extra_space smode +\end{verbatim} + \\ \hline +\multicolumn 1{|c|} +{Defined as no-ops in the {\tt mfplain} package}\\ \hline +\begin{verbatim} +cullit proofoffset +currenttransform screenchars +gfcorners screenrule +grayfont screenstrokes +hround showit +imagerules slantfont +lowres_fix titlefont +nodisplays unitpixel +notransforms vround +openit +\end{verbatim} + \\ \hline +\end{tabular} +\renewcommand{\FancyVerbFormatLine}[1]{#1} +$$ +\caption{Macros and internal variables defined only in the {\tt mfplain} +package.} +\label{mfponly} +\end{table} + + +\begin{table} +\centering +\begin{tabular}{|c|}\hline +MetaPost primitives not found in \MF\\\hline +\begin{minipage}[b]{.75\linewidth}\ttfamily +\begin{multicols}{3} +blackpart\\ +bluepart\\ +bounded\\ +btex\\ +clip\\ +clipped\\ +closefrom\\ +cmykcolor\\ +color\\ +colormodel\\ +cyanpart\\ +dashed\\ +dashpart\\ +defaultcolormodel\\ +etex\\ +filenametemplate\\ +filled\\ +fontmapfile\\ +fontmapline\\ +fontpart\\ +fontsize\\ +for within\\ +glyph of\\ +greenpart\\ +greypart\\ +hour\\ +infont\\ +jobname\\ +linecap\\ +linejoin\\ +llcorner\\ +lrcorner\\ +magentapart\\ +minute\\ +miterlimit\\ +mpprocset\\ +mpxbreak\\ +outputformat\\ +outputtemplate\\ +pathpart\\ +penpart\\ +prologues\\ +readfrom\\ +redpart\\ +restoreclipcolor\\ +rgbcolor\\ +setbounds\\ +stroked\\ +textpart\\ +textual\\ +tracinglostchars\\ +troffmode\\ +truecorners\\ +ulcorner\\ +urcorner\\ +verbatimtex\\ +withcmykcolor\\ +withcolor\\ +withgreyscale\\ +withoutcolor\\ +withpostscript\\ +withprescript\\ +withrgbcolor\\ +write to\\ +yellowpart +\end{multicols} +\end{minipage}\\\hline + +Variables and Macros defined only in Plain MetaPost\\\hline +\begin{minipage}[b]{.75\linewidth}\ttfamily +\begin{multicols}{3} +ahangle\\ +ahlength\\ +background\\ +bbox\\ +bboxmargin\\ +beginfig\\ +beveled\\ +black\\ +blue\\ +buildcycle\\ +butt\\ +center\\ +colorpart\\ +cutafter\\ +cutbefore\\ +cuttings\\ +dashpattern\\ +defaultfont\\ +defaultpen\\ +defaultscale\\ +dotlabel\\ +dotlabels\\ +drawarrow\\ +drawdblarrow\\ +drawoptions\\ +endfig\\ +EOF\\ +evenly\\ +extra\_beginfig\\ +extra\_endfig\\ +green\\ +image\\ +label\\ +labeloffset\\ +mitered\\ +red\\ +rounded\\ +squared\\ +thelabel\\ +white +\end{multicols} +\end{minipage}\\\hline +\end{tabular} +\caption{Macros and internal variables defined in MetaPost but not \MF.} +\label{mponly} +\end{table} + +Another limitation of the {\tt mfplain} package is that certain internal +variables from Plain \MF\index{metafont?\MF} cannot be given reasonable +MetaPost definitions. These include {\tt displaying}, {\tt +currentwindow}, \verb|screen_rows|, and \verb|screen_cols| which depend +on \MF's ability to display images on the computer screen. In addition, +\verb|pixels_per_inch| is irrelevant since MetaPost uses fixed units of +PostScript\index{PostScript!point}\index{point!PostScript} points. + +The reason why some macros and internal variables\index{internal +variables}\index{variables!internal} are not meaningful in MetaPost is +that \MF\ primitive commands {\tt cull}, {\tt display}, {\tt +openwindow}, {\tt numspecial} and {\tt totalweight} are not implemented +in MetaPost. Also not implemented are a number of internal variables as +well as the \tdescr{drawing option} {\tt withweight}. Here is a +complete listing of the internal variables whose primitive meanings in +\MF\ do not make sense in MetaPost: +$$\begin{verbatim} +autorounding fillin proofing tracingpens xoffset +chardx granularity smoothing turningcheck yoffset +chardy hppp tracingedges vppp +\end{verbatim} +$$ + +There is also one \MF\ primitive that has a slightly different meaning in +MetaPost. Both languages allow statements of the +form\index{special?\texttt{special}}\label{Dspecl} +$$ {\tt special}\, \descr{string expression} \hbox{\tt;} $$ +but \MF\ copies the string into its ``generic font'' output file, while +MetaPost interprets the string as a sequence of PostScript\index{PostScript} +commands that are to be placed at the beginning of the next output file. + +In this regard, it is worth mentioning that rules in \TeX\ material +included via {\tt btex..etex} in MetaPost are rounded to the correct +number of pixels according to PostScript\index{PostScript!conversion rules} +conversion rules~\cite{ad:red2}. In \MF, rules are not generated +directly, but simply included in specials and interpreted later by +other programs, such as {\tt gftodvi}\index{gftodvi?\ttt{gftodvi}}, +so there is no special conversion. + +All the other differences between \MF\ and MetaPost are features found +only in MetaPost. These are listed in Table~\ref{mponly}. The only +commands listed in this table that the preceding sections do not discuss +are +\verb|extra_beginfig|\index{extra_beginfig?\texttt{extra\_beginfig}}\label{Dxbfig}, +\verb|extra_endfig|\index{extra_endfig?\texttt{extra\_endfig}}\label{Dxefig}, +and {\tt mpxbreak}. The first two are strings that contain extra +commands to be processed by {\tt +beginfig}\index{beginfig?\texttt{beginfig}} and {\tt +endfig}\index{endfig?\texttt{endfig}} just as \verb|extra_beginchar| and +\verb|extra_endchar| are processed by {\tt beginchar} and {\tt endchar}. +(The file {\tt boxes.mp}\index{boxes.mp?\texttt{boxes.mp}} uses these +features). + +The other new feature listed in Table~\ref{mponly} not listed in the +index is {\tt mpxbreak}\index{mpxbreak?\texttt{mpxbreak}}. This is used +to separate blocks of translated \TeX\index{TeX?\TeX} or +troff\index{troff} commands in {\tt mpx}\index{mpx file?{\tt mpx} + file}\index{files!mpx?{\tt mpx}} files. It should be of no concern to +users since {\tt mpx} files are generated automatically. + +\subsection{File Name Templates} +\label{Dfilenametemplate} + +The output file naming template mechanism introduced in MetaPost +version~1.000 originally used a primitive called +\ttindex{filenametemplate}, as opposed to the internal string variable +\ttt{outputtemplate}\index{outputtemplate?\texttt{outputtemplate}} +described in section~\ref{Doutputtemplate}. This primitive took a +string argument with the same syntax as \ttt{outputtemplate}, except +that it didn't know about the \verb|%{...}| escape sequence for +evaluating internal variables, e.g.,: + +$$\begin{verbatim} +filenametemplate "%j-%c.mps"; +\end{verbatim} +$$ + +The \verb|filenametemplate| primitive has been deprecated since the +introduction of \verb|outputtemplate| (version~1.200), but is still +supported. If you happen to need writing future-proof source files, +that at the same time are backwards compatible to MetaPost versions +between 1.000 and~1.200, this output filename template declaration might +help: + +$$\begin{verbatim} +if scantokens(mpversion) < 1.200: + filenametemplate +else: + outputtemplate := +fi +"%j-%c.mps"; +\end{verbatim} +$$ + + +%%% Local Variables: +%%% mode: latex +%%% TeX-PDF-mode: t +%%% TeX-master: "mpman" +%%% End: diff --git a/Master/texmf-dist/doc/metapost/base/source-manual/mpman-app-optab.tex b/Master/texmf-dist/doc/metapost/base/source-manual/mpman-app-optab.tex index 167e366f611..079ae983e5d 100644 --- a/Master/texmf-dist/doc/metapost/base/source-manual/mpman-app-optab.tex +++ b/Master/texmf-dist/doc/metapost/base/source-manual/mpman-app-optab.tex @@ -1,4 +1,4 @@ -\svnInfo $Id: mpman-app-optab.tex 1072 2009-06-01 16:18:44Z stephanhennig $
+\svnInfo $Id: mpman-app-optab.tex 1416 2010-10-18 06:39:23Z taco $
%%% Two notes on column specification:
%%% (i) Column widths are manually chosen as small as possible to allow
%%% for a wider last X column.
@@ -66,7 +66,7 @@ decimal\index{decimal?\texttt{decimal}}& --& numeric& string& \pageref{Ddeco \pl direction\-point of\index{directionpoint of?\texttt{directionpoint of}}& pair& path& numeric& \pageref{Ddpntof}& Point where a path has a given direction\\\hline
direction\-time of\index{directiontime of?\texttt{directiontime of}}& pair& path& numeric& \pageref{Ddtimof}& `Time' when a path has a given direction\\\hline
\pl div\index{div?\texttt{div}}& numeric& numeric& numeric& --& Integer division $\lfloor l/r\rfloor$\\\hline
-\pl dotprod\index{dotprod?\texttt{dotprod}}& pair& pair& numeric& \pageref{Ddprod}& vector dot product\\\hline
+\pl dotprod\index{dotprod?\texttt{dotprod}}& pair& pair& numeric& \pageref{Ddprod}& Vector dot product\\\hline
filled\index{filled?\texttt{filled}}& --& any& boolean& \pageref{Dfilled}& Is argument a filled outline?\\\hline
floor\index{floor?\texttt{floor}}& --& numeric& numeric& \pageref{Dfloor}& Greatest integer less than or equal to\\\hline
fontpart\index{fontpart?\texttt{fontpart}}& --& picture& string& \pageref{Dfontpart}& Font of a textual picture component\\\hline
@@ -105,14 +105,14 @@ penoffset of\index{penoffset of?\texttt{penoffset of}}& pair& pen& pair& --& penpart\index{penpart?\texttt{penpart}}& --& picture& pen& \pageref{Dpenpart}& Pen of a stroked picture component\\\hline
picture\index{picture?\texttt{picture}}& --& any& boolean& \pageref{Dpictop}& Is the expression of type picture?\\\hline
point of\index{point of?\texttt{point of}}& numeric& path& pair& \pageref{Dpntof}& Point on a path given a time value\\\hline
-postcontrol of\index{postcontrol?\texttt{postcontrol}}& numeric& path& pair& --& First B\'ezier control point on path segment starting at the given time\\\hline
-precontrol of\index{precontrol?\texttt{precontrol}}& numeric& path& pair& --& Last B\'ezier control point on path segment ending at the given time\\\hline
+postcontrol of\index{postcontrol of?\texttt{postcontrol of}}& numeric& path& pair& \pageref{Dprepostctrl}& First B\'ezier control point on path segment starting at the given time\\\hline
+precontrol of\index{precontrol of?\texttt{precontrol of}}& numeric& path& pair& \pageref{Dprepostctrl}& Last B\'ezier control point on path segment ending at the given time\\\hline
readfrom\index{readfrom?\texttt{readfrom}}& --& string& string& \pageref{Dreadfrom}& Read a line from file\\\hline
redpart\index{redpart?\texttt{redpart}}& --& color& numeric& \pageref{Drgbprt}& Extract the first component\\\hline
reverse\index{reverse?\texttt{reverse}}& --& path& path& \pageref{Drevrse}& `time'-reversed path, beginning swapped with ending\\\hline
rgbcolor\index{rgbcolor?\texttt{rgbcolor}}& --& any& boolean& \pageref{Drcolrop}& Is the expression of type color?\\\hline
rotated\index{rotated?\texttt{rotated}}& picture\par path\par pair\par pen\par transform& numeric& picture\par path\par pair\par pen\par transform& \pageref{Dtranop}& Rotate counterclockwise a given number of degrees\\\hline
-\pl round\index{round?\texttt{round}}& --& numeric\par pair& numeric\par pair& \pageref{Dround}& round each component to the nearest integer\\\hline
+\pl round\index{round?\texttt{round}}& --& numeric\par pair& numeric\par pair& \pageref{Dround}& Round each component to the nearest integer\\\hline
\pl rt\index{rt?\texttt{rt}}& --& numeric\par pair& numeric\par pair& \pageref{Drt}& Right side of current pen when centered at given coordinate(s)\\\hline
scaled\index{scaled?\texttt{scaled}}& picture\par path\par pair\par pen\par transform& numeric& picture\par path\par pair\par pen\par transform& \pageref{Dtranop}& Scale all coordinates by the given amount\\\hline
scantokens\index{scantokens?\texttt{scantokens}}& --& string& token sequence& \pageref{Dscantokens}& Converts a string to a token or token sequence. Provides string to numeric conversion, etc.\\\hline
diff --git a/Master/texmf-dist/doc/metapost/base/source-manual/mpman-app-refman.tex b/Master/texmf-dist/doc/metapost/base/source-manual/mpman-app-refman.tex index aefa619a46b..614b6f3be7f 100644 --- a/Master/texmf-dist/doc/metapost/base/source-manual/mpman-app-refman.tex +++ b/Master/texmf-dist/doc/metapost/base/source-manual/mpman-app-refman.tex @@ -1,4 +1,4 @@ -\svnInfo $Id: mpman-app-refman.tex 1128 2009-12-22 08:52:28Z taco $
+\svnInfo $Id: mpman-app-refman.tex 1381 2010-10-08 16:13:03Z stephanhennig $
\section{Reference Manual}
\label{refman}
@@ -8,7 +8,7 @@ \setcounter{topnumber}{10}
\setcounter{totalnumber}{10}
-\subsection{The MetaPost language}
+\subsection{The MetaPost Language}
\label{refman:lang}
Tables \ref{ivartab}--\ref{pseudotab} summarize the built-in features of
@@ -77,48 +77,49 @@ $$\begin{tabular}{|l|r|l|} \hline
\hline
\pl\tt ahangle& \pageref{Dahangle}&
- angle for arrowheads in degrees (default: 45)\\\hline
+ Angle for arrowheads in degrees (default: 45)\\\hline
\pl\tt ahlength& \pageref{Dahlength}&
- size of arrowheads (default: 4{\tt bp})\\\hline
+ Size of arrowheads (default: 4{\tt bp})\\\hline
\pl\tt bboxmargin& \pageref{Dbbmargin}&
- extra space allowed by {\tt bbox} (default 2{\tt bp})\\\hline
+ Extra space allowed by {\tt bbox} (default 2{\tt bp})\\\hline
\tt charcode& \pageref{Dcharcode}&
- the number of the current figure\\\hline
-\tt day& --&
- the current day of the month\\\hline
+ The number of the current figure\\\hline
+\tt day& \pageref{Dday}&
+ The current day of the month\\\hline
\tt defaultcolormodel& \pageref{Ddefaultcolormodel}&
- the initial color model (default: 5, rgb)\\\hline
+ The initial color model (default: 5, rgb)\\\hline
\pl\tt defaultpen& \pageref{Ddefaultpen}&
- numeric index used by {\tt pickup} to select default pen\\\hline
+ Numeric index used by {\tt pickup} to select default pen\\\hline
\pl\tt defaultscale& \pageref{Ddfscale}&
- font scale factor for label strings (default 1)\\\hline
-\tt hour& --&
- the hour of the day this job started\\\hline
+ Font scale factor for label strings (default 1)\\\hline
+\pl\tt dotlabeldiam& \pageref{Ddotlabdiam}&
+ Diameter of the dot drawn by \verb|dotlabel| (default 3{\tt bp})\\\hline
+\tt hour& \pageref{Dhour}&
+ The hour of the day this job started\\\hline
\pl\tt labeloffset& \pageref{Dlaboff}&
- offset distance for labels (default 3{\tt bp})\\\hline
+ Offset distance for labels (default 3{\tt bp})\\\hline
\tt linecap& \pageref{Dlinecap}&
0 for butt, 1 for round, 2 for square\\\hline
\tt linejoin& \pageref{Dlinejoin}&
0 for mitered, 1 for round, 2 for beveled\\\hline
-\tt minute& --&
- the minute of the hour this job started\\\hline
+\tt minute& \pageref{Dminute}&
+ The minute of the hour this job started\\\hline
\tt miterlimit& \pageref{Dmiterlim}&
- controls miter length as in PostScript\\\hline
-\tt month& --&
- the current month (e.g, 3 $\equiv$ March)\\\hline
-\tt mpprocset& --&
- \vtop{\hbox{set this to~1 if you want to include a PostScript
-dictionary}\hbox{of abbreviations in the output}}\\\hline
+ Controls miter length as in PostScript\\\hline
+\tt month& \pageref{Dmonth}&
+ The current month (e.g, 3 $\equiv$ March)\\\hline
+\tt mpprocset& \pageref{Dmpprocset}&
+ Create a PostScript dictionary of command abbreviations\\\hline
\tt pausing& --&
${}>0$ to display lines on the terminal before they are read\\\hline
\tt prologues& \pageref{Dprologues}&
${}>0$ to output conforming PostScript using built-in fonts\\\hline
\tt restoreclipcolor& --&
- restore the graphics state after clip operations (default: 1)\\\hline
+ Restore the graphics state after clip operations (default: 1)\\\hline
\tt showstopping& --&
${}>0$ to stop after each {\tt show} command\\\hline
-\tt time& --&
- the number of minutes past midnight when this job started\\\hline
+\tt time& \pageref{Dtime}&
+ The number of minutes past midnight when this job started\\\hline
\tt tracingcapsules& \pageref{Dtcapsules}&
${}>0$ to show capsules too\\\hline
\tt tracingchoices& \pageref{Dtchoices}&
@@ -144,13 +145,13 @@ dictionary}\hbox{of abbreviations in the output}}\\\hline \tt tracingtitles& --&
${}>0$ to show titles online when they appear\\\hline
\tt troffmode& \pageref{Dtroffmode}&
- set to~1 if a {\tt -troff} or {\tt -T} option was given\\\hline
+ Set to~1 if a {\tt -troff} or {\tt -T} option was given\\\hline
\tt truecorners& \pageref{Dtruecorn}&
${}>0$ to make {\tt llcorner} etc. ignore {\tt setbounds}\\\hline
\tt warningcheck& \pageref{Dwarncheck}&
- controls error message when variable value is large\\\hline
-\tt year& --&
- the current year (e.g., 1992)\\\hline
+ Controls error message when variable value is large\\\hline
+\tt year& \pageref{Dyear}&
+ The current year (e.g., 1992)\\\hline
\end{tabular}
$$
\label{ivartab}%
@@ -194,7 +195,7 @@ $$\begin{tabular}{|l|l|r|l|} \pl\tt currentpicture& picture& \pageref{Dcurpic}&
Accumulate results of {\tt draw} and {\tt fill} commands\\\hline
\pl\tt cuttings& path& \pageref{Dcuttings}&
- subpath cut off by last {\tt cutbefore} or {\tt cutafter}\\\hline
+ Subpath cut off by last {\tt cutbefore} or {\tt cutafter}\\\hline
\pl\tt defaultfont& string& \pageref{Ddffont}&
Font used by label commands for typesetting strings\\\hline
\pl\tt extra\_beginfig& string& \pageref{Dxbfig}&
@@ -274,7 +275,7 @@ $$\begin{tabular}{|l|l|r|l|} \tt pencircle& pen& \pageref{Dpncirc}&
Circular pen of diameter 1\\\hline
\pl\tt pensquare& pen& \pageref{Dpnsqr}&
- square pen of height 1 and width 1\\\hline
+ Square pen of height 1 and width 1\\\hline
\pl\tt pt& numeric& \pageref{Dpt}&
One printer's point in {\tt bp} units [0.99626]\\\hline
\pl\tt quartercircle& path& --&
@@ -347,9 +348,9 @@ $$\begin{tabular}{|l|r|l|} \pl\tt \verb|filldraw|& \pageref{Dfildrw}&
Draw a cyclic path and fill inside it\\\hline
\tt \verb|fontmapfile|& \pageref{Dfontmapfile}&
- read font map entries from file\\\hline
+ Read font map entries from file\\\hline
\tt \verb|fontmapline|& \pageref{Dfontmapline}&
- declare a font map entry\\\hline
+ Declare a font map entry\\\hline
\tt \verb|interim|& \pageref{Dinterm}&
Make a local change to an internal variable\\\hline
\tt \verb|let|& --&
@@ -369,15 +370,15 @@ $$\begin{tabular}{|l|r|l|} \tt \verb|shipout|& \pageref{Dship}&
Low-level command to output a figure\\\hline
\tt \verb|show|& \pageref{Dshow}&
- print out expressions symbolically\\\hline
+ Print out expressions symbolically\\\hline
\tt \verb|showdependencies|& \pageref{Dshdep}&
- print out all unsolved equations\\\hline
+ Print out all unsolved equations\\\hline
\tt \verb|showtoken|& \pageref{Dshtok}&
- print an explanation of what a token is\\\hline
+ Print an explanation of what a token is\\\hline
\tt \verb|showvariable|& \pageref{Dshvar}&
- print variables symbolically\\\hline
+ Print variables symbolically\\\hline
\tt \verb|special|& \pageref{Dspecl}&
- print a string directly in the PostScript output file\\\hline
+ Print a string directly in the PostScript output file\\\hline
\pl\tt \verb|tracingall|& \pageref{Dtall}&
Turn on all tracing\\\hline
\pl\tt \verb|tracingnone|& \pageref{Dtnone}&
@@ -799,93 +800,161 @@ $\tt \descr{suffix list} \rightarrow \descr{suffix} \;|\; \descr{suffix list}\hb \endgroup
-\subsection{Command-line syntax}
+\subsection{Command-Line Syntax}
\label{refman:commandline}
-\index{command-line}
\begingroup
\renewcommand*{\arraystretch}{1.25}% Give command-line switch tables air.
-\newcommand*{\cmdindex}[1]{\index{command-line!\texttt{mpost}!\texttt{#1}}\texttt{#1}}
-\subsubsection*{The \texttt{mpost} program}
+\subsubsection{The MetaPost Program}
\label{refman:mpost}
-The command-line syntax of the MetaPost executable is
-$$ {\tt mpost}\ \descr{switches}\ \descr{file}
-$$\label{Dmpost}
-where $\descr{file}$ is a text file containing statements in the
-MetaPost language. Typically, MetaPost input files have file extension
-\texttt{.mp}\index{mp file?{\tt mp} file}\index{files!mp?{\tt mp}},
-e.g., \texttt{foo.mp}. This extension can then be omitted in the
-\texttt{mpost} call. There exist a number of command-line switches to
-control the run-time behavior of the MetaPost executable. Switches can
-prefixed by one or two dashes. Both forms have the same meaning. An
-exemplary call to MetaPost that compiles file \texttt{foo.mp}, but
-immediately exits after encountering the first error without
-user-interaction would look like:
-$$ \hbox{\tt mpost -halt-on-error foo} $$
-
-Here's a summary of the command-line switches understood by
-\texttt{mpost}:
-
-\begin{longtable}{>{\ttfamily}p{.3\linewidth}>{\raggedright\arraybackslash}p{.6\linewidth}}
+\newcommand*{\opt}[1]{\ensuremath{\big[}#1\ensuremath{\big]}}
+\newcommand*{\cmdsw}{\tdescr{switches}}
+\newcommand*{\cmdmem}{\tdescr{preloadfile}}
+\newcommand*{\cmdin}{\tdescr{infile}}
+\newcommand*{\cmdcode}{\tdescr{commands}}
+\newcommand*{\cmdindex}[1]{\index{command-line!mpost?\texttt{mpost}!#1?\texttt{#1}}\texttt{#1}}
+
+The MetaPost program processes commands in the MetaPost language, either
+read from a file or typed-in interactively, and compiles them into
+PostScript or SVG graphics. The run-time behavior of the executable can
+be controlled by command-line parameters, environment variables, and
+configuration files. The MetaPost executable is named
+mpost\label{Dmpost}\index{mpost} (or occasionally just mp). The
+command-line syntax of the executable is
+$$
+\begin{verbatim}[commandchars=\\\{\}]
+mpost \opt{\cmdsw} \opt{\&\cmdmem} \opt{\cmdin} \opt{\cmdcode}
+\end{verbatim}
+\index{mpost?\texttt{mpost}}\index{command-line!mpost?\texttt{mpost}}
+$$
+Any of the parameters is optional. If no argument is given to the
+\verb|mpost| command, MetaPost enters interactive mode\index{interactive
+ mode}, indicated by a \verb|**| prompt\index{**?\texttt{**}!prompt},
+waiting for a file name to be typed-in by keyboard. The file is then
+read-in and processed as if it were given as parameter \cmdin\ on the
+command-line.
+
+\begin{description}
+\item[\cmdin] A MetaPost input file is a text file containing statements
+ in the MetaPost language. Typically, input files have file extension
+ \texttt{mp}\index{mp file?{\tt mp} file}\index{files!mp?{\tt mp}},
+ e.g., \texttt{fig.mp}. Here is how MetaPost searches for input files.
+ If \cmdin\ doesn't end with \texttt{.mp}\index{mp file?{\tt mp}
+ file}\index{files!mp?{\tt mp}}, MetaPost first looks for a file
+ \texttt{\cmdin.mp}. Only if that file doesn't exist, it looks for
+ file \cmdin\ literally. That way, the \texttt{mp}\index{mp file?{\tt
+ mp} file}\index{files!mp?{\tt mp}} file extension can be omitted
+ when calling MetaPost. If \cmdin\ already ends with
+ \texttt{.mp}\index{mp file?{\tt mp} file}\index{files!mp?{\tt mp}}, no
+ special file name handling takes place and MetaPost looks for that
+ file only.
+
+ As an example, if there are two files \texttt{fig} and \texttt{fig.mp}
+ in the current directory and MetaPost is invoked with
+ \verb|'mpost fig'|, the file that gets processed is \texttt{fig.mp}.
+ To process file \texttt{fig} in this situation, MetaPost can be called
+ as \verb|'mpost fig.'|. Note, the trailing dot is only needed as long
+ as there exists a file \texttt{fig.mp} alongside file \texttt{fig}.
+
+ If MetaPost cannot find any input file by the rules specified above,
+ it complaints with an error message and interactively asks for a new
+ input file name.
+
+\item[\cmdcode] All text on the command-line after \cmdin\ is
+ interpreted as MetaPost code and is processed after \cmdin\ is read.
+ If \cmdin\ already contains an \verb|end| statement, \cmdcode\ gets
+ effectively ignored. If MetaPost doesn't encounter an \verb|end|
+ statement neither in \cmdin\ nor in \cmdcode, it enters interactive
+ mode after processing all input.
+
+\item[\texttt{\&\cmdmem}] The MetaPost program code in the \cmdmem\
+ MetaPost source file (and any other files that it inputs) is executed
+ before all other processing starts. The \cmdmem\ file should end with
+ an \texttt{end}\index{end?\texttt{end}} or
+ \texttt{dump}\index{dump?\texttt{dump}} command.
+
+ If neither \texttt{\&\cmdmem} nor \cmdindex{-mem} is specified on the
+ command line, MetaPost will default to loading the macro file
+ \texttt{mpost.mp}\index{mpost.mp?\texttt{mpost.mp}}, or to whatever
+ the actual name of the MetaPost executable is. The \cmdindex{-ini}
+ switch can be used to suppress this behavior.
+
+\item[\cmdsw] MetaPost provides a number of command-line switches that
+ control the run-time behavior. Switches can be prefixed by one or two
+ dashes. Both forms have the same meaning. An exemplary call to
+ MetaPost that compiles a file \texttt{fig.mp}, using \LaTeX\ to
+ typeset \verb|btex|\slash\verb|etex| labels, would look like:
+$$
+\begin{verbatim}
+mpost -tex=latex fig
+\end{verbatim}
+\index{mpost?\texttt{mpost}}
+$$
+
+Here's a summary of the command-line switches provided by mpost:
+
+\setlength{\LTleft}{\leftmargin}
+\begin{longtable}{>{\ttfamily}p{.35\linewidth}>{\raggedright\arraybackslash}p{.6\linewidth}}
\cmdindex{-debug}
& Don't delete intermediate files.\\
\cmdindex{-dvitomp}
- & Act as the \textit{dvitomp} executable (see below).\\
+ & Act as the \texttt{dvitomp} executable (see below).\\
\cmdindex{-file-line-error}
- & Start error messages with \texttt{filename:lineno:} instead of
- \texttt{!}.\\
+ & Start error messages with \verb|`filename:lineno:'| instead of
+ \verb|`!'|.\\
\cmdindex{-halt-on-error}
& Immediately exit after the first error occurred.\\
\cmdindex{-help}
& Show help on command-line switches.\\
\cmdindex{-ini}
- & Be the INI variant of the program.\\
- \cmdindex{-interaction}=$\descr{string}$
- & Set interaction mode to one of \texttt{batchmode, nonstopmode,
- scrollmode, errorstopmode}.\\
- \cmdindex{-jobname}=$\descr{jobname}$
+ & Do not load any preload file.\\
+ \cmdindex{-interaction}=\tdescr{string}
+ & Set interaction mode to one of \texttt{batchmode},
+ \texttt{nonstopmode}, \texttt{scrollmode}, \texttt{errorstopmode}.\\
+ \cmdindex{-jobname}=\tdescr{jobname}
& Set the name of the job (affects output file names).\\
- \cmdindex{-kpathsea-debug}=$\descr{number}$
+ \cmdindex{-kpathsea-debug}=\tdescr{number}
& Set debugging flags for path searching.\\
- \cmdindex{-mem}=$\descr{string}$
- & Use $\descr{string}$ as memory dump name (same as
- \texttt{\&$\descr{string}$})\\
+ \cmdindex{-mem}=\tdescr{string}
+ & Use \tdescr{string} for the name of the file that contains macros to be preloaded
+ (same as \texttt{\&\cmdmem})\\
\cmdindex{-no-file-line-error}
& Enable normal MetaPost and \TeX\ style error messages.\\
\cmdindex{-no-kpathsea}
- & Do not use \textit{kpathsea} to find files. All files have to be in
- the current directory or specified via a full path.\\
- \cmdindex{-progname}=$\descr{string}$
- & Pretend to be the $\descr{string}$ executable.\\
+ & Do not use the kpathsea program to find files. All files have to be
+ in the current directory or specified via a full path.\\
+ \cmdindex{-progname}=\tdescr{string}
+ & Pretend to be the \tdescr{string} executable.\\
\cmdindex{-recorder}
& Write a list of all opened disk files to
- \texttt{$\descr{jobname}$.fls}\index{fls file?{\tt fls}
+ \texttt{\tdescr{jobname}.fls}\index{fls file?{\tt fls}
file}\index{files!fls?{\tt fls}}. (This functionality is provided
- by \textit{kpathsea}.)\\
- \cmdindex{-s}=$\descr{key}$=$\descr{value}$ \textrm{or}\newline
- -s $\descr{key}$=$\descr{value}$
- & Set internal variable $\descr{key}$ to $\descr{value}$. The
- assignment is applied just before the input file is read-in. This
- switch can be applied multiple times on the command-line.\newline
- $\descr{value}$ can be an integer between -16383 and 16383 or a string
- in double quotes. For strings, double quotes are stripped, but no
- other processing takes place.\newline
- Example: \verb|-s=outputformat="svg" -s=prologues=3|\newline
+ by kpathsea.)\\
+ \cmdindex{-s} \tdescr{key}=\tdescr{value}
+ & Set internal variable \tdescr{key} to \tdescr{value}. This switch
+ can be given multiple times on the command-line. The assignments are
+ applied just before the input file is read-in. \tdescr{value} can be
+ an integer between -16383 and 16383 or a string in double quotes. For
+ strings, double quotes are stripped, but no other processing takes
+ place. To avoid double quotes being already stripped by the shell,
+ the whole assignment can be enclosed in another pair of single quotes.
+ Example: \verb|-s 'outputformat="svg"' -s prologues=3|\newline
Use SVG backend converting font shapes to paths.\\
- \cmdindex{-tex}=$\descr{texprogram}$
- & Load format $\descr{texprogram}$ for rendering \TeX\ material.\\
+ \cmdindex{-tex}=\tdescr{texprogram}
+ & Load format \tdescr{texprogram} for rendering \TeX\ material.\\
\cmdindex{-troff}, \cmdindex{-T}
- & Output \textit{troff} compatible PostScript files.\\
+ & Output troff compatible PostScript files.\\
\cmdindex{-version}
& Print version information and exit.\\
\end{longtable}
The following command-line switches are silently ignored in
-\textit{mplib}-based MetaPost (v1.100 or later), because they are always
+\emph{mplib}-based MetaPost (v1.100 or later), because they are always
`on':
-\begin{longtable}{>{\ttfamily}p{.3\linewidth}>{\raggedright\arraybackslash}p{.6\linewidth}}
+
+\begin{longtable}{>{\ttfamily}p{.35\linewidth}>{\raggedright\arraybackslash}p{.6\linewidth}}
\cmdindex{-8bit}
& \\
\cmdindex{-parse-first-line}
@@ -893,55 +962,87 @@ The following command-line switches are silently ignored in \end{longtable}
The following command-line switches are ignored, but trigger a warning:
-\begin{longtable}{>{\ttfamily}p{.3\linewidth}>{\raggedright\arraybackslash}p{.6\linewidth}}
+
+\begin{longtable}{>{\ttfamily}p{.35\linewidth}>{\raggedright\arraybackslash}p{.6\linewidth}}
\cmdindex{-no-parse-first-line}
& \\
- \cmdindex{-output-directory}=$\descr{string}$
+ \cmdindex{-output-directory}=\tdescr{string}
& \\
- \cmdindex{-translate-file}=$\descr{string}$
+ \cmdindex{-translate-file}=\tdescr{string}
& \\
\end{longtable}
-\renewcommand*{\cmdindex}[1]{\index{command-line!\texttt{dvitomp}!\texttt{#1}}\texttt{#1}}
+\end{description}
-\subsubsection*{The \texttt{dvitomp} program}
+\subsubsection{The dvitomp Program}
\label{refman:dvitomp}
-The \ttindex{dvitomp}\label{Ddvitomp} program converts DVI\index{dvi
- file?{\tt dvi} file}\index{files!dvi?{\tt dvi}} files into low-level
-MetaPost code. The command-line syntax of the executable is
-$$ \hbox{\verb|dvitomp|}\ \descr{switches}\ \descr{infile}\ \descr{outfile}
+\renewcommand*{\cmdindex}[1]{\index{command-line!dvitomp?\texttt{dvitomp}!#1?\texttt{#1}}\texttt{#1}}
+\renewcommand*{\cmdin}{\tdescr{infile}}
+\newcommand*{\cmdout}{\tdescr{outfile}}
+
+The dvitomp\label{Ddvitomp}\index{dvitomp} program converts
+DVI\index{dvi file?{\tt dvi} file}\index{files!dvi?{\tt dvi}} files into
+low-level MetaPost code. MetaPost uses the dvitomp program when
+typesetting \verb|btex|\slash\verb|etex| labels by \TeX\ for the final
+conversion back into MetaPost code. The command-line syntax of the
+executable is
+$$
+\begin{verbatim}[commandchars=\\\{\}]
+dvitomp \opt{\cmdsw} \cmdin \opt{\cmdout}
+\end{verbatim}
+\index{dvitomp?\texttt{dvitomp}}\index{command-line!dvitomp?\texttt{dvitomp}}
$$
-where $\descr{infile}$ is the DVI file to convert and $\descr{outfile}$
-contains the equivalent MetaPost code. MetaPost uses the
-\texttt{dvitomp} program when typesetting \verb|btex| \verb|...|
-\verb|etex| labels by \TeX\ for the final conversion back into MetaPost
-code. Files generated that way end with \texttt{.mpx}\index{mpx
- file?{\tt mpx} file}\index{files!mpx?{\tt mpx}}. The following
-command-line switches are accepted:
-\begin{longtable}{>{\ttfamily}p{.3\linewidth}>{\raggedright\arraybackslash}p{.6\linewidth}}
+Parameters \cmdsw\ and \cmdout\ are optional.
+
+\begin{description}
+\item[\cmdin] This is the name of the DVI file to convert. If the name
+ doesn't end with \texttt{.dvi}\index{dvi file?{\tt dvi}
+ file}\index{files!dvi?{\tt dvi}}, that extension is appended. Note,
+ dvitomp never opens files not ending \texttt{.dvi}. This file is in
+ general automatically generated by \TeX\ or troff, driven by MetaPost.
+
+\item[\cmdout] This is the name of the output file containing the
+ MetaPost code equivalent to \cmdin. If \cmdout\ is not given,
+ \cmdout\ is \cmdin\ with the extension \texttt{.dvi} replaced by
+ \texttt{.mpx}\index{mpx file?{\tt mpx} file}\index{files!mpx?{\tt
+ mpx}}. If \cmdout\ is given and doesn't end with
+ \texttt{.mpx}\index{mpx file?{\tt mpx} file}\index{files!mpx?{\tt
+ mpx}}, that extension is appended.
+
+\item[\cmdsw] Command-line switches can be prefixed by one or two
+ dashes. Both forms have the same meaning. The following command-line
+ switches are provided by dvitomp:
+
+\setlength{\LTleft}{\leftmargin}
+\begin{longtable}{>{\ttfamily}p{.35\linewidth}>{\raggedright\arraybackslash}p{.6\linewidth}}
\cmdindex{-help}
& Show help on command-line switches.\\
- \cmdindex{-kpathsea-debug}=$\descr{number}$
+ \cmdindex{-kpathsea-debug}=\tdescr{number}
& Set debugging flags for path searching.\\
- \cmdindex{-progname}=$\descr{string}$
- & Pretend to be the $\descr{string}$ executable.\\
+ \cmdindex{-progname}=\tdescr{string}
+ & Pretend to be the \tdescr{string} executable.\\
\cmdindex{-version}
& Print version information and exit.\\
\end{longtable}
-The \texttt{dvitomp} program used to be part of a set of external tools,
-called \emph{mpware}\index{mpware tools}\footnote{\texttt{makempx},
- \texttt{mpto}, \texttt{dvitomp}, and \texttt{dmp}.}, that were used by
-MetaPost for processing \verb|btex| \verb|...| \verb|etex| labels.
-Since MetaPost version~1.100, the label conversion is handled internally
-by the \texttt{mpost} executable. The \emph{mpware} tools are therefore
-obsolete and no longer part of the MetaPost distribution. Nowadays, the
-\texttt{dvitomp} executable is either a copy of the \texttt{mpost}
-executable with the name \texttt{dvitomp} or a wrapper, calling
-\texttt{mpost} as
-$$ \hbox{\verb|mpost -dvitomp|}\ \descr{infile}\
-\descr{outfile}\qquad\hbox{.} $$
+\end{description}
+
+The dvitomp program used to be part of a set of external tools, called
+\emph{mpware}\index{mpware tools}\footnote{makempx, mpto, dvitomp, and
+ dmp.}, which were used by MetaPost for processing
+\verb|btex|\slash\verb|etex| labels. Since MetaPost version~1.100, the
+label conversion is handled internally by the mpost\index{mpost}
+program. The \emph{mpware} tools are therefore obsolete and no longer
+part of the MetaPost distribution. Nowadays, dvitomp is either a copy
+of the mpost executable with the name dvitomp or a wrapper, calling
+mpost\index{mpost} as
+$$
+\begin{verbatim}[commandchars=\\\{\}]
+mpost -dvitomp \cmdin \cmdout
+\end{verbatim}
+\index{mpost?\texttt{mpost}}
+$$
\endgroup
diff --git a/Master/texmf-dist/doc/metapost/base/source-manual/mpman-charts.mp b/Master/texmf-dist/doc/metapost/base/source-manual/mpman-charts.mp new file mode 100644 index 00000000000..a1ad1e47b3b --- /dev/null +++ b/Master/texmf-dist/doc/metapost/base/source-manual/mpman-charts.mp @@ -0,0 +1,210 @@ +verbatimtex + \def\appl#1{\applfontx#1\strut} + \def\ext#1{\extfontx#1\strut} + \font\applfontx=ec-lmri10 + \font\extfontx=ec-lmtt10 + \font\extfontvii=ec-lmtt8 at 7pt +etex + +% Use the boxes package for drawing charts. +input boxes +input charts +%prologues:=3; + +if scantokens(mpversion) < 1.200: + filenametemplate +else: + outputtemplate := +fi +"%j-%c.mps"; + + +beginfig(1); + interim applwidth := 72bp; + interim applheight := 28bp; + interim extwidth := 48bp; + interim extheight := 28bp; + interim rowscale := 56bp; + interim colscale := 100bp; + interim ahangle := 30; + +% Build and draw boxes. + ext_rc(fig.mp, (0,0), btex \ext{fig.mp} etex); + appl_rc(mpost, (1,0), btex \appl{MetaPost} etex); + ext_rc(fig.1, (2,0), btex \ext{fig.1} etex); + ext_rc(doc.tex, (0,2), btex \ext{doc.tex} etex); + appl_rc(tex, (1,2), btex \appl{\TeX} etex); + ext_rc(doc.dvi, (2,2), btex \ext{doc.dvi} etex); + appl_rc(dvips, (3,1), btex \appl{dvips} etex); + ext_rc(doc.ps, (4,1), btex \ext{doc.ps} etex); + +% Draw connections. + line(fig.mp, mpost); + line(mpost, fig.1); + line(doc.tex, tex); + line(tex, doc.dvi); + curve(fig.1, dvips, -90, 0); + curve(doc.dvi, dvips, -90, 180); + line(dvips, doc.ps); + +% Draw bounding box link. + boxit.bb(btex + \vbox{ + \hbox to 50bp {\hfil\strut bounding\hfil} + \hbox to 50bp {\hfil\strut box\hfil} + } + etex scaled .8); + path p; + p := fig.1.e{right}..{right}tex.w; + bb.c = point 0.5 of p; + drawunboxed(bb); + draw p cutafter bpath bb dashed evenly; + drawarrow p cutbefore subpath (1,3) of bpath bb dashed evenly; +endfig; + + +verbatimtex + \font\applfontx=ec-lmbxi10 +etex + +beginfig(2); +% Pre-define workflow segment parameters. + wfsegments := 6;% counter + wfsegangle = 360/wfsegments;% angle + wfstart := 90 + wfsegangle;% angle + wfdiam := 200bp;% dimension + wfthick := 48bp;% dimension + wfgap := 8;% angle + wfinset := 8;% angle + +% Pre-define colors of some graphic elements. + color wf_col, appl_col, ext_col; + wf_col = .9white; + appl_col = (.1, .1, .7); + ext_col = 0white; + +% The size of the user icon. + usersize := 20bp; + +% Construct a workflow segment. +% +% * The anchor point of a workflow segment is at its center. +% +% * Array wfang[] stores the angle on a circle corresponding +% to a vertex of a workflow segment. The mapping of indices +% to vertices is: +% +% 2---------0 +% / / +% 3 1 +% \ \ +% 2---------0 +% +% * For array wfscale[] the mapping is: +% +% 3---------0 +% / / +% < < +% \ \ +% 2---------1 +% + numeric wfang[], wfscale[]; + path wfseg; + wfang0 = .5(-wfsegangle + wfgap - wfinset); + wfang1 = .5(-wfsegangle + wfgap + wfinset); + wfang2 = .5( wfsegangle - wfgap - wfinset); + wfang3 = .5( wfsegangle - wfgap + wfinset); + wfscale[0] = 1 + wfthick/wfdiam; + wfscale[1] = 1 - wfthick/wfdiam; + wfscale[2] = 1 - 1.0*wfthick/wfdiam; + wfscale[3] = 1 + 1.0*wfthick/wfdiam; + wfseg := 0.5dir(wfang0)*wfscale0 {dir(wfang0-180)} + ..{dir(wfang1+135)} 0.5dir(wfang1) {dir(wfang1-135)} + ..{dir(wfang0+180)} 0.5dir(wfang0)*wfscale1 {dir(wfang0+90)} + ..{dir(wfang2+90)} 0.5dir(wfang2)*wfscale2 {dir(wfang2)} + ..{dir(wfang3+45)} 0.5dir(wfang3) {dir(wfang3-45)} + ..{dir(wfang2)} 0.5dir(wfang2)*wfscale3 {dir(wfang2-90)} + ..{dir(wfang0-90)} cycle; + +% Store user icon in picture variable. + picture unituserLeft, unituserRight; + unituserRight := image( +% This clip-art has been taken from the Open Clip-Art Library at +% <URL:http://www.openclipart.org/about>. The original SVG file by +% 'yyycatch' is licensed as Public Domain and can be found at +% <URL:http://www.openclipart.org/detail/18601>. It has been +% converted to MetaPost by exporting the SVG file to EPS in Inkscape +% and then coordinates, colours and pens have been manually translated +% to the corresponding MetaPost commands. + path p[]; + p1 = + (39.387, 60.034) .. controls (17.117, 59.401) and (-0.352, 22.932) + .. (7.887, 10.565) .. controls (16.125, -1.802) and (64.465, -1.802) + .. (72.34, 10.565) .. controls (80.215, 22.932) and (62.383, 60.686) + .. cycle; + p2 = + (22.809, 52.588) + -- (59.93, 52.588) .. controls (59.93, 52.588) and (60.523, 15.705) + .. (47.293, 15.705) .. controls (34.852, 15.705) and (22.809, 52.588) + .. cycle; + p3 = + (69.125, 67.963) .. controls (69.125, 50.881) and (56.723, 37.037) + .. (41.422, 37.037) .. controls (26.125, 37.037) and (13.723, 50.881) + .. (13.723, 67.963) .. controls (13.723, 85.045) and (26.125, 98.893) + .. (41.422, 98.893) .. controls (56.633, 98.893) and (69, 85.198) + .. cycle; + p4 = + (31.047, 76.764) .. controls (31.047, 76.764) and (43.93, 63.147) + .. (57.152, 64.209) .. controls (70.398, 65.276) and (79.316, 71.444) + .. (79.316, 71.444) .. controls (79.316, 71.444) and (72.488, 108.674) + .. (41.977, 109.291) .. controls (18.086, 109.791) and (6.324, 86.862) + .. (2.84, 71.444) .. controls (-0.691, 55.83) and (4.152, 55.889) + .. (9.313, 57.221) .. controls (14.477, 58.549) and (31.047, 76.764) + .. cycle; + p5 = + (84.211, 30.416) .. controls (87.586, 25.877) and (85.977, 18.955) + .. (80.617, 14.967) .. controls (75.258, 10.979) and (68.164, 11.428) + .. (64.785, 15.971) + -- (64.734, 16.037) .. controls (62.766, 18.741) and (62.41, 30.342) + .. (65.918, 32.764) .. controls (69.781, 35.428) and (80.832, 34.955) + .. cycle; + interim linecap := butt; + interim linejoin := mitered; + fill p1 withcolor (1, 0.501961, 0.501961); + draw p1 withcolor 0.219608 withpen pencircle scaled 2.577288; + fill p2 withcolor 1; + draw p2 withcolor 0.219608 withpen pencircle scaled 2.782928; + fill p3 withcolor (1, 0.8, 0.666667); + draw p3 withcolor 0.219608 withpen pencircle scaled 3.31912; + fill p4 withcolor (0.827451, 0.552941, 0.372549); + draw p4 withcolor 0.219608 withpen pencircle scaled 3.262816; + fill p5 withcolor (1, 0.8, 0.666667); + draw p5 withcolor 0.219608 withpen pencircle scaled 2.4; + ); +% Normalize user icon. + unituserRight := unituserRight shifted (-llcorner unituserRight -.5urcorner unituserRight); + unituserRight := unituserRight scaled (.5/ypart urcorner unituserRight); + unituserLeft := unituserRight reflectedabout ((0,0), (0,1)); + +% Helper function: transform location on workflow circle in +% degrees into cartesian coordinates. + def degtoxy(expr deg) = + (point deg/45 of fullcircle scaled wfdiam) + enddef; + +% Draw workflow segments. + for i=0 upto wfsegments-1: + fill wfseg scaled wfdiam rotated (wfstart+i*wfsegangle) withcolor wf_col; + endfor + +% Fill-in workflow segment descriptions. + label(btex \appl{Editor} etex, degtoxy(wfstart+wfsegangle*0)) withcolor appl_col; + label(btex \ext{fig.mp} etex, degtoxy(wfstart+wfsegangle*1)) withcolor ext_col; + label(btex \appl{MetaPost} etex, degtoxy(wfstart+wfsegangle*2)) withcolor appl_col; + label(btex \ext{fig.1} etex, degtoxy(wfstart+wfsegangle*3+10)) withcolor ext_col; + label(btex {\extfontvii fig.log} etex, degtoxy(wfstart+wfsegangle*3-10)) withcolor ext_col; + label(btex \appl{Previewer} etex, degtoxy(wfstart+wfsegangle*4)) withcolor appl_col; + label(unituserLeft scaled usersize, degtoxy(wfstart+wfsegangle*5)); +endfig; + +end diff --git a/Master/texmf-dist/doc/metapost/base/source-manual/mpman.bib b/Master/texmf-dist/doc/metapost/base/source-manual/mpman.bib index c476a028e02..e54ed5603e4 100644 --- a/Master/texmf-dist/doc/metapost/base/source-manual/mpman.bib +++ b/Master/texmf-dist/doc/metapost/base/source-manual/mpman.bib @@ -78,7 +78,7 @@ year = 2003, note = "\url{http://www.w3.org/TR/SVG11/}" } -@misc{reckdahl:epslatex, +@manual{reckdahl:epslatex, author = "Keith Reckdahl", title = "Using Imported Graphics in {\LaTeX} and {pdf\LaTeX}", month = jan, @@ -86,3 +86,20 @@ edition = "3.0.1", note = "\texttt{CTAN://info/epslatex}" } +@manual{hagen:metafun, + author = "Hans Hagen", + title = "Metafun", + month = jan, + year = 2002, + note = "\url{http://www.pragma-ade.com/general/manuals/metafun-s.pdf}" +} +@manual{luatex:manual, + organization = "Lua\TeX\ development team", + title = "Lua\TeX: Reference Manual", + note = "\url{http://www.luatex.org/svn/trunk/manual/luatexref-t.pdf}" +} +@manual{hagen:luamplib, + author = "Hans Hagen and Taco Hoekwater and Elie Roux", + title = "The luamplib package", + note = "\texttt{CTAN://macros/luatex/generic/luamplib/luamplib.pdf}" +} diff --git a/Master/texmf-dist/doc/metapost/base/source-manual/mpman.mp b/Master/texmf-dist/doc/metapost/base/source-manual/mpman.mp index d4e81d0d08c..6c97e9d6b79 100644 --- a/Master/texmf-dist/doc/metapost/base/source-manual/mpman.mp +++ b/Master/texmf-dist/doc/metapost/base/source-manual/mpman.mp @@ -16,54 +16,6 @@ vardef dotlabel@#(expr s,z) text t_ = enddef; -%%% TeX macro \place is only used in figure 0. -verbatimtex -\input texnames.sty -\def\place#1{\vphantom{FiMP}\smash{#1}} -etex - -%%% The boxes package is only used in figure 0. -input boxes - -beginfig(0); -ba.dy = bb.dy = d.dy = 12bp; -d.dx = 100bp; -boxit.aa(btex \place{Figures in MetaPost} etex); -boxit.ab(btex \place{\TeX\ Document} etex); -boxit.ba(btex \hbox to 75bp {\hfil\place{MetaPost}\hfil} etex); -boxit.bb.(btex \hbox to 75bp {\hfil\place{\TeX\ or \LaTeX}\hfil} etex); -boxit.ca(btex \place{Figures in PostScript} etex); -boxit.cb(btex \place{{\tt dvi} file} etex); -boxit.d(btex \place{\tt dvips} etex); -boxit.e(btex PostScript etex); -boxit.f(btex \vbox{ - \hbox to 50bp {\hfil\hbox{bounding}\hfil} - \hbox to 50bp {\hfil\hbox{box}\hfil} - } etex scaled .8); -ba.n - aa.s = ca.n - ba.s -= bb.n - ab.s = cb.n - bb.s -= e.n - d.s = down * 25bp; -ypart cb.s - ypart d.n = 25bp; -e.s = origin; -ypart ca.s = ypart cb.s; -xpart aa.n = xpart 0.1[d.nw,d.ne]; -xpart ab.n = xpart 0.9[d.nw,d.ne]; -drawunboxed(aa,ab,ca,cb,e); -drawboxed(ba,bb,d); -drawarrow aa.s -- top ba.n; drawarrow ab.s -- top bb.n; -drawarrow ba.s -- top ca.n; drawarrow bb.s -- top cb.n; -drawarrow ca.s -- top 0.1[d.nw,d.ne]; -drawarrow cb.s -- top 0.9[d.nw,d.ne]; -drawarrow d.s -- top e.n; -path p; -p = ca.e{right} .. {right}bb.w; -f.c = point .5 of p; -drawunboxed(f); -draw p cutafter bpath f dashed evenly; -drawarrow p cutbefore subpath (1,3) of bpath f dashed evenly; -endfig; - - beginfig(1); draw (20,20)--(0,0)--(0,30)--(30,0)--(0,0); endfig; diff --git a/Master/texmf-dist/doc/metapost/base/source-manual/mpman.tex b/Master/texmf-dist/doc/metapost/base/source-manual/mpman.tex index c4a38d43a2c..6889d586176 100644 --- a/Master/texmf-dist/doc/metapost/base/source-manual/mpman.tex +++ b/Master/texmf-dist/doc/metapost/base/source-manual/mpman.tex @@ -1,4 +1,4 @@ -% $Id: mpman.tex 1219 2010-04-01 09:05:51Z taco $ +% $Id: mpman.tex 1432 2010-10-22 12:02:13Z stephanhennig $ % MetaPost manual, by John Hobby. License at end. \listfiles \RequirePackage{ifpdf} @@ -12,8 +12,8 @@ \fi \documentclass{article} % article is NOT the original style \usepackage[nofancy]{svninfo}% Access VCS information. -\svnInfo $Id: mpman.tex 1219 2010-04-01 09:05:51Z taco $ -\newcommand*{\mpversion}{1.211} +\svnInfo $Id: mpman.tex 1432 2010-10-22 12:02:13Z stephanhennig $ +\newcommand*{\mpversion}{1.503} \usepackage[T1]{fontenc} \usepackage{lmodern} @@ -29,6 +29,7 @@ \usepackage[textwidth=6in,textheight=8.65in]{geometry} \usepackage{tocloft} \setlength\cftbeforesecskip{1.3ex plus 0.3ex minus 0.3ex} + \setcounter{tocdepth}{2} \usepackage{ltxtable} @@ -39,7 +40,7 @@ \newcommand\descr[1]{{\langle\hbox{#1}\rangle}} \newcommand\invisgap{\nobreak\hskip0pt\relax} -\newcommand\tdescr[1]{$\langle$\invisgap#1\invisgap$\rangle$} +\newcommand\tdescr[1]{{\normalfont$\langle$\invisgap#1\invisgap$\rangle$}} \newcommand\pl{\dag} @@ -178,8 +179,8 @@ written about MetaPost. For general help, try the \url{http://tug.org/mailman/listinfo/metapost}. The development is currently hosted at -\url{https://foundry.supelec.fr/projects/metapost/}; visit this site -for the current development team members, sources, and much else. +\url{http://foundry.supelec.fr/projects/metapost/}; visit this site for +the current development team members, sources, and much else. Please report bugs and request enhancements either on the \url{metapost@tug.org} list, or through the address given above. @@ -197,8 +198,10 @@ $$ \hbox{\verb|draw (20,20)--(0,0)--(0,30)--(30,0)--(0,0)|} $$ draws a polygonal line like this: $$ \includegraphics{mpman-1.mps} $$ -\label{Ddrawdot}MetaPost also has a \ttt{drawdot} command to print a -single point, as in \ttt{drawdot(30,0)}. +MetaPost also has a +\verb|drawdot|\index{drawdot?\texttt{drawdot}}\label{Ddrawdot} command +to draw a single point with the current pen, as in +\verb|drawdot (30,0)|. What is meant by coordinates like \verb|(30,0)|? MetaPost uses the same default coordinate system that PostScript\index{PostScript!coordinate @@ -237,20 +240,21 @@ generality that we do not need yet. This leads to the strange looking statement\index{pickup?\texttt{pickup}}\index{pencircle?\texttt{pencircle}}% \index{scaled?\texttt{scaled}} $$ \hbox{\verb|pickup pencircle scaled 4pt|} $$ -for setting the line width for subsequent \verb|draw| statements to 4 points. -(This is about eight times the default line width). - -With such a wide line width, even a line of zero length comes out as a big bold -dot\index{dots}. We can use this to make a grid of bold dots by having one -\verb|drawdot| statement -for each grid point. Such a repetitive sequence of \verb|draw| statements is -best written as a pair of nested loops:\index{loops}% -\index{for?\texttt{for}}\index{endfor?\texttt{endfor}} -$$\begin{verbatim} +for setting the line width (actually the pen size) for subsequent +\verb|draw| or \verb|drawdot| statements to 4 points. (This is about +eight times the default pen size). + +With such a large pen size, the \verb|drawdot| statement draws rather +bold dots. We can use this to make a grid of dots by nesting +\verb|drawdot| in a pair of loops:\index{loops}% +$$ +\begin{verbatim} for i=0 upto 2: for j=0 upto 2: drawdot (i*u,j*u); endfor endfor \end{verbatim} +\index{for?\texttt{for}} +\index{endfor?\texttt{endfor}} $$ The outer loop runs for $i=0,1,2$ and the inner loop runs for $j=0,1,2$. The result is a three-by-three grid of bold dots as shown in Figure~\ref{fig1}. @@ -275,393 +279,143 @@ $$ \end{figure} -\section{The MetaPost workflow} +\section{The MetaPost Workflow} \label{workflow} Before describing the MetaPost language in detail, let's have a look at -how the MetaPost program is used for developing graphics: creating -MetaPost source files, compiling sources, previewing MetaPost graphics, -and finally including MetaPost graphics into third-party applications. -This section also contains some technical details about MetaPost that -are useful to know. If you're not interested in these basics right now -you can savely skip those parts and only refer to them later. - -\subsection{Processing source files} -\label{processing} - -To create graphics with MetaPost, you prepare a text file containing -code in the MetaPost language and then invoke the compiler, usually by -giving a command of the form\index{mpost?\texttt{mpost}} -$$ {\tt mpost}\, \descr{filename} $$ -on the command-line. The syntax and program name itself are -system-dependent; sometimes it is named \texttt{mp}. MetaPost input -files\index{files!input} normally have names ending -\texttt{.mp}\index{mp file?{\tt mp} file}\index{files!mp?{\tt mp}} but -this part of the name can be omitted when invoking MetaPost. The -command-line options provided by MetaPost are summarized in -appendix~\ref{refman:commandline}. - -\paragraph{Input file} -\label{inputfile} - -A MetaPost input file normally contains a sequence of -\verb|beginfig()|\index{beginfig?\texttt{beginfig}}, -\verb|endfig|\index{endfig?\texttt{endfig}} pairs with an {\tt - end}\index{end?\texttt{end}} statement after the last one. These are -macros that perform various administrative functions and ensure that the -results of all drawing operations get packaged up and translated into -PostScript. The numeric argument to the \verb|beginfig| macro -determines the name of the corresponding output file, whose name, by -default, is of the form $\descr{jobname}{\tt.}\descr{n}$, where -$\descr{jobname}$ is the base name of the graphics source file and -$\descr{n}$ is the current argument to \verb|beginfig|. As an example, -if a file is named {\tt fig.mp}, the output from drawing statements -between \verb|beginfig(1)| and the next \verb|endfig| is written in a -file {\tt fig.1}\index{files!output}. - -Statements can also appear outside \verb|beginfig/endfig|. Such -statements are processed, but drawing operations generate no visible -output. Typically, global configurations are put outside -\verb|beginfig/endfig|, e.g., assignments to internal variables, such as -\verb|outputtemplate| or \verb|prologues|, or a \LaTeX\ preamble -declaration for enhanced text rendering. - -\paragraph{Log file and version number} -\label{logfile} - -Any terminal I/O is summarized in a -transcript\index{files!transcript}\index{transcript file} file called -$\descr{filename}{\tt.log}$\index{log file?{\tt log} - file}\index{files!log?{\tt log}}. This includes error messages and -any MetaPost commands entered interactively.\footnote{A {\tt - *}\index{*?\texttt{*}} prompt is used for interactive input and a - {\tt **}\index{**?\texttt{**}} prompt indicates that an input file - name is expected. This can be avoided by invoking MetaPost on a file - that ends with an {\tt end}\index{end?\texttt{end}} command.} The -transcript file starts with a banner line that identifies the -version\index{version number} of MetaPost you are using. The version -number is also written to the generated PostScript or SVG file as a -comment (for PostScript output the \texttt{Creator}\index{Creator - comment in PostScript output} line is used). You can also determine -the current version from within a MetaPost program via the predefined -constant string -\texttt{mpversion}\index{mpversion?\texttt{mpversion}}\label{Dmpversion} -(since version~0.9). For instance the following code +MetaPost's graphic design workflow. This section also contains a few +technical details about MetaPost's compilation process, just enough to +get you started. Section~\ref{workflow2} is more elaborate on this +topic. + +In this manual, we'll assume a stand-alone command-line executable of +the MetaPost compiler is used, which is usually called +\texttt{mpost}\index{mpost?\texttt{mpost}}. The syntax and program name +itself are system-dependent; sometimes it is named \texttt{mp}. The +executable is actually a small wrapper program around +\emph{mplib}\index{mplib?\emph{mplib}}, a library containing the +MetaPost compiler. The library can as well be embedded into third-party +applications.\footnote{C~API\index{mplib?\emph{mplib}!C~API} and Lua + bindings\index{mplib?\emph{mplib}!Lua bindings} are described in file + \verb|manual/mplibapi.pdf|\index{mplib?\emph{mplib}!mplibapi.pdf?\texttt{mplibapi.pdf}} + as part of the MetaPost distribution.} Section~\ref{mpimport} has +some brief information on how to use the MetaPost compiler built-into +Lua\TeX. For more information, please refer to the documentation of the +embedding application. + +The basic MetaPost workflow\index{workflow} is depicted in +figure~\ref{fig:mpworkflow}. Being a graphics description language, +creating graphics with MetaPost follows the +\emph{edit-compile-debug}\index{edit}\index{compile}\index{debug} +paradigm known from other programming languages. + +\begin{figure}[tbp] +$$ \includegraphics{mpman-charts-2.mps} $$ +\caption{The basic MetaPost workflow} +\label{fig:mpworkflow} +\end{figure} -$$\begin{verbatim} -message "mp = " & mpversion; -\end{verbatim} +To create graphics with MetaPost, you prepare\index{edit} a text file +containing code in the MetaPost language and then invoke the +compiler\index{compile}, usually by giving a command of the form $$ -writes -$$\begin{verbatim}[commandchars=\\\{\}] -mp = \mpversion +\begin{verbatim}[commandchars=\\\{\}] +mpost \tdescr{input file} \end{verbatim} +\index{mpost?\texttt{mpost}} $$ -to the console and the transcript file. - -\paragraph{Output format} -\label{outputformat} - -MetaPost can generate graphic in two output formats: Encapsulated -PostScript\index{PostScript!structured} (EPSF\index{EPSF}), hence the -name, and, since version~1.200, Scalable Vector Graphics -(SVG\index{SVG}) following version~1.1 of the SVG -specification~\cite{w3c:svg1.1}. By default, MetaPost outputs -PostScript files. The output format can be changed to SVG by assigning -the value \verb|"svg"| to the internal string variable\index{internal - variables}\index{variables!internal} -\ttindex{outputformat}\label{Doutputformat}: +on the command-line. MetaPost input files\index{files!input} normally +have names ending \texttt{.mp}\index{mp file?{\tt mp} + file}\index{files!mp?{\tt mp}} but this part of the name can be +omitted when invoking MetaPost. A complete description of the +command-line syntax can be found in Section~\ref{refman:commandline}. + +Any terminal I/O during the compilation process is summarized in a +transcript\index{files!transcript}\index{transcript file|see{files, + transcript}} file called \texttt{\tdescr{jobname}.log}\index{log + file?{\tt log} file}\index{files!log?{\tt log}}, where +\tdescr{jobname} is the base name of the input file. This includes +error messages and any MetaPost commands entered in interactive mode. + +If all goes well during compilation, MetaPost outputs one or more +graphic files in a variant of the PostScript format, by default. +PostScript output can be previewed\index{preview} with any decent +PostScript viewer, e.g., GSview\index{GSview} or +PS\_View\index{PSView?PS\_View}. Section~\ref{preview} has some tips +and discusses several more elaborate ways for previewing PostScript +output. Particularly, if graphics contain text labels, some more work +might be required to get robust results in a PostScript viewer. +Starting with version~1.200, MetaPost is also capable of generating +graphics in the SVG\index{SVG} format. SVG files can be previewed with +certain web browsers, for example Firefox~3\index{Firefox} or +Konqueror~4.2\index{Konqueror}. -$$\begin{verbatim} -outputformat := "svg"; -\end{verbatim} +What does one do with all the graphic files? PostScript files are +perfectly suitable for inclusion into documents created by +\TeX\index{TeX?\TeX} or troff\index{troff}. The SVG format, as an XML +descendant (Extensible Meta Language), is more aiming at automated data +processing/interchanging and is widely used for web applications. +Section~\ref{mpimport} deals with the import of MetaPost graphics into +external applications. + +A MetaPost input file\index{files!input}\index{edit}\index{edit} +normally contains a sequence of +\verb|beginfig()|\index{beginfig?\texttt{beginfig}}, +\verb|endfig|\index{endfig?\texttt{endfig}} pairs with an +\verb|end|\index{end?\texttt{end}} statement after the last +one.\footnote{Omitting the final \verb|end| statement causes MetaPost to + enter interactive mode\index{interactive mode} after processing the + input file.} These are macros that perform various administrative +functions and ensure that the results of all drawing operations get +packaged up and translated into PostScript or SVG format. The numeric +argument to the \verb|beginfig| macro determines the name of the +corresponding output file, whose name, by default, is of the form +\texttt{\tdescr{jobname}.\tdescr{n}}, where \tdescr{n} is the current +argument to \verb|beginfig| rounded to the nearest integer. As an +example, if a file is named \texttt{fig.mp} and contains the lines $$ - -Any other value makes MetaPost fall back to PostScript output. Variable -\verb|outputformat| is case-sensitive, so assigning it the string -\verb|"SVG"| enables PostScript output, too. Default value of variable -\verb|outputformat| is \verb|"eps"|. - -\paragraph{Output file names} -\label{outputfilenames} - -As discussed earlier, by default, every -\verb|beginfig/endfig| group in an input file corresponds to an output -file that follows the naming scheme $\descr{jobname}{\tt.}\descr{n}$. -That is, all files have varying numeric file extensions. MetaPost -provides a template mechanism that allows for more flexible output file -names. The template mechanism uses \ttt{printf}-style escape sequences -that are re-evaluated at ship-out time, i.e., before each figure is -written to disk. - -To configure the output file naming scheme a string containing the -corresponding escape sequences has to be assigned to the internal string -variable\index{internal variables}\index{variables!internal} -\ttt{outputtemplate}% -\index{outputtemplate?\texttt{outputtemplate}}\label{Doutputtemplate}. -The escape sequences provided are listed in table~\ref{tab:fntmpl}. If -this code is saved in a file \ttt{fig.mp}, - -$$\begin{verbatim} -outputtemplate := "%j-%3c.mps"; +\begin{verbatim}[commandchars=\\\{\}] beginfig(1); - draw origin--(100,100); + \tdescr{drawing statements} endfig; +end \end{verbatim} $$ -it will create the output file \ttt{fig-001.mps} instead of \ttt{fig.1}. -The file extension \texttt{.mps}\index{mps file?{\tt mps} - file}\index{files!mps?{\tt mps}} is conventionally chosen for -MetaPost's PostScript output (see section~\ref{teximport}). For SVG -output one would want to use \texttt{.svg}\index{svg file?{\tt svg} - file}\index{files!svg?{\tt svg}} instead. - -%\suppressfloats[t] -\begin{table} - \def\d{$\langle$0-9$\rangle$} - \centering - \begin{tabular}{|>{\ttfamily}l|>{\ttfamily}l|l|} - \hline - \multicolumn1{|c|}{Escape sequence} & \multicolumn1{c|}{Equivalent} - & \multicolumn1{c|}{Meaning}\\\hline - \%\%\index{\%\%?\texttt{\%\%}} & & percent sign\\ - \%\d\{$\descr{internal - variable}$\}\index{\%\{...\}?\texttt{\%\{\ldots\}}} & & evaluate - internal variable\\ - \%j\index{\%j?\texttt{\%j}} & \%\{jobname\} & current jobname\\ - \%\d c\index{\%c?\texttt{\%c}} & \%\d\{charcode\} & charcode value (\verb|beginfig| - argument)\\ - \%\d y\index{\%y?\texttt{\%y}} & \%\d\{year\} & current year\\ - \%\d m\index{\%m?\texttt{\%m}} & \%\d\{month\} & month (numeric)\\ - \%\d d\index{\%d?\texttt{\%d}} & \%\d\{day\} & day of the month\\ - \%\d H\index{\%H?\texttt{\%H}} & \%\d\{hour\} & hour\\ - \%\d M\index{\%M?\texttt{\%M}} & \%\d\{minute\} & minute\\ - \hline - \end{tabular} - \caption{Allowed escape sequences for \ttt{outputtemplate}} - \label{tab:fntmpl} -\end{table} - -The template mechanism can also be used for naming graphic files -individually, yet keeping all sources in one file. E.\,g., collecting -different diagram sources in a file \ttt{fig.mp} - -$$\begin{verbatim} -outputtemplate := "fig-quality.mps"; -beginfig(1); - ... -endfig; - -outputtemplate := "fig-cost-vs-productivity.mps"; -beginfig(2); - ... -endfig; -\end{verbatim} +the output from statements between \verb|beginfig(1)| and the next +\verb|endfig| is written in a file \texttt{fig.1}\index{files!output}. + +Statements can also appear outside \verb|beginfig| \ldots\ +\verb|endfig|. Such statements are processed, but drawing operations +generate no visible output. Typically, global configurations are put +outside \verb|beginfig| \ldots\ \verb|endfig|, e.g., assignments to +internal variables, such as \texttt{outputtemplate}, or a \LaTeX\ +preamble declaration for enhanced text rendering. +Comments\index{comments} in MetaPost code are introduced by the percent +sign~\verb|%|\index{\%?\texttt{\%}}, which causes the remainder of the +current line to be ignored. + +The remainder of this section briefly introduces three assignments to +internal variables, each one useful by itself, that can often be found +in MetaPost input files: $$ -it might be easier to recall the correct diagram names in a \TeX\ -document than with numbered file names. Note, the argument to -\verb|beginfig| is not relevant as long as there's no \ttt{\%c} pattern -in the file name template string. - -To ensure compatibility with older files, the default value of -\ttt{outputtemplate} is \verb|%j.%c|. -If you assign an empty string, it will revert to that default. MetaPost -versions~1.000 to~1.102 used a different template mechanism, see -section~\ref{Dfilenametemplate} for more information. - -What does one do with all the graphic files? PostScript files are -perfectly suitable for inclusion into documents created by -\TeX\index{TeX?\TeX} or \emph{troff}\index{troff}. The SVG format, as -an XML descendant (Extensible Meta Language), is more aiming at -automated data processing/interchanging and is widely used for web -applications. The next sections deal with the import of MetaPost -graphics into third-party applications. - -At this point, it seems advisable to point out, that it is good style to -create MetaPost graphics at nearly target size. While vector graphics -\emph{can} be scaled without quality degradation, the visual character -of a drawing might change unintentionally at large post-processing scale -factors, because certain properties of a drawing do not scale -proportionally, e.g., line width or arrow size. Additionally, while -outline fonts do scale proportionally, this is not always desired, e.g., -when a font is available in optical sizes, like the Computer Modern font -family. - -\subsection{Previewing MetaPost graphics} -\label{previewing} -\index{previewing} -\index{PostScript!previewing} -\index{SVG!previewing} - -The PostScript output of MetaPost can be previewed with any decent -PostScript viewer, e.\,g., GSview\index{GSview}. -The situation becomes only a little bit fussy when PostScript graphics -contain text. By default, MetaPost doesn't produce self-contained EPS -files, e.\,g., font resources and encoding vectors are not stored in -the output. For that reason MetaPost output containing text may be -rendered with wrong fonts, wrong glyphs or with no text at all in a -PostScript viewer. For a long time, the most reliable way for -previewing was to prepare a test document that includes all MetaPost -figures, process that with \TeX\ or \LaTeX\ and \ttindex{dvips} and -display the resulting \ttt{ps} file in a PostScript -viewer.\footnote{There are alternatives, though. \ttindex{mpstoeps} is -a Perl script that automates the process outlined above. -\ttindex{mptopdf} is another tools that converts MetaPost files to -PDF.} - -However, with MetaPost version~1.000 the situation changed. Since that -version MetaPost is able to produce self-contained EPS files, that can -reliably be previewed, may they contain text or not. The -new behaviour can be triggered by setting MetaPost's internal variable -\ttt{prologues}\index{prologues?\texttt{prologues}} to~3. See -section~\ref{Dbtex} for more information on \ttt{prologues}. - -Starting with version~1.200, MetaPost is also capable of producing -SVG output. SVG files can be previewed with certain web browsers, -for example Firefox~3\index{Firefox} or Konqueror~4.2\index{Konqueror}. - -\subsection{Importing MetaPost graphics in \TeX\ documents} -\label{teximport} -\index{TeX?\TeX!importing MetaPost files} -\index{LaTeX?\LaTeX!importing MetaPost files} -\index{pdfLaTeX?pdf\LaTeX!importing MetaPost files} -\index{pdfTeX?pdf\TeX!importing MetaPost files} -\index{ConTeXt?Con\TeX t!importing MetaPost files} - -MetaPost graphics in the PostScript format can be easily integreated -into documents prepared with \TeX\ and friends. MetaPost's PostScript -output is a low-featured dialect of the Postscript language, called -\emph{purified EPS}, that can be converted into the Portable Document -Format (PDF\index{PDF}) language on-the-fly. For that reason, MetaPost -graphics can be handled by both engines, the traditional \TeX\ with an -external output driver and the newer pdf\TeX, that contains a built-in -PDF output driver. - -Figure~\ref{fig0} shows the process of including a MetaPost graphic into -a plain \TeX\ or \LaTeX\ document. In the \TeX\ document a ``magic -macro'' provided by the format or an external package is used for -including a graphic file. During the typesetting stage, the macro only -reads bounding box information off the PostScript file and reserves the -required space on the page via an empty box. The file reference is -passed-on to the output driver and only then, finally, the file is -embedded into the document. The freely available program -\ttindex{dvips} is used as an output driver in this -example.\footnote{The C source for \ttt{dvips} comes with the web2c - \TeX\ distribution. Similar programs are available from other - sources.} The next paragraphs give brief information on some popular -combinations of formats and engines. - -\begin{figure}[htp] -$$ \includegraphics{mpman-0.mps} $$ -\caption[A diagram of the processing for a document with MetaPost figures] - {A diagram of the processing for a \TeX\ document with figures - in MetaPost} -\label{fig0} -\end{figure} - -\paragraph{Plain \TeX\ with \TeX} -For users of \TeX\ with traditional DVI output the -\ttt{epsf}\index{epsf.tex?\texttt{epsf.tex}} package provides the -``magic macro'' -$$ \verb|\epsfbox{|\descr{filename}\verb|}|% -\index{epsfbox?\texttt{\string\epsfbox}} $$ -for embedding graphics, e.g., \verb|\epsfbox{fig.1}|. - -\paragraph{Plain \TeX\ with pdf\TeX} -Users of Plain \TeX\ and pdf\TeX\ should refer to the standalone macros -of the \ttindex{mptopdf} bundle, that can be found at -\url{http://context.aanhet.net/mptopdf.htm}. - -\paragraph{\LaTeX\ format} -For \LaTeX\ documents the well-known \ttindex{graphics} (or -\ttindex{graphicx}) package aids in external graphics inclusion. The -package supports different engines and can handle several graphic -formats. The ``magic macro'' is -$$ \verb|\includegraphics{|\descr{filename}\verb|}|% -\index{includegraphics?\texttt{\string\includegraphics}} $$ - -When the \ttt{dvips} driver is chosen, the \ttt{graphics} package -assumes all files with an unknown file extension to be in the EPS -format. MetaPost files with a numeric default file extension are -therefore handled correctly -- even if only in a fall-back procedure -(see~\cite{reckdahl:epslatex} for more information). - -For the pdf\TeX\ driver, the situation is a bit different. Only files -with file extension \texttt{.mps}\index{mps file?{\tt mps} - file}\index{files!mps?{\tt mps}} are recognized as purified EPS and -can be converted to PDF\index{PDF} on-the-fly. The recommended -procedure for including MetaPost graphics into \LaTeX\ documents -compiled with the pdf\TeX\ engine should be obvious: change MetaPost's -output file naming scheme to write files ending \texttt{.mps} via -\verb|outputtemplate|\index{outputtemplate?\texttt{outputtemplate}} (see -p.~\pageref{Doutputtemplate}). In the \LaTeX\ document include the -graphic files with full name, e.g., -$$\begin{verbatim} -\includegraphics{fig-1.mps} -\end{verbatim} -$$ - -Note, the latter approach works with the \ttt{dvips} driver, too. Even -though, again, this time \ttt{.mps} is an unknown file extension and -triggers EPS file handling in the fall-back procedure. This property of -the \ttt{graphics} package, that comes in handy for MetaPost files, is -the reason many MetaPost source files start with the line -$$\begin{verbatim} -outputtemplate := "%j-%c.mps"; -\end{verbatim} -$$ - -\paragraph{Con\TeX t} -In Con\TeX t\index{ConTeXt?Con\TeX t} support of MetaPost is integrated -in the kernel. Apart from inline graphics (see MetaFun\index{MetaFun} -manual), one can embed graphics explictely with the -\verb+\externalfigure+% -\index{externalfigure?\texttt{\string\externalfigure}} command. -Numbered graphics are recognized automatically, as are graphics with the -\ttt{mps} suffix. Special features like shading, transparency, image -inclusion, color spaces and such are handled automatically. In practice -Con\TeX t users will probably define MetaPost graphics in the document -source which has some advantages, like a more natural interfacing with -document properties, font support, and automatic processing. Support -for MetaPost inclusion is present in the versions MkII as well as MkIV, -but the used methods are slightly different. Future versions of MkIV -will support an even more tight integration. - -\subsection{Importing MetaPost graphics in \emph{troff} documents} -\label{troffimport} -\index{troff!importing MetaPost files} - -It is also possible to include MetaPost output in a GNU \emph{troff} -document. The procedure is similar to Figure~\ref{fig0}: the -\ttt{grops} output processor includes PostScript files when they are -requested via \emph{troff}'s \ttt{\string\X} command. The -\ttt{-mpspic}\index{mpspic?\texttt{-mpspic}} macro package provides a -command \verb|.PSPIC|\index{PSPIC?\texttt{.PSPIC}}, that does just that -when including an encapsulated PostScript file in the source code. For -instance, the \emph{troff} command -$$ \hbox{\verb|.PSPIC fig.1|} $$ -includes \ttt{fig.1}, using the natural height and width of the -image as given in the file's bounding box. - -\subsection{Handling SVG files} -\label{svgimport} -\index{SVG!third-party applications} - -SVG graphics can be easily embedded into HTML\index{HTML} documents with -the following code snippet: - -$$\begin{verbatim} -<p> - <object data="foo.svg" type="image/svg+xml" width="300" height="200"> - </object> -</p> +\begin{verbatim}[commandchars=\\\{\}] +prologues := 3; \smallskip +outputtemplate := "%j-%c.mps"; \smallskip +outputformat := "svg"; \end{verbatim} $$ -SVG files can also be imported by various interactive graphics editing -programs, for example GIMP\index{GIMP} or Inkscape\index{Inkscape}. See -section~\ref{Dprologues} for information on font handling in SVG -graphics. +If your graphics contain text labels, you might want to set variable +\texttt{prologues} to~3 to make sure the correct fonts are used under +all possible circumstances. The second assignment changes the output +file naming scheme to the form \texttt{\tdescr{jobname}-\tdescr{n}.mps}. +That way, instead of a numeric index, all output files get a uniform +file extension \texttt{mps}\index{mps file?{\tt mps} + file}\index{files!mps?{\tt mps}}, which is typically used for +MetaPost's PostScript output. The last assignment lets MetaPost write +output files in the SVG format rather than in the PostScript format. +More information can be found in Sections~\ref{Dprologues} +and~\ref{workflow2}. \section{Curves} @@ -761,6 +515,9 @@ draw (0,0)..controls (26.8,-1.8) and (51.4,14.6) \end{verbatim} $$ +For a way to extract the control points of a path, given by the user or +calculated by MetaPost, see section~\ref{Dprepostctrl}. + \begin{figure}[htp] $$ \includegraphics{mpman-5.mps} $$ @@ -1317,8 +1074,10 @@ function of pens in MetaPost is to determine line thickness, but they can also be used to achieve calligraphic effects. The statement\index{pickup?\texttt{pickup}}\label{Dpickup} $$ {\tt pickup\ }\descr{pen expression} $$ -causes the given pen to be used in subsequent {\tt draw} statements. -Normally, the pen expression is of the form +causes the given pen to be used in subsequent +\verb|draw|\index{draw?\texttt{draw}} or +\verb|drawdot|\index{drawdot?\texttt{drawdot}} statements. Normally, +the pen expression is of the form $$ {\tt pencircle\ scaled\ }\descr{numeric primary}. $$ This defines a circular pen that produces lines of constant thickness. If calligraphic effects are desired, the pen expression can be adjusted to give @@ -1829,7 +1588,11 @@ statement. This is effectively like a {\tt label} statement followed by a statement drawing a dot at the indicated coordinates. For example $$ \hbox{\tt dotlabel.bot("(0,0)", z0)} $$ -places a dot at {\tt z0} and then puts the label ``(0,0)'' just below the dot. +places a dot at {\tt z0} and then puts the label ``(0,0)'' just below +the dot. The diameter of the dot drawn by the {\tt dotlabel} statement +is determined by the value of the internal variable {\tt + dotlabeldiam}\index{dotlabeldiam?\texttt{dotlabeldiam}}\label{Ddotlabdiam}. +Default value is 3bp. Another alternative is the macro {\tt thelabel}\index{thelabel?\texttt{thelabel}}\label{Dthelab}. This has @@ -2024,7 +1787,7 @@ In MetaPost versions before 1.100, the \TeX\ label preprocessing was handled by an external program that was called upon automatically by MetaPost. On Web2C-based systems, the preprocessor was normally named \ttindex{makempx}, which called the utility \ttindex{mpto} for the -creation of the \TeX\ input file and the utility \ttindex{dvitomp} for +creation of the \TeX\ input file and the utility \texttt{dvitomp}\index{dvitomp?\texttt{dvitomp}} for the conversion to low level MetaPost. In the current MetaPost version, the work of this program is now done internally. However, if the environment variable \ttindex{MPXCOMMAND} is set, the whole label @@ -2102,17 +1865,50 @@ will contain the PostScript font(s) (or a subset) used based on the for generating stand-alone PostScript graphics. In SVG mode, the font glyphs are converted to path definitions that -are included at the top of the output file. This method is currently -the only reliable way to export text objects to SVG. +are included at the top of the output file. \end{itemize} +The correct setting for variable \texttt{prologues} depends on how +MetaPost graphics are post-processed. Here are recommendations for some +popular use-cases: + +\begin{description} + +\item[\normalfont\itshape Previewing:] Section~\ref{preview} discusses + previewing PostScript output. + +\item[\normalfont\itshape \TeX\ and dvips:] When including PostScript + figures into a \TeX\ document that is processed by \TeX\ and a DVI + output processer, e.g., dvips\index{dvips}, variable + \texttt{prologues} should \emph{not} be set to the value~1, unless the + used fonts are known to be resident in the PostScript interpreter. + Make sure that variable \texttt{prologues} is set to either~0 (font + inclusion handled by dvips, but without re-encoding support), 2 (font + inclusion by dvips, with font re-encoding if necessary), or~3 (font + inclusion and re-encoding by MetaPost). Value~3 is safest, but may + result in slightly larger output. + +\item[\normalfont\itshape pdf\TeX:] When generating PDF files with + pdf\TeX\ (and the mptopdf\index{mptopdf} bundle), variable + \texttt{prologues} is not relevant. + +\item[\normalfont\itshape PostScript in external applications:] Some + text\index{text processor} processors or graphics applications can + directly import EPSF files, while for others MetaPost's PostScript + output has to be converted to a different vector or even a bitmap + format first. In any case, as soon as PostScript graphics generated + by MetaPost are leaving the \TeX\ ecosystem, variable + \texttt{prologues} should be set to~3, so that all needed fonts are + embedded (as a subset). + +\item[\normalfont\itshape SVG output:] Converting font glyphs to paths + by setting variable \texttt{prologues} to~3 is currently the only + reliable way to export text objects to SVG. + +\end{description} -It is worth noting that the default value \ttt{prologues:=0} is sufficient -for graphics included in \TeX-based documents. Also, the \ttt{prologues} variable is irrelevant -when processing MetaPost files through the \ttindex{mptopdf} utility -(part of the \ConTeXt\ distribution), because PDF files are, by nature, -stand-alone. Moreover, the value of \ttt{prologues} has no effect on +It is worth noting that the value of \ttt{prologues} has no effect on \MF\ fonts in your MetaPost files, i.\,e., MetaPost never embeds such fonts. Only output drivers, e.\,g., \ttt{dvips} or pdf\LaTeX\ will handle those. @@ -2120,14 +1916,14 @@ handle those. The details on how to include PostScript figures in a paper done in \TeX\ or troff are system-dependent. They can generally be found in manual pages and other on-line documentation, but have a look at -section~\ref{teximport} of this manual for some brief instructions that +section~\ref{mpimport} of this manual for some brief instructions that in many cases should work. The manual for the widely-used Dvips processor is in a file \ttt{dvips.texi}, included in most distributions, and is available online at \url{http://tug.org/texinfohtml/dvips.html}, among many other places and formats. -\subsection{Font map files} +\subsection{Font Map Files} \label{fontmap} If \ttt{prologues} is set to~2, any used fonts in the output file are @@ -2193,18 +1989,18 @@ pdftex.map}, depending on whether or not troff mode is enabled. If statement. Again, this is for backward compatibility only. -\subsection{The \texttt{infont} operator} +\subsection{The \texttt{infont} Operator} \label{Sinfont} Regardless of whether you use \TeX\ or troff, all the real work of adding text to pictures is done by a MetaPost primitive operator called {\tt infont}\index{infont?\texttt{infont}}. It is a \tdescr{primary -binop}\index{primary binop?\tdescr{primary binop}} that takes a + binop}\index{primary binop?\tdescr{primary binop}} that takes a \tdescr{string secondary} as its left argument and a \tdescr{string -primary} as its right argument. The left argument is text, and the + primary} as its right argument. The left argument is text, and the right argument is a font name. The result of the operation is a -\tdescr{picture secondary} that can then be transformed in various ways. -One possibility is enlargement by a given factor via the +\tdescr{picture secondary}, which can then be transformed in various +ways. One possibility is enlargement by a given factor via the syntax\index{scaled?\texttt{scaled}} $$ \descr{picture secondary}\, \hbox{\tt scaled}\, \descr{numeric primary} $$ Thus {\tt label("text",z0)} is equivalent to @@ -2674,6 +2470,31 @@ Thus $$ p_1\ {\tt cutafter}\ p_2 $$ tries to cut off the part of $p_1$ after its last intersection with $p_2$. +The control points of a path can be requested by the two operators +\index{precontrol of?\texttt{precontrol of}} +\index{postcontrol of?\texttt{postcontrol of}} +\label{Dprepostctrl} +\begin{eqnarray*} + {\tt precontrol}\, \descr{numeric expression}\, {\tt of}\, \descr{path primary},\\ + {\tt postcontrol}\, \descr{numeric expression}\, {\tt of}\, \descr{path primary}. +\end{eqnarray*} + +For integer time values~$t$, these operators return the control points +before and after a cardinal point of a path. A segment +$z_{t-1}\verb|..|z_t$ of a path~$p$ has therefore control points +$$ {\tt postcontrol}\ t-1\ {\tt of}\ p +$$ +and +$$ {\tt precontrol}\ t\ {\tt of}\ p. +$$ + +For decimal time values, {\tt precontrol of} returns the last control +point of sub-path $(0, t)$ and {\tt postcontrol of} returns the first +control point of sub-path $(t, \infty)$ of a path. In other words, the +control points at fractional time values correspond to a virtual +cardinal point inserted at the given time value without modifying path +shape. + Another operator\index{direction of?\texttt{direction of}}\label{Ddirof} $$ {\tt direction}\, \descr{numeric expression}\, {\tt of}\, \descr{path primary} $$ @@ -3544,7 +3365,7 @@ $$ {\tt shipout}\, \descr{picture expression} $$ writes out a picture as a PostScript\index{PostScript} file whose file name is determined by {\tt outputtemplate}\index{outputtemplate?\texttt{outputtemplate}} -(see section~\ref{outputfilenames}). By default, the file name ends +(see Section~\ref{Doutputtemplate}). By default, the file name ends {\tt.}{\it nnn}, where {\tt nnn} is the decimal representation of the value of the internal variable\index{internal variables}\index{variables!internal} {\tt @@ -3781,23 +3602,25 @@ To summarize the discussion of mismatching part operators: \index{greypart?\texttt{greypart}|)} -\subsection{Decomposing the glyphs of a font} +\subsection{Decomposing the Glyphs of a Font} \label{glyphs} -MetaPost provides a primitive to convert a glyph of a font in the Adobe -Type~1 Font\index{PostScript!fonts}\index{font!PostScript}\index{Adobe - Type~1 Font}\index{font!Adobe Type~1} format into its constituent -filled paths---the strokes---and store them in a picture variable. A -glyph\index{font!glyph} is the visual representation of a character in a -font. A character\index{character}\index{font!character} is a certain -slot\index{slot} (index) in a font with an associated meaning, e.g., the -capital letter~``M'' or the exclamation mark. The meaning of a slot is -defined by the font encoding\index{encoding}\index{font!encoding}. In -general, the same character is represented by different glyphs in -different fonts. Figure~\ref{glyphsample} shows some glyphs for the -character at slot~103 in the T1 encoding, i.e., the lower-case -letter~``g''. All glyphs are at the same nominal size. Note, how -glyphs may extend beyond their bounding box. +MetaPost provides a primitive to convert a +glyph\index{glyph}\index{font!glyph} of a font in the Adobe Type~1 +Font\index{PostScript!fonts}\index{font!PostScript}\index{Adobe Type~1 + Font}\index{font!Adobe Type~1} format into its constituent filled +paths---the strokes---and store them in a picture variable. A glyph is +the visual representation of a character in a font. A +character\index{character}\index{font!character} is a certain +slot\index{slot}\index{font!slot} (index) in a font with an associated +meaning, e.g., the capital letter~``M'' or the exclamation mark. The +meaning of a slot is defined by the font +encoding\index{encoding}\index{font!encoding}. In general, the same +character is represented by different glyphs in different fonts. +Figure~\ref{glyphsample} shows some glyphs for the character at slot~103 +in the T1 encoding, i.e., the lower-case letter~``g''. All glyphs are +at the same nominal size. Note, how glyphs may extend beyond their +bounding box. \begin{figure}[htp] \centering \setlength{\fboxsep}{0pt} @@ -3819,8 +3642,8 @@ glyphs may extend beyond their bounding box. \label{glyphsample} \end{figure} -The glyphs of an Adobe Type~1 font -\index{PostScript!fonts}\index{font!PostScript}\index{Adobe Type~1 +The glyphs of an Adobe Type~1 +font\index{PostScript!fonts}\index{font!PostScript}\index{Adobe Type~1 Font}\index{font!Adobe Type~1} are composed of two types of contours: Clockwise oriented contours add to the shape of a glyph and are filled with black ink. Counter-clockwise oriented contours @@ -3862,17 +3685,19 @@ glyph visually\footnote{Plain contours already carry enough information fonts\index{PostScript!fonts}\index{font!PostScript}\index{Adobe Type~1 Font}\index{font!Adobe Type~1} use a generalized variant of the non-zero winding number fill rule\index{non-zero fill rule}\index{fill - rule!non-zero}\index{PostScript!fill rule}, that MetaPost doesn't + rule!non-zero}\index{PostScript!fill rule}, which MetaPost doesn't implement (MetaPost cannot handle non-contiguous paths). As a side effect, the interiors of the erasing\index{erasing} contours are an opaque white in the returned picture, while they were transparent in the original glyph. One can think of erasing contours to be unfilled (see p.~\pageref{Dunfill}). For instance, the following code saves the contours of the lower case letter~``g'', bound to slot~103 in the OT1 -encoding\index{OT1 encoding}\index{encoding!OT1}, in the Computer Modern -Roman\index{Computer Modern Roman} font in a picture variable: +encoding\index{OT1 encoding}\index{encoding!OT1}, in the +Computer\index{Computer Modern Roman} Modern Roman font in a picture +variable: -$$\begin{verbatim} +$$ +\begin{verbatim} fontmapline "cmr10 CMR10 <cmr10.pfb"; picture g; g := glyph 103 of "cmr10"; @@ -3893,7 +3718,7 @@ is mandatory for the font in question, given either by section~\ref{Dfontmapline}). In Figure~\ref{fig56}, the contours of the upper case letter~``\v{D}'' -in the Latin Modern Roman\index{Latin Modern Roman} font are saved in a +in the Latin\index{Latin Modern Roman} Modern Roman font are saved in a picture variable. The glyph is identified by its CharString name ``Dcaron''\index{Dcaron}. The code then iterates over all contours and draws them together with their cardinal (black) and control points @@ -3901,7 +3726,8 @@ draws them together with their cardinal (black) and control points cardinal points in this glyph. \begin{figure}[htb] -$$\begin{verbatim} +$$ +\begin{verbatim} fontmapfile "=lm-ec.map"; beginfig(56); picture q; @@ -3945,13 +3771,13 @@ negative). Second, in an Adobe Type~1 font\index{PostScript!fonts}\index{font!PostScript}\index{Adobe Type~1 Font}\index{font!Adobe Type~1}, contours are in general closed by repeating the starting point before applying the \verb|closepath| -operator\index{PostScript!\texttt{closepath} operator}. The MetaPost -representation of such a path would be: +operator\index{PostScript!closepath operator?\texttt{closepath} + operator}. The MetaPost representation of such a path would be: $$ z_0\verb|..|\mbox{\itshape controls}\verb|..|z_1\verb|..| \cdots \verb|..|z_n\verb|..|\mbox{\itshape controls}\verb|..|z_0\verb|--cycle| $$ -A more natural MetaPost representation of this path would be: +However, a more natural MetaPost representation of that path would be: $$ z_0\verb|..|\mbox{\itshape controls}\verb|..|z_1\verb|..| \cdots \verb|..|z_n\verb|..|\mbox{\itshape controls}\verb|..cycle| @@ -3959,20 +3785,20 @@ $$ The \verb|glyph| operator transforms all paths into the latter representation, i.e., the last point is removed, whenever it matches the starting point. Finally, the picture returned by the \verb|glyph| -operator is scaled such that one font design unit\index{font!design +operator is scaled, such that one font design unit\index{font!design unit} equals one PostScript point\index{point!PostScript}\index{PostScript!point} (bp). A usual font design unit is a thousandth part of the font design -size\index{font!design size}. The returned picture will therefore -typically have a height of around 1000\,bp. +size\index{font!design size}. Therefore, the returned picture will +typically have a height of around 1000bp. Converting a text into plain curves is part of a process oftentimes -called ``flattening\index{flattening} a document.'' When flattening -text, all hinting information in fonts are lost. Hinting information -aid a rendering application in aligning certain parts of a glyph on a +called ``flattening\index{flattening}'' a document. During flattening, +all hinting information in fonts are lost. Hinting information aid a +rendering application in aligning certain parts of a glyph on a low-resolution output device. A flattened text may therefore look distorted on screen. In SVG output, all text is automatically -flattened, when the internal variable +flattened, if the internal variable \verb|prologues|\index{prologues?\texttt{prologues}} is set to~3 (see section~\ref{Dprologues}). @@ -4981,7 +4807,7 @@ Explanation: \begin{itemize} \item -The \texttt{\%\&latex} causes \LaTeX\ to be invoked instead of \TeX. +The \texttt{\%\&latex}\index{\%\&?\texttt{\%\&}} causes \LaTeX\ to be invoked instead of \TeX. (See below, also.) Web2C- and MiKTeX-based \TeX\ implementations, at least, understand this \texttt{\%\&} specification; see, e.g., the Web2C documentation for details, \url{http://tug.org/web2c}. (Information on @@ -5027,18 +4853,424 @@ whatever processor, of course). This might be useful from a Makefile, or just a one-off run. \end{itemize} -\subsection{\texttt{mproof.tex}} -\index{mproof.tex?\texttt{mproof.tex}} +\section{Another Look at the MetaPost Workflow} +\label{workflow2} + +\index{workflow} + +In Section~\ref{workflow} we already had a brief look at how MetaPost +compiles input files and generates output files. This section contains +some more information and discusses internal variables that can be used +to control MetaPost's run-time behavior, previewing PostScript output, +debugging MetaPost code, and importing MetaPost graphics into +third-party applications. + +\subsection{Customizing Run-Time Behavior} + +\index{runtime behavior!customize} + +MetaPost knows and obeys a number of internal\index{internal + variables}\index{variables!internal} variables that have no direct +impact on drawing commands, but can be used to customize the way the +MetaPost compiler processes input files. The following paragraphs +describe those variables (in no particular order). + +\paragraph{Date and Time} + +MetaPost provides a number of internal numeric variables that store the +date and time a job was started, i.e., the MetaPost executable was +called on the command-line. Variables +\texttt{year}\index{year?\texttt{year}}\label{Dyear}, +\texttt{month}\index{month?\texttt{month}}\label{Dmonth}, +\texttt{day}\index{day?\texttt{day}}\label{Dday}, +\texttt{hour}\index{hour?\texttt{hour}}\label{Dhour}, and +\texttt{minute}\index{minute?\texttt{minute}}\label{Dminute} should be +self-explanatory. Variable +\texttt{time}\index{time?\texttt{time}}\label{Dtime} returns the number +of minutes past midnight, since the job was started, i.e., +$\texttt{time} = 60*\texttt{hour} + \texttt{minute}$. + +\paragraph{Output File Names} + +As discussed in Section~\ref{workflow}, by default, every +\verb|beginfig| \ldots\ \verb|endfig| group in an input file corresponds +to an output file that follows the naming scheme +\texttt{\tdescr{jobname}.\tdescr{n}}. That is, all files have varying +numeric file extensions. MetaPost provides a template mechanism that +allows for more flexible output file names. The template mechanism uses +\texttt{printf}-style escape sequences that are re-evaluated at ship-out +time, i.e., before each figure is written to disk. + +To configure the output file naming scheme a string containing the +corresponding escape sequences has to be assigned to the internal string +variable +\verb|outputtemplate|\index{outputtemplate?\texttt{outputtemplate}}\label{Doutputtemplate}. +The escape sequences provided are listed in +table~\ref{tab:outputtemplate}. As an example, if this code is saved in +a file \texttt{fig.mp}, +$$ +\begin{verbatim} +outputtemplate := "%j-%c.mps"; +beginfig(1); + drawdot origin; +endfig; +end +\end{verbatim} +$$ +it will create the output file \texttt{fig-1.mps} instead of +\texttt{fig.1}. The file extension \texttt{mps}\index{mps file?{\tt + mps} file}\index{files!mps?{\tt mps}} is conventionally chosen for +MetaPost's PostScript output (see Section~\ref{mpimport}). For SVG +output one would want to use \texttt{svg}\index{svg file?{\tt svg} + file}\index{files!svg?{\tt svg}} instead. + +\begin{table} + \newcommand*{\otesc}[3]{\%#1\index{\%#1?\texttt{\%#1}} & #2 & \%\{#3\}} + \centering + \begin{tabular}{|>{\ttfamily}l|l|>{\ttfamily}l|} + \hline + \multicolumn1{|c|}{Escape sequence} & \multicolumn1{c|}{Meaning} & \multicolumn1{c|}{Alternative}\\\hline + \%\%\index{\%\%?\texttt{\%\%}} & percent sign &\\ + \%\{\tdescr{internal variable}\}\index{\%\{...\}?\texttt{\%\{\tdescr{internal variable}\}}} & evaluate internal variable &\\ + \%j\index{\%j?\texttt{\%j}} & current jobname & \%\{jobname\} \\ + \otesc{c}{charcode value (\texttt{beginfig} argument)}{charcode}\\ + \otesc{y}{current year}{year}\\ + \otesc{m}{month (numeric)}{month}\\ + \otesc{d}{day of the month}{day}\\ + \otesc{H}{hour}{hour}\\ + \otesc{M}{minute}{minute}\\ + \hline + \end{tabular} + \caption{Allowed escape sequences for \ttt{outputtemplate}} + \label{tab:outputtemplate} +\end{table} -\texttt{mproof.tex} is a (plain) \TeX\ routine, not MetaPost at all. It -writes a proof sheet for MetaPost output. Call it like this: +In single-letter escape sequences referring to internal numerics, the +corresponding value is rounded to the nearest integer before it is +converted to a string expression. In such escape sequences, there can +be an optional number (0--99) following \verb|%| that determines the +minimum number of digits in the resulting string expression, like +\verb|%2m|. If the decimal representation of the internal variable +requires more digits, actual string length exceeds the requested length. +If less digits are required, the string is padded to the requested +length with zeros from left. + +In the \verb|%{...}| escape sequence no rounding takes place. +Additionally, neither this nor single-letter escape sequences referring +to internal string variables can be zero-padded. + +For backwards compatibility, the \verb|%c|\index{\%c?\texttt{\%c}} +escape sequence is handled special. If the result of rounding the +charcode value is negative, \verb|%c| evaluates to the string \verb|ps|. +This transformation can be bypassed by using \verb|%{charcode}| instead +of \verb|%c| (bypassing the rounding, too). -$$\hbox{\tt tex mproof {\sl MetaPost-output-filename}}$$ +The template mechanism can also be used for naming graphic files +individually, yet keeping all sources in one file. Collecting, e.g., +different diagram sources in a single file \texttt{fig.mp}, it might be +easier to recall the correct diagram names in a \TeX\ document than with +numbered file names. Note, the argument to \verb|beginfig| is not +relevant as long as there's no \verb|%c| pattern in the file name +template string. +$$ +\begin{verbatim} +outputtemplate := "fig-quality.mps"; +beginfig(1); + ... +endfig; + +outputtemplate := "fig-cost-vs-productivity.mps"; +beginfig(2); + ... +endfig; +\end{verbatim} +$$ + +To ensure compatibility with older files, the default value of +\verb|outputtemplate| is \verb|%j.%c|. If you assign an empty string, +it will revert to that default. MetaPost versions~1.000 to~1.102 used a +different template mechanism, see Section~\ref{Dfilenametemplate} for +more information. + +\paragraph{Output Format} + +MetaPost can generate graphics in two output formats: Encapsulated +PostScript\index{PostScript!structured} (EPSF\index{EPSF}) and, since +version~1.200, Scalable Vector Graphics (SVG\index{SVG}) following +version~1.1 of the SVG specification~\cite{w3c:svg1.1}. By default, +MetaPost outputs PostScript files---hence the name MetaPost. The output +format can be changed to SVG by assigning the value \verb|"svg"| to the +internal string variable\index{internal + variables}\index{variables!internal} +\verb|outputformat|\index{outputformat?\texttt{outputformat}}\label{Doutputformat}: +$$ +\begin{verbatim} +outputformat := "svg"; +\end{verbatim} +$$ + +Any other value makes MetaPost fall back to PostScript output. Variable +\verb|outputformat| is case-sensitive, so assigning it the string +\verb|"SVG"| enables PostScript output, too. Default value of variable +\verb|outputformat| is \verb|"eps"|. + +\paragraph{PostScript Dictionary} + +For PostScript output, MetaPost can define a dictionary of abbreviations +of the PostScript commands, e.g., \verb|l| instead of \verb|lineto|, to +reduce the size of output files. Setting the internal variable +\texttt{mpprocset}\index{mpprocset?\texttt{mpprocset}}\label{Dmpprocset} +to~1 makes MetaPost create an extended preamble setting-up the +dictionary. Default value of variable \texttt{mpprocset} is~0, that is, +no dictionary is used. For SVG output, variable \texttt{mpprocset} is +not relevant. + +\paragraph{Version Number} + +The version number of the MetaPost compiler can be determined from +within a MetaPost program via the predefined constant string +\texttt{mpversion}\index{mpversion?\texttt{mpversion}}\label{Dmpversion} +(since version~0.9). For instance the following code +$$ +\begin{verbatim} +message "mp = " & mpversion; +\end{verbatim} +$$ +writes +$$ +\begin{verbatim}[commandchars=\\\{\}] +mp = \mpversion +\end{verbatim} +$$ +to the console and the transcript file. Variable \texttt{mpversion} can +be used to execute code depending on the MetaPost version like this: +$$ +\begin{verbatim}[commandchars=\\\{\}] +if unknown mpversion: string mpversion; mpversion := "0.000"; fi +if scantokens(mpversion) < 1.200: + errmessage "MetaPost v1.200 or later required (found v" & mpversion & ")"; +else: + \tdescr{code} +fi +\end{verbatim} +$$ +The first line is optional and only added to handle ancient MetaPost +versions gracefully that don't even know about variable +\textrm{mpversion} (prior to v0.9). The second test does the actual +work. + +The version number is also written to output files and the transcript +file. For PostScript output the version number can be found in the +\texttt{Creator}\index{Creator comment in PostScript + output?\texttt{Creator} comment in PostScript output|see{PostScript, + \texttt{Creator} comment}}\index{PostScript!Creator + comment?\texttt{Creator} comment} comment. SVG files contain a simple +comment line near the beginning of the file. The +transcript\index{files!transcript} file starts with a banner line that +identifies the version\index{version number} of the MetaPost compiler. + +\subsection{Previewing PostScript Output} +\label{preview} +\index{preview!PostScript} + +Previewing MetaPost's PostScript output is not difficult, but there are +some catches that one should know about. This section deals with the +following questions: How can graphics be clipped to their true bounding +box in the PostScript viewer application? Why are my text labels +rendered with an ugly font (or not at all) and how to avoid that? How +can several graphics be combined into a multi-page document that can be +previewed within one instance of the viewer application? + +\subsubsection{Bounding Box} +\index{bounding box} +\index{PostScript!bounding box} + +With default settings, MetaPost writes very much stripped-down +PostScript code, containing only the bare graphics code, but no other +ressources, like fonts etc. The PostScript code is somewhat deficient, +because it fails to correctly identify as Encapsulated PostScript +(EPSF\index{EPSF}) in the header. Note, Encapsulated PostScript files +don't have an associated page size, but provide bounding box +information, because they are meant for inclusion into other documents. +Instead MetaPost output wrongly pretends to be full PostScript +(PS\index{PS}), which it is not. + +This is just fine for including MetaPost graphics in, say, \TeX\ +documents (see Section~\ref{mpimport}), but some PostScript viewers have +difficulties rendering those PostScript files correctly. As an example, +because of the wrong ``PS'' header, GSview\index{GSview}---not knowing +better---ignores bounding box information and then clips all contents to +a (configurable) page size. Graphic elements laying outside those fixed +page boundaries are therefore not visible, e.g., when they have negative +coordinates. + +To avoid such situations, the first rule when previewing MetaPost's +PostScript output is to put the line +$$ +\begin{verbatim} +prologues := 2; +\end{verbatim} +\index{prologues?\texttt{prologues}} +$$ +before the first \verb|beginfig| in MetaPost input files (see the +discussion about \texttt{prologues} in Section~\ref{Dprologues}). That +way, MetaPost's PostScript output correctly identifies as Encapsulated +PostScript and viewer applications should always obey the file's +bounding box for on-screen rendering. + +A workaround for MetaPost's deficient default PostScript code that can +sometimes be seen is to move the lower left corner of a figure to the +origin as a last operation by saying +$$ +\begin{verbatim}[commandchars=\\\{\}] +\color{gray}currentpicture := currentpicture shifted -llcorner currentpicture; +\end{verbatim} +\index{currentpicture?\texttt{currentpicture}} +\index{llcorner?\texttt{llcorner}} +$$ +before \verb|endfig|. But this doesn't prevent from clipping on the +right and upper page boundaries. Additionally, the line is required for +all figures, cluttering source code, and it alters all coordinates in +PostScript output, which might complicate debugging. Applying such a +manual transformation is therefore not recommended (which is why the +line is grayed out). Instead, users are advised to adjust +\texttt{prologues} once in the preamble of the input file and enable +clipping to the bounding box in the PostScript viewer. For +GSview\index{GSview}, that can be done by activating \texttt{Options} +$\rightarrow$ \texttt{EPS Clip} and optionally \texttt{Options} +$\rightarrow$ \texttt{Show Bounding Box} for verification. + +\subsubsection{Text Labels} + +Another popular previewing issue concerns graphics that contain text +labels. An observation MetaPost users can often make is that text +labels in graphics are rendered with wrong fonts, wrong glyphs, and +sometimes even not at all. The reason is that with default settings, +again, MetaPost's PostScript output is deficient, in that it uses a +simple, non-standard way to declare what fonts are used in a graphic. +Setting variable \texttt{prologues} to~2, as shown in the previous +section, makes MetaPost generate more complex PostScript code to declare +all needed PostScript\index{PostScript!fonts} fonts and embed the +necessary encoding information. If the PostScript viewer can provide +the requested fonts, this might be sufficient to get text labels +rendered correctly. If you still observe wrong or missing glyphs you +should put the line +$$ +\begin{verbatim} +prologues := 3; +\end{verbatim} +\index{prologues?\texttt{prologues}} +$$ +into the preamble of the input file. That way, MetaPost embeds the used +PostScript\index{PostScript!fonts} fonts into the output file so that +they are always available (see the discussion about \texttt{prologues} +in Section~\ref{Dprologues}). Note, this might enlarge the size of +output files considerably. Additionally, fonts might be embedded +multiple times when several graphics using the same fonts are included +into a document. For that reason, it is recommended to reset variable +\texttt{prologues} to~0 before finally including MetaPost graphics into +external documents. + +\subsubsection{Proof Sheets} +\index{proof sheets} + +If you have lots of figures in a source file and need to preview many of +them at the same time, opening every graphic in a new instance of the +viewer application and switching between them back and forth can get +cumbersom. An alternative is to collect all graphics generated from a +MetaPost input file in a proof sheet, a multi-page document, that can be +previewed and navigated in a single instance of the viewer application. +The MetaPost distribution contains two (plain) \TeX\ scripts, +\texttt{mproof.tex} and \texttt{mpsproof.tex}, that help with the latter +approach. + +\paragraph{\texttt{mproof.tex}} +\index{mproof.tex?\texttt{mproof.tex}} + +To write a proof sheet for MetaPost output, call \texttt{mproof.tex} as +$$ +\begin{verbatim}[commandchars=\\\{\}] +tex mproof \tdescr{MetaPost output files} +\end{verbatim} +$$ Then process the resulting \texttt{.dvi}\index{dvi file?{\tt dvi} - file}\index{files!dvi?{\tt dvi}} file as usual. + file}\index{files!dvi?{\tt dvi}} file as usual. That way, there's no +need to care about different settings of variable +\texttt{prologues}\index{prologues?\texttt{prologues}}, since in proof +sheets MetaPost graphics are already embedded. + +Note, the parameters after \texttt{mproof} are an explicit list of +MetaPost output files, possibly generated from different input files. +On shells that support POSIX\index{shell patterns}\index{POSIX!shell + patterns} shell patterns, these can be used to avoid typing a long +list of files. As an example, for a file \texttt{fig.mp} containing +three figures with charcodes 1, 2, and~3, the proof sheet can be +generated by calling +$$ +\begin{verbatim} +tex mproof fig.? +\end{verbatim} +$$ +The pattern \texttt{fig.?} is automatically expanded to \texttt{fig.1 + fig.2 fig.3} by the shell (but not necessarily in numerically +increasing order) before \TeX\ is run. If there were an output file +\texttt{fig.10}, using patterns \texttt{fig.??} or \texttt{fig.*} to +cover two-digit indices would fail, since those covered the source file +\texttt{fig.mp} as well. To avoid that, output file names have to be +made more significant, e.g., by setting variable \texttt{outputtemplate} +to \verb|%j-%c.mps| (see Section~\ref{Doutputtemplate}). The proof sheet +can then be generated with +$$ +\begin{verbatim} +tex mproof *.mps +\end{verbatim} +$$ -\section{Debugging} +\paragraph{\texttt{mpsproof.tex}} +\index{mpsproof.tex?\texttt{mpsproof.tex}} + +An alternative to \texttt{mproof.tex} is the script +\texttt{mpsproof.tex}, which is similar, but more powerful. While the +former script only runs with \TeX\ and requires a DVI output driver to +generate PostScript files, \texttt{mpsproof.tex} can as well be run +through pdf\TeX\ to directly generate PDF files. Additionally, it +provides some command-line options. + +With the +\verb|\noheaders|\index{mpsproof.tex?\texttt{mpsproof.tex}!noheaders?\texttt{\textbackslash + noheaders}} option, file names, date stamps, and page numbers are +omitted from the proof sheet. Use it like +$$ +\begin{verbatim}[commandchars=\|\{\}] +tex mpsproof \noheaders |tdescr{MetaPost output files} +\end{verbatim} +$$ + +The +\verb|\bbox|\index{mpsproof.tex?\texttt{mpsproof.tex}!bbox?\texttt{\textbackslash + bbox}} option can be used to generate an output file that has +exactly the same page size as a figure's bounding box (\verb|\bbox| is +actually an alias for the longer +\verb|\encapsulate|\index{mpsproof.tex?\texttt{mpsproof.tex}!encapsulate?\texttt{\textbackslash + encapsulate}}). With this option only one figure can be processed +at a time, e.g., +$$ +\begin{verbatim} +pdftex mpsproof \bbox fig.1 +\end{verbatim} +$$ + +\paragraph{Alternatives} Other alternatives for previewing MetaPost +figures, which are not part of the MetaPost distribution, are the +mptopdf\index{mptopdf} bundle or the Perl script \ttindex{mpstoeps.pl}. +There is also an online compiler and viewer for MetaPost code at +\url{http://tlhiv.org/mppreview/}. + +\subsection{Debugging} +\label{debug} + +\index{debug} MetaPost inherits from \MF\index{metafont?\MF} numerous facilities for interactive debugging, most of which can only be mentioned briefly here. @@ -5232,6 +5464,241 @@ shows in the transcript file at the end of the job how many of the MetaPost interpreter's limited resources were used. \end{description} +\subsection{Importing MetaPost Graphics into External Applications} +\label{mpimport} + +MetaPost is very well suited for creating graphics that are to be +included into third-party applications, such as text documents, +presentations or web pages, because MetaPost outputs graphics in vector +formats, which can be scaled without quality degradation. However, +practice shows, that vector graphics, too, are best created with a rough +target size already in mind. Scaling a vector graphic calls for +non-proportional scaling of certain technical parameters, such as line +width, arrow size or fonts. Otherwise, with growing scale factors +scalable graphics tend to change their visual character. Additionally, +during import into a main document, they'll likely fail to match, e.g., +stroke width of the document. To circumvent this, it is advisable to +apply only small post-processing scale factors to vector graphics. The +following sections briefly discuss how to import MetaPost graphics into +documents with selected applications. + +\subsubsection{\TeX\ and Friends} +\label{teximport} +\index{TeX?\TeX!and friends} + +MetaPost graphics in the PostScript\index{PostScript} format can be +easily integrated into documents prepared with \TeX\ and friends. +MetaPost's PostScript output is a low-featured dialect of the Postscript +language, called \emph{purified EPS}\index{EPS!purified}, which can be +converted into the Portable Document Format (PDF\index{PDF}) language +on-the-fly. For that reason, external MetaPost graphics can be used on +both routes: a) using the traditional \TeX\ engine together with an +external PostScript output driver and b) using newer \TeX\ engines, like +pdf\TeX\ or its successor Lua\TeX, which contain a built-in PDF output +driver. Lua\TeX\ can additionally process embedded MetaPost code +natively, falling back to the built-in \emph{mplib} library. + +Figure~\ref{fig:teximport} shows the process of including an external +MetaPost graphic into a \TeX\ document using the PostScript route. In +the \TeX\ source a ``magic macro'' provided by the format or an external +package is used for including a graphic file. During the typesetting +stage, the macro only reads bounding box information off the PostScript +file and reserves the required space on the page via an empty box. The +file reference is passed-on to the output driver and only then, finally, +the file is embedded into the document. The freely available program +\ttindex{dvips} is used as an output driver in this +example.\footnote{The C source for \ttt{dvips} comes with the web2c + \TeX\ distribution. Similar programs are available from other + sources.} The next paragraphs give more detailed information on some +popular combinations of \TeX\ formats and engines. + +\begin{figure} +$$ \includegraphics{mpman-charts-1.mps} $$ +\caption{A diagram of the processing for a \TeX\ document embedding + MetaPost figures} +\label{fig:teximport} +\end{figure} + +\paragraph{Plain \TeX\ Format} +\label{plaintexformat} +\index{TeX?\TeX!format, plain!importing MetaPost files} + +For users of the Plain \TeX\ format and the traditional \TeX\ +engine\index{TeX?\TeX!engine} with Device Independend output +(DVI\index{dvi file?{\tt dvi} file}) the +\ttt{epsf}\index{epsf.tex?\texttt{epsf.tex}} package provides the +``magic macro'' +$$ \verb|\epsfbox{|\descr{filename}\verb|}|% +\index{epsfbox?\texttt{\string\epsfbox}} $$ +for embedding graphics, e.g., \verb|\epsfbox{fig.1}|. + +Users of the pdf\TeX\ engine\index{pdfTeX?pdf\TeX!engine} should refer +to the standalone macros of the mptopdf\index{mptopdf} bundle, which can +be found at \url{http://context.aanhet.net/mptopdf.htm}. + +With the Lua\TeX\ engine\index{LuaTeX?Lua\TeX!engine}, embedding +external graphics works the same as with pdf\TeX. Additionally, +Lua\TeX\ users can inline MetaPost code directly into Plain \TeX\ +documents. Lua\TeX\ is able to process such MetaPost code snippets, +falling back to the built-in \emph{mplib}\index{mplib?\emph{mplib}} +library. Note, \emph{mplib} doesn't support +\verb|verbatimtex|\slash\verb|btex| \ldots\ \verb|etex| constructs, +currently. Here is an example of a MetaPost graphic inlined into a +Plain \TeX\ document. For more information, please refer to the +Lua\TeX~\cite[chap.~4.8]{luatex:manual} and +\ttindex{luamplib}~\cite{hagen:luamplib} documentation. + +$$\begin{verbatim} +\input luamplib.sty +\mplibcode +beginfig(1); + ... +endfig; +\endmplibcode +\bye +\end{verbatim} +$$ + +\paragraph{\LaTeX\ Format} +\label{latexformat} +\index{LaTeX?\LaTeX!format!importing MetaPost files} +\index{TeX?\TeX!engine} +\index{pdfTeX?pdf\TeX!engine} + +For users of the \LaTeX\ format and the traditional \TeX\ engine with +Device Independent output (DVI\index{dvi file?{\tt dvi} file}) the +well-known \ttindex{graphics} (or \ttindex{graphicx}) package aids in +external graphics inclusion. The package supports different engines, +guessing the correct output driver automatically, and can handle several +graphic formats. The ``magic macro'' is +$$ \verb|\includegraphics{|\descr{filename}\verb|}|% +\index{includegraphics?\texttt{\string\includegraphics}} $$ + +In DVI output driver mode the \ttt{graphics} package assumes all files +with an unknown file extension, such as \texttt{.1} etc., to be in the +EPS\index{EPS} format. It therefore handles MetaPost files with a +numeric default file extension correctly (see~\cite{reckdahl:epslatex} +for more information). + +When using the pdf\TeX\ engine with a built-in PDF output driver, the +situation is a bit different. Only files with file extension +\texttt{.mps}\index{mps file?{\tt mps} file}\index{files!mps?{\tt mps}} +are recognized as purified EPS\index{EPS!purified} and can be converted +to PDF\index{PDF} on-the-fly. The recommended procedure for embedding +MetaPost graphics into \LaTeX\ documents compiled with pdf\TeX\ is +therefore to change MetaPost's output file name extension via +\verb|outputtemplate|\index{outputtemplate?\texttt{outputtemplate}} (see +p.~\pageref{Doutputtemplate}). In the \LaTeX\ document include the +graphic files with full name, e.g., +$$\begin{verbatim} +\includegraphics{fig-1.mps} +\end{verbatim} +$$ + +Note, the latter approach works with the \ttindex{dvips} driver, too. +Even though, again, this time \ttt{.mps} is an unknown file extension, +triggering EPS file handling in a fall-back procedure. This property of +the \ttt{graphics} package, which comes in handy for MetaPost files, is +the reason many MetaPost source files start with the line +$$\begin{verbatim} +outputtemplate := "%j-%c.mps"; +\end{verbatim} +$$ + +With the Lua\TeX\ engine\index{LuaTeX?Lua\TeX!engine}, embedding +external graphics works the same as with pdf\TeX. Additionally, +Lua\TeX\ users can inline MetaPost code directly into La\TeX\ documents. +Lua\TeX\ is able to process such MetaPost code snippets, falling back to +the built-in \emph{mplib}\index{mplib?\emph{mplib}} library. Note, +\emph{mplib} doesn't support \verb|verbatimtex|\slash\verb|btex| \ldots\ +\verb|etex| constructs, currently. Here is an example of a MetaPost +graphic inlined into a \LaTeX\ document. For more information, please +refer to the Lua\TeX~\cite[chap.~4.8]{luatex:manual} and +\ttindex{luamplib}~\cite{hagen:luamplib} documentation. +$$\begin{verbatim} +\documentclass{article} +\usepackage{luamplib} +\begin{document} +\begin{mplibcode} +beginfig(1); + ... +endfig; +\end{mplibcode} +\end{document} +\end{verbatim} +$$ + +\paragraph{Con\TeX t Format} +\label{contextformat} +\index{ConTeXt?Con\TeX t!format!importing MetaPost files} + +In Con\TeX t\index{ConTeXt?Con\TeX t} graphics support is integrated in +the kernel, covering advanced features like shading, transparency, color +spaces or image inclusion. The ``magic macro'' for embedding external +graphics is +$$ \verb|\externalfigure[|\descr{filename}\verb|]|% +\index{externalfigure?\texttt{\string\externalfigure}} $$ +% +The macro can handle numbered files as well as files with the \ttt{mps} +suffix. + +Alternatively, Con\TeX t users can inline MetaPost code in the document +source, which allows for more natural interfacing with document +properties, font support, and automatic processing~\cite{hagen:metafun}. +Here is an example of a MetaPost graphic inlined into a Con\TeX t +document. +$$\begin{verbatim}[commandchars=|\[\]] +\starttext +\startuseMPgraphic{|tdescr[name]} + ... +\stopuseMPgraphic +\useMPgraphic{|tdescr[name]} +\stoptext +\end{verbatim} +$$ + +Con\TeX t MkIV, being based on the Lua\TeX\ engine, provides a much +tighter integration of MetaPost than older versions, since it can +fall-back to the built-in \emph{mplib}\index{mplib?\emph{mplib}} +library. + +\subsubsection{Troff} +\label{troffimport} +\index{troff!importing MetaPost files} + +It is also possible to include MetaPost output in a GNU +troff\index{troff} document. The procedure is similar to +Figure~\ref{fig:teximport}: the \ttindex{grops} output processor +includes PostScript files when they are requested via troff's +\ttt{\string\X}\index{X?\texttt{\string\X}} command. The +\ttt{-mpspic}\index{mpspic?\texttt{-mpspic}} macro package provides a +command \verb|.PSPIC|\index{PSPIC?\texttt{.PSPIC}}, which does just that +when including an encapsulated PostScript file in the source code. For +instance, the troff command +$$ \hbox{\verb|.PSPIC fig.1|} $$ +includes \ttt{fig.1}, using the natural height and width of the +image as given in the file's bounding box. + +\subsubsection{Web Applications} +\label{webimport} +\index{SVG!files} + +An SVG\index{svg file?{\tt svg} file}\index{files!svg?{\tt svg}} file +\texttt{fig.svg} can be easily embedded into HTML\index{HTML} documents +with the following code snippet: +$$\begin{verbatim} +<p> + <object data="fig.svg" type="image/svg+xml" width="300" height="200"> + </object> +</p> +\end{verbatim} +$$ + +SVG files can also be imported by various interactive graphics editing +programs, for example GIMP\index{GIMP} or Inkscape\index{Inkscape}. See +Section~\ref{Dprologues} for information on font handling in SVG +graphics. + \section*{Acknowledgement} diff --git a/Master/texmf-dist/metapost/base/mfplain.mp b/Master/texmf-dist/metapost/base/mfplain.mp index 8a4f732483d..8cefaf1c289 100644 --- a/Master/texmf-dist/metapost/base/mfplain.mp +++ b/Master/texmf-dist/metapost/base/mfplain.mp @@ -581,3 +581,5 @@ let bye = end; outer end,bye; clear_pen_memory; % initialize the `savepen' mechanism mode_setup; % establish proof mode as the default numeric mode,mag; % but leave mode and mag undefined +dump; + diff --git a/Master/texmf-dist/tex/generic/metapost/mpsproof.tex b/Master/texmf-dist/tex/generic/metapost/mpsproof.tex index 1c3e0923939..30e256c9ee5 100644 --- a/Master/texmf-dist/tex/generic/metapost/mpsproof.tex +++ b/Master/texmf-dist/tex/generic/metapost/mpsproof.tex @@ -1,131 +1,135 @@ -% This is a modification of mproof.tex (as found on CTAN in
-% /systems/knuth/local/lib). The name was changed to mpsproof.tex
-% to honor the license restrictions.
-%
-%%%% These are the comments of the original mproof.tex:
-%% To produce proof sheets from MetaPost output (includes date and time):
-%% tex mproof <MetaPost output file names>
-%%%%
-%
-% In contrast, mpsproof allows either pdftex or ordinary tex to be used:
-%
-% [pdf]tex mpsproof [\noheaders] <mpost output file names>
-%
-% Examples: pdftex mpsproof figur.1 figur.2
-% tex mpsproof \noheaders figur.1 figur.2 figur.3
-%
-% With \noheaders, the filenames, datestamp and page numbers are omitted.
-% Using mpsproof one can also produce encapsulated output:
-%
-% [pdf]tex mpsproof \encapsulate <one_mpost_output_file_name>
-%
-% Examples: pdftex mpsproof \encapsulate figur.1
-% tex mpsproof \encapsulate figur.3
-%
-% "Encapsulated" means that the pdf output (or the .ps resulting from
-% dvips mpsproof.dvi), has the same "bounding box" as the MetaPost figure.
-% Actually, it produces a file whose *page size* is the same as that
-% bounding box.
-%
-% If pdftex is used and \pdfoutput = 1, the result will be a pdf file,
-% otherwise the result is a dvi file containing dvips graphics \specials.
-%
-% Limitations: the production of pdf requires supp-pdf.tex and the
-% macros therein use up TeX's memory. The specific portion that is used
-% is the "save_size". A lot of short paths is no problem, but a single
-% long path might exceed the limits on your system. On most systems the
-% limit can be increased by editing texmf.cnf.
-%
-% Placed in the public domain by Daniel H. Luecking.
-%
-% Change HISTORY:
-% 2003/04/08 -- Original release
-% 2004/05/02 -- Allow "_" in filenames.
-% 2004/05/07 -- Added the \noheaders option.
-% 2005/10/07 -- sanitize the filename (\meaning)
-% 2006/04/06 -- encapsulation added
-% 2006/06/30 -- Better documentation (the above comments)
-%
-% Test for pdf:
-\newif\ifpdf
-\pdffalse
-\ifx\pdfoutput\undefined
- \else\ifx\pdfoutput\relax
- \else\ifnum\pdfoutput<1
- \else \pdftrue
-\fi\fi\fi
-
-% Graphic inclusion macros:
-\ifpdf
- \input supp-pdf
- \def\includemps#1{\convertMPtoPDF{#1}{1}{1}}
- \def\MPSpresets{\pdfoutput=1 \pdfcompresslevel=9
- \chardef\makeMPintoPDFobject=1\relax}
- \def\encapspecial{\pdfpageheight=\ht0 \pdfpagewidth=\wd0}
-\else
- \input epsf
- \def\includemps#1{\hbox{\epsfverbosetrue\epsfbox{#1}}}
- \def\MPSpresets{}
- \def\encapspecial{\special{papersize=\the\wd0,\the\ht0}}
-\fi
-
-\let\EA\expandafter
-\def\EAEAEA{\EA\EA\EA}
-
-\def\processMPfile{%
- \MPSpresets \hsize=100in \vsize=\hsize
- \hoffset=-1in \voffset=\hoffset \topskip=0pt
- \setbox0=\vbox{\EA\includemps\EA{\theMPfile}}%
- \ifdim\wd0<1in \message{[warning: width<1in]}\fi
- \ifdim\ht0<1in \message{[warning: height<1in]}\fi
- \encapspecial \box0 \end}
-
-% To omit printing of files name, page numbers and date stamp:
-\newif\ifheaders\headerstrue
-\def\noheaders{\headersfalse}
-
-% produce output with same bounding box:
-\newif\ifencapsulate
-\def\encapsulate{\noheaders\encapsulatetrue}
-
-\def\maybeprint#1{\ifheaders#1\fi}
-\def\stripprefix#1>{}
-
-\font\textt=cmtex10 \textt
-\def\okbreak{\vfil\penalty2000\vfilneg}
-
-\def\today{\ifcase\month\or
- January\or February\or March\or April\or May\or June\or
- July\or August\or September\or October\or November\or December\fi
- \space\number\day, \number\year}
-\newcount\m \newcount\n
-\n=\time \divide\n 60 \m=-\n \multiply\m 60 \advance\m \time
-\def\hours{\twodigits\n\twodigits\m}
-\def\twodigits#1{\ifnum #1<10 0\fi \number#1}
-
-\footline={\ifheaders\sevenrm\today\ at \hours\hss\tenrm\folio\hss\fi}
-
-% DHL - \convertMPtoPDF redefines \\, so we replace it with \?.
-\def\endit{\?}
-
-\def\munch#1 {\def\theMPfile{#1}%
- \ifx\theMPfile\endit
- \EA\endit
- \else
- % sanitize the filename:
- \edef\theMPfile{\EA\stripprefix\meaning\theMPfile}%
- \ifencapsulate \EAEAEA\processMPfile
- \else \leavevmode\maybeprint\theMPfile
- $$\EA\includemps\EA{\theMPfile}$$\par\okbreak%
- \fi
- \fi\munch}
-
-\def\?{\let\munch=\relax\def\?{}}
-
-\everypar{\everypar{}\relax\munchline}
-
-\parindent 0pt
-% We need the space before \? to ensure that it doesn't end up
-% in a filename.
-\obeylines\def\munchline#1
-{\catcode`\^^M=5 \munch#1 \? \vfill\end}%
+% This is a modification of mproof.tex (as found on CTAN in +% /systems/knuth/local/lib). The name was changed to mpsproof.tex +% to honor the license restrictions. +% +%%%% These are the comments of the original mproof.tex: +%% To produce proof sheets from MetaPost output (includes date and time): +%% tex mproof <MetaPost output file names> +%%%% +% +% In contrast, mpsproof allows either pdftex or ordinary tex to be used: +% +% [pdf]tex mpsproof [\noheaders] <mpost output file names> +% +% Examples: pdftex mpsproof figur.1 figur.2 +% tex mpsproof \noheaders figur.1 figur.2 figur.3 +% +% With \noheaders, the filenames, datestamp and page numbers are omitted. +% Using mpsproof one can also produce encapsulated output: +% +% [pdf]tex mpsproof \encapsulate <one_mpost_output_file_name> +% +% Examples: pdftex mpsproof \encapsulate figur.1 +% tex mpsproof \encapsulate figur.3 +% +% "Encapsulated" means that the pdf output (or the .ps resulting from +% dvips mpsproof.dvi), has the same "bounding box" as the MetaPost figure. +% Actually, it produces a file whose *page size* is the same as that +% bounding box. The shorthand \bbox can be used in place of +% \encapsulate. +% +% If pdftex is used and \pdfoutput = 1, the result will be a pdf file, +% otherwise the result is a dvi file containing dvips graphics \specials. +% +% Limitations: the production of pdf requires supp-pdf.tex and the +% macros therein use up TeX's memory. The specific portion that is used +% is the "save_size". A lot of short paths is no problem, but a single +% long path might exceed the limits on your system. On most systems the +% limit can be increased by editing texmf.cnf. +% +% Placed in the public domain by Daniel H. Luecking. +% +% Change HISTORY: +% 2003/04/08 -- Original release +% 2004/05/02 -- Allow "_" in filenames. +% 2004/05/07 -- Added the \noheaders option. +% 2005/10/07 -- Sanitize the filename (\meaning) +% 2006/04/06 -- Encapsulation added +% 2006/06/30 -- Better documentation (the above comments) +% 2008/11/11 -- Make dvi output identical to mproof.tex +% 2010/10/13 -- Added \bbox (short for \encapsulate) +% +% Test for pdf: +\newif\ifpdf +\pdffalse +\ifx\pdfoutput\undefined + \else\ifx\pdfoutput\relax + \else\ifnum\pdfoutput<1 + \else \pdftrue +\fi\fi\fi + +% Graphic inclusion macros: +\ifpdf + \input supp-pdf + \def\includemps#1{\convertMPtoPDF{#1}{1}{1}} + \def\MPSpresets{\pdfoutput=1 \pdfcompresslevel=9 + \chardef\makeMPintoPDFobject=1\relax} + \def\encapspecial{\pdfpageheight=\ht0 \pdfpagewidth=\wd0} +\else + \input epsf + \def\includemps#1{\epsfverbosetrue\epsfbox{#1}} + \def\MPSpresets{} + \def\encapspecial{\special{papersize=\the\wd0,\the\ht0}} +\fi + +\let\EA\expandafter +\def\EAEAEA{\EA\EA\EA} + +\def\processMPfile{% + \MPSpresets \hsize=100in \vsize=\hsize + \hoffset=-1in \voffset=\hoffset \topskip=0pt + \setbox0=\vbox{\hbox{\EA\includemps\EA{\theMPfile}}}% + \ifdim\wd0<1in \message{[warning: width<1in]}\fi + \ifdim\ht0<1in \message{[warning: height<1in]}\fi + \encapspecial \box0 \end} + +% To omit printing of files name, page numbers and date stamp: +\newif\ifheaders\headerstrue +\def\noheaders{\headersfalse} + +% produce output with same bounding box: +\newif\ifencapsulate +\def\encapsulate{\noheaders\encapsulatetrue} +\let\bbox\encapsulate + +\def\maybeprint#1{\ifheaders#1\fi} +\def\stripprefix#1>{} + +\font\textt=cmtex10 \textt +\def\okbreak{\vfil\penalty2000\vfilneg} + +\def\today{\ifcase\month\or + January\or February\or March\or April\or May\or June\or + July\or August\or September\or October\or November\or December\fi + \space\number\day, \number\year} +\newcount\m \newcount\n +\n=\time \divide\n 60 \m=-\n \multiply\m 60 \advance\m \time +\def\hours{\twodigits\n\twodigits\m} +\def\twodigits#1{\ifnum #1<10 0\fi \number#1} + +\footline={\ifheaders\sevenrm\today\ at \hours\hss\tenrm\folio\hss\fi} + +% DHL - \convertMPtoPDF redefines \\, so we replace it with \?. +\def\endit{\?} + +\def\munch#1 {\def\theMPfile{#1}% + \ifx\theMPfile\endit + \EA\endit + \else + % sanitize the filename: + \edef\theMPfile{\EA\stripprefix\meaning\theMPfile}% + \ifencapsulate \EAEAEA\processMPfile + \else \leavevmode\maybeprint\theMPfile + $$\EA\includemps\EA{\theMPfile}$$\par\okbreak% + \fi + \fi\munch} + +\def\?{\let\munch=\relax\def\?{}} + +% Encapsulation requires no indentation. +\everypar{{\ifencapsulate\setbox0=\lastbox\fi}\everypar{}\relax\munchline} + +% We need the space before \? to ensure that it doesn't end up +% in a filename. +\obeylines\def\munchline#1 +{\catcode`\^^M=5 \munch#1 \? \vfill\end}% diff --git a/Master/tlpkg/tlpsrc/metapost.tlpsrc b/Master/tlpkg/tlpsrc/metapost.tlpsrc index c3bfacd9dfb..879f070d6d9 100644 --- a/Master/tlpkg/tlpsrc/metapost.tlpsrc +++ b/Master/tlpkg/tlpsrc/metapost.tlpsrc @@ -1,6 +1,4 @@ depend kpathsea -execute AddFormat name=mpost engine=mpost options=mpost.mp -execute AddFormat name=mfplain engine=mpost options=mfplain.ini srcpattern f ignore binpattern f bin/${ARCH}/dvitomp binpattern f bin/${ARCH}/mfplain |