Fossil

Artifact [abbb16e518]
Login

Artifact [abbb16e518]

Artifact abbb16e518199b702367d1bc64a164b7a6641d22878fcc9524228ce4a2d9b0f1:


/* This file is automatically generated by Lemon from input grammar
** source file "pikchr.y". */
/*
** Zero-Clause BSD license:
**
** Copyright (C) 2020-09-01 by D. Richard Hipp <drh@sqlite.org>
**
** Permission to use, copy, modify, and/or distribute this software for
** any purpose with or without fee is hereby granted.
**
****************************************************************************
**
** This software translates a PIC-inspired diagram language into SVG.
**
** PIKCHR (pronounced like "picture") is *mostly* backwards compatible
** with legacy PIC, though some features of legacy PIC are removed 
** (for example, the "sh" command is removed for security) and
** many enhancements are added.
**
** PIKCHR is designed for use in an internet facing web environment.
** In particular, PIKCHR is designed to safely generate benign SVG from
** source text that provided by a hostile agent. 
**
** This code was originally written by D. Richard Hipp using documentation
** from prior PIC implementations but without reference to prior code.
** All of the code in this project is original.
**
** This file implements a C-language subroutine that accepts a string
** of PIKCHR language text and generates a second string of SVG output that
** renders the drawing defined by the input.  Space to hold the returned
** string is obtained from malloc() and should be freed by the caller.
** NULL might be returned if there is a memory allocation error.
**
** If there are errors in the PIKCHR input, the output will consist of an
** error message and the original PIKCHR input text (inside of <pre>...</pre>).
**
** The subroutine implemented by this file is intended to be stand-alone.
** It uses no external routines other than routines commonly found in
** the standard C library.
**
****************************************************************************
** COMPILING:
**
** The original source text is a mixture of C99 and "Lemon"
** (See https://sqlite.org/src/file/doc/lemon.html).  Lemon is an LALR(1)
** parser generator program, similar to Yacc.  The grammar of the
** input language is specified in Lemon.  C-code is attached.  Lemon
** runs to generate a single output file ("pikchr.c") which is then
** compiled to generate the Pikchr library.  This header comment is
** preserved in the Lemon output, so you might be reading this in either
** the generated "pikchr.c" file that is output by Lemon, or in the
** "pikchr.y" source file that is input into Lemon.  If you make changes,
** you should change the input source file "pikchr.y", not the
** Lemon-generated output file.
**
** Basic compilation steps:
**
**      lemon pikchr.y
**      cc pikchr.c -o pikchr.o
**
** Add -DPIKCHR_SHELL to add a main() routine that reads input files
** and sends them through Pikchr, for testing.  Add -DPIKCHR_FUZZ for
** -fsanitizer=fuzzer testing.
** 
****************************************************************************
** IMPLEMENTATION NOTES (for people who want to understand the internal
** operation of this software, perhaps to extend the code or to fix bugs):
**
** Each call to pikchr() uses a single instance of the Pik structure to
** track its internal state.  The Pik structure lives for the duration
** of the pikchr() call.
**
** The input is a sequence of objects or "elements".  Each element is
** parsed into a PElem object.  These are stored on an extensible array
** called PEList.  All parameters to each PElem are computed as the
** object is parsed.  (Hence, the parameters to a PElem may only refer
** to prior elements.) Once the PElem is completely assembled, it is
** added to the end of a PEList and never changes thereafter - except,
** PElem objects that are part of a "[...]" block might have their
** absolute position shifted when the outer [...] block is positioned.
** But apart from this repositioning, PElem objects are unchanged once
** they are added to the list. The order of elements on a PEList does
** not change.
**
** After all input has been parsed, the top-level PEList is walked to
** generate output.  Sub-lists resulting from [...] blocks are scanned
** as they are encountered.  All input must be collected and parsed ahead
** of output generation because the size and position of elements must be
** known in order to compute a bounding box on the output.
**
** Each PElem is on a "layer".  (The common case is that all PElem's are
** on a single layer, but multiple layers are possible.)  A separate pass
** is made through the list for each layer.
**
** After all output is generated, the Pik object and all the PEList
** and PElem objects are deallocated and the generated output string is
** returned.  Upon any error, the Pik.nErr flag is set, processing quickly
** stops, and the stack unwinds.  No attempt is made to continue reading
** input after an error.
**
** Most elements begin with a class name like "box" or "arrow" or "move".
** There is a class named "text" which is used for elements that begin
** with a string literal.  You can also specify the "text" class.
** A Sublist ("[...]") is a single object that contains a pointer to
** its subelements, all gathered onto a separate PEList object.
**
** Variables go into PVar objects that form a linked list.
**
** Each PElem has zero or one names.  Input constructs that attempt
** to assign a new name from an older name, for example:
**
**      Abc:  Abc + (0.5cm, 0)
**
** Statements like these generate a new "noop" object at the specified
** place and with the given name. As place-names are searched by scanning
** the list in reverse order, this has the effect of overriding the "Abc"
** name when referenced by subsequent objects.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <assert.h>
#define count(X) (sizeof(X)/sizeof(X[0]))
#ifndef M_PI
# define M_PI 3.1415926535897932385
#endif

/* Tag intentionally unused parameters with this macro to prevent
** compiler warnings with -Wextra */
#define UNUSED_PARAMETER(X)  (void)(X)

typedef struct Pik Pik;          /* Complete parsing context */
typedef struct PToken PToken;    /* A single token */
typedef struct PElem PElem;      /* A single diagram object or "element" */
typedef struct PEList PEList;    /* A list of elements */
typedef struct PClass PClass;    /* Description of elements types */
typedef double PNum;             /* Numeric value */
typedef struct PRel PRel;        /* Absolute or percentage value */
typedef struct PPoint PPoint;    /* A position in 2-D space */
typedef struct PVar PVar;        /* script-defined variable */
typedef struct PBox PBox;        /* A bounding box */

/* Compass points */
#define CP_N      1
#define CP_NE     2
#define CP_E      3
#define CP_SE     4
#define CP_S      5
#define CP_SW     6
#define CP_W      7
#define CP_NW     8
#define CP_C      9   /* .center or .c */
#define CP_END   10   /* .end */
#define CP_START 11   /* .start */

/* Heading angles corresponding to compass points */
static const PNum pik_hdg_angle[] = {
/* none  */   0.0,
  /* N  */    0.0,
  /* NE */   45.0,
  /* E  */   90.0,
  /* SE */  135.0,
  /* S  */  180.0,
  /* SW */  225.0,
  /* W  */  270.0,
  /* NW */  315.0,
  /* C  */    0.0,
};

/* Built-in functions */
#define FN_ABS    0
#define FN_COS    1
#define FN_INT    2
#define FN_MAX    3
#define FN_MIN    4
#define FN_SIN    5
#define FN_SQRT   6

/* Text position and style flags.  Stored in PToken.eCode so limited
** to 15 bits. */
#define TP_LJUST   0x0001  /* left justify......          */
#define TP_RJUST   0x0002  /*            ...Right justify */
#define TP_JMASK   0x0003  /* Mask for justification bits */
#define TP_ABOVE2  0x0004  /* Position text way above PElem.ptAt */
#define TP_ABOVE   0x0008  /* Position text above PElem.ptAt */
#define TP_CENTER  0x0010  /* On the line */
#define TP_BELOW   0x0020  /* Position text below PElem.ptAt */
#define TP_BELOW2  0x0040  /* Position text way below PElem.ptAt */
#define TP_VMASK   0x007c  /* Mask for text positioning flags */
#define TP_BIG     0x0100  /* Larger font */
#define TP_SMALL   0x0200  /* Smaller font */
#define TP_XTRA    0x0400  /* Amplify TP_BIG or TP_SMALL */
#define TP_SZMASK  0x0700  /* Font size mask */
#define TP_ITALIC  0x1000  /* Italic font */
#define TP_BOLD    0x2000  /* Bold font */
#define TP_FMASK   0x3000  /* Mask for font style */
#define TP_ALIGN   0x4000  /* Rotate to align with the line */

/* An object to hold a position in 2-D space */
struct PPoint {
  PNum x, y;             /* X and Y coordinates */
};

/* A bounding box */
struct PBox {
  PPoint sw, ne;         /* Lower-left and top-right corners */
};

/* An Absolute or a relative distance.  The absolute distance
** is stored in rAbs and the relative distance is stored in rRel.
** Usually, one or the other will be 0.0.  When using a PRel to
** update an existing value, the computation is usually something
** like this:
**
**          value = PRel.rAbs + value*PRel.rRel
**
*/
struct PRel {
  PNum rAbs;            /* Absolute value */
  PNum rRel;            /* Value relative to current value */
};

/* A variable created by the ID = EXPR construct of the PIKCHR script 
**
** PIKCHR (and PIC) scripts do not use many varaibles, so it is reasonable
** to store them all on a linked list.
*/
struct PVar {
  const char *zName;       /* Name of the variable */
  PNum val;                /* Value of the variable */
  PVar *pNext;             /* Next variable in a list of them all */
};

/* A single token in the parser input stream
*/
struct PToken {
  const char *z;             /* Pointer to the token text */
  unsigned int n;            /* Length of the token in bytes */
  short int eCode;           /* Auxiliary code */
  unsigned char eType;       /* The numeric parser code */
  unsigned char eEdge;       /* Corner value for corner keywords */
};

/* Return negative, zero, or positive if pToken is less than, equal to
** or greater than the zero-terminated string z[]
*/
static int pik_token_eq(PToken *pToken, const char *z){
  int c = strncmp(pToken->z,z,pToken->n);
  if( c==0 && z[pToken->n]!=0 ) c = -1;
  return c;
}

/* Extra token types not generated by LEMON but needed by the
** tokenizer
*/
#define T_WHITESPACE 254     /* Whitespace of comments */
#define T_ERROR      255     /* Any text that is not a valid token */

/* Directions of movement */
#define DIR_RIGHT     0
#define DIR_DOWN      1
#define DIR_LEFT      2
#define DIR_UP        3
#define ValidDir(X)     ((X)>=0 && (X)<=3)
#define IsUpDown(X)     (((X)&1)==1)
#define IsLeftRight(X)  (((X)&1)==0)

/* Bitmask for the various attributes for PElem.  These bits are
** collected in PElem.mProp and PElem.mCalc to check for constraint
** errors. */
#define A_WIDTH         0x0001
#define A_HEIGHT        0x0002
#define A_RADIUS        0x0004
#define A_THICKNESS     0x0008
#define A_DASHED        0x0010 /* Includes "dotted" */
#define A_FILL          0x0020
#define A_COLOR         0x0040
#define A_ARROW         0x0080
#define A_FROM          0x0100
#define A_CW            0x0200
#define A_AT            0x0400
#define A_TO            0x0800 /* one or more movement attributes */


/* A single element */
struct PElem {
  const PClass *type;      /* Element type */
  PToken errTok;           /* Reference token for error messages */
  PPoint ptAt;             /* Reference point for the object */
  PPoint ptEnter, ptExit;  /* Entry and exit points */
  PEList *pSublist;        /* Substructure for [...] elements */
  char *zName;             /* Name assigned to this element */
  PNum w;                  /* "width" property */
  PNum h;                  /* "height" property */
  PNum rad;                /* "radius" property */
  PNum sw;                 /* "thickness" property. (Mnemonic: "stroke width")*/
  PNum dotted;             /* "dotted" property.   <=0.0 for off */
  PNum dashed;             /* "dashed" property.   <=0.0 for off */
  PNum fill;               /* "fill" property.  Negative for off */
  PNum color;              /* "color" property */
  PPoint with;             /* Position constraint from WITH clause */
  char eWith;              /* Type of heading point on WITH clause */
  char cw;                 /* True for clockwise arc */
  char larrow;             /* Arrow at beginning (<- or <->) */
  char rarrow;             /* Arrow at end  (-> or <->) */
  char bClose;             /* True if "close" is seen */
  char bChop;              /* True if "chop" is seen */
  unsigned char nTxt;      /* Number of text values */
  unsigned mProp;          /* Masks of properties set so far */
  unsigned mCalc;          /* Values computed from other constraints */
  PToken aTxt[5];          /* Text with .eCode holding TP flags */
  int iLayer;              /* Rendering order */
  int inDir, outDir;       /* Entry and exit directions */
  int nPath;               /* Number of path points */
  PPoint *aPath;           /* Array of path points */
  PBox bbox;               /* Bounding box */
};

/* A list of elements */
struct PEList {
  int n;          /* Number of elements in the list */
  int nAlloc;     /* Allocated slots in a[] */
  PElem **a;      /* Pointers to individual elements */
};

/* Each call to the pikchr() subroutine uses an instance of the following
** object to pass around context to all of its subroutines.
*/
struct Pik {
  unsigned nErr;           /* Number of errors seen */
  const char *zIn;         /* Input PIKCHR-language text.  zero-terminated */
  unsigned int nIn;        /* Number of bytes in zIn */
  char *zOut;              /* Result accumulates here */
  unsigned int nOut;       /* Bytes written to zOut[] so far */
  unsigned int nOutAlloc;  /* Space allocated to zOut[] */
  unsigned char eDir;      /* Current direction */
  unsigned int mFlags;     /* Flags passed to pikchr() */
  PElem *cur;              /* Element under construction */
  PEList *list;            /* Element list under construction */
  PVar *pVar;              /* Application-defined variables */
  PBox bbox;               /* Bounding box around all elements */
                           /* Cache of layout values.  <=0.0 for unknown... */
  PNum rScale;                 /* Multiply to convert inches to pixels */
  PNum fontScale;              /* Scale fonts by this percent */
  PNum charWidth;              /* Character width */
  PNum charHeight;             /* Character height */
  PNum wArrow;                 /* Width of arrowhead at the fat end */
  PNum hArrow;                 /* Ht of arrowhead - dist from tip to fat end */
  char bLayoutVars;            /* True if cache is valid */
  char thenFlag;           /* True if "then" seen */
  char samePath;           /* aTPath copied by "same" */
  const char *zClass;      /* Class name for the <svg> */
  int wSVG, hSVG;          /* Width and height of the <svg> */
  /* Paths for lines are constructed here first, then transferred into
  ** the PElem object at the end: */
  int nTPath;              /* Number of entries on aTPath[] */
  int mTPath;              /* For last entry, 1: x set,  2: y set */
  PPoint aTPath[1000];     /* Path under construction */
};


/*
** Flag values for Pik.mFlags (to be picked up by makeheaders on systems
** that use makeheaders.
*/
#undef INTERFACE
#define INTERFACE 1
#if INTERFACE
#define PIKCHR_INCLUDE_SOURCE 0x0001  /* Include Pikchr src in SVG output */
#endif /* INTERFACE */

/*
** The behavior of an object class is defined by an instance of
** this structure. This is the "virtual method" table.
*/
struct PClass {
  const char *zName;                     /* Name of class */
  char isLine;                           /* True if a line class */
  char eJust;                            /* Use box-style text justification */
  void (*xInit)(Pik*,PElem*);              /* Initializer */
  void (*xNumProp)(Pik*,PElem*,PToken*);   /* Value change notification */
  void (*xCheck)(Pik*,PElem*);             /* Checks to after parsing */
  PPoint (*xChop)(Pik*,PElem*,PPoint*);    /* Chopper */
  PPoint (*xOffset)(Pik*,PElem*,int);      /* Offset from .c to edge point */
  void (*xFit)(Pik*,PElem*,PNum w,PNum h); /* Size to fit text */
  void (*xRender)(Pik*,PElem*);            /* Render */
};


/* Forward declarations */
static void pik_append(Pik*, const char*,int);
static void pik_append_text(Pik*,const char*,int,int);
static void pik_append_num(Pik*,const char*,PNum);
static void pik_append_point(Pik*,const char*,PPoint*);
static void pik_append_x(Pik*,const char*,PNum,const char*);
static void pik_append_y(Pik*,const char*,PNum,const char*);
static void pik_append_xy(Pik*,const char*,PNum,PNum);
static void pik_append_dis(Pik*,const char*,PNum,const char*);
static void pik_append_arc(Pik*,PNum,PNum,PNum,PNum);
static void pik_append_clr(Pik*,const char*,PNum,const char*);
static void pik_append_style(Pik*,PElem*,int);
static void pik_append_txt(Pik*,PElem*, PBox*);
static void pik_draw_arrowhead(Pik*,PPoint*pFrom,PPoint*pTo,PElem*);
static void pik_chop(PPoint*pFrom,PPoint*pTo,PNum);
static void pik_error(Pik*,PToken*,const char*);
static void pik_elist_free(Pik*,PEList*);
static void pik_elem_free(Pik*,PElem*);
static void pik_render(Pik*,PEList*);
static PEList *pik_elist_append(Pik*,PEList*,PElem*);
static PElem *pik_elem_new(Pik*,PToken*,PToken*,PEList*);
static void pik_set_direction(Pik*,int);
static void pik_elem_setname(Pik*,PElem*,PToken*);
static void pik_set_var(Pik*,PToken*,PNum,PToken*);
static PNum pik_value(Pik*,const char*,int,int*);
static PNum pik_lookup_color(Pik*,PToken*);
static PNum pik_get_var(Pik*,PToken*);
static PNum pik_atof(PToken*);
static void pik_after_adding_attributes(Pik*,PElem*);
static void pik_elem_move(PElem*,PNum dx, PNum dy);
static void pik_elist_move(PEList*,PNum dx, PNum dy);
static void pik_set_numprop(Pik*,PToken*,PRel*);
static void pik_set_clrprop(Pik*,PToken*,PNum);
static void pik_set_dashed(Pik*,PToken*,PNum*);
static void pik_then(Pik*,PToken*,PElem*);
static void pik_add_direction(Pik*,PToken*,PRel*);
static void pik_move_hdg(Pik*,PRel*,PToken*,PNum,PToken*,PToken*);
static void pik_evenwith(Pik*,PToken*,PPoint*);
static void pik_set_from(Pik*,PElem*,PToken*,PPoint*);
static void pik_add_to(Pik*,PElem*,PToken*,PPoint*);
static void pik_close_path(Pik*,PToken*);
static void pik_set_at(Pik*,PToken*,PPoint*,PToken*);
static short int pik_nth_value(Pik*,PToken*);
static PElem *pik_find_nth(Pik*,PElem*,PToken*);
static PElem *pik_find_byname(Pik*,PElem*,PToken*);
static PPoint pik_place_of_elem(Pik*,PElem*,PToken*);
static int pik_bbox_isempty(PBox*);
static void pik_bbox_init(PBox*);
static void pik_bbox_addbox(PBox*,PBox*);
static void pik_bbox_add_xy(PBox*,PNum,PNum);
static void pik_bbox_addellipse(PBox*,PNum x,PNum y,PNum rx,PNum ry);
static void pik_add_txt(Pik*,PToken*,int);
static int pik_text_length(const PToken *pToken);
static void pik_size_to_fit(Pik*,PToken*);
static int pik_text_position(int,PToken*);
static PNum pik_property_of(PElem*,PToken*);
static PNum pik_func(Pik*,PToken*,PNum,PNum);
static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2);
static PPoint pik_position_at_angle(PNum dist, PNum r, PPoint pt);
static PPoint pik_position_at_hdg(PNum dist, PToken *pD, PPoint pt);
static void pik_same(Pik *p, PElem*, PToken*);
static PPoint pik_nth_vertex(Pik *p, PToken *pNth, PToken *pErr, PElem *pElem);
static PToken pik_next_semantic_token(PToken *pThis);
static void pik_compute_layout_settings(Pik*);
static void pik_behind(Pik*,PElem*);
static PElem *pik_assert(Pik*,PNum,PToken*,PNum);
static PElem *pik_place_assert(Pik*,PPoint*,PToken*,PPoint*);


#line 487 "pikchr.c"
/**************** End of %include directives **********************************/
/* These constants specify the various numeric values for terminal symbols.
***************** Begin token definitions *************************************/
#ifndef T_ID
#define T_ID                              1
#define T_EDGEPT                          2
#define T_OF                              3
#define T_PLUS                            4
#define T_MINUS                           5
#define T_STAR                            6
#define T_SLASH                           7
#define T_PERCENT                         8
#define T_UMINUS                          9
#define T_EOL                            10
#define T_ASSIGN                         11
#define T_PLACENAME                      12
#define T_COLON                          13
#define T_ASSERT                         14
#define T_LP                             15
#define T_EQ                             16
#define T_RP                             17
#define T_FILL                           18
#define T_COLOR                          19
#define T_THICKNESS                      20
#define T_PRINT                          21
#define T_STRING                         22
#define T_COMMA                          23
#define T_CLASSNAME                      24
#define T_LB                             25
#define T_RB                             26
#define T_UP                             27
#define T_DOWN                           28
#define T_LEFT                           29
#define T_RIGHT                          30
#define T_CLOSE                          31
#define T_CHOP                           32
#define T_FROM                           33
#define T_TO                             34
#define T_THEN                           35
#define T_HEADING                        36
#define T_GO                             37
#define T_AT                             38
#define T_WITH                           39
#define T_SAME                           40
#define T_AS                             41
#define T_FIT                            42
#define T_BEHIND                         43
#define T_UNTIL                          44
#define T_EVEN                           45
#define T_DOT_E                          46
#define T_HEIGHT                         47
#define T_WIDTH                          48
#define T_RADIUS                         49
#define T_DIAMETER                       50
#define T_DOTTED                         51
#define T_DASHED                         52
#define T_CW                             53
#define T_CCW                            54
#define T_LARROW                         55
#define T_RARROW                         56
#define T_LRARROW                        57
#define T_INVIS                          58
#define T_THICK                          59
#define T_THIN                           60
#define T_CENTER                         61
#define T_LJUST                          62
#define T_RJUST                          63
#define T_ABOVE                          64
#define T_BELOW                          65
#define T_ITALIC                         66
#define T_BOLD                           67
#define T_ALIGNED                        68
#define T_BIG                            69
#define T_SMALL                          70
#define T_AND                            71
#define T_LT                             72
#define T_GT                             73
#define T_ON                             74
#define T_WAY                            75
#define T_BETWEEN                        76
#define T_THE                            77
#define T_NTH                            78
#define T_VERTEX                         79
#define T_TOP                            80
#define T_BOTTOM                         81
#define T_START                          82
#define T_END                            83
#define T_IN                             84
#define T_DOT_U                          85
#define T_LAST                           86
#define T_NUMBER                         87
#define T_FUNC1                          88
#define T_FUNC2                          89
#define T_DOT_XY                         90
#define T_X                              91
#define T_Y                              92
#define T_DOT_L                          93
#endif
/**************** End token definitions ***************************************/

/* The next sections is a series of control #defines.
** various aspects of the generated parser.
**    YYCODETYPE         is the data type used to store the integer codes
**                       that represent terminal and non-terminal symbols.
**                       "unsigned char" is used if there are fewer than
**                       256 symbols.  Larger types otherwise.
**    YYNOCODE           is a number of type YYCODETYPE that is not used for
**                       any terminal or nonterminal symbol.
**    YYFALLBACK         If defined, this indicates that one or more tokens
**                       (also known as: "terminal symbols") have fall-back
**                       values which should be used if the original symbol
**                       would not parse.  This permits keywords to sometimes
**                       be used as identifiers, for example.
**    YYACTIONTYPE       is the data type used for "action codes" - numbers
**                       that indicate what to do in response to the next
**                       token.
**    pik_parserTOKENTYPE     is the data type used for minor type for terminal
**                       symbols.  Background: A "minor type" is a semantic
**                       value associated with a terminal or non-terminal
**                       symbols.  For example, for an "ID" terminal symbol,
**                       the minor type might be the name of the identifier.
**                       Each non-terminal can have a different minor type.
**                       Terminal symbols all have the same minor type, though.
**                       This macros defines the minor type for terminal 
**                       symbols.
**    YYMINORTYPE        is the data type used for all minor types.
**                       This is typically a union of many types, one of
**                       which is pik_parserTOKENTYPE.  The entry in the union
**                       for terminal symbols is called "yy0".
**    YYSTACKDEPTH       is the maximum depth of the parser's stack.  If
**                       zero the stack is dynamically sized using realloc()
**    pik_parserARG_SDECL     A static variable declaration for the %extra_argument
**    pik_parserARG_PDECL     A parameter declaration for the %extra_argument
**    pik_parserARG_PARAM     Code to pass %extra_argument as a subroutine parameter
**    pik_parserARG_STORE     Code to store %extra_argument into yypParser
**    pik_parserARG_FETCH     Code to extract %extra_argument from yypParser
**    pik_parserCTX_*         As pik_parserARG_ except for %extra_context
**    YYERRORSYMBOL      is the code number of the error symbol.  If not
**                       defined, then do no error processing.
**    YYNSTATE           the combined number of states.
**    YYNRULE            the number of rules in the grammar
**    YYNTOKEN           Number of terminal symbols
**    YY_MAX_SHIFT       Maximum value for shift actions
**    YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions
**    YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions
**    YY_ERROR_ACTION    The yy_action[] code for syntax error
**    YY_ACCEPT_ACTION   The yy_action[] code for accept
**    YY_NO_ACTION       The yy_action[] code for no-op
**    YY_MIN_REDUCE      Minimum value for reduce actions
**    YY_MAX_REDUCE      Maximum value for reduce actions
*/
#ifndef INTERFACE
# define INTERFACE 1
#endif
/************* Begin control #defines *****************************************/
#define YYCODETYPE unsigned char
#define YYNOCODE 130
#define YYACTIONTYPE unsigned short int
#define pik_parserTOKENTYPE PToken
typedef union {
  int yyinit;
  pik_parserTOKENTYPE yy0;
  PRel yy60;
  PEList* yy72;
  PNum yy73;
  int yy74;
  PPoint yy139;
  PElem* yy254;
} YYMINORTYPE;
#ifndef YYSTACKDEPTH
#define YYSTACKDEPTH 100
#endif
#define pik_parserARG_SDECL
#define pik_parserARG_PDECL
#define pik_parserARG_PARAM
#define pik_parserARG_FETCH
#define pik_parserARG_STORE
#define pik_parserCTX_SDECL Pik *p;
#define pik_parserCTX_PDECL ,Pik *p
#define pik_parserCTX_PARAM ,p
#define pik_parserCTX_FETCH Pik *p=yypParser->p;
#define pik_parserCTX_STORE yypParser->p=p;
#define YYFALLBACK 1
#define YYNSTATE             157
#define YYNRULE              151
#define YYNRULE_WITH_ACTION  111
#define YYNTOKEN             94
#define YY_MAX_SHIFT         156
#define YY_MIN_SHIFTREDUCE   276
#define YY_MAX_SHIFTREDUCE   426
#define YY_ERROR_ACTION      427
#define YY_ACCEPT_ACTION     428
#define YY_NO_ACTION         429
#define YY_MIN_REDUCE        430
#define YY_MAX_REDUCE        580
/************* End control #defines *******************************************/
#define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])))

/* Define the yytestcase() macro to be a no-op if is not already defined
** otherwise.
**
** Applications can choose to define yytestcase() in the %include section
** to a macro that can assist in verifying code coverage.  For production
** code the yytestcase() macro should be turned off.  But it is useful
** for testing.
*/
#ifndef yytestcase
# define yytestcase(X)
#endif


/* Next are the tables used to determine what action to take based on the
** current state and lookahead token.  These tables are used to implement
** functions that take a state number and lookahead value and return an
** action integer.  
**
** Suppose the action integer is N.  Then the action is determined as
** follows
**
**   0 <= N <= YY_MAX_SHIFT             Shift N.  That is, push the lookahead
**                                      token onto the stack and goto state N.
**
**   N between YY_MIN_SHIFTREDUCE       Shift to an arbitrary state then
**     and YY_MAX_SHIFTREDUCE           reduce by rule N-YY_MIN_SHIFTREDUCE.
**
**   N == YY_ERROR_ACTION               A syntax error has occurred.
**
**   N == YY_ACCEPT_ACTION              The parser accepts its input.
**
**   N == YY_NO_ACTION                  No such action.  Denotes unused
**                                      slots in the yy_action[] table.
**
**   N between YY_MIN_REDUCE            Reduce by rule N-YY_MIN_REDUCE
**     and YY_MAX_REDUCE
**
** The action table is constructed as a single large table named yy_action[].
** Given state S and lookahead X, the action is computed as either:
**
**    (A)   N = yy_action[ yy_shift_ofst[S] + X ]
**    (B)   N = yy_default[S]
**
** The (A) formula is preferred.  The B formula is used instead if
** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X.
**
** The formulas above are for computing the action when the lookahead is
** a terminal symbol.  If the lookahead is a non-terminal (as occurs after
** a reduce action) then the yy_reduce_ofst[] array is used in place of
** the yy_shift_ofst[] array.
**
** The following are the tables generated in this section:
**
**  yy_action[]        A single table containing all actions.
**  yy_lookahead[]     A table containing the lookahead for each entry in
**                     yy_action.  Used to detect hash collisions.
**  yy_shift_ofst[]    For each state, the offset into yy_action for
**                     shifting terminals.
**  yy_reduce_ofst[]   For each state, the offset into yy_action for
**                     shifting non-terminals after a reduce.
**  yy_default[]       Default action for each state.
**
*********** Begin parsing tables **********************************************/
#define YY_ACTTAB_COUNT (1159)
static const YYACTIONTYPE yy_action[] = {
 /*     0 */   554,  430,   21,  436,   25,   71,  126,  142,   50,   46,
 /*    10 */   554,   33,  437,  111,  118,  154,  117,  125,  411,  412,
 /*    20 */   327,  538,   78,  512,  539,  540,  554,   62,   61,   60,
 /*    30 */    59,  310,  311,    6,    5,   30,  143,   29,    4,   69,
 /*    40 */   124,   24,  323,   64,  558,  294,   45,  327,  327,  327,
 /*    50 */   327,  409,  410,  328,  329,  330,  331,  332,  333,  334,
 /*    60 */   335,  457,   62,   61,   60,   59,  119,  431,  438,   25,
 /*    70 */    33,  457,   67,  434,   27,  286,  478,  154,  117,  411,
 /*    80 */   412,  327,   28,   78,  156,  344,  298,  457,  428,   23,
 /*    90 */   318,    8,  310,  311,    6,    5,   30,   80,   29,    4,
 /*   100 */    69,  124,   81,  323,   64,  475,  154,  117,  327,  327,
 /*   110 */   327,  327,  409,  410,  328,  329,  330,  331,  332,  333,
 /*   120 */   334,  335,  379,  419,   41,   57,   58,   73,   62,   61,
 /*   130 */    60,   59,   32,  362,  382,  383,   20,  287,  379,  419,
 /*   140 */    40,   57,   58,   77,  316,  296,   76,   60,   59,  362,
 /*   150 */   425,  424,   20,   67,  337,  337,  337,  337,  337,  337,
 /*   160 */   337,  337,  337,  337,  366,  150,  425,  424,  387,  362,
 /*   170 */   364,  151,   62,   61,   60,   59,  301,   34,   42,  146,
 /*   180 */    26,  123,  418,  365,  152,  388,  389,  390,  392,   77,
 /*   190 */    65,  296,   76,   73,  395,  396,  397,  398,  418,  104,
 /*   200 */     2,  420,  421,  422,  423,  142,    7,  115,  378,  149,
 /*   210 */   148,  121,  120,  154,  117,  104,    3,  420,  421,  422,
 /*   220 */   423,  128,  408,  115,  378,  149,  148,  379,  419,  407,
 /*   230 */    57,   58,  116,  343,  121,  106,  416,   71,  362,  142,
 /*   240 */    63,   56,  414,  115,  480,  111,  118,  154,  117,   36,
 /*   250 */   132,  133,   52,   35,    9,  425,  424,   11,  136,  134,
 /*   260 */    62,   61,   60,   59,   62,   61,   60,   59,  143,  395,
 /*   270 */   396,  397,  398,  377,  556,   74,  556,  377,   13,   51,
 /*   280 */    85,   39,  142,   43,   14,  141,  140,  418,  122,  118,
 /*   290 */   154,  117,   38,  364,  151,  136,  134,   62,   61,   60,
 /*   300 */    59,   15,   66,  112,  104,  346,  420,  421,  422,  423,
 /*   310 */    18,  143,  115,  378,  149,  148,   51,  516,    1,   54,
 /*   320 */    17,   16,  141,  140,   55,  510,  154,  117,   19,   38,
 /*   330 */   139,  135,  415,  379,  370,  363,   57,   58,   62,   61,
 /*   340 */    60,   59,  368,  369,  103,   68,  153,   56,  155,   37,
 /*   350 */   289,  290,  291,  429,  293,  116,  516,   17,   16,  379,
 /*   360 */   429,  516,   57,   58,  516,   19,  429,  139,  135,  415,
 /*   370 */   362,  379,  456,   56,   57,   58,  429,  429,   62,   61,
 /*   380 */    60,   59,  362,  429,  379,   56,  429,   57,   58,  429,
 /*   390 */   379,  377,  429,   57,   58,  362,  429,   47,   56,  429,
 /*   400 */   429,  103,  129,  127,   56,  419,  456,  515,  154,  117,
 /*   410 */   104,   62,   61,   60,   59,  362,  429,  429,  115,  378,
 /*   420 */   149,  148,  429,  379,  137,  429,   57,   58,  513,  154,
 /*   430 */   117,  429,  425,  424,  362,  429,  104,   56,  379,  138,
 /*   440 */    10,   57,   58,  429,  115,  378,  149,  148,  104,  362,
 /*   450 */   429,  379,   56,  429,   57,   58,  115,  378,  149,  148,
 /*   460 */   429,  104,  362,  429,  418,   44,  429,  104,  429,  115,
 /*   470 */   378,  149,  148,  429,  429,  115,  378,  149,  148,  429,
 /*   480 */   429,  104,  429,  420,  421,  422,  423,  379,  429,  115,
 /*   490 */    57,   58,   62,   61,   60,   59,  429,  429,  362,  429,
 /*   500 */   104,   48,  429,  105,  429,  429,  429,  429,  115,  378,
 /*   510 */   149,  148,  118,  154,  117,  104,  461,  411,  412,  327,
 /*   520 */   429,   12,  105,  115,  378,  149,  148,  419,  104,  429,
 /*   530 */   429,  118,  154,  117,  147,  446,  115,  378,  149,  148,
 /*   540 */   429,  429,   75,   75,  429,  429,  327,  327,  327,  327,
 /*   550 */   409,  410,  429,  147,  425,  424,  429,  429,   71,  429,
 /*   560 */   142,  429,  429,  429,  104,  479,  111,  118,  154,  117,
 /*   570 */    86,   72,  115,  378,  149,  148,  429,  429,  429,  118,
 /*   580 */   154,  117,  429,   71,  429,  142,  418,  429,  429,  143,
 /*   590 */   473,  111,  118,  154,  117,  429,  429,   62,   61,   60,
 /*   600 */    59,  147,  429,  429,  429,  420,  421,  422,  423,   71,
 /*   610 */   342,  142,  429,  429,  143,  429,  467,  111,  118,  154,
 /*   620 */   117,  429,   71,  429,  142,  429,  429,  429,  429,  466,
 /*   630 */   111,  118,  154,  117,  429,   71,  429,  142,  429,  429,
 /*   640 */   143,  429,  499,  111,  118,  154,  117,   71,  429,  142,
 /*   650 */    87,  429,  429,  143,  131,  111,  118,  154,  117,  118,
 /*   660 */   154,  117,  429,   71,  429,  142,  143,  429,  429,  429,
 /*   670 */   507,  111,  118,  154,  117,  429,  429,  429,  143,  429,
 /*   680 */   429,  147,   71,  429,  142,  429,  429,  429,  429,  509,
 /*   690 */   111,  118,  154,  117,  143,   62,   61,   60,   59,  429,
 /*   700 */   429,   62,   61,   60,   59,  429,  429,   71,  341,  142,
 /*   710 */   429,  429,  429,  143,  506,  111,  118,  154,  117,   71,
 /*   720 */    49,  142,   62,   61,   60,   59,  508,  111,  118,  154,
 /*   730 */   117,  429,   71,  429,  142,  381,  429,  429,  143,  505,
 /*   740 */   111,  118,  154,  117,   71,  419,  142,  429,  429,  429,
 /*   750 */   143,  504,  111,  118,  154,  117,  429,   71,  429,  142,
 /*   760 */   429,  429,  429,  143,  503,  111,  118,  154,  117,   71,
 /*   770 */   429,  142,  425,  424,  429,  143,  502,  111,  118,  154,
 /*   780 */   117,  429,  429,   71,  429,  142,  429,  429,  143,  429,
 /*   790 */   501,  111,  118,  154,  117,  429,   62,   61,   60,   59,
 /*   800 */   143,  429,  429,  429,  418,  429,  429,  429,   71,  380,
 /*   810 */   142,  429,  429,  429,  143,  144,  111,  118,  154,  117,
 /*   820 */    71,  429,  142,  420,  421,  422,  423,  145,  111,  118,
 /*   830 */   154,  117,  429,   70,  429,  142,  429,  429,  429,  143,
 /*   840 */   110,  111,  118,  154,  117,   71,  429,  142,  429,  429,
 /*   850 */   429,  143,  130,  111,  118,  154,  117,  429,   71,  429,
 /*   860 */   142,  429,   92,  429,  143,  463,  111,  118,  154,  117,
 /*   870 */   429,  118,  154,  117,   92,  429,  143,   22,  462,  462,
 /*   880 */   429,  429,  429,  118,  154,  117,  432,  438,   25,  143,
 /*   890 */    79,  105,  434,  147,  429,  429,  429,  449,  429,   31,
 /*   900 */   118,  154,  117,  156,  446,  147,  429,  429,   23,  429,
 /*   910 */   429,  429,  548,  107,  431,  438,   25,  429,   92,  429,
 /*   920 */   434,  429,  147,  429,  429,  429,  429,  118,  154,  117,
 /*   930 */    92,  156,  429,  429,  108,  108,   23,  429,  429,  118,
 /*   940 */   154,  117,   92,  429,  429,  429,  109,  109,  429,  147,
 /*   950 */   429,  118,  154,  117,  105,  429,  429,  429,  458,   84,
 /*   960 */   429,  147,  429,  118,  154,  117,   97,  435,  118,  154,
 /*   970 */   117,   98,  429,  147,  429,  118,  154,  117,   88,  429,
 /*   980 */   118,  154,  117,   82,  429,  147,  429,  118,  154,  117,
 /*   990 */   147,  429,  118,  154,  117,  429,   99,  147,  429,  429,
 /*  1000 */   429,   89,  147,  429,  429,  118,  154,  117,  429,  147,
 /*  1010 */   118,  154,  117,   90,  147,   62,   61,   60,   59,   83,
 /*  1020 */   429,  429,  118,  154,  117,  429,  100,  147,  118,  154,
 /*  1030 */   117,  429,  147,  429,   53,  118,  154,  117,   91,   62,
 /*  1040 */    61,   60,   59,  429,  147,  101,  429,  118,  154,  117,
 /*  1050 */   147,  102,  377,  429,  118,  154,  117,  147,  429,  429,
 /*  1060 */   118,  154,  117,   93,  429,  429,  429,  429,   94,  147,
 /*  1070 */   429,  429,  118,  154,  117,   95,  147,  118,  154,  117,
 /*  1080 */    96,  429,  147,  429,  118,  154,  117,  530,  429,  118,
 /*  1090 */   154,  117,  529,  429,  147,  429,  118,  154,  117,  147,
 /*  1100 */   429,  118,  154,  117,  528,  429,  147,  429,  429,  429,
 /*  1110 */   527,  147,  429,  118,  154,  117,  429,  429,  147,  118,
 /*  1120 */   154,  117,  113,  147,  429,  429,  429,  114,  429,  429,
 /*  1130 */   429,  118,  154,  117,  429,  147,  118,  154,  117,  429,
 /*  1140 */   429,  147,  429,  429,  429,  429,  429,  429,  429,  429,
 /*  1150 */   429,  429,  429,  147,  429,  429,  429,  429,  147,
};
static const YYCODETYPE yy_lookahead[] = {
 /*     0 */     0,    0,  128,   96,   97,   98,  100,  100,    4,    5,
 /*    10 */    10,   10,  105,  106,  107,  108,  109,  100,   18,   19,
 /*    20 */    20,   99,   22,  100,  102,  103,   26,    4,    5,    6,
 /*    30 */     7,   31,   32,   33,   34,   35,  129,   37,   38,   39,
 /*    40 */    40,  101,   42,   43,  127,   23,   23,   47,   48,   49,
 /*    50 */    50,   51,   52,   53,   54,   55,   56,   57,   58,   59,
 /*    60 */    60,    0,    4,    5,    6,    7,   94,   95,   96,   97,
 /*    70 */    10,   10,    3,  101,  120,   17,  107,  108,  109,   18,
 /*    80 */    19,   20,  122,   22,  112,   17,   26,   26,  116,  117,
 /*    90 */     2,   23,   31,   32,   33,   34,   35,  110,   37,   38,
 /*   100 */    39,   40,  110,   42,   43,  107,  108,  109,   47,   48,
 /*   110 */    49,   50,   51,   52,   53,   54,   55,   56,   57,   58,
 /*   120 */    59,   60,    1,    2,   36,    4,    5,   46,    4,    5,
 /*   130 */     6,    7,  123,   12,   91,   92,   15,   17,    1,    2,
 /*   140 */    16,    4,    5,   22,    2,   24,   25,    6,    7,   12,
 /*   150 */    29,   30,   15,   84,   61,   62,   63,   64,   65,   66,
 /*   160 */    67,   68,   69,   70,   24,   25,   29,   30,    1,   12,
 /*   170 */    24,   25,    4,    5,    6,    7,    8,   16,   36,   12,
 /*   180 */    15,   14,   61,   24,   25,   18,   19,   20,   21,   22,
 /*   190 */    41,   24,   25,   46,   27,   28,   29,   30,   61,   78,
 /*   200 */    38,   80,   81,   82,   83,  100,   71,   86,   87,   88,
 /*   210 */    89,  106,  107,  108,  109,   78,   38,   80,   81,   82,
 /*   220 */    83,   45,   39,   86,   87,   88,   89,    1,    2,   39,
 /*   230 */     4,    5,   86,   17,  129,   78,   76,   98,   12,  100,
 /*   240 */    93,   15,   76,   86,  105,  106,  107,  108,  109,   99,
 /*   250 */    75,   77,  102,  103,    3,   29,   30,    3,    2,    3,
 /*   260 */     4,    5,    6,    7,    4,    5,    6,    7,  129,   27,
 /*   270 */    28,   29,   30,   17,  124,  125,  126,   17,    3,   23,
 /*   280 */    98,   36,  100,   23,    3,   29,   30,   61,  106,  107,
 /*   290 */   108,  109,   36,   24,   25,    2,    3,    4,    5,    6,
 /*   300 */     7,    3,    3,   90,   78,   73,   80,   81,   82,   83,
 /*   310 */    23,  129,   86,   87,   88,   89,   23,   46,   13,   15,
 /*   320 */    64,   65,   29,   30,   15,  107,  108,  109,   72,   36,
 /*   330 */    74,   75,   76,    1,   26,   12,    4,    5,    4,    5,
 /*   340 */     6,    7,   26,   26,   12,    3,   85,   15,   79,   11,
 /*   350 */    18,   19,   20,  130,   22,   86,   85,   64,   65,    1,
 /*   360 */   130,   90,    4,    5,   93,   72,  130,   74,   75,   76,
 /*   370 */    12,    1,    2,   15,    4,    5,  130,  130,    4,    5,
 /*   380 */     6,    7,   12,  130,    1,   15,  130,    4,    5,  130,
 /*   390 */     1,   17,  130,    4,    5,   12,  130,   23,   15,  130,
 /*   400 */   130,   12,   44,   45,   15,    2,   36,  107,  108,  109,
 /*   410 */    78,    4,    5,    6,    7,   12,  130,  130,   86,   87,
 /*   420 */    88,   89,  130,    1,    2,  130,    4,    5,  107,  108,
 /*   430 */   109,  130,   29,   30,   12,  130,   78,   15,    1,    2,
 /*   440 */    33,    4,    5,  130,   86,   87,   88,   89,   78,   12,
 /*   450 */   130,    1,   15,  130,    4,    5,   86,   87,   88,   89,
 /*   460 */   130,   78,   12,  130,   61,   15,  130,   78,  130,   86,
 /*   470 */    87,   88,   89,  130,  130,   86,   87,   88,   89,  130,
 /*   480 */   130,   78,  130,   80,   81,   82,   83,    1,  130,   86,
 /*   490 */     4,    5,    4,    5,    6,    7,  130,  130,   12,  130,
 /*   500 */    78,   15,  130,   98,  130,  130,  130,  130,   86,   87,
 /*   510 */    88,   89,  107,  108,  109,   78,  111,   18,   19,   20,
 /*   520 */   130,   33,   98,   86,   87,   88,   89,    2,   78,  130,
 /*   530 */   130,  107,  108,  109,  129,  111,   86,   87,   88,   89,
 /*   540 */   130,  130,  118,  119,  130,  130,   47,   48,   49,   50,
 /*   550 */    51,   52,  130,  129,   29,   30,  130,  130,   98,  130,
 /*   560 */   100,  130,  130,  130,   78,  105,  106,  107,  108,  109,
 /*   570 */    98,   46,   86,   87,   88,   89,  130,  130,  130,  107,
 /*   580 */   108,  109,  130,   98,  130,  100,   61,  130,  130,  129,
 /*   590 */   105,  106,  107,  108,  109,  130,  130,    4,    5,    6,
 /*   600 */     7,  129,  130,  130,  130,   80,   81,   82,   83,   98,
 /*   610 */    17,  100,  130,  130,  129,  130,  105,  106,  107,  108,
 /*   620 */   109,  130,   98,  130,  100,  130,  130,  130,  130,  105,
 /*   630 */   106,  107,  108,  109,  130,   98,  130,  100,  130,  130,
 /*   640 */   129,  130,  105,  106,  107,  108,  109,   98,  130,  100,
 /*   650 */    98,  130,  130,  129,  105,  106,  107,  108,  109,  107,
 /*   660 */   108,  109,  130,   98,  130,  100,  129,  130,  130,  130,
 /*   670 */   105,  106,  107,  108,  109,  130,  130,  130,  129,  130,
 /*   680 */   130,  129,   98,  130,  100,  130,  130,  130,  130,  105,
 /*   690 */   106,  107,  108,  109,  129,    4,    5,    6,    7,  130,
 /*   700 */   130,    4,    5,    6,    7,  130,  130,   98,   17,  100,
 /*   710 */   130,  130,  130,  129,  105,  106,  107,  108,  109,   98,
 /*   720 */    23,  100,    4,    5,    6,    7,  105,  106,  107,  108,
 /*   730 */   109,  130,   98,  130,  100,   17,  130,  130,  129,  105,
 /*   740 */   106,  107,  108,  109,   98,    2,  100,  130,  130,  130,
 /*   750 */   129,  105,  106,  107,  108,  109,  130,   98,  130,  100,
 /*   760 */   130,  130,  130,  129,  105,  106,  107,  108,  109,   98,
 /*   770 */   130,  100,   29,   30,  130,  129,  105,  106,  107,  108,
 /*   780 */   109,  130,  130,   98,  130,  100,  130,  130,  129,  130,
 /*   790 */   105,  106,  107,  108,  109,  130,    4,    5,    6,    7,
 /*   800 */   129,  130,  130,  130,   61,  130,  130,  130,   98,   17,
 /*   810 */   100,  130,  130,  130,  129,  105,  106,  107,  108,  109,
 /*   820 */    98,  130,  100,   80,   81,   82,   83,  105,  106,  107,
 /*   830 */   108,  109,  130,   98,  130,  100,  130,  130,  130,  129,
 /*   840 */   105,  106,  107,  108,  109,   98,  130,  100,  130,  130,
 /*   850 */   130,  129,  105,  106,  107,  108,  109,  130,   98,  130,
 /*   860 */   100,  130,   98,  130,  129,  105,  106,  107,  108,  109,
 /*   870 */   130,  107,  108,  109,   98,  130,  129,  113,  114,  115,
 /*   880 */   130,  130,  130,  107,  108,  109,   95,   96,   97,  129,
 /*   890 */   114,   98,  101,  129,  130,  130,  130,  121,  130,  123,
 /*   900 */   107,  108,  109,  112,  111,  129,  130,  130,  117,  130,
 /*   910 */   130,  130,  119,   94,   95,   96,   97,  130,   98,  130,
 /*   920 */   101,  130,  129,  130,  130,  130,  130,  107,  108,  109,
 /*   930 */    98,  112,  130,  130,  114,  115,  117,  130,  130,  107,
 /*   940 */   108,  109,   98,  130,  130,  130,  114,  115,  130,  129,
 /*   950 */   130,  107,  108,  109,   98,  130,  130,  130,  114,   98,
 /*   960 */   130,  129,  130,  107,  108,  109,   98,  111,  107,  108,
 /*   970 */   109,   98,  130,  129,  130,  107,  108,  109,   98,  130,
 /*   980 */   107,  108,  109,   98,  130,  129,  130,  107,  108,  109,
 /*   990 */   129,  130,  107,  108,  109,  130,   98,  129,  130,  130,
 /*  1000 */   130,   98,  129,  130,  130,  107,  108,  109,  130,  129,
 /*  1010 */   107,  108,  109,   98,  129,    4,    5,    6,    7,   98,
 /*  1020 */   130,  130,  107,  108,  109,  130,   98,  129,  107,  108,
 /*  1030 */   109,  130,  129,  130,   23,  107,  108,  109,   98,    4,
 /*  1040 */     5,    6,    7,  130,  129,   98,  130,  107,  108,  109,
 /*  1050 */   129,   98,   17,  130,  107,  108,  109,  129,  130,  130,
 /*  1060 */   107,  108,  109,   98,  130,  130,  130,  130,   98,  129,
 /*  1070 */   130,  130,  107,  108,  109,   98,  129,  107,  108,  109,
 /*  1080 */    98,  130,  129,  130,  107,  108,  109,   98,  130,  107,
 /*  1090 */   108,  109,   98,  130,  129,  130,  107,  108,  109,  129,
 /*  1100 */   130,  107,  108,  109,   98,  130,  129,  130,  130,  130,
 /*  1110 */    98,  129,  130,  107,  108,  109,  130,  130,  129,  107,
 /*  1120 */   108,  109,   98,  129,  130,  130,  130,   98,  130,  130,
 /*  1130 */   130,  107,  108,  109,  130,  129,  107,  108,  109,  130,
 /*  1140 */   130,  129,  130,  130,  130,  130,  130,  130,  130,  130,
 /*  1150 */   130,  130,  130,  129,  130,  130,  130,  130,  129,   94,
 /*  1160 */    94,   94,   94,   94,   94,   94,   94,   94,   94,   94,
 /*  1170 */    94,   94,   94,   94,   94,   94,   94,   94,   94,   94,
 /*  1180 */    94,   94,   94,   94,   94,   94,   94,   94,   94,   94,
 /*  1190 */    94,   94,   94,   94,   94,   94,   94,   94,   94,   94,
 /*  1200 */    94,   94,   94,   94,   94,   94,   94,   94,   94,   94,
 /*  1210 */    94,   94,   94,   94,   94,   94,   94,   94,   94,   94,
 /*  1220 */    94,   94,   94,   94,   94,   94,   94,   94,   94,   94,
 /*  1230 */    94,   94,   94,   94,   94,   94,   94,   94,   94,   94,
 /*  1240 */    94,   94,   94,   94,   94,   94,   94,   94,   94,   94,
 /*  1250 */    94,   94,   94,
};
#define YY_SHIFT_COUNT    (156)
#define YY_SHIFT_MIN      (0)
#define YY_SHIFT_MAX      (1035)
static const unsigned short int yy_shift_ofst[] = {
 /*     0 */   167,  121,  137,  137,  137,  137,  137,  137,  137,  137,
 /*    10 */   137,  137,  137,  137,  137,  137,  137,  137,  137,  137,
 /*    20 */   137,  137,  137,  332,  358,  383,  226,  332,  167,  370,
 /*    30 */   370,    0,   61,  167,  403,  389,  383,  389,  422,  437,
 /*    40 */   383,  383,  383,  383,  383,  383,  450,  383,  383,  383,
 /*    50 */   486,  383,  383,  383,  383,  383,  383,  383,  383,  383,
 /*    60 */   383,  383,  383,  499,  157,  157,  157,  157,  157,  525,
 /*    70 */   256,  293,  743,  743,  242,   22, 1159, 1159, 1159, 1159,
 /*    80 */    93,   93,  260,  374,   58,  124,  407,  488,  593,   23,
 /*    90 */   691,  697,  168,  718, 1011,  792, 1035,  334,  334,  334,
 /*   100 */   334,  334,  334,  271,  269,  334,  146,   60,   88,  142,
 /*   110 */    68,    4,   43,  141,  141,  140,  159,   69,  147,    1,
 /*   120 */    81,  120,  161,  165,  149,  162,  178,  183,  190,  176,
 /*   130 */   135,  216,  160,  175,  174,  166,  251,  254,  275,  245,
 /*   140 */   281,  298,  299,  213,  232,  287,  305,  213,  304,  309,
 /*   150 */   308,  316,  317,  323,  261,  342,  338,
};
#define YY_REDUCE_COUNT (79)
#define YY_REDUCE_MIN   (-126)
#define YY_REDUCE_MAX   (1029)
static const short yy_reduce_ofst[] = {
 /*     0 */   -28,  -93,  139,  460,  485,  511,  524,  537,  549,  565,
 /*    10 */   584,  609,  621,  634,  646,  659,  671,  685,  710,  722,
 /*    20 */   735,  747,  760,  424,  764,  776,  182,  793,  819,  820,
 /*    30 */   832,  150,  150,  791,  105,  405,  844,  856,  472,  552,
 /*    40 */   861,  868,  873,  880,  885,  898,  903,  915,  921,  928,
 /*    50 */   940,  947,  953,  965,  970,  977,  982,  989,  994, 1006,
 /*    60 */  1012, 1024, 1029,  -78,  -31,   -2,  218,  300,  321,  -83,
 /*    70 */  -126, -126,  -94,  -77,  -60,  -46,  -40,  -13,   -8,    9,
};
static const YYACTIONTYPE yy_default[] = {
 /*     0 */   433,  427,  427,  427,  427,  427,  427,  427,  427,  427,
 /*    10 */   427,  427,  427,  427,  427,  427,  427,  427,  427,  427,
 /*    20 */   427,  427,  427,  427,  456,  555,  427,  427,  433,  559,
 /*    30 */   468,  560,  560,  433,  427,  427,  427,  427,  427,  427,
 /*    40 */   427,  427,  427,  427,  427,  427,  427,  427,  427,  427,
 /*    50 */   427,  427,  460,  427,  427,  427,  427,  427,  427,  427,
 /*    60 */   427,  427,  427,  427,  427,  427,  427,  427,  427,  427,
 /*    70 */   427,  427,  427,  427,  427,  439,  453,  490,  490,  555,
 /*    80 */   451,  476,  427,  427,  427,  427,  427,  427,  427,  427,
 /*    90 */   427,  427,  454,  427,  427,  427,  427,  471,  469,  494,
 /*   100 */   493,  492,  459,  442,  427,  545,  427,  427,  427,  427,
 /*   110 */   427,  567,  427,  526,  525,  521,  427,  514,  511,  427,
 /*   120 */   511,  427,  427,  427,  474,  427,  427,  427,  427,  427,
 /*   130 */   427,  427,  427,  427,  427,  427,  427,  427,  427,  427,
 /*   140 */   427,  427,  427,  571,  427,  427,  427,  427,  427,  427,
 /*   150 */   427,  427,  427,  427,  580,  427,  427,
};
/********** End of lemon-generated parsing tables *****************************/

/* The next table maps tokens (terminal symbols) into fallback tokens.  
** If a construct like the following:
** 
**      %fallback ID X Y Z.
**
** appears in the grammar, then ID becomes a fallback token for X, Y,
** and Z.  Whenever one of the tokens X, Y, or Z is input to the parser
** but it does not parse, the type of the token is changed to ID and
** the parse is retried before an error is thrown.
**
** This feature can be used, for example, to cause some keywords in a language
** to revert to identifiers if they keyword does not apply in the context where
** it appears.
*/
#ifdef YYFALLBACK
static const YYCODETYPE yyFallback[] = {
    0,  /*          $ => nothing */
    0,  /*         ID => nothing */
    1,  /*     EDGEPT => ID */
    0,  /*         OF => nothing */
    0,  /*       PLUS => nothing */
    0,  /*      MINUS => nothing */
    0,  /*       STAR => nothing */
    0,  /*      SLASH => nothing */
    0,  /*    PERCENT => nothing */
    0,  /*     UMINUS => nothing */
    0,  /*        EOL => nothing */
    0,  /*     ASSIGN => nothing */
    0,  /*  PLACENAME => nothing */
    0,  /*      COLON => nothing */
    0,  /*     ASSERT => nothing */
    0,  /*         LP => nothing */
    0,  /*         EQ => nothing */
    0,  /*         RP => nothing */
    0,  /*       FILL => nothing */
    0,  /*      COLOR => nothing */
    0,  /*  THICKNESS => nothing */
    0,  /*      PRINT => nothing */
    0,  /*     STRING => nothing */
    0,  /*      COMMA => nothing */
    0,  /*  CLASSNAME => nothing */
    0,  /*         LB => nothing */
    0,  /*         RB => nothing */
    0,  /*         UP => nothing */
    0,  /*       DOWN => nothing */
    0,  /*       LEFT => nothing */
    0,  /*      RIGHT => nothing */
    0,  /*      CLOSE => nothing */
    0,  /*       CHOP => nothing */
    0,  /*       FROM => nothing */
    0,  /*         TO => nothing */
    0,  /*       THEN => nothing */
    0,  /*    HEADING => nothing */
    0,  /*         GO => nothing */
    0,  /*         AT => nothing */
    0,  /*       WITH => nothing */
    0,  /*       SAME => nothing */
    0,  /*         AS => nothing */
    0,  /*        FIT => nothing */
    0,  /*     BEHIND => nothing */
    0,  /*      UNTIL => nothing */
    0,  /*       EVEN => nothing */
    0,  /*      DOT_E => nothing */
    0,  /*     HEIGHT => nothing */
    0,  /*      WIDTH => nothing */
    0,  /*     RADIUS => nothing */
    0,  /*   DIAMETER => nothing */
    0,  /*     DOTTED => nothing */
    0,  /*     DASHED => nothing */
    0,  /*         CW => nothing */
    0,  /*        CCW => nothing */
    0,  /*     LARROW => nothing */
    0,  /*     RARROW => nothing */
    0,  /*    LRARROW => nothing */
    0,  /*      INVIS => nothing */
    0,  /*      THICK => nothing */
    0,  /*       THIN => nothing */
    0,  /*     CENTER => nothing */
    0,  /*      LJUST => nothing */
    0,  /*      RJUST => nothing */
    0,  /*      ABOVE => nothing */
    0,  /*      BELOW => nothing */
    0,  /*     ITALIC => nothing */
    0,  /*       BOLD => nothing */
    0,  /*    ALIGNED => nothing */
    0,  /*        BIG => nothing */
    0,  /*      SMALL => nothing */
    0,  /*        AND => nothing */
    0,  /*         LT => nothing */
    0,  /*         GT => nothing */
    0,  /*         ON => nothing */
    0,  /*        WAY => nothing */
    0,  /*    BETWEEN => nothing */
    0,  /*        THE => nothing */
    0,  /*        NTH => nothing */
    0,  /*     VERTEX => nothing */
    0,  /*        TOP => nothing */
    0,  /*     BOTTOM => nothing */
    0,  /*      START => nothing */
    0,  /*        END => nothing */
    0,  /*         IN => nothing */
    0,  /*      DOT_U => nothing */
    0,  /*       LAST => nothing */
    0,  /*     NUMBER => nothing */
    0,  /*      FUNC1 => nothing */
    0,  /*      FUNC2 => nothing */
    0,  /*     DOT_XY => nothing */
    0,  /*          X => nothing */
    0,  /*          Y => nothing */
    0,  /*      DOT_L => nothing */
};
#endif /* YYFALLBACK */

/* The following structure represents a single element of the
** parser's stack.  Information stored includes:
**
**   +  The state number for the parser at this level of the stack.
**
**   +  The value of the token stored at this level of the stack.
**      (In other words, the "major" token.)
**
**   +  The semantic value stored at this level of the stack.  This is
**      the information used by the action routines in the grammar.
**      It is sometimes called the "minor" token.
**
** After the "shift" half of a SHIFTREDUCE action, the stateno field
** actually contains the reduce action for the second half of the
** SHIFTREDUCE.
*/
struct yyStackEntry {
  YYACTIONTYPE stateno;  /* The state-number, or reduce action in SHIFTREDUCE */
  YYCODETYPE major;      /* The major token value.  This is the code
                         ** number for the token at this stack level */
  YYMINORTYPE minor;     /* The user-supplied minor token value.  This
                         ** is the value of the token  */
};
typedef struct yyStackEntry yyStackEntry;

/* The state of the parser is completely contained in an instance of
** the following structure */
struct yyParser {
  yyStackEntry *yytos;          /* Pointer to top element of the stack */
#ifdef YYTRACKMAXSTACKDEPTH
  int yyhwm;                    /* High-water mark of the stack */
#endif
#ifndef YYNOERRORRECOVERY
  int yyerrcnt;                 /* Shifts left before out of the error */
#endif
  pik_parserARG_SDECL                /* A place to hold %extra_argument */
  pik_parserCTX_SDECL                /* A place to hold %extra_context */
#if YYSTACKDEPTH<=0
  int yystksz;                  /* Current side of the stack */
  yyStackEntry *yystack;        /* The parser's stack */
  yyStackEntry yystk0;          /* First stack entry */
#else
  yyStackEntry yystack[YYSTACKDEPTH];  /* The parser's stack */
  yyStackEntry *yystackEnd;            /* Last entry in the stack */
#endif
};
typedef struct yyParser yyParser;

#ifndef NDEBUG
#include <stdio.h>
#include <assert.h>
static FILE *yyTraceFILE = 0;
static char *yyTracePrompt = 0;
#endif /* NDEBUG */

#ifndef NDEBUG
/* 
** Turn parser tracing on by giving a stream to which to write the trace
** and a prompt to preface each trace message.  Tracing is turned off
** by making either argument NULL 
**
** Inputs:
** <ul>
** <li> A FILE* to which trace output should be written.
**      If NULL, then tracing is turned off.
** <li> A prefix string written at the beginning of every
**      line of trace output.  If NULL, then tracing is
**      turned off.
** </ul>
**
** Outputs:
** None.
*/
void pik_parserTrace(FILE *TraceFILE, char *zTracePrompt){
  yyTraceFILE = TraceFILE;
  yyTracePrompt = zTracePrompt;
  if( yyTraceFILE==0 ) yyTracePrompt = 0;
  else if( yyTracePrompt==0 ) yyTraceFILE = 0;
}
#endif /* NDEBUG */

#if defined(YYCOVERAGE) || !defined(NDEBUG)
/* For tracing shifts, the names of all terminals and nonterminals
** are required.  The following table supplies these names */
static const char *const yyTokenName[] = { 
  /*    0 */ "$",
  /*    1 */ "ID",
  /*    2 */ "EDGEPT",
  /*    3 */ "OF",
  /*    4 */ "PLUS",
  /*    5 */ "MINUS",
  /*    6 */ "STAR",
  /*    7 */ "SLASH",
  /*    8 */ "PERCENT",
  /*    9 */ "UMINUS",
  /*   10 */ "EOL",
  /*   11 */ "ASSIGN",
  /*   12 */ "PLACENAME",
  /*   13 */ "COLON",
  /*   14 */ "ASSERT",
  /*   15 */ "LP",
  /*   16 */ "EQ",
  /*   17 */ "RP",
  /*   18 */ "FILL",
  /*   19 */ "COLOR",
  /*   20 */ "THICKNESS",
  /*   21 */ "PRINT",
  /*   22 */ "STRING",
  /*   23 */ "COMMA",
  /*   24 */ "CLASSNAME",
  /*   25 */ "LB",
  /*   26 */ "RB",
  /*   27 */ "UP",
  /*   28 */ "DOWN",
  /*   29 */ "LEFT",
  /*   30 */ "RIGHT",
  /*   31 */ "CLOSE",
  /*   32 */ "CHOP",
  /*   33 */ "FROM",
  /*   34 */ "TO",
  /*   35 */ "THEN",
  /*   36 */ "HEADING",
  /*   37 */ "GO",
  /*   38 */ "AT",
  /*   39 */ "WITH",
  /*   40 */ "SAME",
  /*   41 */ "AS",
  /*   42 */ "FIT",
  /*   43 */ "BEHIND",
  /*   44 */ "UNTIL",
  /*   45 */ "EVEN",
  /*   46 */ "DOT_E",
  /*   47 */ "HEIGHT",
  /*   48 */ "WIDTH",
  /*   49 */ "RADIUS",
  /*   50 */ "DIAMETER",
  /*   51 */ "DOTTED",
  /*   52 */ "DASHED",
  /*   53 */ "CW",
  /*   54 */ "CCW",
  /*   55 */ "LARROW",
  /*   56 */ "RARROW",
  /*   57 */ "LRARROW",
  /*   58 */ "INVIS",
  /*   59 */ "THICK",
  /*   60 */ "THIN",
  /*   61 */ "CENTER",
  /*   62 */ "LJUST",
  /*   63 */ "RJUST",
  /*   64 */ "ABOVE",
  /*   65 */ "BELOW",
  /*   66 */ "ITALIC",
  /*   67 */ "BOLD",
  /*   68 */ "ALIGNED",
  /*   69 */ "BIG",
  /*   70 */ "SMALL",
  /*   71 */ "AND",
  /*   72 */ "LT",
  /*   73 */ "GT",
  /*   74 */ "ON",
  /*   75 */ "WAY",
  /*   76 */ "BETWEEN",
  /*   77 */ "THE",
  /*   78 */ "NTH",
  /*   79 */ "VERTEX",
  /*   80 */ "TOP",
  /*   81 */ "BOTTOM",
  /*   82 */ "START",
  /*   83 */ "END",
  /*   84 */ "IN",
  /*   85 */ "DOT_U",
  /*   86 */ "LAST",
  /*   87 */ "NUMBER",
  /*   88 */ "FUNC1",
  /*   89 */ "FUNC2",
  /*   90 */ "DOT_XY",
  /*   91 */ "X",
  /*   92 */ "Y",
  /*   93 */ "DOT_L",
  /*   94 */ "element_list",
  /*   95 */ "element",
  /*   96 */ "unnamed_element",
  /*   97 */ "basetype",
  /*   98 */ "expr",
  /*   99 */ "numproperty",
  /*  100 */ "edge",
  /*  101 */ "direction",
  /*  102 */ "dashproperty",
  /*  103 */ "colorproperty",
  /*  104 */ "locproperty",
  /*  105 */ "position",
  /*  106 */ "place",
  /*  107 */ "object",
  /*  108 */ "objectname",
  /*  109 */ "nth",
  /*  110 */ "textposition",
  /*  111 */ "rvalue",
  /*  112 */ "lvalue",
  /*  113 */ "even",
  /*  114 */ "relexpr",
  /*  115 */ "optrelexpr",
  /*  116 */ "document",
  /*  117 */ "print",
  /*  118 */ "prlist",
  /*  119 */ "pritem",
  /*  120 */ "prsep",
  /*  121 */ "attribute_list",
  /*  122 */ "savelist",
  /*  123 */ "alist",
  /*  124 */ "attribute",
  /*  125 */ "go",
  /*  126 */ "boolproperty",
  /*  127 */ "withclause",
  /*  128 */ "between",
  /*  129 */ "place2",
};
#endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */

#ifndef NDEBUG
/* For tracing reduce actions, the names of all rules are required.
*/
static const char *const yyRuleName[] = {
 /*   0 */ "document ::= element_list",
 /*   1 */ "element_list ::= element",
 /*   2 */ "element_list ::= element_list EOL element",
 /*   3 */ "element ::=",
 /*   4 */ "element ::= direction",
 /*   5 */ "element ::= lvalue ASSIGN rvalue",
 /*   6 */ "element ::= PLACENAME COLON unnamed_element",
 /*   7 */ "element ::= PLACENAME COLON position",
 /*   8 */ "element ::= unnamed_element",
 /*   9 */ "element ::= print prlist",
 /*  10 */ "element ::= ASSERT LP expr EQ expr RP",
 /*  11 */ "element ::= ASSERT LP place EQ place RP",
 /*  12 */ "rvalue ::= PLACENAME",
 /*  13 */ "pritem ::= FILL",
 /*  14 */ "pritem ::= COLOR",
 /*  15 */ "pritem ::= THICKNESS",
 /*  16 */ "pritem ::= rvalue",
 /*  17 */ "pritem ::= STRING",
 /*  18 */ "prsep ::= COMMA",
 /*  19 */ "unnamed_element ::= basetype attribute_list",
 /*  20 */ "basetype ::= CLASSNAME",
 /*  21 */ "basetype ::= STRING textposition",
 /*  22 */ "basetype ::= LB savelist element_list RB",
 /*  23 */ "savelist ::=",
 /*  24 */ "relexpr ::= expr",
 /*  25 */ "relexpr ::= expr PERCENT",
 /*  26 */ "optrelexpr ::=",
 /*  27 */ "attribute_list ::= relexpr alist",
 /*  28 */ "attribute ::= numproperty relexpr",
 /*  29 */ "attribute ::= dashproperty expr",
 /*  30 */ "attribute ::= dashproperty",
 /*  31 */ "attribute ::= colorproperty rvalue",
 /*  32 */ "attribute ::= go direction optrelexpr",
 /*  33 */ "attribute ::= go direction even position",
 /*  34 */ "attribute ::= CLOSE",
 /*  35 */ "attribute ::= CHOP",
 /*  36 */ "attribute ::= FROM position",
 /*  37 */ "attribute ::= TO position",
 /*  38 */ "attribute ::= THEN",
 /*  39 */ "attribute ::= THEN optrelexpr HEADING expr",
 /*  40 */ "attribute ::= THEN optrelexpr EDGEPT",
 /*  41 */ "attribute ::= GO optrelexpr HEADING expr",
 /*  42 */ "attribute ::= GO optrelexpr EDGEPT",
 /*  43 */ "attribute ::= AT position",
 /*  44 */ "attribute ::= SAME",
 /*  45 */ "attribute ::= SAME AS object",
 /*  46 */ "attribute ::= STRING textposition",
 /*  47 */ "attribute ::= FIT",
 /*  48 */ "attribute ::= BEHIND object",
 /*  49 */ "withclause ::= DOT_E edge AT position",
 /*  50 */ "withclause ::= edge AT position",
 /*  51 */ "numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS",
 /*  52 */ "boolproperty ::= CW",
 /*  53 */ "boolproperty ::= CCW",
 /*  54 */ "boolproperty ::= LARROW",
 /*  55 */ "boolproperty ::= RARROW",
 /*  56 */ "boolproperty ::= LRARROW",
 /*  57 */ "boolproperty ::= INVIS",
 /*  58 */ "boolproperty ::= THICK",
 /*  59 */ "boolproperty ::= THIN",
 /*  60 */ "textposition ::=",
 /*  61 */ "textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|ALIGNED|BIG|SMALL",
 /*  62 */ "position ::= expr COMMA expr",
 /*  63 */ "position ::= place PLUS expr COMMA expr",
 /*  64 */ "position ::= place MINUS expr COMMA expr",
 /*  65 */ "position ::= place PLUS LP expr COMMA expr RP",
 /*  66 */ "position ::= place MINUS LP expr COMMA expr RP",
 /*  67 */ "position ::= LP position COMMA position RP",
 /*  68 */ "position ::= LP position RP",
 /*  69 */ "position ::= expr between position AND position",
 /*  70 */ "position ::= expr LT position COMMA position GT",
 /*  71 */ "position ::= expr ABOVE position",
 /*  72 */ "position ::= expr BELOW position",
 /*  73 */ "position ::= expr LEFT OF position",
 /*  74 */ "position ::= expr RIGHT OF position",
 /*  75 */ "position ::= expr ON HEADING EDGEPT OF position",
 /*  76 */ "position ::= expr HEADING EDGEPT OF position",
 /*  77 */ "position ::= expr EDGEPT OF position",
 /*  78 */ "position ::= expr ON HEADING expr FROM position",
 /*  79 */ "position ::= expr HEADING expr FROM position",
 /*  80 */ "place ::= edge OF object",
 /*  81 */ "place2 ::= object",
 /*  82 */ "place2 ::= object DOT_E edge",
 /*  83 */ "place2 ::= NTH VERTEX OF object",
 /*  84 */ "object ::= nth",
 /*  85 */ "object ::= nth OF|IN object",
 /*  86 */ "objectname ::= PLACENAME",
 /*  87 */ "objectname ::= objectname DOT_U PLACENAME",
 /*  88 */ "nth ::= NTH CLASSNAME",
 /*  89 */ "nth ::= NTH LAST CLASSNAME",
 /*  90 */ "nth ::= LAST CLASSNAME",
 /*  91 */ "nth ::= LAST",
 /*  92 */ "nth ::= NTH LB RB",
 /*  93 */ "nth ::= NTH LAST LB RB",
 /*  94 */ "nth ::= LAST LB RB",
 /*  95 */ "expr ::= expr PLUS expr",
 /*  96 */ "expr ::= expr MINUS expr",
 /*  97 */ "expr ::= expr STAR expr",
 /*  98 */ "expr ::= expr SLASH expr",
 /*  99 */ "expr ::= MINUS expr",
 /* 100 */ "expr ::= PLUS expr",
 /* 101 */ "expr ::= LP expr RP",
 /* 102 */ "expr ::= NUMBER",
 /* 103 */ "expr ::= ID",
 /* 104 */ "expr ::= FUNC1 LP expr RP",
 /* 105 */ "expr ::= FUNC2 LP expr COMMA expr RP",
 /* 106 */ "expr ::= place2 DOT_XY X",
 /* 107 */ "expr ::= place2 DOT_XY Y",
 /* 108 */ "expr ::= object DOT_L numproperty",
 /* 109 */ "expr ::= object DOT_L dashproperty",
 /* 110 */ "expr ::= object DOT_L colorproperty",
 /* 111 */ "lvalue ::= ID",
 /* 112 */ "lvalue ::= FILL",
 /* 113 */ "lvalue ::= COLOR",
 /* 114 */ "lvalue ::= THICKNESS",
 /* 115 */ "rvalue ::= expr",
 /* 116 */ "print ::= PRINT",
 /* 117 */ "prlist ::= pritem",
 /* 118 */ "prlist ::= prlist prsep pritem",
 /* 119 */ "direction ::= UP",
 /* 120 */ "direction ::= DOWN",
 /* 121 */ "direction ::= LEFT",
 /* 122 */ "direction ::= RIGHT",
 /* 123 */ "optrelexpr ::= relexpr",
 /* 124 */ "attribute_list ::= alist",
 /* 125 */ "alist ::=",
 /* 126 */ "alist ::= alist attribute",
 /* 127 */ "attribute ::= boolproperty",
 /* 128 */ "attribute ::= WITH withclause",
 /* 129 */ "go ::= GO",
 /* 130 */ "go ::=",
 /* 131 */ "even ::= UNTIL EVEN WITH",
 /* 132 */ "even ::= EVEN WITH",
 /* 133 */ "dashproperty ::= DOTTED",
 /* 134 */ "dashproperty ::= DASHED",
 /* 135 */ "colorproperty ::= FILL",
 /* 136 */ "colorproperty ::= COLOR",
 /* 137 */ "position ::= place",
 /* 138 */ "between ::= WAY BETWEEN",
 /* 139 */ "between ::= BETWEEN",
 /* 140 */ "between ::= OF THE WAY BETWEEN",
 /* 141 */ "place ::= place2",
 /* 142 */ "edge ::= CENTER",
 /* 143 */ "edge ::= EDGEPT",
 /* 144 */ "edge ::= TOP",
 /* 145 */ "edge ::= BOTTOM",
 /* 146 */ "edge ::= START",
 /* 147 */ "edge ::= END",
 /* 148 */ "edge ::= RIGHT",
 /* 149 */ "edge ::= LEFT",
 /* 150 */ "object ::= objectname",
};
#endif /* NDEBUG */


#if YYSTACKDEPTH<=0
/*
** Try to increase the size of the parser stack.  Return the number
** of errors.  Return 0 on success.
*/
static int yyGrowStack(yyParser *p){
  int newSize;
  int idx;
  yyStackEntry *pNew;

  newSize = p->yystksz*2 + 100;
  idx = p->yytos ? (int)(p->yytos - p->yystack) : 0;
  if( p->yystack==&p->yystk0 ){
    pNew = malloc(newSize*sizeof(pNew[0]));
    if( pNew ) pNew[0] = p->yystk0;
  }else{
    pNew = realloc(p->yystack, newSize*sizeof(pNew[0]));
  }
  if( pNew ){
    p->yystack = pNew;
    p->yytos = &p->yystack[idx];
#ifndef NDEBUG
    if( yyTraceFILE ){
      fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n",
              yyTracePrompt, p->yystksz, newSize);
    }
#endif
    p->yystksz = newSize;
  }
  return pNew==0; 
}
#endif

/* Datatype of the argument to the memory allocated passed as the
** second argument to pik_parserAlloc() below.  This can be changed by
** putting an appropriate #define in the %include section of the input
** grammar.
*/
#ifndef YYMALLOCARGTYPE
# define YYMALLOCARGTYPE size_t
#endif

/* Initialize a new parser that has already been allocated.
*/
void pik_parserInit(void *yypRawParser pik_parserCTX_PDECL){
  yyParser *yypParser = (yyParser*)yypRawParser;
  pik_parserCTX_STORE
#ifdef YYTRACKMAXSTACKDEPTH
  yypParser->yyhwm = 0;
#endif
#if YYSTACKDEPTH<=0
  yypParser->yytos = NULL;
  yypParser->yystack = NULL;
  yypParser->yystksz = 0;
  if( yyGrowStack(yypParser) ){
    yypParser->yystack = &yypParser->yystk0;
    yypParser->yystksz = 1;
  }
#endif
#ifndef YYNOERRORRECOVERY
  yypParser->yyerrcnt = -1;
#endif
  yypParser->yytos = yypParser->yystack;
  yypParser->yystack[0].stateno = 0;
  yypParser->yystack[0].major = 0;
#if YYSTACKDEPTH>0
  yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1];
#endif
}

#ifndef pik_parser_ENGINEALWAYSONSTACK
/* 
** This function allocates a new parser.
** The only argument is a pointer to a function which works like
** malloc.
**
** Inputs:
** A pointer to the function used to allocate memory.
**
** Outputs:
** A pointer to a parser.  This pointer is used in subsequent calls
** to pik_parser and pik_parserFree.
*/
void *pik_parserAlloc(void *(*mallocProc)(YYMALLOCARGTYPE) pik_parserCTX_PDECL){
  yyParser *yypParser;
  yypParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) );
  if( yypParser ){
    pik_parserCTX_STORE
    pik_parserInit(yypParser pik_parserCTX_PARAM);
  }
  return (void*)yypParser;
}
#endif /* pik_parser_ENGINEALWAYSONSTACK */


/* The following function deletes the "minor type" or semantic value
** associated with a symbol.  The symbol can be either a terminal
** or nonterminal. "yymajor" is the symbol code, and "yypminor" is
** a pointer to the value to be deleted.  The code used to do the 
** deletions is derived from the %destructor and/or %token_destructor
** directives of the input grammar.
*/
static void yy_destructor(
  yyParser *yypParser,    /* The parser */
  YYCODETYPE yymajor,     /* Type code for object to destroy */
  YYMINORTYPE *yypminor   /* The object to be destroyed */
){
  pik_parserARG_FETCH
  pik_parserCTX_FETCH
  switch( yymajor ){
    /* Here is inserted the actions which take place when a
    ** terminal or non-terminal is destroyed.  This can happen
    ** when the symbol is popped from the stack during a
    ** reduce or during error processing or when a parser is 
    ** being destroyed before it is finished parsing.
    **
    ** Note: during a reduce, the only symbols destroyed are those
    ** which appear on the RHS of the rule, but which are *not* used
    ** inside the C code.
    */
/********* Begin destructor definitions ***************************************/
    case 94: /* element_list */
{
#line 476 "pikchr.y"
pik_elist_free(p,(yypminor->yy72));
#line 1664 "pikchr.c"
}
      break;
    case 95: /* element */
    case 96: /* unnamed_element */
    case 97: /* basetype */
{
#line 478 "pikchr.y"
pik_elem_free(p,(yypminor->yy254));
#line 1673 "pikchr.c"
}
      break;
/********* End destructor definitions *****************************************/
    default:  break;   /* If no destructor action specified: do nothing */
  }
}

/*
** Pop the parser's stack once.
**
** If there is a destructor routine associated with the token which
** is popped from the stack, then call it.
*/
static void yy_pop_parser_stack(yyParser *pParser){
  yyStackEntry *yytos;
  assert( pParser->yytos!=0 );
  assert( pParser->yytos > pParser->yystack );
  yytos = pParser->yytos--;
#ifndef NDEBUG
  if( yyTraceFILE ){
    fprintf(yyTraceFILE,"%sPopping %s\n",
      yyTracePrompt,
      yyTokenName[yytos->major]);
  }
#endif
  yy_destructor(pParser, yytos->major, &yytos->minor);
}

/*
** Clear all secondary memory allocations from the parser
*/
void pik_parserFinalize(void *p){
  yyParser *pParser = (yyParser*)p;
  while( pParser->yytos>pParser->yystack ) yy_pop_parser_stack(pParser);
#if YYSTACKDEPTH<=0
  if( pParser->yystack!=&pParser->yystk0 ) free(pParser->yystack);
#endif
}

#ifndef pik_parser_ENGINEALWAYSONSTACK
/* 
** Deallocate and destroy a parser.  Destructors are called for
** all stack elements before shutting the parser down.
**
** If the YYPARSEFREENEVERNULL macro exists (for example because it
** is defined in a %include section of the input grammar) then it is
** assumed that the input pointer is never NULL.
*/
void pik_parserFree(
  void *p,                    /* The parser to be deleted */
  void (*freeProc)(void*)     /* Function used to reclaim memory */
){
#ifndef YYPARSEFREENEVERNULL
  if( p==0 ) return;
#endif
  pik_parserFinalize(p);
  (*freeProc)(p);
}
#endif /* pik_parser_ENGINEALWAYSONSTACK */

/*
** Return the peak depth of the stack for a parser.
*/
#ifdef YYTRACKMAXSTACKDEPTH
int pik_parserStackPeak(void *p){
  yyParser *pParser = (yyParser*)p;
  return pParser->yyhwm;
}
#endif

/* This array of booleans keeps track of the parser statement
** coverage.  The element yycoverage[X][Y] is set when the parser
** is in state X and has a lookahead token Y.  In a well-tested
** systems, every element of this matrix should end up being set.
*/
#if defined(YYCOVERAGE)
static unsigned char yycoverage[YYNSTATE][YYNTOKEN];
#endif

/*
** Write into out a description of every state/lookahead combination that
**
**   (1)  has not been used by the parser, and
**   (2)  is not a syntax error.
**
** Return the number of missed state/lookahead combinations.
*/
#if defined(YYCOVERAGE)
int pik_parserCoverage(FILE *out){
  int stateno, iLookAhead, i;
  int nMissed = 0;
  for(stateno=0; stateno<YYNSTATE; stateno++){
    i = yy_shift_ofst[stateno];
    for(iLookAhead=0; iLookAhead<YYNTOKEN; iLookAhead++){
      if( yy_lookahead[i+iLookAhead]!=iLookAhead ) continue;
      if( yycoverage[stateno][iLookAhead]==0 ) nMissed++;
      if( out ){
        fprintf(out,"State %d lookahead %s %s\n", stateno,
                yyTokenName[iLookAhead],
                yycoverage[stateno][iLookAhead] ? "ok" : "missed");
      }
    }
  }
  return nMissed;
}
#endif

/*
** Find the appropriate action for a parser given the terminal
** look-ahead token iLookAhead.
*/
static YYACTIONTYPE yy_find_shift_action(
  YYCODETYPE iLookAhead,    /* The look-ahead token */
  YYACTIONTYPE stateno      /* Current state number */
){
  int i;

  if( stateno>YY_MAX_SHIFT ) return stateno;
  assert( stateno <= YY_SHIFT_COUNT );
#if defined(YYCOVERAGE)
  yycoverage[stateno][iLookAhead] = 1;
#endif
  do{
    i = yy_shift_ofst[stateno];
    assert( i>=0 );
    assert( i<=YY_ACTTAB_COUNT );
    assert( i+YYNTOKEN<=(int)YY_NLOOKAHEAD );
    assert( iLookAhead!=YYNOCODE );
    assert( iLookAhead < YYNTOKEN );
    i += iLookAhead;
    assert( i<(int)YY_NLOOKAHEAD );
    if( yy_lookahead[i]!=iLookAhead ){
#ifdef YYFALLBACK
      YYCODETYPE iFallback;            /* Fallback token */
      assert( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0]) );
      iFallback = yyFallback[iLookAhead];
      if( iFallback!=0 ){
#ifndef NDEBUG
        if( yyTraceFILE ){
          fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
             yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
        }
#endif
        assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */
        iLookAhead = iFallback;
        continue;
      }
#endif
#ifdef YYWILDCARD
      {
        int j = i - iLookAhead + YYWILDCARD;
        assert( j<(int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])) );
        if( yy_lookahead[j]==YYWILDCARD && iLookAhead>0 ){
#ifndef NDEBUG
          if( yyTraceFILE ){
            fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
               yyTracePrompt, yyTokenName[iLookAhead],
               yyTokenName[YYWILDCARD]);
          }
#endif /* NDEBUG */
          return yy_action[j];
        }
      }
#endif /* YYWILDCARD */
      return yy_default[stateno];
    }else{
      assert( i>=0 && i<(int)(sizeof(yy_action)/sizeof(yy_action[0])) );
      return yy_action[i];
    }
  }while(1);
}

/*
** Find the appropriate action for a parser given the non-terminal
** look-ahead token iLookAhead.
*/
static YYACTIONTYPE yy_find_reduce_action(
  YYACTIONTYPE stateno,     /* Current state number */
  YYCODETYPE iLookAhead     /* The look-ahead token */
){
  int i;
#ifdef YYERRORSYMBOL
  if( stateno>YY_REDUCE_COUNT ){
    return yy_default[stateno];
  }
#else
  assert( stateno<=YY_REDUCE_COUNT );
#endif
  i = yy_reduce_ofst[stateno];
  assert( iLookAhead!=YYNOCODE );
  i += iLookAhead;
#ifdef YYERRORSYMBOL
  if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
    return yy_default[stateno];
  }
#else
  assert( i>=0 && i<YY_ACTTAB_COUNT );
  assert( yy_lookahead[i]==iLookAhead );
#endif
  return yy_action[i];
}

/*
** The following routine is called if the stack overflows.
*/
static void yyStackOverflow(yyParser *yypParser){
   pik_parserARG_FETCH
   pik_parserCTX_FETCH
#ifndef NDEBUG
   if( yyTraceFILE ){
     fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
   }
#endif
   while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
   /* Here code is inserted which will execute if the parser
   ** stack every overflows */
/******** Begin %stack_overflow code ******************************************/
#line 510 "pikchr.y"

  pik_error(p, 0, "parser stack overflow");
#line 1894 "pikchr.c"
/******** End %stack_overflow code ********************************************/
   pik_parserARG_STORE /* Suppress warning about unused %extra_argument var */
   pik_parserCTX_STORE
}

/*
** Print tracing information for a SHIFT action
*/
#ifndef NDEBUG
static void yyTraceShift(yyParser *yypParser, int yyNewState, const char *zTag){
  if( yyTraceFILE ){
    if( yyNewState<YYNSTATE ){
      fprintf(yyTraceFILE,"%s%s '%s', go to state %d\n",
         yyTracePrompt, zTag, yyTokenName[yypParser->yytos->major],
         yyNewState);
    }else{
      fprintf(yyTraceFILE,"%s%s '%s', pending reduce %d\n",
         yyTracePrompt, zTag, yyTokenName[yypParser->yytos->major],
         yyNewState - YY_MIN_REDUCE);
    }
  }
}
#else
# define yyTraceShift(X,Y,Z)
#endif

/*
** Perform a shift action.
*/
static void yy_shift(
  yyParser *yypParser,          /* The parser to be shifted */
  YYACTIONTYPE yyNewState,      /* The new state to shift in */
  YYCODETYPE yyMajor,           /* The major token to shift in */
  pik_parserTOKENTYPE yyMinor        /* The minor token to shift in */
){
  yyStackEntry *yytos;
  yypParser->yytos++;
#ifdef YYTRACKMAXSTACKDEPTH
  if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
    yypParser->yyhwm++;
    assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) );
  }
#endif
#if YYSTACKDEPTH>0 
  if( yypParser->yytos>yypParser->yystackEnd ){
    yypParser->yytos--;
    yyStackOverflow(yypParser);
    return;
  }
#else
  if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz] ){
    if( yyGrowStack(yypParser) ){
      yypParser->yytos--;
      yyStackOverflow(yypParser);
      return;
    }
  }
#endif
  if( yyNewState > YY_MAX_SHIFT ){
    yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
  }
  yytos = yypParser->yytos;
  yytos->stateno = yyNewState;
  yytos->major = yyMajor;
  yytos->minor.yy0 = yyMinor;
  yyTraceShift(yypParser, yyNewState, "Shift");
}

/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side
** of that rule */
static const YYCODETYPE yyRuleInfoLhs[] = {
   116,  /* (0) document ::= element_list */
    94,  /* (1) element_list ::= element */
    94,  /* (2) element_list ::= element_list EOL element */
    95,  /* (3) element ::= */
    95,  /* (4) element ::= direction */
    95,  /* (5) element ::= lvalue ASSIGN rvalue */
    95,  /* (6) element ::= PLACENAME COLON unnamed_element */
    95,  /* (7) element ::= PLACENAME COLON position */
    95,  /* (8) element ::= unnamed_element */
    95,  /* (9) element ::= print prlist */
    95,  /* (10) element ::= ASSERT LP expr EQ expr RP */
    95,  /* (11) element ::= ASSERT LP place EQ place RP */
   111,  /* (12) rvalue ::= PLACENAME */
   119,  /* (13) pritem ::= FILL */
   119,  /* (14) pritem ::= COLOR */
   119,  /* (15) pritem ::= THICKNESS */
   119,  /* (16) pritem ::= rvalue */
   119,  /* (17) pritem ::= STRING */
   120,  /* (18) prsep ::= COMMA */
    96,  /* (19) unnamed_element ::= basetype attribute_list */
    97,  /* (20) basetype ::= CLASSNAME */
    97,  /* (21) basetype ::= STRING textposition */
    97,  /* (22) basetype ::= LB savelist element_list RB */
   122,  /* (23) savelist ::= */
   114,  /* (24) relexpr ::= expr */
   114,  /* (25) relexpr ::= expr PERCENT */
   115,  /* (26) optrelexpr ::= */
   121,  /* (27) attribute_list ::= relexpr alist */
   124,  /* (28) attribute ::= numproperty relexpr */
   124,  /* (29) attribute ::= dashproperty expr */
   124,  /* (30) attribute ::= dashproperty */
   124,  /* (31) attribute ::= colorproperty rvalue */
   124,  /* (32) attribute ::= go direction optrelexpr */
   124,  /* (33) attribute ::= go direction even position */
   124,  /* (34) attribute ::= CLOSE */
   124,  /* (35) attribute ::= CHOP */
   124,  /* (36) attribute ::= FROM position */
   124,  /* (37) attribute ::= TO position */
   124,  /* (38) attribute ::= THEN */
   124,  /* (39) attribute ::= THEN optrelexpr HEADING expr */
   124,  /* (40) attribute ::= THEN optrelexpr EDGEPT */
   124,  /* (41) attribute ::= GO optrelexpr HEADING expr */
   124,  /* (42) attribute ::= GO optrelexpr EDGEPT */
   124,  /* (43) attribute ::= AT position */
   124,  /* (44) attribute ::= SAME */
   124,  /* (45) attribute ::= SAME AS object */
   124,  /* (46) attribute ::= STRING textposition */
   124,  /* (47) attribute ::= FIT */
   124,  /* (48) attribute ::= BEHIND object */
   127,  /* (49) withclause ::= DOT_E edge AT position */
   127,  /* (50) withclause ::= edge AT position */
    99,  /* (51) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */
   126,  /* (52) boolproperty ::= CW */
   126,  /* (53) boolproperty ::= CCW */
   126,  /* (54) boolproperty ::= LARROW */
   126,  /* (55) boolproperty ::= RARROW */
   126,  /* (56) boolproperty ::= LRARROW */
   126,  /* (57) boolproperty ::= INVIS */
   126,  /* (58) boolproperty ::= THICK */
   126,  /* (59) boolproperty ::= THIN */
   110,  /* (60) textposition ::= */
   110,  /* (61) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|ALIGNED|BIG|SMALL */
   105,  /* (62) position ::= expr COMMA expr */
   105,  /* (63) position ::= place PLUS expr COMMA expr */
   105,  /* (64) position ::= place MINUS expr COMMA expr */
   105,  /* (65) position ::= place PLUS LP expr COMMA expr RP */
   105,  /* (66) position ::= place MINUS LP expr COMMA expr RP */
   105,  /* (67) position ::= LP position COMMA position RP */
   105,  /* (68) position ::= LP position RP */
   105,  /* (69) position ::= expr between position AND position */
   105,  /* (70) position ::= expr LT position COMMA position GT */
   105,  /* (71) position ::= expr ABOVE position */
   105,  /* (72) position ::= expr BELOW position */
   105,  /* (73) position ::= expr LEFT OF position */
   105,  /* (74) position ::= expr RIGHT OF position */
   105,  /* (75) position ::= expr ON HEADING EDGEPT OF position */
   105,  /* (76) position ::= expr HEADING EDGEPT OF position */
   105,  /* (77) position ::= expr EDGEPT OF position */
   105,  /* (78) position ::= expr ON HEADING expr FROM position */
   105,  /* (79) position ::= expr HEADING expr FROM position */
   106,  /* (80) place ::= edge OF object */
   129,  /* (81) place2 ::= object */
   129,  /* (82) place2 ::= object DOT_E edge */
   129,  /* (83) place2 ::= NTH VERTEX OF object */
   107,  /* (84) object ::= nth */
   107,  /* (85) object ::= nth OF|IN object */
   108,  /* (86) objectname ::= PLACENAME */
   108,  /* (87) objectname ::= objectname DOT_U PLACENAME */
   109,  /* (88) nth ::= NTH CLASSNAME */
   109,  /* (89) nth ::= NTH LAST CLASSNAME */
   109,  /* (90) nth ::= LAST CLASSNAME */
   109,  /* (91) nth ::= LAST */
   109,  /* (92) nth ::= NTH LB RB */
   109,  /* (93) nth ::= NTH LAST LB RB */
   109,  /* (94) nth ::= LAST LB RB */
    98,  /* (95) expr ::= expr PLUS expr */
    98,  /* (96) expr ::= expr MINUS expr */
    98,  /* (97) expr ::= expr STAR expr */
    98,  /* (98) expr ::= expr SLASH expr */
    98,  /* (99) expr ::= MINUS expr */
    98,  /* (100) expr ::= PLUS expr */
    98,  /* (101) expr ::= LP expr RP */
    98,  /* (102) expr ::= NUMBER */
    98,  /* (103) expr ::= ID */
    98,  /* (104) expr ::= FUNC1 LP expr RP */
    98,  /* (105) expr ::= FUNC2 LP expr COMMA expr RP */
    98,  /* (106) expr ::= place2 DOT_XY X */
    98,  /* (107) expr ::= place2 DOT_XY Y */
    98,  /* (108) expr ::= object DOT_L numproperty */
    98,  /* (109) expr ::= object DOT_L dashproperty */
    98,  /* (110) expr ::= object DOT_L colorproperty */
   112,  /* (111) lvalue ::= ID */
   112,  /* (112) lvalue ::= FILL */
   112,  /* (113) lvalue ::= COLOR */
   112,  /* (114) lvalue ::= THICKNESS */
   111,  /* (115) rvalue ::= expr */
   117,  /* (116) print ::= PRINT */
   118,  /* (117) prlist ::= pritem */
   118,  /* (118) prlist ::= prlist prsep pritem */
   101,  /* (119) direction ::= UP */
   101,  /* (120) direction ::= DOWN */
   101,  /* (121) direction ::= LEFT */
   101,  /* (122) direction ::= RIGHT */
   115,  /* (123) optrelexpr ::= relexpr */
   121,  /* (124) attribute_list ::= alist */
   123,  /* (125) alist ::= */
   123,  /* (126) alist ::= alist attribute */
   124,  /* (127) attribute ::= boolproperty */
   124,  /* (128) attribute ::= WITH withclause */
   125,  /* (129) go ::= GO */
   125,  /* (130) go ::= */
   113,  /* (131) even ::= UNTIL EVEN WITH */
   113,  /* (132) even ::= EVEN WITH */
   102,  /* (133) dashproperty ::= DOTTED */
   102,  /* (134) dashproperty ::= DASHED */
   103,  /* (135) colorproperty ::= FILL */
   103,  /* (136) colorproperty ::= COLOR */
   105,  /* (137) position ::= place */
   128,  /* (138) between ::= WAY BETWEEN */
   128,  /* (139) between ::= BETWEEN */
   128,  /* (140) between ::= OF THE WAY BETWEEN */
   106,  /* (141) place ::= place2 */
   100,  /* (142) edge ::= CENTER */
   100,  /* (143) edge ::= EDGEPT */
   100,  /* (144) edge ::= TOP */
   100,  /* (145) edge ::= BOTTOM */
   100,  /* (146) edge ::= START */
   100,  /* (147) edge ::= END */
   100,  /* (148) edge ::= RIGHT */
   100,  /* (149) edge ::= LEFT */
   107,  /* (150) object ::= objectname */
};

/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number
** of symbols on the right-hand side of that rule. */
static const signed char yyRuleInfoNRhs[] = {
   -1,  /* (0) document ::= element_list */
   -1,  /* (1) element_list ::= element */
   -3,  /* (2) element_list ::= element_list EOL element */
    0,  /* (3) element ::= */
   -1,  /* (4) element ::= direction */
   -3,  /* (5) element ::= lvalue ASSIGN rvalue */
   -3,  /* (6) element ::= PLACENAME COLON unnamed_element */
   -3,  /* (7) element ::= PLACENAME COLON position */
   -1,  /* (8) element ::= unnamed_element */
   -2,  /* (9) element ::= print prlist */
   -6,  /* (10) element ::= ASSERT LP expr EQ expr RP */
   -6,  /* (11) element ::= ASSERT LP place EQ place RP */
   -1,  /* (12) rvalue ::= PLACENAME */
   -1,  /* (13) pritem ::= FILL */
   -1,  /* (14) pritem ::= COLOR */
   -1,  /* (15) pritem ::= THICKNESS */
   -1,  /* (16) pritem ::= rvalue */
   -1,  /* (17) pritem ::= STRING */
   -1,  /* (18) prsep ::= COMMA */
   -2,  /* (19) unnamed_element ::= basetype attribute_list */
   -1,  /* (20) basetype ::= CLASSNAME */
   -2,  /* (21) basetype ::= STRING textposition */
   -4,  /* (22) basetype ::= LB savelist element_list RB */
    0,  /* (23) savelist ::= */
   -1,  /* (24) relexpr ::= expr */
   -2,  /* (25) relexpr ::= expr PERCENT */
    0,  /* (26) optrelexpr ::= */
   -2,  /* (27) attribute_list ::= relexpr alist */
   -2,  /* (28) attribute ::= numproperty relexpr */
   -2,  /* (29) attribute ::= dashproperty expr */
   -1,  /* (30) attribute ::= dashproperty */
   -2,  /* (31) attribute ::= colorproperty rvalue */
   -3,  /* (32) attribute ::= go direction optrelexpr */
   -4,  /* (33) attribute ::= go direction even position */
   -1,  /* (34) attribute ::= CLOSE */
   -1,  /* (35) attribute ::= CHOP */
   -2,  /* (36) attribute ::= FROM position */
   -2,  /* (37) attribute ::= TO position */
   -1,  /* (38) attribute ::= THEN */
   -4,  /* (39) attribute ::= THEN optrelexpr HEADING expr */
   -3,  /* (40) attribute ::= THEN optrelexpr EDGEPT */
   -4,  /* (41) attribute ::= GO optrelexpr HEADING expr */
   -3,  /* (42) attribute ::= GO optrelexpr EDGEPT */
   -2,  /* (43) attribute ::= AT position */
   -1,  /* (44) attribute ::= SAME */
   -3,  /* (45) attribute ::= SAME AS object */
   -2,  /* (46) attribute ::= STRING textposition */
   -1,  /* (47) attribute ::= FIT */
   -2,  /* (48) attribute ::= BEHIND object */
   -4,  /* (49) withclause ::= DOT_E edge AT position */
   -3,  /* (50) withclause ::= edge AT position */
   -1,  /* (51) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */
   -1,  /* (52) boolproperty ::= CW */
   -1,  /* (53) boolproperty ::= CCW */
   -1,  /* (54) boolproperty ::= LARROW */
   -1,  /* (55) boolproperty ::= RARROW */
   -1,  /* (56) boolproperty ::= LRARROW */
   -1,  /* (57) boolproperty ::= INVIS */
   -1,  /* (58) boolproperty ::= THICK */
   -1,  /* (59) boolproperty ::= THIN */
    0,  /* (60) textposition ::= */
   -2,  /* (61) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|ALIGNED|BIG|SMALL */
   -3,  /* (62) position ::= expr COMMA expr */
   -5,  /* (63) position ::= place PLUS expr COMMA expr */
   -5,  /* (64) position ::= place MINUS expr COMMA expr */
   -7,  /* (65) position ::= place PLUS LP expr COMMA expr RP */
   -7,  /* (66) position ::= place MINUS LP expr COMMA expr RP */
   -5,  /* (67) position ::= LP position COMMA position RP */
   -3,  /* (68) position ::= LP position RP */
   -5,  /* (69) position ::= expr between position AND position */
   -6,  /* (70) position ::= expr LT position COMMA position GT */
   -3,  /* (71) position ::= expr ABOVE position */
   -3,  /* (72) position ::= expr BELOW position */
   -4,  /* (73) position ::= expr LEFT OF position */
   -4,  /* (74) position ::= expr RIGHT OF position */
   -6,  /* (75) position ::= expr ON HEADING EDGEPT OF position */
   -5,  /* (76) position ::= expr HEADING EDGEPT OF position */
   -4,  /* (77) position ::= expr EDGEPT OF position */
   -6,  /* (78) position ::= expr ON HEADING expr FROM position */
   -5,  /* (79) position ::= expr HEADING expr FROM position */
   -3,  /* (80) place ::= edge OF object */
   -1,  /* (81) place2 ::= object */
   -3,  /* (82) place2 ::= object DOT_E edge */
   -4,  /* (83) place2 ::= NTH VERTEX OF object */
   -1,  /* (84) object ::= nth */
   -3,  /* (85) object ::= nth OF|IN object */
   -1,  /* (86) objectname ::= PLACENAME */
   -3,  /* (87) objectname ::= objectname DOT_U PLACENAME */
   -2,  /* (88) nth ::= NTH CLASSNAME */
   -3,  /* (89) nth ::= NTH LAST CLASSNAME */
   -2,  /* (90) nth ::= LAST CLASSNAME */
   -1,  /* (91) nth ::= LAST */
   -3,  /* (92) nth ::= NTH LB RB */
   -4,  /* (93) nth ::= NTH LAST LB RB */
   -3,  /* (94) nth ::= LAST LB RB */
   -3,  /* (95) expr ::= expr PLUS expr */
   -3,  /* (96) expr ::= expr MINUS expr */
   -3,  /* (97) expr ::= expr STAR expr */
   -3,  /* (98) expr ::= expr SLASH expr */
   -2,  /* (99) expr ::= MINUS expr */
   -2,  /* (100) expr ::= PLUS expr */
   -3,  /* (101) expr ::= LP expr RP */
   -1,  /* (102) expr ::= NUMBER */
   -1,  /* (103) expr ::= ID */
   -4,  /* (104) expr ::= FUNC1 LP expr RP */
   -6,  /* (105) expr ::= FUNC2 LP expr COMMA expr RP */
   -3,  /* (106) expr ::= place2 DOT_XY X */
   -3,  /* (107) expr ::= place2 DOT_XY Y */
   -3,  /* (108) expr ::= object DOT_L numproperty */
   -3,  /* (109) expr ::= object DOT_L dashproperty */
   -3,  /* (110) expr ::= object DOT_L colorproperty */
   -1,  /* (111) lvalue ::= ID */
   -1,  /* (112) lvalue ::= FILL */
   -1,  /* (113) lvalue ::= COLOR */
   -1,  /* (114) lvalue ::= THICKNESS */
   -1,  /* (115) rvalue ::= expr */
   -1,  /* (116) print ::= PRINT */
   -1,  /* (117) prlist ::= pritem */
   -3,  /* (118) prlist ::= prlist prsep pritem */
   -1,  /* (119) direction ::= UP */
   -1,  /* (120) direction ::= DOWN */
   -1,  /* (121) direction ::= LEFT */
   -1,  /* (122) direction ::= RIGHT */
   -1,  /* (123) optrelexpr ::= relexpr */
   -1,  /* (124) attribute_list ::= alist */
    0,  /* (125) alist ::= */
   -2,  /* (126) alist ::= alist attribute */
   -1,  /* (127) attribute ::= boolproperty */
   -2,  /* (128) attribute ::= WITH withclause */
   -1,  /* (129) go ::= GO */
    0,  /* (130) go ::= */
   -3,  /* (131) even ::= UNTIL EVEN WITH */
   -2,  /* (132) even ::= EVEN WITH */
   -1,  /* (133) dashproperty ::= DOTTED */
   -1,  /* (134) dashproperty ::= DASHED */
   -1,  /* (135) colorproperty ::= FILL */
   -1,  /* (136) colorproperty ::= COLOR */
   -1,  /* (137) position ::= place */
   -2,  /* (138) between ::= WAY BETWEEN */
   -1,  /* (139) between ::= BETWEEN */
   -4,  /* (140) between ::= OF THE WAY BETWEEN */
   -1,  /* (141) place ::= place2 */
   -1,  /* (142) edge ::= CENTER */
   -1,  /* (143) edge ::= EDGEPT */
   -1,  /* (144) edge ::= TOP */
   -1,  /* (145) edge ::= BOTTOM */
   -1,  /* (146) edge ::= START */
   -1,  /* (147) edge ::= END */
   -1,  /* (148) edge ::= RIGHT */
   -1,  /* (149) edge ::= LEFT */
   -1,  /* (150) object ::= objectname */
};

static void yy_accept(yyParser*);  /* Forward Declaration */

/*
** Perform a reduce action and the shift that must immediately
** follow the reduce.
**
** The yyLookahead and yyLookaheadToken parameters provide reduce actions
** access to the lookahead token (if any).  The yyLookahead will be YYNOCODE
** if the lookahead token has already been consumed.  As this procedure is
** only called from one place, optimizing compilers will in-line it, which
** means that the extra parameters have no performance impact.
*/
static YYACTIONTYPE yy_reduce(
  yyParser *yypParser,         /* The parser */
  unsigned int yyruleno,       /* Number of the rule by which to reduce */
  int yyLookahead,             /* Lookahead token, or YYNOCODE if none */
  pik_parserTOKENTYPE yyLookaheadToken  /* Value of the lookahead token */
  pik_parserCTX_PDECL                   /* %extra_context */
){
  int yygoto;                     /* The next state */
  YYACTIONTYPE yyact;             /* The next action */
  yyStackEntry *yymsp;            /* The top of the parser's stack */
  int yysize;                     /* Amount to pop the stack */
  pik_parserARG_FETCH
  (void)yyLookahead;
  (void)yyLookaheadToken;
  yymsp = yypParser->yytos;
#ifndef NDEBUG
  if( yyTraceFILE && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
    yysize = yyRuleInfoNRhs[yyruleno];
    if( yysize ){
      fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n",
        yyTracePrompt,
        yyruleno, yyRuleName[yyruleno],
        yyruleno<YYNRULE_WITH_ACTION ? "" : " without external action",
        yymsp[yysize].stateno);
    }else{
      fprintf(yyTraceFILE, "%sReduce %d [%s]%s.\n",
        yyTracePrompt, yyruleno, yyRuleName[yyruleno],
        yyruleno<YYNRULE_WITH_ACTION ? "" : " without external action");
    }
  }
#endif /* NDEBUG */

  /* Check that the stack is large enough to grow by a single entry
  ** if the RHS of the rule is empty.  This ensures that there is room
  ** enough on the stack to push the LHS value */
  if( yyRuleInfoNRhs[yyruleno]==0 ){
#ifdef YYTRACKMAXSTACKDEPTH
    if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
      yypParser->yyhwm++;
      assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack));
    }
#endif
#if YYSTACKDEPTH>0 
    if( yypParser->yytos>=yypParser->yystackEnd ){
      yyStackOverflow(yypParser);
      /* The call to yyStackOverflow() above pops the stack until it is
      ** empty, causing the main parser loop to exit.  So the return value
      ** is never used and does not matter. */
      return 0;
    }
#else
    if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){
      if( yyGrowStack(yypParser) ){
        yyStackOverflow(yypParser);
        /* The call to yyStackOverflow() above pops the stack until it is
        ** empty, causing the main parser loop to exit.  So the return value
        ** is never used and does not matter. */
        return 0;
      }
      yymsp = yypParser->yytos;
    }
#endif
  }

  switch( yyruleno ){
  /* Beginning here are the reduction cases.  A typical example
  ** follows:
  **   case 0:
  **  #line <lineno> <grammarfile>
  **     { ... }           // User supplied code
  **  #line <lineno> <thisfile>
  **     break;
  */
/********** Begin reduce actions **********************************************/
        YYMINORTYPE yylhsminor;
      case 0: /* document ::= element_list */
#line 514 "pikchr.y"
{pik_render(p,yymsp[0].minor.yy72);}
#line 2365 "pikchr.c"
        break;
      case 1: /* element_list ::= element */
#line 517 "pikchr.y"
{ yylhsminor.yy72 = pik_elist_append(p,0,yymsp[0].minor.yy254); }
#line 2370 "pikchr.c"
  yymsp[0].minor.yy72 = yylhsminor.yy72;
        break;
      case 2: /* element_list ::= element_list EOL element */
#line 519 "pikchr.y"
{ yylhsminor.yy72 = pik_elist_append(p,yymsp[-2].minor.yy72,yymsp[0].minor.yy254); }
#line 2376 "pikchr.c"
  yymsp[-2].minor.yy72 = yylhsminor.yy72;
        break;
      case 3: /* element ::= */
#line 522 "pikchr.y"
{ yymsp[1].minor.yy254 = 0; }
#line 2382 "pikchr.c"
        break;
      case 4: /* element ::= direction */
#line 523 "pikchr.y"
{ pik_set_direction(p,yymsp[0].minor.yy0.eCode);  yylhsminor.yy254=0; }
#line 2387 "pikchr.c"
  yymsp[0].minor.yy254 = yylhsminor.yy254;
        break;
      case 5: /* element ::= lvalue ASSIGN rvalue */
#line 524 "pikchr.y"
{pik_set_var(p,&yymsp[-2].minor.yy0,yymsp[0].minor.yy73,&yymsp[-1].minor.yy0); yylhsminor.yy254=0;}
#line 2393 "pikchr.c"
  yymsp[-2].minor.yy254 = yylhsminor.yy254;
        break;
      case 6: /* element ::= PLACENAME COLON unnamed_element */
#line 526 "pikchr.y"
{ yylhsminor.yy254 = yymsp[0].minor.yy254;  pik_elem_setname(p,yymsp[0].minor.yy254,&yymsp[-2].minor.yy0); }
#line 2399 "pikchr.c"
  yymsp[-2].minor.yy254 = yylhsminor.yy254;
        break;
      case 7: /* element ::= PLACENAME COLON position */
#line 528 "pikchr.y"
{ yylhsminor.yy254 = pik_elem_new(p,0,0,0);
                 if(yylhsminor.yy254){ yylhsminor.yy254->ptAt = yymsp[0].minor.yy139; pik_elem_setname(p,yylhsminor.yy254,&yymsp[-2].minor.yy0); }}
#line 2406 "pikchr.c"
  yymsp[-2].minor.yy254 = yylhsminor.yy254;
        break;
      case 8: /* element ::= unnamed_element */
#line 530 "pikchr.y"
{yylhsminor.yy254 = yymsp[0].minor.yy254;}
#line 2412 "pikchr.c"
  yymsp[0].minor.yy254 = yylhsminor.yy254;
        break;
      case 9: /* element ::= print prlist */
#line 531 "pikchr.y"
{pik_append(p,"<br>\n",5); yymsp[-1].minor.yy254=0;}
#line 2418 "pikchr.c"
        break;
      case 10: /* element ::= ASSERT LP expr EQ expr RP */
#line 536 "pikchr.y"
{yymsp[-5].minor.yy254=pik_assert(p,yymsp[-3].minor.yy73,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy73);}
#line 2423 "pikchr.c"
        break;
      case 11: /* element ::= ASSERT LP place EQ place RP */
#line 538 "pikchr.y"
{yymsp[-5].minor.yy254=pik_place_assert(p,&yymsp[-3].minor.yy139,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy139);}
#line 2428 "pikchr.c"
        break;
      case 12: /* rvalue ::= PLACENAME */
#line 549 "pikchr.y"
{yylhsminor.yy73 = pik_lookup_color(p,&yymsp[0].minor.yy0);}
#line 2433 "pikchr.c"
  yymsp[0].minor.yy73 = yylhsminor.yy73;
        break;
      case 13: /* pritem ::= FILL */
      case 14: /* pritem ::= COLOR */ yytestcase(yyruleno==14);
      case 15: /* pritem ::= THICKNESS */ yytestcase(yyruleno==15);
#line 554 "pikchr.y"
{pik_append_num(p,"",pik_value(p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.n,0));}
#line 2441 "pikchr.c"
        break;
      case 16: /* pritem ::= rvalue */
#line 557 "pikchr.y"
{pik_append_num(p,"",yymsp[0].minor.yy73);}
#line 2446 "pikchr.c"
        break;
      case 17: /* pritem ::= STRING */
#line 558 "pikchr.y"
{pik_append_text(p,yymsp[0].minor.yy0.z+1,yymsp[0].minor.yy0.n-2,0);}
#line 2451 "pikchr.c"
        break;
      case 18: /* prsep ::= COMMA */
#line 559 "pikchr.y"
{pik_append(p, " ", 1);}
#line 2456 "pikchr.c"
        break;
      case 19: /* unnamed_element ::= basetype attribute_list */
#line 562 "pikchr.y"
{yylhsminor.yy254 = yymsp[-1].minor.yy254; pik_after_adding_attributes(p,yylhsminor.yy254);}
#line 2461 "pikchr.c"
  yymsp[-1].minor.yy254 = yylhsminor.yy254;
        break;
      case 20: /* basetype ::= CLASSNAME */
#line 564 "pikchr.y"
{yylhsminor.yy254 = pik_elem_new(p,&yymsp[0].minor.yy0,0,0); }
#line 2467 "pikchr.c"
  yymsp[0].minor.yy254 = yylhsminor.yy254;
        break;
      case 21: /* basetype ::= STRING textposition */
#line 566 "pikchr.y"
{yymsp[-1].minor.yy0.eCode = yymsp[0].minor.yy74; yylhsminor.yy254 = pik_elem_new(p,0,&yymsp[-1].minor.yy0,0); }
#line 2473 "pikchr.c"
  yymsp[-1].minor.yy254 = yylhsminor.yy254;
        break;
      case 22: /* basetype ::= LB savelist element_list RB */
#line 568 "pikchr.y"
{ p->list = yymsp[-2].minor.yy72; yymsp[-3].minor.yy254 = pik_elem_new(p,0,0,yymsp[-1].minor.yy72); if(yymsp[-3].minor.yy254) yymsp[-3].minor.yy254->errTok = yymsp[0].minor.yy0; }
#line 2479 "pikchr.c"
        break;
      case 23: /* savelist ::= */
#line 573 "pikchr.y"
{yymsp[1].minor.yy72 = p->list; p->list = 0;}
#line 2484 "pikchr.c"
        break;
      case 24: /* relexpr ::= expr */
#line 580 "pikchr.y"
{yylhsminor.yy60.rAbs = yymsp[0].minor.yy73; yylhsminor.yy60.rRel = 0;}
#line 2489 "pikchr.c"
  yymsp[0].minor.yy60 = yylhsminor.yy60;
        break;
      case 25: /* relexpr ::= expr PERCENT */
#line 581 "pikchr.y"
{yylhsminor.yy60.rAbs = 0; yylhsminor.yy60.rRel = yymsp[-1].minor.yy73/100;}
#line 2495 "pikchr.c"
  yymsp[-1].minor.yy60 = yylhsminor.yy60;
        break;
      case 26: /* optrelexpr ::= */
#line 583 "pikchr.y"
{yymsp[1].minor.yy60.rAbs = 0; yymsp[1].minor.yy60.rRel = 1.0;}
#line 2501 "pikchr.c"
        break;
      case 27: /* attribute_list ::= relexpr alist */
#line 585 "pikchr.y"
{pik_add_direction(p,0,&yymsp[-1].minor.yy60);}
#line 2506 "pikchr.c"
        break;
      case 28: /* attribute ::= numproperty relexpr */
#line 589 "pikchr.y"
{ pik_set_numprop(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy60); }
#line 2511 "pikchr.c"
        break;
      case 29: /* attribute ::= dashproperty expr */
#line 590 "pikchr.y"
{ pik_set_dashed(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy73); }
#line 2516 "pikchr.c"
        break;
      case 30: /* attribute ::= dashproperty */
#line 591 "pikchr.y"
{ pik_set_dashed(p,&yymsp[0].minor.yy0,0);  }
#line 2521 "pikchr.c"
        break;
      case 31: /* attribute ::= colorproperty rvalue */
#line 592 "pikchr.y"
{ pik_set_clrprop(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy73); }
#line 2526 "pikchr.c"
        break;
      case 32: /* attribute ::= go direction optrelexpr */
#line 593 "pikchr.y"
{ pik_add_direction(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy60);}
#line 2531 "pikchr.c"
        break;
      case 33: /* attribute ::= go direction even position */
#line 594 "pikchr.y"
{pik_evenwith(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy139);}
#line 2536 "pikchr.c"
        break;
      case 34: /* attribute ::= CLOSE */
#line 595 "pikchr.y"
{ pik_close_path(p,&yymsp[0].minor.yy0); }
#line 2541 "pikchr.c"
        break;
      case 35: /* attribute ::= CHOP */
#line 596 "pikchr.y"
{ p->cur->bChop = 1; }
#line 2546 "pikchr.c"
        break;
      case 36: /* attribute ::= FROM position */
#line 597 "pikchr.y"
{ pik_set_from(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy139); }
#line 2551 "pikchr.c"
        break;
      case 37: /* attribute ::= TO position */
#line 598 "pikchr.y"
{ pik_add_to(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy139); }
#line 2556 "pikchr.c"
        break;
      case 38: /* attribute ::= THEN */
#line 599 "pikchr.y"
{ pik_then(p, &yymsp[0].minor.yy0, p->cur); }
#line 2561 "pikchr.c"
        break;
      case 39: /* attribute ::= THEN optrelexpr HEADING expr */
      case 41: /* attribute ::= GO optrelexpr HEADING expr */ yytestcase(yyruleno==41);
#line 601 "pikchr.y"
{pik_move_hdg(p,&yymsp[-2].minor.yy60,&yymsp[-1].minor.yy0,yymsp[0].minor.yy73,0,&yymsp[-3].minor.yy0);}
#line 2567 "pikchr.c"
        break;
      case 40: /* attribute ::= THEN optrelexpr EDGEPT */
      case 42: /* attribute ::= GO optrelexpr EDGEPT */ yytestcase(yyruleno==42);
#line 602 "pikchr.y"
{pik_move_hdg(p,&yymsp[-1].minor.yy60,0,0,&yymsp[0].minor.yy0,&yymsp[-2].minor.yy0);}
#line 2573 "pikchr.c"
        break;
      case 43: /* attribute ::= AT position */
#line 607 "pikchr.y"
{ pik_set_at(p,0,&yymsp[0].minor.yy139,&yymsp[-1].minor.yy0); }
#line 2578 "pikchr.c"
        break;
      case 44: /* attribute ::= SAME */
#line 609 "pikchr.y"
{pik_same(p,0,&yymsp[0].minor.yy0);}
#line 2583 "pikchr.c"
        break;
      case 45: /* attribute ::= SAME AS object */
#line 610 "pikchr.y"
{pik_same(p,yymsp[0].minor.yy254,&yymsp[-2].minor.yy0);}
#line 2588 "pikchr.c"
        break;
      case 46: /* attribute ::= STRING textposition */
#line 611 "pikchr.y"
{pik_add_txt(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy74);}
#line 2593 "pikchr.c"
        break;
      case 47: /* attribute ::= FIT */
#line 612 "pikchr.y"
{pik_size_to_fit(p,&yymsp[0].minor.yy0); }
#line 2598 "pikchr.c"
        break;
      case 48: /* attribute ::= BEHIND object */
#line 613 "pikchr.y"
{pik_behind(p,yymsp[0].minor.yy254);}
#line 2603 "pikchr.c"
        break;
      case 49: /* withclause ::= DOT_E edge AT position */
      case 50: /* withclause ::= edge AT position */ yytestcase(yyruleno==50);
#line 621 "pikchr.y"
{ pik_set_at(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy139,&yymsp[-1].minor.yy0); }
#line 2609 "pikchr.c"
        break;
      case 51: /* numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */
#line 625 "pikchr.y"
{yylhsminor.yy0 = yymsp[0].minor.yy0;}
#line 2614 "pikchr.c"
  yymsp[0].minor.yy0 = yylhsminor.yy0;
        break;
      case 52: /* boolproperty ::= CW */
#line 636 "pikchr.y"
{p->cur->cw = 1;}
#line 2620 "pikchr.c"
        break;
      case 53: /* boolproperty ::= CCW */
#line 637 "pikchr.y"
{p->cur->cw = 0;}
#line 2625 "pikchr.c"
        break;
      case 54: /* boolproperty ::= LARROW */
#line 638 "pikchr.y"
{p->cur->larrow=1; p->cur->rarrow=0; }
#line 2630 "pikchr.c"
        break;
      case 55: /* boolproperty ::= RARROW */
#line 639 "pikchr.y"
{p->cur->larrow=0; p->cur->rarrow=1; }
#line 2635 "pikchr.c"
        break;
      case 56: /* boolproperty ::= LRARROW */
#line 640 "pikchr.y"
{p->cur->larrow=1; p->cur->rarrow=1; }
#line 2640 "pikchr.c"
        break;
      case 57: /* boolproperty ::= INVIS */
#line 641 "pikchr.y"
{p->cur->sw = 0.0;}
#line 2645 "pikchr.c"
        break;
      case 58: /* boolproperty ::= THICK */
#line 642 "pikchr.y"
{p->cur->sw *= 1.5;}
#line 2650 "pikchr.c"
        break;
      case 59: /* boolproperty ::= THIN */
#line 643 "pikchr.y"
{p->cur->sw *= 0.67;}
#line 2655 "pikchr.c"
        break;
      case 60: /* textposition ::= */
#line 645 "pikchr.y"
{yymsp[1].minor.yy74 = 0;}
#line 2660 "pikchr.c"
        break;
      case 61: /* textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|ALIGNED|BIG|SMALL */
#line 648 "pikchr.y"
{yylhsminor.yy74 = pik_text_position(yymsp[-1].minor.yy74,&yymsp[0].minor.yy0);}
#line 2665 "pikchr.c"
  yymsp[-1].minor.yy74 = yylhsminor.yy74;
        break;
      case 62: /* position ::= expr COMMA expr */
#line 651 "pikchr.y"
{yylhsminor.yy139.x=yymsp[-2].minor.yy73; yylhsminor.yy139.y=yymsp[0].minor.yy73;}
#line 2671 "pikchr.c"
  yymsp[-2].minor.yy139 = yylhsminor.yy139;
        break;
      case 63: /* position ::= place PLUS expr COMMA expr */
#line 653 "pikchr.y"
{yylhsminor.yy139.x=yymsp[-4].minor.yy139.x+yymsp[-2].minor.yy73; yylhsminor.yy139.y=yymsp[-4].minor.yy139.y+yymsp[0].minor.yy73;}
#line 2677 "pikchr.c"
  yymsp[-4].minor.yy139 = yylhsminor.yy139;
        break;
      case 64: /* position ::= place MINUS expr COMMA expr */
#line 654 "pikchr.y"
{yylhsminor.yy139.x=yymsp[-4].minor.yy139.x-yymsp[-2].minor.yy73; yylhsminor.yy139.y=yymsp[-4].minor.yy139.y-yymsp[0].minor.yy73;}
#line 2683 "pikchr.c"
  yymsp[-4].minor.yy139 = yylhsminor.yy139;
        break;
      case 65: /* position ::= place PLUS LP expr COMMA expr RP */
#line 656 "pikchr.y"
{yylhsminor.yy139.x=yymsp[-6].minor.yy139.x+yymsp[-3].minor.yy73; yylhsminor.yy139.y=yymsp[-6].minor.yy139.y+yymsp[-1].minor.yy73;}
#line 2689 "pikchr.c"
  yymsp[-6].minor.yy139 = yylhsminor.yy139;
        break;
      case 66: /* position ::= place MINUS LP expr COMMA expr RP */
#line 658 "pikchr.y"
{yylhsminor.yy139.x=yymsp[-6].minor.yy139.x-yymsp[-3].minor.yy73; yylhsminor.yy139.y=yymsp[-6].minor.yy139.y-yymsp[-1].minor.yy73;}
#line 2695 "pikchr.c"
  yymsp[-6].minor.yy139 = yylhsminor.yy139;
        break;
      case 67: /* position ::= LP position COMMA position RP */
#line 659 "pikchr.y"
{yymsp[-4].minor.yy139.x=yymsp[-3].minor.yy139.x; yymsp[-4].minor.yy139.y=yymsp[-1].minor.yy139.y;}
#line 2701 "pikchr.c"
        break;
      case 68: /* position ::= LP position RP */
#line 660 "pikchr.y"
{yymsp[-2].minor.yy139=yymsp[-1].minor.yy139;}
#line 2706 "pikchr.c"
        break;
      case 69: /* position ::= expr between position AND position */
#line 662 "pikchr.y"
{yylhsminor.yy139 = pik_position_between(yymsp[-4].minor.yy73,yymsp[-2].minor.yy139,yymsp[0].minor.yy139);}
#line 2711 "pikchr.c"
  yymsp[-4].minor.yy139 = yylhsminor.yy139;
        break;
      case 70: /* position ::= expr LT position COMMA position GT */
#line 664 "pikchr.y"
{yylhsminor.yy139 = pik_position_between(yymsp[-5].minor.yy73,yymsp[-3].minor.yy139,yymsp[-1].minor.yy139);}
#line 2717 "pikchr.c"
  yymsp[-5].minor.yy139 = yylhsminor.yy139;
        break;
      case 71: /* position ::= expr ABOVE position */
#line 665 "pikchr.y"
{yylhsminor.yy139=yymsp[0].minor.yy139; yylhsminor.yy139.y += yymsp[-2].minor.yy73;}
#line 2723 "pikchr.c"
  yymsp[-2].minor.yy139 = yylhsminor.yy139;
        break;
      case 72: /* position ::= expr BELOW position */
#line 666 "pikchr.y"
{yylhsminor.yy139=yymsp[0].minor.yy139; yylhsminor.yy139.y -= yymsp[-2].minor.yy73;}
#line 2729 "pikchr.c"
  yymsp[-2].minor.yy139 = yylhsminor.yy139;
        break;
      case 73: /* position ::= expr LEFT OF position */
#line 667 "pikchr.y"
{yylhsminor.yy139=yymsp[0].minor.yy139; yylhsminor.yy139.x -= yymsp[-3].minor.yy73;}
#line 2735 "pikchr.c"
  yymsp[-3].minor.yy139 = yylhsminor.yy139;
        break;
      case 74: /* position ::= expr RIGHT OF position */
#line 668 "pikchr.y"
{yylhsminor.yy139=yymsp[0].minor.yy139; yylhsminor.yy139.x += yymsp[-3].minor.yy73;}
#line 2741 "pikchr.c"
  yymsp[-3].minor.yy139 = yylhsminor.yy139;
        break;
      case 75: /* position ::= expr ON HEADING EDGEPT OF position */
#line 670 "pikchr.y"
{yylhsminor.yy139 = pik_position_at_hdg(yymsp[-5].minor.yy73,&yymsp[-2].minor.yy0,yymsp[0].minor.yy139);}
#line 2747 "pikchr.c"
  yymsp[-5].minor.yy139 = yylhsminor.yy139;
        break;
      case 76: /* position ::= expr HEADING EDGEPT OF position */
#line 672 "pikchr.y"
{yylhsminor.yy139 = pik_position_at_hdg(yymsp[-4].minor.yy73,&yymsp[-2].minor.yy0,yymsp[0].minor.yy139);}
#line 2753 "pikchr.c"
  yymsp[-4].minor.yy139 = yylhsminor.yy139;
        break;
      case 77: /* position ::= expr EDGEPT OF position */
#line 674 "pikchr.y"
{yylhsminor.yy139 = pik_position_at_hdg(yymsp[-3].minor.yy73,&yymsp[-2].minor.yy0,yymsp[0].minor.yy139);}
#line 2759 "pikchr.c"
  yymsp[-3].minor.yy139 = yylhsminor.yy139;
        break;
      case 78: /* position ::= expr ON HEADING expr FROM position */
#line 676 "pikchr.y"
{yylhsminor.yy139 = pik_position_at_angle(yymsp[-5].minor.yy73,yymsp[-2].minor.yy73,yymsp[0].minor.yy139);}
#line 2765 "pikchr.c"
  yymsp[-5].minor.yy139 = yylhsminor.yy139;
        break;
      case 79: /* position ::= expr HEADING expr FROM position */
#line 678 "pikchr.y"
{yylhsminor.yy139 = pik_position_at_angle(yymsp[-4].minor.yy73,yymsp[-2].minor.yy73,yymsp[0].minor.yy139);}
#line 2771 "pikchr.c"
  yymsp[-4].minor.yy139 = yylhsminor.yy139;
        break;
      case 80: /* place ::= edge OF object */
#line 690 "pikchr.y"
{yylhsminor.yy139 = pik_place_of_elem(p,yymsp[0].minor.yy254,&yymsp[-2].minor.yy0);}
#line 2777 "pikchr.c"
  yymsp[-2].minor.yy139 = yylhsminor.yy139;
        break;
      case 81: /* place2 ::= object */
#line 691 "pikchr.y"
{yylhsminor.yy139 = pik_place_of_elem(p,yymsp[0].minor.yy254,0);}
#line 2783 "pikchr.c"
  yymsp[0].minor.yy139 = yylhsminor.yy139;
        break;
      case 82: /* place2 ::= object DOT_E edge */
#line 692 "pikchr.y"
{yylhsminor.yy139 = pik_place_of_elem(p,yymsp[-2].minor.yy254,&yymsp[0].minor.yy0);}
#line 2789 "pikchr.c"
  yymsp[-2].minor.yy139 = yylhsminor.yy139;
        break;
      case 83: /* place2 ::= NTH VERTEX OF object */
#line 693 "pikchr.y"
{yylhsminor.yy139 = pik_nth_vertex(p,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,yymsp[0].minor.yy254);}
#line 2795 "pikchr.c"
  yymsp[-3].minor.yy139 = yylhsminor.yy139;
        break;
      case 84: /* object ::= nth */
#line 705 "pikchr.y"
{yylhsminor.yy254 = pik_find_nth(p,0,&yymsp[0].minor.yy0);}
#line 2801 "pikchr.c"
  yymsp[0].minor.yy254 = yylhsminor.yy254;
        break;
      case 85: /* object ::= nth OF|IN object */
#line 706 "pikchr.y"
{yylhsminor.yy254 = pik_find_nth(p,yymsp[0].minor.yy254,&yymsp[-2].minor.yy0);}
#line 2807 "pikchr.c"
  yymsp[-2].minor.yy254 = yylhsminor.yy254;
        break;
      case 86: /* objectname ::= PLACENAME */
#line 708 "pikchr.y"
{yylhsminor.yy254 = pik_find_byname(p,0,&yymsp[0].minor.yy0);}
#line 2813 "pikchr.c"
  yymsp[0].minor.yy254 = yylhsminor.yy254;
        break;
      case 87: /* objectname ::= objectname DOT_U PLACENAME */
#line 710 "pikchr.y"
{yylhsminor.yy254 = pik_find_byname(p,yymsp[-2].minor.yy254,&yymsp[0].minor.yy0);}
#line 2819 "pikchr.c"
  yymsp[-2].minor.yy254 = yylhsminor.yy254;
        break;
      case 88: /* nth ::= NTH CLASSNAME */
#line 712 "pikchr.y"
{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-1].minor.yy0); }
#line 2825 "pikchr.c"
  yymsp[-1].minor.yy0 = yylhsminor.yy0;
        break;
      case 89: /* nth ::= NTH LAST CLASSNAME */
#line 713 "pikchr.y"
{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-2].minor.yy0); }
#line 2831 "pikchr.c"
  yymsp[-2].minor.yy0 = yylhsminor.yy0;
        break;
      case 90: /* nth ::= LAST CLASSNAME */
#line 714 "pikchr.y"
{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.eCode = -1;}
#line 2837 "pikchr.c"
        break;
      case 91: /* nth ::= LAST */
#line 715 "pikchr.y"
{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -1;}
#line 2842 "pikchr.c"
  yymsp[0].minor.yy0 = yylhsminor.yy0;
        break;
      case 92: /* nth ::= NTH LB RB */
#line 716 "pikchr.y"
{yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-2].minor.yy0);}
#line 2848 "pikchr.c"
  yymsp[-2].minor.yy0 = yylhsminor.yy0;
        break;
      case 93: /* nth ::= NTH LAST LB RB */
#line 717 "pikchr.y"
{yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-3].minor.yy0);}
#line 2854 "pikchr.c"
  yymsp[-3].minor.yy0 = yylhsminor.yy0;
        break;
      case 94: /* nth ::= LAST LB RB */
#line 718 "pikchr.y"
{yymsp[-2].minor.yy0=yymsp[-1].minor.yy0; yymsp[-2].minor.yy0.eCode = -1; }
#line 2860 "pikchr.c"
        break;
      case 95: /* expr ::= expr PLUS expr */
#line 720 "pikchr.y"
{yylhsminor.yy73=yymsp[-2].minor.yy73+yymsp[0].minor.yy73;}
#line 2865 "pikchr.c"
  yymsp[-2].minor.yy73 = yylhsminor.yy73;
        break;
      case 96: /* expr ::= expr MINUS expr */
#line 721 "pikchr.y"
{yylhsminor.yy73=yymsp[-2].minor.yy73-yymsp[0].minor.yy73;}
#line 2871 "pikchr.c"
  yymsp[-2].minor.yy73 = yylhsminor.yy73;
        break;
      case 97: /* expr ::= expr STAR expr */
#line 722 "pikchr.y"
{yylhsminor.yy73=yymsp[-2].minor.yy73*yymsp[0].minor.yy73;}
#line 2877 "pikchr.c"
  yymsp[-2].minor.yy73 = yylhsminor.yy73;
        break;
      case 98: /* expr ::= expr SLASH expr */
#line 723 "pikchr.y"
{
  if( yymsp[0].minor.yy73==0.0 ){ pik_error(p, &yymsp[-1].minor.yy0, "division by zero"); yylhsminor.yy73 = 0.0; }
  else{ yylhsminor.yy73 = yymsp[-2].minor.yy73/yymsp[0].minor.yy73; }
}
#line 2886 "pikchr.c"
  yymsp[-2].minor.yy73 = yylhsminor.yy73;
        break;
      case 99: /* expr ::= MINUS expr */
#line 727 "pikchr.y"
{yymsp[-1].minor.yy73=-yymsp[0].minor.yy73;}
#line 2892 "pikchr.c"
        break;
      case 100: /* expr ::= PLUS expr */
#line 728 "pikchr.y"
{yymsp[-1].minor.yy73=yymsp[0].minor.yy73;}
#line 2897 "pikchr.c"
        break;
      case 101: /* expr ::= LP expr RP */
#line 729 "pikchr.y"
{yymsp[-2].minor.yy73=yymsp[-1].minor.yy73;}
#line 2902 "pikchr.c"
        break;
      case 102: /* expr ::= NUMBER */
#line 730 "pikchr.y"
{yylhsminor.yy73=pik_atof(&yymsp[0].minor.yy0);}
#line 2907 "pikchr.c"
  yymsp[0].minor.yy73 = yylhsminor.yy73;
        break;
      case 103: /* expr ::= ID */
#line 731 "pikchr.y"
{yylhsminor.yy73=pik_get_var(p,&yymsp[0].minor.yy0);}
#line 2913 "pikchr.c"
  yymsp[0].minor.yy73 = yylhsminor.yy73;
        break;
      case 104: /* expr ::= FUNC1 LP expr RP */
#line 732 "pikchr.y"
{yylhsminor.yy73 = pik_func(p,&yymsp[-3].minor.yy0,yymsp[-1].minor.yy73,0.0);}
#line 2919 "pikchr.c"
  yymsp[-3].minor.yy73 = yylhsminor.yy73;
        break;
      case 105: /* expr ::= FUNC2 LP expr COMMA expr RP */
#line 733 "pikchr.y"
{yylhsminor.yy73 = pik_func(p,&yymsp[-5].minor.yy0,yymsp[-3].minor.yy73,yymsp[-1].minor.yy73);}
#line 2925 "pikchr.c"
  yymsp[-5].minor.yy73 = yylhsminor.yy73;
        break;
      case 106: /* expr ::= place2 DOT_XY X */
#line 734 "pikchr.y"
{yylhsminor.yy73 = yymsp[-2].minor.yy139.x;}
#line 2931 "pikchr.c"
  yymsp[-2].minor.yy73 = yylhsminor.yy73;
        break;
      case 107: /* expr ::= place2 DOT_XY Y */
#line 735 "pikchr.y"
{yylhsminor.yy73 = yymsp[-2].minor.yy139.y;}
#line 2937 "pikchr.c"
  yymsp[-2].minor.yy73 = yylhsminor.yy73;
        break;
      case 108: /* expr ::= object DOT_L numproperty */
      case 109: /* expr ::= object DOT_L dashproperty */ yytestcase(yyruleno==109);
      case 110: /* expr ::= object DOT_L colorproperty */ yytestcase(yyruleno==110);
#line 736 "pikchr.y"
{yylhsminor.yy73=pik_property_of(yymsp[-2].minor.yy254,&yymsp[0].minor.yy0);}
#line 2945 "pikchr.c"
  yymsp[-2].minor.yy73 = yylhsminor.yy73;
        break;
      default:
      /* (111) lvalue ::= ID */ yytestcase(yyruleno==111);
      /* (112) lvalue ::= FILL */ yytestcase(yyruleno==112);
      /* (113) lvalue ::= COLOR */ yytestcase(yyruleno==113);
      /* (114) lvalue ::= THICKNESS */ yytestcase(yyruleno==114);
      /* (115) rvalue ::= expr */ yytestcase(yyruleno==115);
      /* (116) print ::= PRINT */ yytestcase(yyruleno==116);
      /* (117) prlist ::= pritem (OPTIMIZED OUT) */ assert(yyruleno!=117);
      /* (118) prlist ::= prlist prsep pritem */ yytestcase(yyruleno==118);
      /* (119) direction ::= UP */ yytestcase(yyruleno==119);
      /* (120) direction ::= DOWN */ yytestcase(yyruleno==120);
      /* (121) direction ::= LEFT */ yytestcase(yyruleno==121);
      /* (122) direction ::= RIGHT */ yytestcase(yyruleno==122);
      /* (123) optrelexpr ::= relexpr (OPTIMIZED OUT) */ assert(yyruleno!=123);
      /* (124) attribute_list ::= alist */ yytestcase(yyruleno==124);
      /* (125) alist ::= */ yytestcase(yyruleno==125);
      /* (126) alist ::= alist attribute */ yytestcase(yyruleno==126);
      /* (127) attribute ::= boolproperty (OPTIMIZED OUT) */ assert(yyruleno!=127);
      /* (128) attribute ::= WITH withclause */ yytestcase(yyruleno==128);
      /* (129) go ::= GO */ yytestcase(yyruleno==129);
      /* (130) go ::= */ yytestcase(yyruleno==130);
      /* (131) even ::= UNTIL EVEN WITH */ yytestcase(yyruleno==131);
      /* (132) even ::= EVEN WITH */ yytestcase(yyruleno==132);
      /* (133) dashproperty ::= DOTTED */ yytestcase(yyruleno==133);
      /* (134) dashproperty ::= DASHED */ yytestcase(yyruleno==134);
      /* (135) colorproperty ::= FILL */ yytestcase(yyruleno==135);
      /* (136) colorproperty ::= COLOR */ yytestcase(yyruleno==136);
      /* (137) position ::= place */ yytestcase(yyruleno==137);
      /* (138) between ::= WAY BETWEEN */ yytestcase(yyruleno==138);
      /* (139) between ::= BETWEEN */ yytestcase(yyruleno==139);
      /* (140) between ::= OF THE WAY BETWEEN */ yytestcase(yyruleno==140);
      /* (141) place ::= place2 */ yytestcase(yyruleno==141);
      /* (142) edge ::= CENTER */ yytestcase(yyruleno==142);
      /* (143) edge ::= EDGEPT */ yytestcase(yyruleno==143);
      /* (144) edge ::= TOP */ yytestcase(yyruleno==144);
      /* (145) edge ::= BOTTOM */ yytestcase(yyruleno==145);
      /* (146) edge ::= START */ yytestcase(yyruleno==146);
      /* (147) edge ::= END */ yytestcase(yyruleno==147);
      /* (148) edge ::= RIGHT */ yytestcase(yyruleno==148);
      /* (149) edge ::= LEFT */ yytestcase(yyruleno==149);
      /* (150) object ::= objectname */ yytestcase(yyruleno==150);
        break;
/********** End reduce actions ************************************************/
  };
  assert( yyruleno<sizeof(yyRuleInfoLhs)/sizeof(yyRuleInfoLhs[0]) );
  yygoto = yyRuleInfoLhs[yyruleno];
  yysize = yyRuleInfoNRhs[yyruleno];
  yyact = yy_find_reduce_action(yymsp[yysize].stateno,(YYCODETYPE)yygoto);

  /* There are no SHIFTREDUCE actions on nonterminals because the table
  ** generator has simplified them to pure REDUCE actions. */
  assert( !(yyact>YY_MAX_SHIFT && yyact<=YY_MAX_SHIFTREDUCE) );

  /* It is not possible for a REDUCE to be followed by an error */
  assert( yyact!=YY_ERROR_ACTION );

  yymsp += yysize+1;
  yypParser->yytos = yymsp;
  yymsp->stateno = (YYACTIONTYPE)yyact;
  yymsp->major = (YYCODETYPE)yygoto;
  yyTraceShift(yypParser, yyact, "... then shift");
  return yyact;
}

/*
** The following code executes when the parse fails
*/
#ifndef YYNOERRORRECOVERY
static void yy_parse_failed(
  yyParser *yypParser           /* The parser */
){
  pik_parserARG_FETCH
  pik_parserCTX_FETCH
#ifndef NDEBUG
  if( yyTraceFILE ){
    fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt);
  }
#endif
  while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
  /* Here code is inserted which will be executed whenever the
  ** parser fails */
/************ Begin %parse_failure code ***************************************/
/************ End %parse_failure code *****************************************/
  pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */
  pik_parserCTX_STORE
}
#endif /* YYNOERRORRECOVERY */

/*
** The following code executes when a syntax error first occurs.
*/
static void yy_syntax_error(
  yyParser *yypParser,           /* The parser */
  int yymajor,                   /* The major type of the error token */
  pik_parserTOKENTYPE yyminor         /* The minor type of the error token */
){
  pik_parserARG_FETCH
  pik_parserCTX_FETCH
#define TOKEN yyminor
/************ Begin %syntax_error code ****************************************/
#line 502 "pikchr.y"

  if( TOKEN.z && TOKEN.z[0] ){
    pik_error(p, &TOKEN, "syntax error");
  }else{
    pik_error(p, 0, "syntax error");
  }
  UNUSED_PARAMETER(yymajor);
#line 3056 "pikchr.c"
/************ End %syntax_error code ******************************************/
  pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */
  pik_parserCTX_STORE
}

/*
** The following is executed when the parser accepts
*/
static void yy_accept(
  yyParser *yypParser           /* The parser */
){
  pik_parserARG_FETCH
  pik_parserCTX_FETCH
#ifndef NDEBUG
  if( yyTraceFILE ){
    fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt);
  }
#endif
#ifndef YYNOERRORRECOVERY
  yypParser->yyerrcnt = -1;
#endif
  assert( yypParser->yytos==yypParser->yystack );
  /* Here code is inserted which will be executed whenever the
  ** parser accepts */
/*********** Begin %parse_accept code *****************************************/
/*********** End %parse_accept code *******************************************/
  pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */
  pik_parserCTX_STORE
}

/* The main parser program.
** The first argument is a pointer to a structure obtained from
** "pik_parserAlloc" which describes the current state of the parser.
** The second argument is the major token number.  The third is
** the minor token.  The fourth optional argument is whatever the
** user wants (and specified in the grammar) and is available for
** use by the action routines.
**
** Inputs:
** <ul>
** <li> A pointer to the parser (an opaque structure.)
** <li> The major token number.
** <li> The minor token number.
** <li> An option argument of a grammar-specified type.
** </ul>
**
** Outputs:
** None.
*/
void pik_parser(
  void *yyp,                   /* The parser */
  int yymajor,                 /* The major token code number */
  pik_parserTOKENTYPE yyminor       /* The value for the token */
  pik_parserARG_PDECL               /* Optional %extra_argument parameter */
){
  YYMINORTYPE yyminorunion;
  YYACTIONTYPE yyact;   /* The parser action. */
#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
  int yyendofinput;     /* True if we are at the end of input */
#endif
#ifdef YYERRORSYMBOL
  int yyerrorhit = 0;   /* True if yymajor has invoked an error */
#endif
  yyParser *yypParser = (yyParser*)yyp;  /* The parser */
  pik_parserCTX_FETCH
  pik_parserARG_STORE

  assert( yypParser->yytos!=0 );
#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
  yyendofinput = (yymajor==0);
#endif

  yyact = yypParser->yytos->stateno;
#ifndef NDEBUG
  if( yyTraceFILE ){
    if( yyact < YY_MIN_REDUCE ){
      fprintf(yyTraceFILE,"%sInput '%s' in state %d\n",
              yyTracePrompt,yyTokenName[yymajor],yyact);
    }else{
      fprintf(yyTraceFILE,"%sInput '%s' with pending reduce %d\n",
              yyTracePrompt,yyTokenName[yymajor],yyact-YY_MIN_REDUCE);
    }
  }
#endif

  do{
    assert( yyact==yypParser->yytos->stateno );
    yyact = yy_find_shift_action((YYCODETYPE)yymajor,yyact);
    if( yyact >= YY_MIN_REDUCE ){
      yyact = yy_reduce(yypParser,yyact-YY_MIN_REDUCE,yymajor,
                        yyminor pik_parserCTX_PARAM);
    }else if( yyact <= YY_MAX_SHIFTREDUCE ){
      yy_shift(yypParser,yyact,(YYCODETYPE)yymajor,yyminor);
#ifndef YYNOERRORRECOVERY
      yypParser->yyerrcnt--;
#endif
      break;
    }else if( yyact==YY_ACCEPT_ACTION ){
      yypParser->yytos--;
      yy_accept(yypParser);
      return;
    }else{
      assert( yyact == YY_ERROR_ACTION );
      yyminorunion.yy0 = yyminor;
#ifdef YYERRORSYMBOL
      int yymx;
#endif
#ifndef NDEBUG
      if( yyTraceFILE ){
        fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt);
      }
#endif
#ifdef YYERRORSYMBOL
      /* A syntax error has occurred.
      ** The response to an error depends upon whether or not the
      ** grammar defines an error token "ERROR".  
      **
      ** This is what we do if the grammar does define ERROR:
      **
      **  * Call the %syntax_error function.
      **
      **  * Begin popping the stack until we enter a state where
      **    it is legal to shift the error symbol, then shift
      **    the error symbol.
      **
      **  * Set the error count to three.
      **
      **  * Begin accepting and shifting new tokens.  No new error
      **    processing will occur until three tokens have been
      **    shifted successfully.
      **
      */
      if( yypParser->yyerrcnt<0 ){
        yy_syntax_error(yypParser,yymajor,yyminor);
      }
      yymx = yypParser->yytos->major;
      if( yymx==YYERRORSYMBOL || yyerrorhit ){
#ifndef NDEBUG
        if( yyTraceFILE ){
          fprintf(yyTraceFILE,"%sDiscard input token %s\n",
             yyTracePrompt,yyTokenName[yymajor]);
        }
#endif
        yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion);
        yymajor = YYNOCODE;
      }else{
        while( yypParser->yytos >= yypParser->yystack
            && (yyact = yy_find_reduce_action(
                        yypParser->yytos->stateno,
                        YYERRORSYMBOL)) > YY_MAX_SHIFTREDUCE
        ){
          yy_pop_parser_stack(yypParser);
        }
        if( yypParser->yytos < yypParser->yystack || yymajor==0 ){
          yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
          yy_parse_failed(yypParser);
#ifndef YYNOERRORRECOVERY
          yypParser->yyerrcnt = -1;
#endif
          yymajor = YYNOCODE;
        }else if( yymx!=YYERRORSYMBOL ){
          yy_shift(yypParser,yyact,YYERRORSYMBOL,yyminor);
        }
      }
      yypParser->yyerrcnt = 3;
      yyerrorhit = 1;
      if( yymajor==YYNOCODE ) break;
      yyact = yypParser->yytos->stateno;
#elif defined(YYNOERRORRECOVERY)
      /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to
      ** do any kind of error recovery.  Instead, simply invoke the syntax
      ** error routine and continue going as if nothing had happened.
      **
      ** Applications can set this macro (for example inside %include) if
      ** they intend to abandon the parse upon the first syntax error seen.
      */
      yy_syntax_error(yypParser,yymajor, yyminor);
      yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
      break;
#else  /* YYERRORSYMBOL is not defined */
      /* This is what we do if the grammar does not define ERROR:
      **
      **  * Report an error message, and throw away the input token.
      **
      **  * If the input token is $, then fail the parse.
      **
      ** As before, subsequent error messages are suppressed until
      ** three input tokens have been successfully shifted.
      */
      if( yypParser->yyerrcnt<=0 ){
        yy_syntax_error(yypParser,yymajor, yyminor);
      }
      yypParser->yyerrcnt = 3;
      yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
      if( yyendofinput ){
        yy_parse_failed(yypParser);
#ifndef YYNOERRORRECOVERY
        yypParser->yyerrcnt = -1;
#endif
      }
      break;
#endif
    }
  }while( yypParser->yytos>yypParser->yystack );
#ifndef NDEBUG
  if( yyTraceFILE ){
    yyStackEntry *i;
    char cDiv = '[';
    fprintf(yyTraceFILE,"%sReturn. Stack=",yyTracePrompt);
    for(i=&yypParser->yystack[1]; i<=yypParser->yytos; i++){
      fprintf(yyTraceFILE,"%c%s", cDiv, yyTokenName[i->major]);
      cDiv = ' ';
    }
    fprintf(yyTraceFILE,"]\n");
  }
#endif
  return;
}

/*
** Return the fallback token corresponding to canonical token iToken, or
** 0 if iToken has no fallback.
*/
int pik_parserFallback(int iToken){
#ifdef YYFALLBACK
  assert( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) );
  return yyFallback[iToken];
#else
  (void)iToken;
  return 0;
#endif
}
#line 741 "pikchr.y"



/* Chart of the 140 official HTML color names with their
** corresponding RGB value.
**
** Two new names "None" and "Off" are added with a value
** of -1.
*/
static const struct {
  const char *zName;  /* Name of the color */
  int val;            /* RGB value */
} aColor[] = {
  { "AliceBlue",                   0xf0f8ff },
  { "AntiqueWhite",                0xfaebd7 },
  { "Aqua",                        0x00ffff },
  { "AquaMarine",                  0x7fffd4 },
  { "Azure",                       0xf0ffff },
  { "Beige",                       0xf5f5dc },
  { "Bisque",                      0xffe4c4 },
  { "Black",                       0x000000 },
  { "BlanchedAlmond",              0xffebcd },
  { "Blue",                        0x0000ff },
  { "BlueViolet",                  0x8a2be2 },
  { "Brown",                       0xa52a2a },
  { "BurlyWood",                   0xdeb887 },
  { "CadetBlue",                   0x5f9ea0 },
  { "Chartreuse",                  0x7fff00 },
  { "Chocolate",                   0xd2691e },
  { "Coral",                       0xff7f50 },
  { "CornFlowerBlue",              0x6495ed },
  { "Cornsilk",                    0xfff8dc },
  { "Crimson",                     0xdc143c },
  { "Cyan",                        0x00ffff },
  { "DarkBlue",                    0x00008b },
  { "DarkCyan",                    0x008b8b },
  { "DarkGoldenRod",               0xb8860b },
  { "DarkGray",                    0xa9a9a9 },
  { "DarkGreen",                   0x006400 },
  { "DarkKhaki",                   0xbdb76b },
  { "DarkMagenta",                 0x8b008b },
  { "DarkOliveGreen",              0x556b2f },
  { "DarkOrange",                  0xff8c00 },
  { "DarkOrchid",                  0x9932cc },
  { "DarkRed",                     0x8b0000 },
  { "DarkSalmon",                  0xe9967a },
  { "DarkSeaGreen",                0x8fbc8f },
  { "DarkSlateBlue",               0x483d8b },
  { "DarkSlateGray",               0x2f4f4f },
  { "DarkTurquoise",               0x00ced1 },
  { "DarkViolet",                  0x9400d3 },
  { "DeepPink",                    0xff1493 },
  { "DeepSkyBlue",                 0x00bfff },
  { "DimGray",                     0x696969 },
  { "DodgerBlue",                  0x1e90ff },
  { "FireBrick",                   0xb22222 },
  { "FloralWhite",                 0xfffaf0 },
  { "ForestGreen",                 0x228b22 },
  { "Fuchsia",                     0xff00ff },
  { "Gainsboro",                   0xdcdcdc },
  { "GhostWhite",                  0xf8f8ff },
  { "Gold",                        0xffd700 },
  { "GoldenRod",                   0xdaa520 },
  { "Gray",                        0x808080 },
  { "Green",                       0x008000 },
  { "GreenYellow",                 0xadff2f },
  { "HoneyDew",                    0xf0fff0 },
  { "HotPink",                     0xff69b4 },
  { "IndianRed",                   0xcd5c5c },
  { "Indigo",                      0x4b0082 },
  { "Ivory",                       0xfffff0 },
  { "Khaki",                       0xf0e68c },
  { "Lavender",                    0xe6e6fa },
  { "LavenderBlush",               0xfff0f5 },
  { "LawnGreen",                   0x7cfc00 },
  { "LemonChiffon",                0xfffacd },
  { "LightBlue",                   0xadd8e6 },
  { "LightCoral",                  0xf08080 },
  { "LightCyan",                   0xe0ffff },
  { "LightGoldenrodYellow",        0xfafad2 },
  { "LightGray",                   0xd3d3d3 },
  { "LightGreen",                  0x90ee90 },
  { "LightPink",                   0xffb6c1 },
  { "LightSalmon",                 0xffa07a },
  { "LightSeaGreen",               0x20b2aa },
  { "LightSkyBlue",                0x87cefa },
  { "LightSlateGray",              0x778899 },
  { "LightSteelBlue",              0xb0c4de },
  { "LightYellow",                 0xffffe0 },
  { "Lime",                        0x00ff00 },
  { "LimeGreen",                   0x32cd32 },
  { "Linen",                       0xfaf0e6 },
  { "Magenta",                     0xff00ff },
  { "Maroon",                      0x800000 },
  { "MediumAquaMarine",            0x66cdaa },
  { "MediumBlue",                  0x0000cd },
  { "MediumOrchid",                0xba55d3 },
  { "MediumPurple",                0x9370d8 },
  { "MediumSeaGreen",              0x3cb371 },
  { "MediumSlateBlue",             0x7b68ee },
  { "MediumSpringGreen",           0x00fa9a },
  { "MediumTurquoise",             0x48d1cc },
  { "MediumVioletRed",             0xc71585 },
  { "MidnightBlue",                0x191970 },
  { "MintCream",                   0xf5fffa },
  { "MistyRose",                   0xffe4e1 },
  { "Moccasin",                    0xffe4b5 },
  { "NavajoWhite",                 0xffdead },
  { "Navy",                        0x000080 },
  { "None",                              -1 },  /* Non-standard addition */
  { "Off",                               -1 },  /* Non-standard addition */
  { "OldLace",                     0xfdf5e6 },
  { "Olive",                       0x808000 },
  { "OliveDrab",                   0x6b8e23 },
  { "Orange",                      0xffa500 },
  { "OrangeRed",                   0xff4500 },
  { "Orchid",                      0xda70d6 },
  { "PaleGoldenRod",               0xeee8aa },
  { "PaleGreen",                   0x98fb98 },
  { "PaleTurquoise",               0xafeeee },
  { "PaleVioletRed",               0xdb7093 },
  { "PapayaWhip",                  0xffefd5 },
  { "PeachPuff",                   0xffdab9 },
  { "Peru",                        0xcd853f },
  { "Pink",                        0xffc0cb },
  { "Plum",                        0xdda0dd },
  { "PowderBlue",                  0xb0e0e6 },
  { "Purple",                      0x800080 },
  { "Red",                         0xff0000 },
  { "RosyBrown",                   0xbc8f8f },
  { "RoyalBlue",                   0x4169e1 },
  { "SaddleBrown",                 0x8b4513 },
  { "Salmon",                      0xfa8072 },
  { "SandyBrown",                  0xf4a460 },
  { "SeaGreen",                    0x2e8b57 },
  { "SeaShell",                    0xfff5ee },
  { "Sienna",                      0xa0522d },
  { "Silver",                      0xc0c0c0 },
  { "SkyBlue",                     0x87ceeb },
  { "SlateBlue",                   0x6a5acd },
  { "SlateGray",                   0x708090 },
  { "Snow",                        0xfffafa },
  { "SpringGreen",                 0x00ff7f },
  { "SteelBlue",                   0x4682b4 },
  { "Tan",                         0xd2b48c },
  { "Teal",                        0x008080 },
  { "Thistle",                     0xd8bfd8 },
  { "Tomato",                      0xff6347 },
  { "Turquoise",                   0x40e0d0 },
  { "Violet",                      0xee82ee },
  { "Wheat",                       0xf5deb3 },
  { "White",                       0xffffff },
  { "WhiteSmoke",                  0xf5f5f5 },
  { "Yellow",                      0xffff00 },
  { "YellowGreen",                 0x9acd32 },
};

/* Built-in variable names.
**
** This array is constant.  When a script changes the value of one of
** these built-ins, a new PVar record is added at the head of
** the Pik.pVar list, which is searched first.  Thus the new PVar entry
** will override this default value.
**
** Units are in inches, except for "color" and "fill" which are 
** interpreted as 24-bit RGB values.
**
** Binary search used.  Must be kept in sorted order.
*/
static const struct { const char *zName; PNum val; } aBuiltin[] = {
  { "arcrad",      0.25  },
  { "arrowhead",   2.0   },
  { "arrowht",     0.08  },
  { "arrowwid",    0.06  },
  { "boxht",       0.5   },
  { "boxrad",      0.0   },
  { "boxwid",      0.75  },
  { "charht",      0.14  },
  { "charwid",     0.08  },
  { "circlerad",   0.25  },
  { "color",       0.0   },
  { "cylht",       0.5   },
  { "cylrad",      0.075 },
  { "cylwid",      0.75  },
  { "dashwid",     0.05  },
  { "dotrad",      0.015 },
  { "ellipseht",   0.5   },
  { "ellipsewid",  0.75  },
  { "fileht",      0.75  },
  { "filerad",     0.15  },
  { "filewid",     0.5   },
  { "fill",        -1.0  },
  { "lineht",      0.5   },
  { "linewid",     0.5   },
  { "movewid",     0.5   },
  { "ovalht",      0.5   },
  { "ovalwid",     1.0   },
  { "scale",       1.0   },
  { "textht",      0.5   },
  { "textwid",     0.75  },
  { "thickness",   0.015 },
};


/* Methods for the "arc" class */
static void arcInit(Pik *p, PElem *pElem){
  pElem->w = pik_value(p, "arcrad",6,0);
  pElem->h = pElem->w;
}
/* Hack: Arcs are here rendered as quadratic Bezier curves rather
** than true arcs.  Multiple reasons: (1) the legacy-PIC parameters
** that control arcs are obscure and I could not figure out what they
** mean based on available documentation.  (2) Arcs are rarely used,
** and so do not seem that important.
*/
static PPoint arcControlPoint(int cw, PPoint f, PPoint t, PNum rScale){
  PPoint m;
  PNum dx, dy;
  m.x = 0.5*(f.x+t.x);
  m.y = 0.5*(f.y+t.y);
  dx = t.x - f.x;
  dy = t.y - f.y;
  if( cw ){
    m.x -= 0.5*rScale*dy;
    m.y += 0.5*rScale*dx;
  }else{
    m.x += 0.5*rScale*dy;
    m.y -= 0.5*rScale*dx;
  }
  return m;
}
static void arcCheck(Pik *p, PElem *pElem){
  PPoint m;
  if( p->nTPath>2 ){
    pik_error(p, &pElem->errTok, "arc geometry error");
    return;
  }
  m = arcControlPoint(pElem->cw, p->aTPath[0], p->aTPath[1], 0.5);
  pik_bbox_add_xy(&pElem->bbox, m.x, m.y);
}
static void arcRender(Pik *p, PElem *pElem){
  PPoint f, m, t;
  if( pElem->nPath<2 ) return;
  if( pElem->sw<=0.0 ) return;
  f = pElem->aPath[0];
  t = pElem->aPath[1];
  m = arcControlPoint(pElem->cw,f,t,1.0);
  if( pElem->larrow ){
    pik_draw_arrowhead(p,&m,&f,pElem);
  }
  if( pElem->rarrow ){
    pik_draw_arrowhead(p,&m,&t,pElem);
  }
  pik_append_xy(p,"<path d=\"M", f.x, f.y);
  pik_append_xy(p,"Q", m.x, m.y);
  pik_append_xy(p," ", t.x, t.y);
  pik_append(p,"\" ",2);
  pik_append_style(p,pElem,0);
  pik_append(p,"\" />\n", -1);

  pik_append_txt(p, pElem, 0);
}


/* Methods for the "arrow" class */
static void arrowInit(Pik *p, PElem *pElem){
  pElem->w = pik_value(p, "linewid",7,0);
  pElem->h = pik_value(p, "lineht",6,0);
  pElem->rad = pik_value(p, "linerad",7,0);
  pElem->fill = -1.0;
  pElem->rarrow = 1;
}

/* Methods for the "box" class */
static void boxInit(Pik *p, PElem *pElem){
  pElem->w = pik_value(p, "boxwid",6,0);
  pElem->h = pik_value(p, "boxht",5,0);
  pElem->rad = pik_value(p, "boxrad",6,0);
}
/* Return offset from the center of the box to the compass point 
** given by parameter cp */
static PPoint boxOffset(Pik *p, PElem *pElem, int cp){
  PPoint pt;
  PNum w2 = 0.5*pElem->w;
  PNum h2 = 0.5*pElem->h;
  PNum rad = pElem->rad;
  PNum rx;
  if( rad<=0.0 ){
    rx = 0.0;
  }else{
    if( rad>w2 ) rad = w2;
    if( rad>h2 ) rad = h2;
    rx = 0.29289321881345252392*rad;
  }
  pt.x = pt.y = 0.0;
  switch( cp ){
    case CP_C:   pt.x = 0.0;      pt.y = 0.0;    break;
    case CP_N:   pt.x = 0.0;      pt.y = h2;     break;
    case CP_NE:  pt.x = w2-rx;    pt.y = h2-rx;  break;
    case CP_E:   pt.x = w2;       pt.y = 0.0;    break;
    case CP_SE:  pt.x = w2-rx;    pt.y = rx-h2;  break;
    case CP_S:   pt.x = 0.0;      pt.y = -h2;    break;
    case CP_SW:  pt.x = rx-w2;    pt.y = rx-h2;  break;
    case CP_W:   pt.x = -w2;      pt.y = 0.0;    break;
    case CP_NW:  pt.x = rx-w2;    pt.y = h2-rx;  break;
  }
  UNUSED_PARAMETER(p);
  return pt;
}
static PPoint boxChop(Pik *p, PElem *pElem, PPoint *pPt){
  PNum dx, dy;
  int cp = CP_C;
  PPoint chop = pElem->ptAt;
  if( pElem->w<=0.0 ) return chop;
  if( pElem->h<=0.0 ) return chop;
  dx = (pPt->x - pElem->ptAt.x)*pElem->h/pElem->w;
  dy = (pPt->y - pElem->ptAt.y);
  if( dx>0.0 ){
    if( dy>=2.414*dx ){
      cp = CP_N;
    }else if( dy>=0.414*dx ){
      cp = CP_NE;
    }else if( dy>=-0.414*dx ){
      cp = CP_E;
    }else if( dy>-2.414*dx ){
      cp = CP_SE;
    }else{
      cp = CP_S;
    }
  }else{
    if( dy>=-2.414*dx ){
      cp = CP_N;
    }else if( dy>=-0.414*dx ){
      cp = CP_NW;
    }else if( dy>=0.414*dx ){
      cp = CP_W;
    }else if( dy>2.414*dx ){
      cp = CP_SW;
    }else{
      cp = CP_S;
    }
  }
  chop = pElem->type->xOffset(p,pElem,cp);
  chop.x += pElem->ptAt.x;
  chop.y += pElem->ptAt.y;
  return chop;
}
static void boxFit(Pik *p, PElem *pElem, PNum w, PNum h){
  if( w>0 ) pElem->w = w;
  if( h>0 ) pElem->h = h;
  UNUSED_PARAMETER(p);
}
static void boxRender(Pik *p, PElem *pElem){
  PNum w2 = 0.5*pElem->w;
  PNum h2 = 0.5*pElem->h;
  PNum rad = pElem->rad;
  PPoint pt = pElem->ptAt;
  if( pElem->sw>0.0 ){
    if( rad<=0.0 ){
      pik_append_xy(p,"<path d=\"M", pt.x-w2,pt.y-h2);
      pik_append_xy(p,"L", pt.x+w2,pt.y-h2);
      pik_append_xy(p,"L", pt.x+w2,pt.y+h2);
      pik_append_xy(p,"L", pt.x-w2,pt.y+h2);
      pik_append(p,"Z\" ",-1);
    }else{
      /*
      **         ----       - y3
      **        /    \
      **       /      \     _ y2
      **      |        |    
      **      |        |    _ y1
      **       \      /
      **        \    /
      **         ----       _ y0
      **
      **      '  '  '  '
      **     x0 x1 x2 x3
      */
      PNum x0,x1,x2,x3,y0,y1,y2,y3;
      if( rad>w2 ) rad = w2;
      if( rad>h2 ) rad = h2;
      x0 = pt.x - w2;
      x1 = x0 + rad;
      x3 = pt.x + w2;
      x2 = x3 - rad;
      y0 = pt.y - h2;
      y1 = y0 + rad;
      y3 = pt.y + h2;
      y2 = y3 - rad;
      pik_append_xy(p,"<path d=\"M", x1, y0);
      if( x2>x1 ) pik_append_xy(p, "L", x2, y0);
      pik_append_arc(p, rad, rad, x3, y1);
      if( y2>y1 ) pik_append_xy(p, "L", x3, y2);
      pik_append_arc(p, rad, rad, x2, y3);
      if( x2>x1 ) pik_append_xy(p, "L", x1, y3);
      pik_append_arc(p, rad, rad, x0, y2);
      if( y2>y1 ) pik_append_xy(p, "L", x0, y1);
      pik_append_arc(p, rad, rad, x1, y0);
      pik_append(p,"Z\" ",-1);
    }
    pik_append_style(p,pElem,1);
    pik_append(p,"\" />\n", -1);
  }
  pik_append_txt(p, pElem, 0);
}

/* Methods for the "circle" class */
static void circleInit(Pik *p, PElem *pElem){
  pElem->w = pik_value(p, "circlerad",9,0)*2;
  pElem->h = pElem->w;
  pElem->rad = 0.5*pElem->w;
}
static void circleNumProp(Pik *p, PElem *pElem, PToken *pId){
  /* For a circle, the width must equal the height and both must
  ** be twice the radius.  Enforce those constraints. */
  switch( pId->eType ){
    case T_RADIUS:
      pElem->w = pElem->h = 2.0*pElem->rad;
      break;
    case T_WIDTH:
      pElem->h = pElem->w;
      pElem->rad = 0.5*pElem->w;
      break;
    case T_HEIGHT:
      pElem->w = pElem->h;
      pElem->rad = 0.5*pElem->w;
      break;
  }
  UNUSED_PARAMETER(p);
}
static PPoint circleChop(Pik *p, PElem *pElem, PPoint *pPt){
  PPoint chop;
  PNum dx = pPt->x - pElem->ptAt.x;
  PNum dy = pPt->y - pElem->ptAt.y;
  PNum dist = hypot(dx,dy);
  if( dist<pElem->rad ) return pElem->ptAt;
  chop.x = pElem->ptAt.x + dx*pElem->rad/dist;
  chop.y = pElem->ptAt.y + dy*pElem->rad/dist;
  UNUSED_PARAMETER(p);
  return chop;
}
static void circleFit(Pik *p, PElem *pElem, PNum w, PNum h){
  PNum mx = 0.0;
  if( w>0 ) mx = w;
  if( h>mx ) mx = h;
  if( (w*w + h*h) > mx*mx ){
    mx = hypot(w,h);
  }
  if( mx>0.0 ){
    pElem->rad = 0.5*mx;
    pElem->w = pElem->h = mx;
  }
  UNUSED_PARAMETER(p);
}

static void circleRender(Pik *p, PElem *pElem){
  PNum r = pElem->rad;
  PPoint pt = pElem->ptAt;
  if( pElem->sw>0.0 ){
    pik_append_x(p,"<circle cx=\"", pt.x, "\"");
    pik_append_y(p," cy=\"", pt.y, "\"");
    pik_append_dis(p," r=\"", r, "\" ");
    pik_append_style(p,pElem,1);
    pik_append(p,"\" />\n", -1);
  }
  pik_append_txt(p, pElem, 0);
}

/* Methods for the "cylinder" class */
static void cylinderInit(Pik *p, PElem *pElem){
  pElem->w = pik_value(p, "cylwid",6,0);
  pElem->h = pik_value(p, "cylht",5,0);
  pElem->rad = pik_value(p, "cylrad",6,0); /* Minor radius of ellipses */
}
static void cylinderFit(Pik *p, PElem *pElem, PNum w, PNum h){
  if( w>0 ) pElem->w = w;
  if( h>0 ) pElem->h = h + 4*pElem->rad + pElem->sw;
  UNUSED_PARAMETER(p);
}
static void cylinderRender(Pik *p, PElem *pElem){
  PNum w2 = 0.5*pElem->w;
  PNum h2 = 0.5*pElem->h;
  PNum rad = pElem->rad;
  PPoint pt = pElem->ptAt;
  if( pElem->sw>0.0 ){
    pik_append_xy(p,"<path d=\"M", pt.x-w2,pt.y+h2-rad);
    pik_append_xy(p,"L", pt.x-w2,pt.y-h2+rad);
    pik_append_arc(p,w2,rad,pt.x+w2,pt.y-h2+rad);
    pik_append_xy(p,"L", pt.x+w2,pt.y+h2-rad);
    pik_append_arc(p,w2,rad,pt.x-w2,pt.y+h2-rad);
    pik_append_arc(p,w2,rad,pt.x+w2,pt.y+h2-rad);
    pik_append(p,"\" ",-1);
    pik_append_style(p,pElem,1);
    pik_append(p,"\" />\n", -1);
  }
  pik_append_txt(p, pElem, 0);
}
static PPoint cylinderOffset(Pik *p, PElem *pElem, int cp){
  PPoint pt;
  PNum w2 = pElem->w*0.5;
  PNum h1 = pElem->h*0.5;
  PNum h2 = h1 - pElem->rad;
  switch( cp ){
    case CP_C:   pt.x = 0.0;   pt.y = 0.0;    break;
    case CP_N:   pt.x = 0.0;   pt.y = h1;     break;
    case CP_NE:  pt.x = w2;    pt.y = h2;     break;
    case CP_E:   pt.x = w2;    pt.y = 0.0;    break;
    case CP_SE:  pt.x = w2;    pt.y = -h2;    break;
    case CP_S:   pt.x = 0.0;   pt.y = -h1;    break;
    case CP_SW:  pt.x = -w2;   pt.y = -h2;    break;
    case CP_W:   pt.x = -w2;   pt.y = 0.0;    break;
    case CP_NW:  pt.x = -w2;   pt.y = h2;     break;
  }
  UNUSED_PARAMETER(p);
  return pt;
}

/* Methods for the "dot" class */
static void dotInit(Pik *p, PElem *pElem){
  pElem->rad = pik_value(p, "dotrad",6,0);
  pElem->h = pElem->w = pElem->rad*6;
  pElem->fill = pElem->color;
}
static void dotNumProp(Pik *p, PElem *pElem, PToken *pId){
  switch( pId->eType ){
    case T_COLOR:
      pElem->fill = pElem->color;
      break;
    case T_FILL:
      pElem->color = pElem->fill;
      break;
  }
  UNUSED_PARAMETER(p);
}
static void dotCheck(Pik *p, PElem *pElem){
  pElem->w = pElem->h = 0;
  pik_bbox_addellipse(&pElem->bbox, pElem->ptAt.x, pElem->ptAt.y,
                       pElem->rad, pElem->rad);
  UNUSED_PARAMETER(p);
}
static PPoint dotOffset(Pik *p, PElem *pElem, int cp){
  PPoint zero;
  zero.x = zero.y = 0;
  UNUSED_PARAMETER(p);
  UNUSED_PARAMETER(pElem);
  UNUSED_PARAMETER(cp);
  return zero;
}
static void dotRender(Pik *p, PElem *pElem){
  PNum r = pElem->rad;
  PPoint pt = pElem->ptAt;
  if( pElem->sw>0.0 ){
    pik_append_x(p,"<circle cx=\"", pt.x, "\"");
    pik_append_y(p," cy=\"", pt.y, "\"");
    pik_append_dis(p," r=\"", r, "\"");
    pik_append_style(p,pElem,1);
    pik_append(p,"\" />\n", -1);
  }
  pik_append_txt(p, pElem, 0);
}



/* Methods for the "ellipse" class */
static void ellipseInit(Pik *p, PElem *pElem){
  pElem->w = pik_value(p, "ellipsewid",10,0);
  pElem->h = pik_value(p, "ellipseht",9,0);
}
static PPoint ellipseChop(Pik *p, PElem *pElem, PPoint *pPt){
  PPoint chop;
  PNum s, dq, dist;
  PNum dx = pPt->x - pElem->ptAt.x;
  PNum dy = pPt->y - pElem->ptAt.y;
  if( pElem->w<=0.0 ) return pElem->ptAt;
  if( pElem->h<=0.0 ) return pElem->ptAt;
  s = pElem->h/pElem->w;
  dq = dx*s;
  dist = hypot(dq,dy);
  if( dist<pElem->h ) return pElem->ptAt;
  chop.x = pElem->ptAt.x + 0.5*dq*pElem->h/(dist*s);
  chop.y = pElem->ptAt.y + 0.5*dy*pElem->h/dist;
  UNUSED_PARAMETER(p);
  return chop;
}
static PPoint ellipseOffset(Pik *p, PElem *pElem, int cp){
  PPoint pt;
  PNum w = pElem->w*0.5;
  PNum w2 = w*0.70710678118654747608;
  PNum h = pElem->h*0.5;
  PNum h2 = h*0.70710678118654747608;
  switch( cp ){
    case CP_C:   pt.x = 0.0;   pt.y = 0.0;    break;
    case CP_N:   pt.x = 0.0;   pt.y = h;      break;
    case CP_NE:  pt.x = w2;    pt.y = h2;     break;
    case CP_E:   pt.x = w;     pt.y = 0.0;    break;
    case CP_SE:  pt.x = w2;    pt.y = -h2;    break;
    case CP_S:   pt.x = 0.0;   pt.y = -h;     break;
    case CP_SW:  pt.x = -w2;   pt.y = -h2;    break;
    case CP_W:   pt.x = -w;    pt.y = 0.0;    break;
    case CP_NW:  pt.x = -w2;   pt.y = h2;     break;
  }
  UNUSED_PARAMETER(p);
  return pt;
}
static void ellipseRender(Pik *p, PElem *pElem){
  PNum w = pElem->w;
  PNum h = pElem->h;
  PPoint pt = pElem->ptAt;
  if( pElem->sw>0.0 ){
    pik_append_x(p,"<ellipse cx=\"", pt.x, "\"");
    pik_append_y(p," cy=\"", pt.y, "\"");
    pik_append_dis(p," rx=\"", w/2.0, "\"");
    pik_append_dis(p," ry=\"", h/2.0, "\" ");
    pik_append_style(p,pElem,1);
    pik_append(p,"\" />\n", -1);
  }
  pik_append_txt(p, pElem, 0);
}

/* Methods for the "file" object */
static void fileInit(Pik *p, PElem *pElem){
  pElem->w = pik_value(p, "filewid",7,0);
  pElem->h = pik_value(p, "fileht",6,0);
  pElem->rad = pik_value(p, "filerad",7,0);
}
/* Return offset from the center of the file to the compass point 
** given by parameter cp */
static PPoint fileOffset(Pik *p, PElem *pElem, int cp){
  PPoint pt;
  PNum w2 = 0.5*pElem->w;
  PNum h2 = 0.5*pElem->h;
  PNum rx = pElem->rad;
  PNum mn = w2<h2 ? w2 : h2;
  if( rx>mn ) rx = mn;
  if( rx<mn*0.25 ) rx = mn*0.25;
  pt.x = pt.y = 0.0;
  rx *= 0.5;
  switch( cp ){
    case CP_C:   pt.x = 0.0;      pt.y = 0.0;    break;
    case CP_N:   pt.x = 0.0;      pt.y = h2;     break;
    case CP_NE:  pt.x = w2-rx;    pt.y = h2-rx;  break;
    case CP_E:   pt.x = w2;       pt.y = 0.0;    break;
    case CP_SE:  pt.x = w2;       pt.y = -h2;    break;
    case CP_S:   pt.x = 0.0;      pt.y = -h2;    break;
    case CP_SW:  pt.x = -w2;      pt.y = -h2;    break;
    case CP_W:   pt.x = -w2;      pt.y = 0.0;    break;
    case CP_NW:  pt.x = -w2;      pt.y = h2;     break;
  }
  UNUSED_PARAMETER(p);
  return pt;
}
static void fileFit(Pik *p, PElem *pElem, PNum w, PNum h){
  if( w>0 ) pElem->w = w;
  if( h>0 ) pElem->h = h + 2*pElem->rad;
  UNUSED_PARAMETER(p);
}
static void fileRender(Pik *p, PElem *pElem){
  PNum w2 = 0.5*pElem->w;
  PNum h2 = 0.5*pElem->h;
  PNum rad = pElem->rad;
  PPoint pt = pElem->ptAt;
  PNum mn = w2<h2 ? w2 : h2;
  if( rad>mn ) rad = mn;
  if( rad<mn*0.25 ) rad = mn*0.25;
  if( pElem->sw>0.0 ){
    pik_append_xy(p,"<path d=\"M", pt.x-w2,pt.y-h2);
    pik_append_xy(p,"L", pt.x+w2,pt.y-h2);
    pik_append_xy(p,"L", pt.x+w2,pt.y+(h2-rad));
    pik_append_xy(p,"L", pt.x+(w2-rad),pt.y+h2);
    pik_append_xy(p,"L", pt.x-w2,pt.y+h2);
    pik_append(p,"Z\" ",-1);
    pik_append_style(p,pElem,1);
    pik_append(p,"\" />\n",-1);
    pik_append_xy(p,"<path d=\"M", pt.x+(w2-rad), pt.y+h2);
    pik_append_xy(p,"L", pt.x+(w2-rad),pt.y+(h2-rad));
    pik_append_xy(p,"L", pt.x+w2, pt.y+(h2-rad));
    pik_append(p,"\" ",-1);
    pik_append_style(p,pElem,0);
    pik_append(p,"\" />\n",-1);
  }
  pik_append_txt(p, pElem, 0);
}


/* Methods for the "line" class */
static void lineInit(Pik *p, PElem *pElem){
  pElem->w = pik_value(p, "linewid",7,0);
  pElem->h = pik_value(p, "lineht",6,0);
  pElem->rad = pik_value(p, "linerad",7,0);
  pElem->fill = -1.0;
}
static PPoint lineOffset(Pik *p, PElem *pElem, int cp){
#if 0
  /* In legacy PIC, the .center of an unclosed line is half way between
  ** its .start and .end. */
  if( cp==CP_C && !pElem->bClose ){
    PPoint out;
    out.x = 0.5*(pElem->ptEnter.x + pElem->ptExit.x) - pElem->ptAt.x;
    out.y = 0.5*(pElem->ptEnter.x + pElem->ptExit.y) - pElem->ptAt.y;
    return out;
  }
#endif
  return boxOffset(p,pElem,cp);
}
static void lineRender(Pik *p, PElem *pElem){
  int i;
  if( pElem->sw>0.0 ){
    const char *z = "<path d=\"M";
    int n = pElem->nPath;
    if( pElem->larrow ){
      pik_draw_arrowhead(p,&pElem->aPath[1],&pElem->aPath[0],pElem);
    }
    if( pElem->rarrow ){
      pik_draw_arrowhead(p,&pElem->aPath[n-2],&pElem->aPath[n-1],pElem);
    }
    for(i=0; i<pElem->nPath; i++){
      pik_append_xy(p,z,pElem->aPath[i].x,pElem->aPath[i].y);
      z = "L";
    }
    if( pElem->bClose ){
      pik_append(p,"Z",1);
    }else{
      pElem->fill = -1.0;
    }
    pik_append(p,"\" ",-1);
    pik_append_style(p,pElem,pElem->bClose);
    pik_append(p,"\" />\n", -1);
  }
  pik_append_txt(p, pElem, 0);
}

/* Methods for the "move" class */
static void moveInit(Pik *p, PElem *pElem){
  pElem->w = pik_value(p, "movewid",7,0);
  pElem->h = pElem->w;
  pElem->fill = -1.0;
  pElem->color = -1.0;
  pElem->sw = -1.0;
}
static void moveRender(Pik *p, PElem *pElem){
  /* No-op */
  UNUSED_PARAMETER(p);
  UNUSED_PARAMETER(pElem);
}

/* Methods for the "oval" class */
static void ovalInit(Pik *p, PElem *pElem){
  pElem->h = pik_value(p, "ovalht",6,0);
  pElem->w = pik_value(p, "ovalwid",7,0);
  pElem->rad = 0.5*(pElem->h<pElem->w?pElem->h:pElem->w);
}
static void ovalNumProp(Pik *p, PElem *pElem, PToken *pId){
  UNUSED_PARAMETER(p);
  UNUSED_PARAMETER(pId);
  /* Always adjust the radius to be half of the smaller of
  ** the width and height. */
  pElem->rad = 0.5*(pElem->h<pElem->w?pElem->h:pElem->w);
}
static void ovalFit(Pik *p, PElem *pElem, PNum w, PNum h){
  UNUSED_PARAMETER(p);
  if( w>0 ) pElem->w = w;
  if( h>0 ) pElem->h = h;
  if( pElem->w<pElem->h ) pElem->w = pElem->h;
}



/* Methods for the "spline" class */
static void splineInit(Pik *p, PElem *pElem){
  pElem->w = pik_value(p, "linewid",7,0);
  pElem->h = pik_value(p, "lineht",6,0);
  pElem->rad = 1000;
  pElem->fill = -1.0;  /* Disable fill by default */
}
/* Return a point along the path from "f" to "t" that is r units
** prior to reaching "t", except if the path is less than 2*r total,
** return the midpoint.
*/
static PPoint radiusMidpoint(PPoint f, PPoint t, PNum r, int *pbMid){
  PNum dx = t.x - f.x;
  PNum dy = t.y - f.y;
  PNum dist = hypot(dx,dy);
  PPoint m;
  if( dist<=0.0 ) return t;
  dx /= dist;
  dy /= dist;
  if( r > 0.5*dist ){
    r = 0.5*dist;
    *pbMid = 1;
  }else{
    *pbMid = 0;
  }
  m.x = t.x - r*dx;
  m.y = t.y - r*dy;
  return m;
}
static void radiusPath(Pik *p, PElem *pElem, PNum r){
  int i;
  int n = pElem->nPath;
  const PPoint *a = pElem->aPath;
  PPoint m;
  int isMid = 0;

  pik_append_xy(p,"<path d=\"M", a[0].x, a[0].y);
  m = radiusMidpoint(a[0], a[1], r, &isMid);
  pik_append_xy(p," L ",m.x,m.y);
  for(i=1; i<n-1; i++){
    m = radiusMidpoint(a[i+1],a[i],r, &isMid);
    pik_append_xy(p," Q ",a[i].x,a[i].y);
    pik_append_xy(p," ",m.x,m.y);
    if( !isMid ){
      m = radiusMidpoint(a[i],a[i+1],r, &isMid);
      pik_append_xy(p," L ",m.x,m.y);
    }
  }
  pik_append_xy(p," L ",a[i].x,a[i].y);
  pik_append(p,"\" ",-1);
  pik_append_style(p,pElem,0);
  pik_append(p,"\" />\n", -1);
}
static void splineRender(Pik *p, PElem *pElem){
  if( pElem->sw>0.0 ){
    int n = pElem->nPath;
    PNum r = pElem->rad;
    if( n<3 || r<=0.0 ){
      lineRender(p,pElem);
      return;
    }
    if( pElem->larrow ){
      pik_draw_arrowhead(p,&pElem->aPath[1],&pElem->aPath[0],pElem);
    }
    if( pElem->rarrow ){
      pik_draw_arrowhead(p,&pElem->aPath[n-2],&pElem->aPath[n-1],pElem);
    }
    radiusPath(p,pElem,pElem->rad);
  }
  pik_append_txt(p, pElem, 0);
}


/* Methods for the "text" class */
static void textInit(Pik *p, PElem *pElem){
  pik_value(p, "textwid",7,0);
  pik_value(p, "textht",6,0);
  pElem->sw = 0.0;
}
static PPoint textOffset(Pik *p, PElem *pElem, int cp){
  /* Automatically slim-down the width and height of text
  ** elements so that the bounding box tightly encloses the text,
  ** then get boxOffset() to do the offset computation.
  */
  pik_size_to_fit(p, &pElem->errTok);
  return boxOffset(p, pElem, cp);
}

/* Methods for the "sublist" class */
static void sublistInit(Pik *p, PElem *pElem){
  PEList *pList = pElem->pSublist;
  int i;
  UNUSED_PARAMETER(p);
  pik_bbox_init(&pElem->bbox);
  for(i=0; i<pList->n; i++){
    pik_bbox_addbox(&pElem->bbox, &pList->a[i]->bbox);
  }
  pElem->w = pElem->bbox.ne.x - pElem->bbox.sw.x;
  pElem->h = pElem->bbox.ne.y - pElem->bbox.sw.y;
  pElem->ptAt.x = 0.5*(pElem->bbox.ne.x + pElem->bbox.sw.x);
  pElem->ptAt.y = 0.5*(pElem->bbox.ne.y + pElem->bbox.sw.y);
  pElem->mCalc |= A_WIDTH|A_HEIGHT|A_RADIUS;
}


/*
** The following array holds all the different kinds of named
** elements.  The special STRING and [] elements are separate.
*/
static const PClass aClass[] = {
   {  /* name */          "arc",
      /* isline */        1,
      /* eJust */         0,
      /* xInit */         arcInit,
      /* xNumProp */      0,
      /* xCheck */        arcCheck,
      /* xChop */         0,
      /* xOffset */       boxOffset,
      /* xFit */          0,
      /* xRender */       arcRender
   },
   {  /* name */          "arrow",
      /* isline */        1,
      /* eJust */         0,
      /* xInit */         arrowInit,
      /* xNumProp */      0,
      /* xCheck */        0,
      /* xChop */         0,
      /* xOffset */       lineOffset,
      /* xFit */          0,
      /* xRender */       splineRender 
   },
   {  /* name */          "box",
      /* isline */        0,
      /* eJust */         1,
      /* xInit */         boxInit,
      /* xNumProp */      0,
      /* xCheck */        0,
      /* xChop */         boxChop,
      /* xOffset */       boxOffset,
      /* xFit */          boxFit,
      /* xRender */       boxRender 
   },
   {  /* name */          "circle",
      /* isline */        0,
      /* eJust */         0,
      /* xInit */         circleInit,
      /* xNumProp */      circleNumProp,
      /* xCheck */        0,
      /* xChop */         circleChop,
      /* xOffset */       ellipseOffset,
      /* xFit */          circleFit,
      /* xRender */       circleRender 
   },
   {  /* name */          "cylinder",
      /* isline */        0,
      /* eJust */         1,
      /* xInit */         cylinderInit,
      /* xNumProp */      0,
      /* xCheck */        0,
      /* xChop */         boxChop,
      /* xOffset */       cylinderOffset,
      /* xFit */          cylinderFit,
      /* xRender */       cylinderRender
   },
   {  /* name */          "dot",
      /* isline */        0,
      /* eJust */         0,
      /* xInit */         dotInit,
      /* xNumProp */      dotNumProp,
      /* xCheck */        dotCheck,
      /* xChop */         circleChop,
      /* xOffset */       dotOffset,
      /* xFit */          0,
      /* xRender */       dotRender 
   },
   {  /* name */          "ellipse",
      /* isline */        0,
      /* eJust */         0,
      /* xInit */         ellipseInit,
      /* xNumProp */      0,
      /* xCheck */        0,
      /* xChop */         ellipseChop,
      /* xOffset */       ellipseOffset,
      /* xFit */          0,
      /* xRender */       ellipseRender
   },
   {  /* name */          "file",
      /* isline */        0,
      /* eJust */         1,
      /* xInit */         fileInit,
      /* xNumProp */      0,
      /* xCheck */        0,
      /* xChop */         boxChop,
      /* xOffset */       fileOffset,
      /* xFit */          fileFit,
      /* xRender */       fileRender 
   },
   {  /* name */          "line",
      /* isline */        1,
      /* eJust */         0,
      /* xInit */         lineInit,
      /* xNumProp */      0,
      /* xCheck */        0,
      /* xChop */         0,
      /* xOffset */       lineOffset,
      /* xFit */          0,
      /* xRender */       splineRender
   },
   {  /* name */          "move",
      /* isline */        1,
      /* eJust */         0,
      /* xInit */         moveInit,
      /* xNumProp */      0,
      /* xCheck */        0,
      /* xChop */         0,
      /* xOffset */       boxOffset,
      /* xFit */          0,
      /* xRender */       moveRender
   },
   {  /* name */          "oval",
      /* isline */        0,
      /* eJust */         1,
      /* xInit */         ovalInit,
      /* xNumProp */      ovalNumProp,
      /* xCheck */        0,
      /* xChop */         boxChop,
      /* xOffset */       boxOffset,
      /* xFit */          ovalFit,
      /* xRender */       boxRender
   },
   {  /* name */          "spline",
      /* isline */        1,
      /* eJust */         0,
      /* xInit */         splineInit,
      /* xNumProp */      0,
      /* xCheck */        0,
      /* xChop */         0,
      /* xOffset */       lineOffset,
      /* xFit */          0,
      /* xRender */       splineRender
   },
   {  /* name */          "text",
      /* isline */        0,
      /* eJust */         0,
      /* xInit */         textInit,
      /* xNumProp */      0,
      /* xCheck */        0,
      /* xChop */         boxChop,
      /* xOffset */       textOffset,
      /* xFit */          boxFit,
      /* xRender */       boxRender 
   },
};
static const PClass sublistClass = 
   {  /* name */          "[]",
      /* isline */        0,
      /* eJust */         0,
      /* xInit */         sublistInit,
      /* xNumProp */      0,
      /* xCheck */        0,
      /* xChop */         0,
      /* xOffset */       boxOffset,
      /* xFit */          0,
      /* xRender */       0 
   };
static const PClass noopClass = 
   {  /* name */          "noop",
      /* isline */        0,
      /* eJust */         0,
      /* xInit */         0,
      /* xNumProp */      0,
      /* xCheck */        0,
      /* xChop */         0,
      /* xOffset */       boxOffset,
      /* xFit */          0,
      /* xRender */       0
   };


/*
** Reduce the length of the line segment by amt (if possible) by
** modifying the location of *t.
*/
static void pik_chop(PPoint *f, PPoint *t, PNum amt){
  PNum dx = t->x - f->x;
  PNum dy = t->y - f->y;
  PNum dist = hypot(dx,dy);
  PNum r;
  if( dist<=amt ){
    *t = *f;
    return;
  }
  r = 1.0 - amt/dist;
  t->x = f->x + r*dx;
  t->y = f->y + r*dy;
}

/*
** Draw an arrowhead on the end of the line segment from pFrom to pTo.
** Also, shorten the line segment (by changing the value of pTo) so that
** the shaft of the arrow does not extend into the arrowhead.
*/
static void pik_draw_arrowhead(Pik *p, PPoint *f, PPoint *t, PElem *pElem){
  PNum dx = t->x - f->x;
  PNum dy = t->y - f->y;
  PNum dist = hypot(dx,dy);
  PNum h = p->hArrow * pElem->sw;
  PNum w = p->wArrow * pElem->sw;
  PNum e1, ddx, ddy;
  PNum bx, by;
  if( pElem->color<0.0 ) return;
  if( pElem->sw<=0.0 ) return;
  if( dist<=0.0 ) return;  /* Unable */
  dx /= dist;
  dy /= dist;
  e1 = dist - h;
  if( e1<0.0 ){
    e1 = 0.0;
    h = dist;
  }
  ddx = -w*dy;
  ddy = w*dx;
  bx = f->x + e1*dx;
  by = f->y + e1*dy;
  pik_append_xy(p,"<polygon points=\"", t->x, t->y);
  pik_append_xy(p," ",bx-ddx, by-ddy);
  pik_append_xy(p," ",bx+ddx, by+ddy);
  pik_append_clr(p,"\" style=\"fill:",pElem->color,"\"/>\n");
  pik_chop(f,t,h/2);
}

/*
** Compute the relative offset to an edge location from the reference for a
** an element.
*/
static PPoint pik_elem_offset(Pik *p, PElem *pElem, int cp){
  return pElem->type->xOffset(p, pElem, cp);
}


/*
** Append raw text to zOut
*/
static void pik_append(Pik *p, const char *zText, int n){
  if( n<0 ) n = (int)strlen(zText);
  if( p->nOut+n>=p->nOutAlloc ){
    int nNew = (p->nOut+n)*2 + 1;
    char *z = realloc(p->zOut, nNew);
    if( z==0 ){
      pik_error(p, 0, 0);
      return;
    }
    p->zOut = z;
    p->nOutAlloc = n;
  }
  memcpy(p->zOut+p->nOut, zText, n);
  p->nOut += n;
  p->zOut[p->nOut] = 0;
}

/*
** Append text to zOut with HTML characters escaped.
**
**   *  The space character is changed into non-breaking space (U+0080)
**      if mFlags has the 0x01 bit set. This is needed when outputting
**      text to preserve leading and trailing whitespace.  Turns out we
**      cannot use &nbsp; as that is an HTML-ism and is not valid in XML.
**
**   *  The "&" character is changed into "&amp;" if mFlags has the
**      0x02 bit set.  This is needed when generating error message text.
**
**   *  Except for the above, only "<" and ">" are escaped.
*/
static void pik_append_text(Pik *p, const char *zText, int n, int mFlags){
  int i;
  char c;
  int bQSpace = mFlags & 1;
  int bQAmp = mFlags & 2;
  if( n<0 ) n = (int)strlen(zText);
  while( n>0 ){
    for(i=0; i<n; i++){
      c = zText[i];
      if( c=='<' || c=='>' ) break;
      if( c==' ' && bQSpace ) break;
      if( c=='&' && bQAmp ) break;
    }
    if( i ) pik_append(p, zText, i);
    if( i==n ) break;
    switch( c ){
      case '<': {  pik_append(p, "&lt;", 4);  break;  }
      case '>': {  pik_append(p, "&gt;", 4);  break;  }
      case '&': {  pik_append(p, "&amp;", 5);  break;  }
      case ' ': {  pik_append(p, "\302\240;", 2);  break;  }
    }
    i++;
    n -= i;
    zText += i;
    i = 0;
  }
}

/* Append a PNum value
*/
static void pik_append_num(Pik *p, const char *z,PNum v){
  char buf[100];
  snprintf(buf, sizeof(buf)-1, "%.10g", (double)v);
  buf[sizeof(buf)-1] = 0;
  pik_append(p, z, -1);
  pik_append(p, buf, -1);
}

/* Append a PPoint value  (Used for debugging only)
*/
static void pik_append_point(Pik *p, const char *z, PPoint *pPt){
  char buf[100];
  snprintf(buf, sizeof(buf)-1, "%.10g,%.10g", 
          (double)pPt->x, (double)pPt->y);
  buf[sizeof(buf)-1] = 0;
  pik_append(p, z, -1);
  pik_append(p, buf, -1);
}

/* Append a PNum value surrounded by text.  Do coordinate transformations
** on the value.
*/
static void pik_append_x(Pik *p, const char *z1, PNum v, const char *z2){
  char buf[200];
  v -= p->bbox.sw.x;
  snprintf(buf, sizeof(buf)-1, "%s%d%s", z1, (int)(p->rScale*v), z2);
  buf[sizeof(buf)-1] = 0;
  pik_append(p, buf, -1);
}
static void pik_append_y(Pik *p, const char *z1, PNum v, const char *z2){
  char buf[200];
  v = p->bbox.ne.y - v;
  snprintf(buf, sizeof(buf)-1, "%s%d%s", z1, (int)(p->rScale*v), z2);
  buf[sizeof(buf)-1] = 0;
  pik_append(p, buf, -1);
}
static void pik_append_xy(Pik *p, const char *z1, PNum x, PNum y){
  char buf[200];
  x = x - p->bbox.sw.x;
  y = p->bbox.ne.y - y;
  snprintf(buf, sizeof(buf)-1, "%s%d,%d", z1,
       (int)(p->rScale*x), (int)(p->rScale*y));
  buf[sizeof(buf)-1] = 0;
  pik_append(p, buf, -1);
}
static void pik_append_dis(Pik *p, const char *z1, PNum v, const char *z2){
  char buf[200];
  snprintf(buf, sizeof(buf)-1, "%s%g%s", z1, p->rScale*v, z2);
  buf[sizeof(buf)-1] = 0;
  pik_append(p, buf, -1);
}
static void pik_append_clr(Pik *p, const char *z1, PNum v, const char *z2){
  char buf[200];
  int x = (int)v;
  int r = (x>>16) & 0xff;
  int g = (x>>8) & 0xff;
  int b = x & 0xff;
  snprintf(buf, sizeof(buf)-1, "%srgb(%d,%d,%d)%s", z1, r, g, b, z2);
  buf[sizeof(buf)-1] = 0;
  pik_append(p, buf, -1);
}

/* Append an SVG path A record:
**
**    A r1 r2 0 0 0 x y
*/
static void pik_append_arc(Pik *p, PNum r1, PNum r2, PNum x, PNum y){
  char buf[200];
  x = x - p->bbox.sw.x;
  y = p->bbox.ne.y - y;
  snprintf(buf, sizeof(buf)-1, "A%d %d 0 0 0 %d %d", 
     (int)(p->rScale*r1), (int)(p->rScale*r2),
     (int)(p->rScale*x), (int)(p->rScale*y));
  buf[sizeof(buf)-1] = 0;
  pik_append(p, buf, -1);
}

/* Append a style="..." text.  But, leave the quote unterminated, in case
** the caller wants to add some more.
*/
static void pik_append_style(Pik *p, PElem *pElem, int bFill){
  pik_append(p, " style=\"", -1);
  if( pElem->fill>=0 && bFill ){
    pik_append_clr(p, "fill:", pElem->fill, ";");
  }else{
    pik_append(p,"fill:none;",-1);
  }
  if( pElem->sw>0.0 && pElem->color>=0.0 ){
    PNum sw = pElem->sw;
    pik_append_dis(p, "stroke-width:", sw, ";");
    if( pElem->nPath>2 && pElem->rad<=pElem->sw ){
      pik_append(p, "stroke-linejoin:round;", -1);
    }
    pik_append_clr(p, "stroke:",pElem->color,";");
    if( pElem->dotted>0.0 ){
      PNum v = pElem->dotted;
      if( sw<2.1/p->rScale ) sw = 2.1/p->rScale;
      pik_append_dis(p,"stroke-dasharray:",sw,"");
      pik_append_dis(p,",",v,";");
    }else if( pElem->dashed>0.0 ){
      PNum v = pElem->dashed;
      pik_append_dis(p,"stroke-dasharray:",v,"");
      pik_append_dis(p,",",v,";");
    }
  }
}

/*
** Compute the vertical locations for all text items in the
** element pElem.  In other words, set every pElem->aTxt[*].eCode
** value to contain exactly one of: TP_ABOVE2, TP_ABOVE, TP_CENTER,
** TP_BELOW, or TP_BELOW2 is set.
*/
static void pik_txt_vertical_layout(PElem *pElem){
  int n, i;
  PToken *aTxt;
  n = pElem->nTxt;
  if( n==0 ) return;
  aTxt = pElem->aTxt;
  if( n==1 ){
    if( (aTxt[0].eCode & TP_VMASK)==0 ){
      aTxt[0].eCode |= TP_CENTER;
    }
  }else{
    int allSlots = 0;
    int aFree[5];
    int iSlot;
    int j, mJust;
    /* If there is more than one TP_ABOVE, change the first to TP_ABOVE2. */
    for(j=mJust=0, i=n-1; i>=0; i--){
      if( aTxt[i].eCode & TP_ABOVE ){
        if( j==0 ){
          j++;
          mJust = aTxt[i].eCode & TP_JMASK;
        }else if( j==1 && mJust!=0 && (aTxt[i].eCode & mJust)==0 ){
          j++;
        }else{
          aTxt[i].eCode = (aTxt[i].eCode & ~TP_VMASK) | TP_ABOVE2;
          break;
        }
      }
    }
    /* If there is more than one TP_BELOW, change the last to TP_BELOW2 */
    for(j=mJust=0, i=0; i<n; i++){
      if( aTxt[i].eCode & TP_BELOW ){
        if( j==0 ){
          j++;
          mJust = aTxt[i].eCode & TP_JMASK;
        }else if( j==1 && mJust!=0 && (aTxt[i].eCode & mJust)==0 ){
          j++;
        }else{
          aTxt[i].eCode = (aTxt[i].eCode & ~TP_VMASK) | TP_BELOW2;
          break;
        }
      }
    }
    /* Compute a mask of all slots used */
    for(i=0; i<n; i++) allSlots |= aTxt[i].eCode & TP_VMASK;
    /* Set of an array of available slots */
    if( n==2
     && ((aTxt[0].eCode|aTxt[1].eCode)&TP_JMASK)==(TP_LJUST|TP_RJUST)
    ){
      /* Special case of two texts that have opposite justification:
      ** Allow them both to float to center. */
      iSlot = 2;
      aFree[0] = aFree[1] = TP_CENTER;
    }else{
      /* Set up the arrow so that available slots are filled from top to
      ** bottom */
      iSlot = 0;
      if( n>=4 && (allSlots & TP_ABOVE2)==0 ) aFree[iSlot++] = TP_ABOVE2;
      if( (allSlots & TP_ABOVE)==0 ) aFree[iSlot++] = TP_ABOVE;
      if( (n&1)!=0 ) aFree[iSlot++] = TP_CENTER;
      if( (allSlots & TP_BELOW)==0 ) aFree[iSlot++] = TP_BELOW;
      if( n>=4 && (allSlots & TP_BELOW2)==0 ) aFree[iSlot++] = TP_BELOW2;
    }
    /* Set the VMASK for all unassigned texts */
    for(i=iSlot=0; i<n; i++){
      if( (aTxt[i].eCode & TP_VMASK)==0 ){
        aTxt[i].eCode |= aFree[iSlot++];
      }
    }
  }
}

/* Append multiple <text> SVG element for the text fields of the PElem.
** Parameters:
**
**    p          The Pik object into which we are rendering
**
**    pElem      Object containing the text to be rendered
**
**    pBox       If not NULL, do no rendering at all.  Instead
**               expand the box object so that it will include all
**               of the text.
*/
static void pik_append_txt(Pik *p, PElem *pElem, PBox *pBox){
  PNum dy;          /* Half the height of a single line of text */
  PNum dy2;         /* Extra vertical space around the center */
  PNum jw;          /* Justification margin relative to center */
  int n, i, nz;
  PNum x, y, orig_y;
  const char *z;
  PToken *aTxt;
  int hasCenter = 0;

  if( p->nErr ) return;
  if( pElem->nTxt==0 ) return;
  aTxt = pElem->aTxt;
  dy = 0.5*p->charHeight;
  n = pElem->nTxt;
  pik_txt_vertical_layout(pElem);
  x = pElem->ptAt.x;
  for(i=0; i<n; i++){
    if( (pElem->aTxt[i].eCode & TP_CENTER)!=0 ) hasCenter = 1;
  }
  if( hasCenter ){
    dy2 = dy;
  }else if( pElem->type->isLine ){
    dy2 = pElem->sw;
  }else{
    dy2 = 0.0;
  }
  if( pElem->type->eJust==1 ){
    jw = 0.5*(pElem->w - 0.5*(p->charWidth + pElem->sw));
  }else{
    jw = 0.0;
  }
  for(i=0; i<n; i++){
    PToken *t = &aTxt[i];
    PNum xtraFontScale = 1.0;
    orig_y = y = pElem->ptAt.y;
    PNum nx = x;
    if( t->eCode & TP_ABOVE2 ) y += dy2 + 3*dy;
    if( t->eCode & TP_ABOVE  ) y += dy2 + dy;
    if( t->eCode & TP_BELOW  ) y -= dy2 + dy;
    if( t->eCode & TP_BELOW2 ) y -= dy2 + 3*dy;
    if( t->eCode & TP_BIG    ) xtraFontScale *= 1.25;
    if( t->eCode & TP_SMALL  ) xtraFontScale *= 0.8;
    if( t->eCode & TP_XTRA   ) xtraFontScale *= xtraFontScale;
    if( t->eCode & TP_LJUST  ) nx -= jw;
    if( t->eCode & TP_RJUST  ) nx += jw;

    if( pBox!=0 ){
      /* If pBox is not NULL, do not draw any <text>.  Instead, just expand
      ** pBox to include the text */
      PNum cw = pik_text_length(t)*p->charWidth*xtraFontScale;
      PNum ch = p->charHeight*0.5*xtraFontScale;
      if( t->eCode & TP_RJUST ){
        pik_bbox_add_xy(pBox, nx, y-ch);
        pik_bbox_add_xy(pBox, nx-cw, y+ch);
      }else if( t->eCode & TP_LJUST ){
        pik_bbox_add_xy(pBox, nx, y-ch);
        pik_bbox_add_xy(pBox, nx+cw, y+ch);
      }else{
        pik_bbox_add_xy(pBox, nx+cw/2, y+ch);
        pik_bbox_add_xy(pBox, nx-cw/2, y-ch);
      }
      continue;
    }

    pik_append_x(p, "<text x=\"", nx, "\"");
    pik_append_y(p, " y=\"", y, "\"");
    if( t->eCode & TP_RJUST ){
      pik_append(p, " text-anchor=\"end\"", -1);
    }else if( t->eCode & TP_LJUST ){
      pik_append(p, " text-anchor=\"start\"", -1);
    }else{
      pik_append(p, " text-anchor=\"middle\"", -1);
    }
    if( t->eCode & TP_ITALIC ){
      pik_append(p, " font-style=\"italic\"", -1);
    }
    if( t->eCode & TP_BOLD ){
      pik_append(p, " font-weight=\"bold\"", -1);
    }
    if( pElem->color>=0.0 ){
      pik_append_clr(p, " fill=\"", pElem->color, "\"");
    }
    xtraFontScale *= p->fontScale;
    if( xtraFontScale<=0.99 || xtraFontScale>=1.01 ){
      pik_append_num(p, " font-size=\"", xtraFontScale*100.0);
      pik_append(p, "%\"", 2);
    }
    if( (t->eCode & TP_ALIGN)!=0 && pElem->nPath>=2 ){
      int n = pElem->nPath;
      PNum dx = pElem->aPath[n-1].x - pElem->aPath[0].x;
      PNum dy = pElem->aPath[n-1].y - pElem->aPath[0].y;
      PNum ang = atan2(dy,dx)*-180/M_PI;
      pik_append_num(p, " transform=\"rotate(", ang);
      pik_append_xy(p, " ", x, orig_y);
      pik_append(p,")\"",2);
    }
    pik_append(p," dominant-baseline=\"central\">",-1);
    z = t->z+1;
    nz = t->n-2;
    while( nz>0 ){
      int j;
      for(j=0; j<nz && z[j]!='\\'; j++){}
      if( j ) pik_append_text(p, z, j, 1);
      nz -= j+1;
      z += j+1;
    }
    pik_append(p, "</text>\n", -1);
  }
}


/*
** Generate an error message for the output.  pErr is the token at which
** the error should point.  zMsg is the text of the error message. If
** either pErr or zMsg is NULL, generate an out-of-memory error message.
**
** This routine is a no-op if there has already been an error reported.
*/
static void pik_error(Pik *p, PToken *pErr, const char *zMsg){
  int i, j;
  int iCol;
  int nExtra;
  char c;
  if( p==0 ) return;
  if( p->nErr ) return;
  p->nErr++;
  if( zMsg==0 ){
    pik_append(p, "\n<div><p>Out of memory</p></div>\n", -1);
    return;
  }
  if( pErr==0 ){
    pik_append(p, "\n", 1);
    pik_append_text(p, zMsg, -1, 0);
    return;
  }
  i = (int)(pErr->z - p->zIn);
  for(j=i; j>0 && p->zIn[j-1]!='\n'; j--){}
  iCol = i - j;
  for(nExtra=0; (c = p->zIn[i+nExtra])!=0 && c!='\n'; nExtra++){}
  pik_append(p, "<div><pre>\n", -1);
  pik_append_text(p, p->zIn, i+nExtra, 3);
  pik_append(p, "\n", 1);
  for(i=0; i<iCol; i++){ pik_append(p, " ", 1); }
  for(i=0; i<(int)pErr->n; i++) pik_append(p, "^", 1);
  pik_append(p, "\nERROR: ", -1);
  pik_append_text(p, zMsg, -1, 0);
  pik_append(p, "\n", 1);
  pik_append(p, "\n</pre></div>\n", -1);
}

/*
** Process an "assert( e1 == e2 )" statement.  Always return NULL.
*/
static PElem *pik_assert(Pik *p, PNum e1, PToken *pEq, PNum e2){
  char zE1[100], zE2[100], zMsg[300];

  /* Convert the numbers to strings using %g for comparison.  This
  ** limits the precision of the comparison to account for rounding error. */
  snprintf(zE1, sizeof(zE1), "%g", e1); zE1[sizeof(zE1)-1] = 0;
  snprintf(zE2, sizeof(zE2), "%g", e2); zE1[sizeof(zE2)-1] = 0;
  if( strcmp(zE1,zE2)!=0 ){
    snprintf(zMsg, sizeof(zMsg), "%.50s != %.50s", zE1, zE2);
    pik_error(p, pEq, zMsg);
  }
  return 0;
}

/*
** Process an "assert( place1 == place2 )" statement.  Always return NULL.
*/
static PElem *pik_place_assert(Pik *p, PPoint *e1, PToken *pEq, PPoint *e2){
  char zE1[100], zE2[100], zMsg[210];

  /* Convert the numbers to strings using %g for comparison.  This
  ** limits the precision of the comparison to account for rounding error. */
  snprintf(zE1, sizeof(zE1), "(%g,%g)", e1->x, e1->y); zE1[sizeof(zE1)-1] = 0;
  snprintf(zE2, sizeof(zE2), "(%g,%g)", e2->x, e2->y); zE1[sizeof(zE2)-1] = 0;
  if( strcmp(zE1,zE2)!=0 ){
    snprintf(zMsg, sizeof(zMsg), "%s != %s", zE1, zE2);
    pik_error(p, pEq, zMsg);
  }
  return 0;
}

/* Free a complete list of elements */
static void pik_elist_free(Pik *p, PEList *pEList){
  int i;
  if( pEList==0 ) return;
  for(i=0; i<pEList->n; i++){
    pik_elem_free(p, pEList->a[i]);
  }
  free(pEList->a);
  free(pEList);
  return;
}

/* Free a single element, and its substructure */
static void pik_elem_free(Pik *p, PElem *pElem){
  if( pElem==0 ) return;
  free(pElem->zName);
  pik_elist_free(p, pElem->pSublist);
  free(pElem->aPath);
  free(pElem);
}

/* Convert a numeric literal into a number.  Return that number.
** There is no error handling because the tokenizer has already
** assured us that the numeric literal is valid.
**
** Allowed number forms:
**
**   (1)    Floating point literal
**   (2)    Same as (1) but followed by a unit: "cm", "mm", "in",
**          "px", "pt", or "pc".
**   (3)    Hex integers: 0x000000
**
** This routine returns the result in inches.  If a different unit
** is specified, the conversion happens automatically.
*/
PNum pik_atof(PToken *num){
  char *endptr;
  PNum ans;
  if( num->n>=3 && num->z[0]=='0' && (num->z[1]=='x'||num->z[1]=='X') ){
    return (PNum)strtol(num->z+2, 0, 16);
  }
  ans = strtod(num->z, &endptr);
  if( (int)(endptr - num->z)==(int)num->n-2 ){
    char c1 = endptr[0];
    char c2 = endptr[1];
    if( c1=='c' && c2=='m' ){
      ans /= 2.54;
    }else if( c1=='m' && c2=='m' ){
      ans /= 25.4;
    }else if( c1=='p' && c2=='x' ){
      ans /= 96;
    }else if( c1=='p' && c2=='t' ){
      ans /= 72;
    }else if( c1=='p' && c2=='c' ){
      ans /= 6;
    }
  }
  return ans;
}

/* Return true if a bounding box is empty.
*/
static int pik_bbox_isempty(PBox *p){
  return p->sw.x>p->ne.x;
}

/* Initialize a bounding box to an empty container
*/
static void pik_bbox_init(PBox *p){
  p->sw.x = 1.0;
  p->sw.y = 1.0;
  p->ne.x = 0.0;
  p->ne.y = 0.0;
}

/* Enlarge the PBox of the first argument so that it fully
** covers the second PBox
*/
static void pik_bbox_addbox(PBox *pA, PBox *pB){
  if( pik_bbox_isempty(pA) ){
    *pA = *pB;
  }
  if( pik_bbox_isempty(pB) ) return;
  if( pA->sw.x>pB->sw.x ) pA->sw.x = pB->sw.x;
  if( pA->sw.y>pB->sw.y ) pA->sw.y = pB->sw.y;
  if( pA->ne.x<pB->ne.x ) pA->ne.x = pB->ne.x;
  if( pA->ne.y<pB->ne.y ) pA->ne.y = pB->ne.y;
}

/* Enlarge the PBox of the first argument, if necessary, so that
** it contains the point described by the 2nd and 3rd arguments.
*/
static void pik_bbox_add_xy(PBox *pA, PNum x, PNum y){
  if( pik_bbox_isempty(pA) ){
    pA->ne.x = x;
    pA->ne.y = y;
    pA->sw.x = x;
    pA->sw.y = y;
    return;
  }
  if( pA->sw.x>x ) pA->sw.x = x;
  if( pA->sw.y>y ) pA->sw.y = y;
  if( pA->ne.x<x ) pA->ne.x = x;
  if( pA->ne.y<y ) pA->ne.y = y;
}

/* Enlarge the PBox so that it is able to contain an ellipse
** centered at x,y and with radiuses rx and ry.
*/
static void pik_bbox_addellipse(PBox *pA, PNum x, PNum y, PNum rx, PNum ry){
  if( pik_bbox_isempty(pA) ){
    pA->ne.x = x+rx;
    pA->ne.y = y+ry;
    pA->sw.x = x-rx;
    pA->sw.y = y-ry;
    return;
  }
  if( pA->sw.x>x-rx ) pA->sw.x = x-rx;
  if( pA->sw.y>y-ry ) pA->sw.y = y-ry;
  if( pA->ne.x<x+rx ) pA->ne.x = x+rx;
  if( pA->ne.y<y+ry ) pA->ne.y = y+ry;
}



/* Append a new element onto the end of an element_list.  The
** element_list is created if it does not already exist.  Return
** the new element list.
*/
static PEList *pik_elist_append(Pik *p, PEList *pEList, PElem *pElem){
  if( pElem==0 ) return pEList;
  if( pEList==0 ){
    pEList = malloc(sizeof(*pEList));
    if( pEList==0 ){
      pik_error(p, 0, 0);
      pik_elem_free(p, pElem);
      return 0;
    }
    memset(pEList, 0, sizeof(*pEList));
  }
  if( pEList->n>=pEList->nAlloc ){
    int nNew = (pEList->n+5)*2;
    PElem **pNew = realloc(pEList->a, sizeof(PElem*)*nNew);
    if( pNew==0 ){
      pik_error(p, 0, 0);
      pik_elem_free(p, pElem);
      return pEList;
    }
    pEList->nAlloc = nNew;
    pEList->a = pNew;
  }
  pEList->a[pEList->n++] = pElem;
  p->list = pEList;
  return pEList;
}

/* Convert an element class name into a PClass pointer
*/
static const PClass *pik_find_class(PToken *pId){
  int first = 0;
  int last = count(aClass) - 1;
  do{
    int mid = (first+last)/2;
    int c = strncmp(aClass[mid].zName, pId->z, pId->n);
    if( c==0 ){
      c = aClass[mid].zName[pId->n]!=0;
      if( c==0 ) return &aClass[mid];
    }
    if( c<0 ){
      first = mid + 1;
    }else{
      last = mid - 1;
    }
  }while( first<=last );
  return 0;
}

/* Allocate and return a new PElem object.
**
** If pId!=0 then pId is an identifier that defines the element class.
** If pStr!=0 then it is a STRING literal that defines a text object.
** If pSublist!=0 then this is a [...] object. If all three parameters
** are NULL then this is a no-op object used to define a PLACENAME.
*/
static PElem *pik_elem_new(Pik *p, PToken *pId, PToken *pStr,PEList *pSublist){
  PElem *pNew;
  int miss = 0;

  if( p->nErr ) return 0;
  pNew = malloc( sizeof(*pNew) );
  if( pNew==0 ){
    pik_error(p,0,0);
    pik_elist_free(p, pSublist);
    return 0;
  }
  memset(pNew, 0, sizeof(*pNew));
  p->cur = pNew;
  p->nTPath = 1;
  p->thenFlag = 0;
  if( p->list==0 || p->list->n==0 ){
    pNew->ptAt.x = pNew->ptAt.y = 0.0;
  }else{
    PElem *pPrior = p->list->a[p->list->n-1];
    pNew->ptAt = pPrior->ptExit;
    switch( p->eDir ){
      default:         pNew->eWith = CP_W;   break;
      case DIR_LEFT:   pNew->eWith = CP_E;   break;
      case DIR_UP:     pNew->eWith = CP_S;   break;
      case DIR_DOWN:   pNew->eWith = CP_N;   break;
    }
  }
  p->aTPath[0] = pNew->ptAt;
  pNew->with = pNew->ptAt;
  pNew->outDir = pNew->inDir = p->eDir;
  pNew->iLayer = (int)pik_value(p, "layer", 5, &miss);
  if( miss ) pNew->iLayer = 1000;
  if( pNew->iLayer<0 ) pNew->iLayer = 0;
  if( pSublist ){
    pNew->type = &sublistClass;
    pNew->pSublist = pSublist;
    sublistClass.xInit(p,pNew);
    return pNew;
  }
  if( pStr ){
    PToken n;
    n.z = "text";
    n.n = 4;
    pNew->type = pik_find_class(&n);
    assert( pNew->type!=0 );
    pNew->errTok = *pStr;
    pNew->type->xInit(p, pNew);
    pik_add_txt(p, pStr, pStr->eCode);
    return pNew;
  }
  if( pId ){
    pNew->errTok = *pId;
    const PClass *pClass = pik_find_class(pId);
    if( pClass ){
      pNew->type = pClass;
      pNew->sw = pik_value(p, "thickness",9,0);
      pNew->fill = pik_value(p, "fill",4,0);
      pNew->color = pik_value(p, "color",5,0);
      pClass->xInit(p, pNew);
      return pNew;
    }
    pik_error(p, pId, "unknown element type");
    pik_elem_free(p, pNew);
    return 0;
  }
  pNew->type = &noopClass;
  pNew->ptExit = pNew->ptEnter = pNew->ptAt;
  return pNew;
}

/*
** Set the output direction and exit point for an element.
*/
static void pik_elem_set_exit(PElem *pElem, int eDir){
  assert( ValidDir(eDir) );
  pElem->outDir = eDir;
  if( !pElem->type->isLine || pElem->bClose ){
    pElem->ptExit = pElem->ptAt;
    switch( pElem->outDir ){
      default:         pElem->ptExit.x += pElem->w*0.5;  break;
      case DIR_LEFT:   pElem->ptExit.x -= pElem->w*0.5;  break;
      case DIR_UP:     pElem->ptExit.y += pElem->h*0.5;  break;
      case DIR_DOWN:   pElem->ptExit.y -= pElem->h*0.5;  break;
    }
  }
}

/* Change the layout direction.
*/
static void pik_set_direction(Pik *p, int eDir){
  assert( ValidDir(eDir) );
  p->eDir = eDir;

  /* It seems to make sense to reach back into the last object and
  ** change its exit point (its ".end") to correspond to the new
  ** direction.  Things just seem to work better this way.  However,
  ** legacy PIC does *not* do this.
  **
  ** The difference can be seen in a script like this:
  **
  **      arrow; circle; down; arrow
  **
  ** You can make pikchr render the above exactly like PIC
  ** by deleting the following three lines.  But I (drh) think
  ** it works better with those lines in place.
  */
  if( p->list && p->list->n ){
    pik_elem_set_exit(p->list->a[p->list->n-1], eDir);
  }
}

/* Move all coordinates contained within an element (and within its
** substructure) by dx, dy
*/
static void pik_elem_move(PElem *pElem, PNum dx, PNum dy){
  int i;
  pElem->ptAt.x += dx;
  pElem->ptAt.y += dy;
  pElem->ptEnter.x += dx;
  pElem->ptEnter.y += dy;
  pElem->ptExit.x += dx;
  pElem->ptExit.y += dy;
  pElem->bbox.ne.x += dx;
  pElem->bbox.ne.y += dy;
  pElem->bbox.sw.x += dx;
  pElem->bbox.sw.y += dy;
  for(i=0; i<pElem->nPath; i++){
    pElem->aPath[i].x += dx;
    pElem->aPath[i].y += dy;
  }
  if( pElem->pSublist ){
    pik_elist_move(pElem->pSublist, dx, dy);
  }
}
static void pik_elist_move(PEList *pList, PNum dx, PNum dy){
  int i;
  for(i=0; i<pList->n; i++){
    pik_elem_move(pList->a[i], dx, dy);
  }
}

/*
** Check to see if it is ok to set the value of paraemeter mThis.
** Return 0 if it is ok. If it not ok, generate an appropriate
** error message and return non-zero.
**
** Flags are set in pElem so that the same element or conflicting
** elements may not be set again.
**
** To be ok, bit mThis must be clear and no more than one of
** the bits identified by mBlockers may be set.
*/
static int pik_param_ok(
  Pik *p,             /* For storing the error message (if any) */
  PElem *pElem,       /* The element under construction */
  PToken *pId,        /* Make the error point to this token */
  int mThis           /* Value we are trying to set */
){
  if( pElem->mProp & mThis ){
    pik_error(p, pId, "value is already set");
    return 1;
  }
  if( pElem->mCalc & mThis ){
    pik_error(p, pId, "value already fixed by prior constraints");
    return 1;
  }
  pElem->mProp |= mThis;
  return 0;
}


/*
** Set a numeric property like "width 7" or "radius 200%".
**
** The rAbs term is an absolute value to add in.  rRel is
** a relative value by which to change the current value.
*/
void pik_set_numprop(Pik *p, PToken *pId, PRel *pVal){
  PElem *pElem = p->cur;
  switch( pId->eType ){
    case T_HEIGHT:
      if( pik_param_ok(p, pElem, pId, A_HEIGHT) ) return;
      pElem->h = pElem->h*pVal->rRel + pVal->rAbs;
      break;
    case T_WIDTH:
      if( pik_param_ok(p, pElem, pId, A_WIDTH) ) return;
      pElem->w = pElem->w*pVal->rRel + pVal->rAbs;
      break;
    case T_RADIUS:
      if( pik_param_ok(p, pElem, pId, A_RADIUS) ) return;
      pElem->rad = pElem->rad*pVal->rRel + pVal->rAbs;
      break;
    case T_DIAMETER:
      if( pik_param_ok(p, pElem, pId, A_RADIUS) ) return;
      pElem->rad = pElem->rad*pVal->rRel + 0.5*pVal->rAbs; /* diam it 2x rad */
      break;
    case T_THICKNESS:
      if( pik_param_ok(p, pElem, pId, A_THICKNESS) ) return;
      pElem->sw = pElem->sw*pVal->rRel + pVal->rAbs;
      break;
  }
  if( pElem->type->xNumProp ){
    pElem->type->xNumProp(p, pElem, pId);
  }
  return;
}

/*
** Set a color property.  The argument is an RGB value.
*/
void pik_set_clrprop(Pik *p, PToken *pId, PNum rClr){
  PElem *pElem = p->cur;
  switch( pId->eType ){
    case T_FILL:
      if( pik_param_ok(p, pElem, pId, A_FILL) ) return;
      pElem->fill = rClr;
      break;
    case T_COLOR:
      if( pik_param_ok(p, pElem, pId, A_COLOR) ) return;
      pElem->color = rClr;
      break;
  }
  if( pElem->type->xNumProp ){
    pElem->type->xNumProp(p, pElem, pId);
  }
  return;
}

/*
** Set a "dashed" property like "dash 0.05"
**
** Use the value supplied by pVal if available.  If pVal==0, use
** a default.
*/
void pik_set_dashed(Pik *p, PToken *pId, PNum *pVal){
  PElem *pElem = p->cur;
  PNum v;
  switch( pId->eType ){
    case T_DOTTED:  {
      v = pVal==0 ? pik_value(p,"dashwid",7,0) : *pVal;
      pElem->dotted = v;
      pElem->dashed = 0.0;
      break;
    }
    case T_DASHED:  {
      v = pVal==0 ? pik_value(p,"dashwid",7,0) : *pVal;
      pElem->dashed = v;
      pElem->dotted = 0.0;
      break;
    }
  }
}

/*
** If the current path information came from a "same" or "same as"
** reset it.
*/
static void pik_reset_samepath(Pik *p){
  if( p->samePath ){
    p->samePath = 0;
    p->nTPath = 1;
  }
}


/* Add a new term to the path for a line-oriented object by transferring
** the information in the ptTo field over onto the path and into ptFrom
** resetting the ptTo.
*/
static void pik_then(Pik *p, PToken *pToken, PElem *pElem){
  int n;
  if( !pElem->type->isLine ){
    pik_error(p, pToken, "use with line-oriented objects only");
    return;
  }
  n = p->nTPath - 1;
  if( n<1 && (pElem->mProp & A_FROM)==0 ){
    pik_error(p, pToken, "no prior path points");
    return;
  }
  p->thenFlag = 1;
}

/* Advance to the next entry in p->aTPath.  Return its index.
*/
static int pik_next_rpath(Pik *p, PToken *pErr){
  int n = p->nTPath - 1;
  if( n+1>=(int)count(p->aTPath) ){
    pik_error(0, pErr, "too many path elements");
    return n;
  }
  n++;
  p->nTPath++;
  p->aTPath[n] = p->aTPath[n-1];
  p->mTPath = 0;
  return n;
}

/* Add a direction term to an element.  "up 0.5", or "left 3", or "down"
** or "down 50%".
*/
static void pik_add_direction(Pik *p, PToken *pDir, PRel *pVal){
  PElem *pElem = p->cur;
  int n;
  int dir;
  if( !pElem->type->isLine ){
    if( pDir ){
      pik_error(p, pDir, "use with line-oriented objects only");
    }else{
      PToken x = pik_next_semantic_token(&pElem->errTok);
      pik_error(p, &x, "syntax error");
    }
    return;
  }
  pik_reset_samepath(p);
  n = p->nTPath - 1;
  if( p->thenFlag || p->mTPath==3 || n==0 ){
    n = pik_next_rpath(p, pDir);
    p->thenFlag = 0;
  }
  dir = pDir ? pDir->eCode : p->eDir;
  switch( dir ){
    case DIR_UP:
       if( p->mTPath & 2 ) n = pik_next_rpath(p, pDir);
       p->aTPath[n].y += pVal->rAbs + pElem->h*pVal->rRel;
       p->mTPath |= 2;
       break;
    case DIR_DOWN:
       if( p->mTPath & 2 ) n = pik_next_rpath(p, pDir);
       p->aTPath[n].y -= pVal->rAbs + pElem->h*pVal->rRel;
       p->mTPath |= 2;
       break;
    case DIR_RIGHT:
       if( p->mTPath & 1 ) n = pik_next_rpath(p, pDir);
       p->aTPath[n].x += pVal->rAbs + pElem->w*pVal->rRel;
       p->mTPath |= 1;
       break;
    case DIR_LEFT:
       if( p->mTPath & 1 ) n = pik_next_rpath(p, pDir);
       p->aTPath[n].x -= pVal->rAbs + pElem->w*pVal->rRel;
       p->mTPath |= 1;
       break;
  }
  pElem->outDir = dir;
}

/* Process a movement attribute of one of these forms:
**
**         pDist   pHdgKW  rHdg    pEdgept
**     GO distance HEADING angle
**     GO distance               compasspoint
*/
static void pik_move_hdg(
  Pik *p,              /* The Pikchr context */
  PRel *pDist,         /* Distance to move */
  PToken *pHeading,    /* "heading" keyword if present */
  PNum rHdg,           /* Angle argument to "heading" keyword */
  PToken *pEdgept,     /* EDGEPT keyword "ne", "sw", etc... */
  PToken *pErr         /* Token to use for error messages */
){
  PElem *pElem = p->cur;
  int n;
  PNum rDist = pDist->rAbs + pik_value(p,"linewid",7,0)*pDist->rRel;
  if( !pElem->type->isLine ){
    pik_error(p, pErr, "use with line-oriented objects only");
    return;
  }
  pik_reset_samepath(p);
  do{
    n = pik_next_rpath(p, pErr);
  }while( n<1 );
  if( pHeading ){
    if( rHdg<0.0 || rHdg>360.0 ){
      pik_error(p, pHeading, "headings should be between 0 and 360");
      return;
    }
  }else if( pEdgept->eEdge==CP_C ){
    pik_error(p, pEdgept, "syntax error");
    return;
  }else{
    rHdg = pik_hdg_angle[pEdgept->eEdge];
  }
  if( rHdg<=45.0 ){
    pElem->outDir = DIR_UP;
  }else if( rHdg<=135.0 ){
    pElem->outDir = DIR_RIGHT;
  }else if( rHdg<=225.0 ){
    pElem->outDir = DIR_DOWN;
  }else if( rHdg<=315.0 ){
    pElem->outDir = DIR_LEFT;
  }else{
    pElem->outDir = DIR_UP;
  }
  rHdg *= 0.017453292519943295769;  /* degrees to radians */
  p->aTPath[n].x += rDist*sin(rHdg);
  p->aTPath[n].y += rDist*cos(rHdg);
  p->mTPath = 2;
}


/* Process a movement attribute of the form "right until even with ..."
**
** pDir is the first keyword, "right" or "left" or "up" or "down".
** The movement is in that direction until its closest approach to
** the point specified by pPoint.
*/
static void pik_evenwith(Pik *p, PToken *pDir, PPoint *pPlace){
  PElem *pElem = p->cur;
  int n;
  if( !pElem->type->isLine ){
    pik_error(p, pDir, "use with line-oriented objects only");
    return;
  }
  pik_reset_samepath(p);
  n = p->nTPath - 1;
  if( p->thenFlag || p->mTPath==3 || n==0 ){
    n = pik_next_rpath(p, pDir);
    p->thenFlag = 0;
  }
  switch( pDir->eCode ){
    case DIR_DOWN:
    case DIR_UP:
       if( p->mTPath & 2 ) n = pik_next_rpath(p, pDir);
       p->aTPath[n].y = pPlace->y;
       p->mTPath |= 2;
       break;
    case DIR_RIGHT:
    case DIR_LEFT:
       if( p->mTPath & 1 ) n = pik_next_rpath(p, pDir);
       p->aTPath[n].x = pPlace->x;
       p->mTPath |= 1;
       break;
  }
  pElem->outDir = pDir->eCode;
}

/* Set the "from" of an element
*/
static void pik_set_from(Pik *p, PElem *pElem, PToken *pTk, PPoint *pPt){
  if( !pElem->type->isLine ){
    pik_error(p, pTk, "use \"at\" to position this object");
    return;
  }
  if( pElem->mProp & A_FROM ){
    pik_error(p, pTk, "line start location already fixed");
    return;
  }
  if( pElem->bClose ){
    pik_error(p, pTk, "polygon is closed");
    return;
  }
  if( p->nTPath>1 ){
    PNum dx = pPt->x - p->aTPath[0].x;
    PNum dy = pPt->y - p->aTPath[0].y;
    int i;
    for(i=1; i<p->nTPath; i++){
      p->aTPath[i].x += dx;
      p->aTPath[i].y += dy;
    }
  }
  p->aTPath[0] = *pPt;
  p->mTPath = 3;
  pElem->mProp |= A_FROM;
}

/* Set the "to" of an element
*/
static void pik_add_to(Pik *p, PElem *pElem, PToken *pTk, PPoint *pPt){
  int n = p->nTPath-1;
  if( !pElem->type->isLine ){
    pik_error(p, pTk, "use \"at\" to position this object");
    return;
  }
  if( pElem->bClose ){
    pik_error(p, pTk, "polygon is closed");
    return;
  }
  if( n==0 || p->mTPath==3 || p->thenFlag ){
    n = pik_next_rpath(p, pTk);
  }
  p->aTPath[n] = *pPt;
  p->mTPath = 3;
}

static void pik_close_path(Pik *p, PToken *pErr){
  PElem *pElem = p->cur;
  if( p->nTPath<3 ){
    pik_error(p, pErr,
      "need at least 3 vertexes in order to close the polygon");
    return;
  }
  if( pElem->bClose ){
    pik_error(p, pErr, "polygon already closed");
    return;
  }
  pElem->bClose = 1;
}

/* Lower the layer of the current element so that it is behind the
** given element.
*/
static void pik_behind(Pik *p, PElem *pOther){
  PElem *pElem = p->cur;
  if( pElem->iLayer>=pOther->iLayer ){
    pElem->iLayer = pOther->iLayer - 1;
  }
}


/* Set the "at" of an element
*/
static void pik_set_at(Pik *p, PToken *pEdge, PPoint *pAt, PToken *pErrTok){
  PElem *pElem;
  if( p->nErr ) return;
  pElem = p->cur;

  if( pElem->type->isLine ){
    pik_error(p, pErrTok, "use \"from\" and \"to\" to position this object");
    return;
  }
  if( pElem->mProp & A_AT ){
    pik_error(p, pErrTok, "location fixed by prior \"at\"");
    return;
  }
  pElem->mProp |= A_AT;
  pElem->eWith = pEdge ? pEdge->eEdge : CP_C;
  pElem->with = *pAt;
}

/*
** Try to add a text attribute to an element
*/
static void pik_add_txt(Pik *p, PToken *pTxt, int iPos){
  PElem *pElem = p->cur;
  PToken *pT;
  if( pElem->nTxt >= count(pElem->aTxt) ){
    pik_error(p, pTxt, "too many text terms");
    return;
  }
  pT = &pElem->aTxt[pElem->nTxt++];
  *pT = *pTxt;
  pT->eCode = iPos;
}

/* Merge "text-position" flags
*/
static int pik_text_position(int iPrev, PToken *pFlag){
  int iRes = iPrev;
  switch( pFlag->eType ){
    case T_LJUST:    iRes = (iRes&~TP_JMASK) | TP_LJUST;  break;
    case T_RJUST:    iRes = (iRes&~TP_JMASK) | TP_RJUST;  break;
    case T_ABOVE:    iRes = (iRes&~TP_VMASK) | TP_ABOVE;  break;
    case T_CENTER:   iRes = (iRes&~TP_VMASK) | TP_CENTER; break;
    case T_BELOW:    iRes = (iRes&~TP_VMASK) | TP_BELOW;  break;
    case T_ITALIC:   iRes |= TP_ITALIC;                   break; 
    case T_BOLD:     iRes |= TP_BOLD;                     break; 
    case T_ALIGNED:  iRes |= TP_ALIGN;                    break;
    case T_BIG:      if( iRes & TP_BIG ) iRes |= TP_XTRA;
                     else iRes = (iRes &~TP_SZMASK)|TP_BIG;   break;
    case T_SMALL:    if( iRes & TP_SMALL ) iRes |= TP_XTRA;
                     else iRes = (iRes &~TP_SZMASK)|TP_SMALL; break;
  }
  return iRes;
}

/* Return an estimate of the actual number of displayed characters
** in a character string.
**
** Omit "\" used to escape characters.  And count entities like
** "&lt;" as a single character.  Multi-byte UTF8 characters count
** as a single character.
*/
static int pik_text_length(const PToken *pToken){
  int n = pToken->n;
  const char *z = pToken->z;
  int cnt, j;
  for(j=1, cnt=0; j<n-1; j++){
    if( (z[j] & 0xc0)==0xc0 ) continue;
    cnt++;
    if( z[j]=='\\' && z[j+1]!='&' ){
      j++;
    }else if( z[j]=='&' ){
      int k;
      for(k=j+1; k<j+7 && z[k]!=0 && z[k]!=';'; k++){}
      if( z[k]==';' ) j = k;
    }
  }
  return cnt;
}

/* Adjust the width, height, and/or radius of the object so that
** it fits around the text that has been added so far.
**
**    (1) Only text specified prior to this attribute is considered.
**    (2) The text size is estimated based on the charht and charwid
**        variable settings.
**    (3) The fitted attributes can be changed again after this
**        attribute, for example using "width 110%" if this auto-fit
**        underestimates the text size.
**    (4) Previously set attributes will not be altered.  In other words,
**        "width 1in fit" might cause the height to change, but the
**        width is now set.
**    (5) This only works for attributes that have an xFit method.
*/
static void pik_size_to_fit(Pik *p, PToken *pFit){
  PElem *pElem;
  int w = 0, h = 0;
  int i;
  if( p->nErr ) return;
  pElem = p->cur;

  if( pElem->nTxt==0 ){
    pik_error(0, pFit, "no text to fit to");
    return;
  }
  if( pElem->type->xFit==0 ) return;
  if( (pElem->mProp & A_HEIGHT)==0 ){
    int hasCenter = 0;
    int hasSingleStack = 0;
    int hasDoubleStack = 0;
    pik_txt_vertical_layout(pElem);
    for(i=0; i<pElem->nTxt; i++){
      if( pElem->aTxt[i].eCode & TP_CENTER ){
        hasCenter = 1;
      }else if( pElem->aTxt[i].eCode & (TP_ABOVE2|TP_BELOW2) ){
        hasDoubleStack = 1;
      }else if( pElem->aTxt[i].eCode & (TP_ABOVE|TP_BELOW) ){
        hasSingleStack = 1;
      }
    }
    h = hasCenter + hasSingleStack*2 + hasDoubleStack*2;
  }
  if( (pElem->mProp & A_WIDTH)==0 ){
    for(i=0; i<pElem->nTxt; i++){
      int cnt = pik_text_length(&pElem->aTxt[i]);
      if( pElem->type->eJust==0 
       && (pElem->aTxt[i].eCode & TP_JMASK)!=0
      ){
         cnt *= 2;
      }
      if( cnt>w ) w = cnt;
    }
  }
  if( h>0 || w>0 ){
    pik_compute_layout_settings(p);
    pElem->type->xFit(p, pElem, w*p->charWidth, h*p->charHeight);
  }
}

/* Set a local variable name to "val".
**
** The name might be a built-in variable or a color name.  In either case,
** a new application-defined variable is set.  Since app-defined variables
** are searched first, this will override any built-in variables.
*/
static void pik_set_var(Pik *p, PToken *pId, PNum val, PToken *pOp){
  PVar *pVar = p->pVar;
  while( pVar ){
    if( pik_token_eq(pId,pVar->zName)==0 ) break;
    pVar = pVar->pNext;
  }
  if( pVar==0 ){
    char *z;
    pVar = malloc( pId->n+1 + sizeof(*pVar) );
    if( pVar==0 ){
      pik_error(p, 0, 0);
      return;
    }
    pVar->zName = z = (char*)&pVar[1];
    memcpy(z, pId->z, pId->n);
    z[pId->n] = 0;
    pVar->pNext = p->pVar;
    pVar->val = pik_value(p, pId->z, pId->n, 0);
    p->pVar = pVar;
  }
  switch( pOp->eCode ){
    case T_PLUS:  pVar->val += val; break;
    case T_STAR:  pVar->val *= val; break;
    case T_MINUS: pVar->val -= val; break;
    case T_SLASH:
      if( val==0.0 ){
        pik_error(p, pOp, "division by zero");
      }else{
        pVar->val /= val;
      }
      break;
    default:      pVar->val = val; break;
  }
  p->bLayoutVars = 0;  /* Clear the layout setting cache */
}

/*
** Search for the variable named z[0..n-1] in:
**
**   * Application defined variables
**   * Built-in variables
**
** Return the value of the variable if found.  If not found
** return 0.0.  Also if pMiss is not NULL, then set it to 1
** if not found.
**
** This routine is a subroutine to pik_get_var().  But it is also
** used by object implementations to look up (possibly overwritten)
** values for built-in variables like "boxwid".
*/
static PNum pik_value(Pik *p, const char *z, int n, int *pMiss){
  PVar *pVar;
  int first, last, mid, c;
  for(pVar=p->pVar; pVar; pVar=pVar->pNext){
    if( strncmp(pVar->zName,z,n)==0 && pVar->zName[n]==0 ){
      return pVar->val;
    }
  }
  first = 0;
  last = count(aBuiltin)-1;
  while( first<=last ){
    mid = (first+last)/2;
    c = strncmp(z,aBuiltin[mid].zName,n);
    if( c==0 && aBuiltin[mid].zName[n] ) c = 1;
    if( c==0 ) return aBuiltin[mid].val;
    if( c>0 ){
      first = mid+1;
    }else{
      last = mid-1;
    }
  }
  if( pMiss ) *pMiss = 1;
  return 0.0;
}

/*
** Look up a color-name.  Unlike other names in this program, the
** color-names are not case sensitive.  So "DarkBlue" and "darkblue"
** and "DARKBLUE" all find the same value (139).
**
** If not found, return -1.0.  Also post an error if p!=NULL.
**
** Special color names "None" and "Off" return -1.0 without causing
** an error.
*/
static PNum pik_lookup_color(Pik *p, PToken *pId){
  int first, last, mid, c = 0;
  first = 0;
  last = count(aColor)-1;
  while( first<=last ){
    const char *zClr;
    int c1, c2;
    unsigned int i;
    mid = (first+last)/2;
    zClr = aColor[mid].zName;
    for(i=0; i<pId->n; i++){
      c1 = zClr[i]&0x7f;
      if( isupper(c1) ) c1 = tolower(c1);
      c2 = pId->z[i]&0x7f;
      if( isupper(c2) ) c2 = tolower(c2);
      c = c2 - c1;
      if( c ) break;
    }
    if( c==0 && aColor[mid].zName[pId->n] ) c = -1;
    if( c==0 ) return (double)aColor[mid].val;
    if( c>0 ){
      first = mid+1;
    }else{
      last = mid-1;
    }
  }
  if( p ) pik_error(p, pId, "not a known color name");
  return -1.0;
}

/* Get the value of a variable.
**
** Search in order:
**
**    *  Application defined variables
**    *  Built-in variables
**    *  Color names
**
** If no such variable is found, throw an error.
*/
static PNum pik_get_var(Pik *p, PToken *pId){
  int miss = 0;
  PNum v = pik_value(p, pId->z, pId->n, &miss);
  if( miss==0 ) return v;
  v = pik_lookup_color(0, pId);
  if( v>=0.0 ) return v;
  pik_error(p,pId,"no such variable");
  return 0.0;
}

/* Convert a T_NTH token (ex: "2nd", "5th"} into a numeric value and
** return that value.  Throw an error if the value is too big.
*/
static short int pik_nth_value(Pik *p, PToken *pNth){
  int i = atoi(pNth->z);
  if( i>1000 ){
    pik_error(p, pNth, "value too big - max '1000th'");
    i = 1;
  }
  if( i==0 && pik_token_eq(pNth,"first")==0 ) i = 1;
  return i;
}

/* Search for the NTH element.
**
** If pBasis is not NULL then it should be a [] element.  Use the
** sublist of that [] element for the search.  If pBasis is not a []
** element, then throw an error.
**
** The pNth token describes the N-th search.  The pNth->eCode value
** is one more than the number of items to skip.  It is negative
** to search backwards.  If pNth->eType==T_ID, then it is the name
** of a class to search for.  If pNth->eType==T_LB, then
** search for a [] object.  If pNth->eType==T_LAST, then search for
** any type.
**
** Raise an error if the item is not found.
*/
static PElem *pik_find_nth(Pik *p, PElem *pBasis, PToken *pNth){
  PEList *pList;
  int i, n;
  const PClass *pClass;
  if( pBasis==0 ){
    pList = p->list;
  }else{
    pList = pBasis->pSublist;
  }
  if( pList==0 ){
    pik_error(p, pNth, "no such object");
    return 0;
  }
  if( pNth->eType==T_LAST ){
    pClass = 0;
  }else if( pNth->eType==T_LB ){
    pClass = &sublistClass;
  }else{
    pClass = pik_find_class(pNth);
    if( pClass==0 ){
      pik_error(0, pNth, "no such object type");
      return 0;
    }
  }
  n = pNth->eCode;
  if( n<0 ){
    for(i=pList->n-1; i>=0; i--){
      PElem *pElem = pList->a[i];
      if( pClass && pElem->type!=pClass ) continue;
      n++;
      if( n==0 ){ return pElem; }
    }
  }else{
    for(i=0; i<pList->n; i++){
      PElem *pElem = pList->a[i];
      if( pClass && pElem->type!=pClass ) continue;
      n--;
      if( n==0 ){ return pElem; }
    }
  }
  pik_error(p, pNth, "no such object");
  return 0;
}

/* Search for an element by name.
**
** Search in pBasis->pSublist if pBasis is not NULL.  If pBasis is NULL
** then search in p->list.
*/
static PElem *pik_find_byname(Pik *p, PElem *pBasis, PToken *pName){
  PEList *pList;
  int i, j;
  if( pBasis==0 ){
    pList = p->list;
  }else{
    pList = pBasis->pSublist;
  }
  if( pList==0 ){
    pik_error(p, pName, "no such object");
    return 0;
  }
  /* First look explicitly tagged objects */
  for(i=pList->n-1; i>=0; i--){
    PElem *pElem = pList->a[i];
    if( pElem->zName && pik_token_eq(pName,pElem->zName)==0 ){
      return pElem;
    }
  }
  /* If not found, do a second pass looking for any object containing
  ** text which exactly matches pName */
  for(i=pList->n-1; i>=0; i--){
    PElem *pElem = pList->a[i];
    for(j=0; j<pElem->nTxt; j++){
      if( pElem->aTxt[j].n==pName->n+2
       && memcmp(pElem->aTxt[j].z+1,pName->z,pName->n)==0 ){
        return pElem;
      }
    }
  }
  pik_error(p, pName, "no such object");
  return 0;
}

/* Change most of the settings for the current object to be the
** same as the pOther object, or the most recent element of the same
** type if pOther is NULL.
*/
static void pik_same(Pik *p, PElem *pOther, PToken *pErrTok){
  PElem *pElem = p->cur;
  if( p->nErr ) return;
  if( pOther==0 ){
    int i;
    for(i=(p->list ? p->list->n : 0)-1; i>=0; i--){
      pOther = p->list->a[i];
      if( pOther->type==pElem->type ) break;
    }
    if( i<0 ){
      pik_error(p, pErrTok, "no prior objects of the same type");
      return;
    }
  }
  if( pOther->nPath && pElem->type->isLine ){
    PNum dx, dy;
    int i;
    dx = p->aTPath[0].x - pOther->aPath[0].x;
    dy = p->aTPath[0].y - pOther->aPath[0].y;
    for(i=1; i<pOther->nPath; i++){
      p->aTPath[i].x = pOther->aPath[i].x + dx;
      p->aTPath[i].y = pOther->aPath[i].y + dy;
    }
    p->nTPath = pOther->nPath;
    p->mTPath = 3;
    p->samePath = 1;
  }
  if( !pElem->type->isLine ){
    pElem->w = pOther->w;
    pElem->h = pOther->h;
  }
  pElem->rad = pOther->rad;
  pElem->sw = pOther->sw;
  pElem->dashed = pOther->dashed;
  pElem->dotted = pOther->dotted;
  pElem->fill = pOther->fill;
  pElem->color = pOther->color;
  pElem->cw = pOther->cw;
  pElem->larrow = pOther->larrow;
  pElem->rarrow = pOther->rarrow;
  pElem->bClose = pOther->bClose;
  pElem->bChop = pOther->bChop;
  pElem->inDir = pOther->inDir;
  pElem->outDir = pOther->outDir;
}


/* Return a "Place" associated with element pElem.  If pEdge is NULL
** return the center of the object.  Otherwise, return the corner
** described by pEdge.
*/
static PPoint pik_place_of_elem(Pik *p, PElem *pElem, PToken *pEdge){
  PPoint pt;
  const PClass *pClass;
  pt.x = 0.0;
  pt.y = 0.0;
  if( pElem==0 ) return pt;
  if( pEdge==0 ){
    return pElem->ptAt;
  }
  pClass = pElem->type;
  if( pEdge->eType==T_EDGEPT || (pEdge->eEdge>0 && pEdge->eEdge<CP_END) ){
    pt = pClass->xOffset(p, pElem, pEdge->eEdge);
    pt.x += pElem->ptAt.x;
    pt.y += pElem->ptAt.y;
    return pt;
  }
  if( pEdge->eType==T_START ){
    return pElem->ptEnter;
  }else{
    return pElem->ptExit;
  }
}

/* Do a linear interpolation of two positions.
*/
static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2){
  PPoint out;
  if( x<0.0 ) x = 0.0;
  if( x>1.0 ) x = 1.0;
  out.x = p2.x*x + p1.x*(1.0 - x);
  out.y = p2.y*x + p1.y*(1.0 - x);
  return out;
}

/* Compute the position that is dist away from pt at an heading angle of r
**
** The angle is a compass heading in degrees.  North is 0 (or 360).
** East is 90.  South is 180.  West is 270.  And so forth.
*/
static PPoint pik_position_at_angle(PNum dist, PNum r, PPoint pt){
  r *= 0.017453292519943295769;  /* degrees to radians */
  pt.x += dist*sin(r);
  pt.y += dist*cos(r);
  return pt;
}

/* Compute the position that is dist away at a compass point
*/
static PPoint pik_position_at_hdg(PNum dist, PToken *pD, PPoint pt){
  return pik_position_at_angle(dist, pik_hdg_angle[pD->eEdge], pt);
}

/* Return the coordinates for the n-th vertex of a line.
*/
static PPoint pik_nth_vertex(Pik *p, PToken *pNth, PToken *pErr, PElem *pObj){
  static const PPoint zero;
  int n;
  if( p->nErr || pObj==0 ) return p->aTPath[0];
  if( !pObj->type->isLine ){
    pik_error(p, pErr, "object is not a line");
    return zero;
  }
  n = atoi(pNth->z);
  if( n<1 || n>pObj->nPath ){
    pik_error(p, pNth, "no such vertex");
    return zero;
  }
  return pObj->aPath[n-1];
}

/* Return the value of a property of an object.
*/
static PNum pik_property_of(PElem *pElem, PToken *pProp){
  PNum v = 0.0;
  switch( pProp->eType ){
    case T_HEIGHT:    v = pElem->h;            break;
    case T_WIDTH:     v = pElem->w;            break;
    case T_RADIUS:    v = pElem->rad;          break;
    case T_DIAMETER:  v = pElem->rad*2.0;      break;
    case T_THICKNESS: v = pElem->sw;           break;
    case T_DASHED:    v = pElem->dashed;       break;
    case T_DOTTED:    v = pElem->dotted;       break;
    case T_FILL:      v = pElem->fill;         break;
    case T_COLOR:     v = pElem->color;        break;
    case T_X:         v = pElem->ptAt.x;       break;
    case T_Y:         v = pElem->ptAt.y;       break;
    case T_TOP:       v = pElem->bbox.ne.y;    break;
    case T_BOTTOM:    v = pElem->bbox.sw.y;    break;
    case T_LEFT:      v = pElem->bbox.sw.x;    break;
    case T_RIGHT:     v = pElem->bbox.ne.x;    break;
  }
  return v;
}

/* Compute one of the built-in functions
*/
static PNum pik_func(Pik *p, PToken *pFunc, PNum x, PNum y){
  PNum v = 0.0;
  switch( pFunc->eCode ){
    case FN_ABS:  v = v<0.0 ? -v : v;  break;
    case FN_COS:  v = cos(x);          break;
    case FN_INT:  v = rint(x);         break;
    case FN_SIN:  v = sin(x);          break;
    case FN_SQRT:
      if( x<0.0 ){
        pik_error(p, pFunc, "sqrt of negative value");
        v = 0.0;
      }else{
        v = sqrt(x);
      }
      break;
    case FN_MAX:  v = x>y ? x : y;   break;
    case FN_MIN:  v = x<y ? x : y;   break;
    default:      v = 0.0;
  }
  return v;
}

/* Attach a name to an element
*/
static void pik_elem_setname(Pik *p, PElem *pElem, PToken *pName){
  if( pElem==0 ) return;
  if( pName==0 ) return;
  free(pElem->zName);
  pElem->zName = malloc(pName->n+1);
  if( pElem->zName==0 ){
    pik_error(p,0,0);
  }else{
    memcpy(pElem->zName,pName->z,pName->n);
    pElem->zName[pName->n] = 0;
  }
  return;
}

/*
** Search for object located at *pCenter that has an xChop method.
** Return a pointer to the object, or NULL if not found.
*/
static PElem *pik_find_chopper(PEList *pList, PPoint *pCenter){
  int i;
  if( pList==0 ) return 0;
  for(i=pList->n-1; i>=0; i--){
    PElem *pElem = pList->a[i];
    if( pElem->type->xChop!=0
     && pElem->ptAt.x==pCenter->x
     && pElem->ptAt.y==pCenter->y
    ){
      return pElem;
    }else if( pElem->pSublist ){
      pElem = pik_find_chopper(pElem->pSublist,pCenter);
      if( pElem ) return pElem;
    }
  }
  return 0;
}

/*
** There is a line traveling from pFrom to pTo.
**
** If point pTo is the exact enter of a choppable object,
** then adjust pTo by the appropriate amount in the direction
** of pFrom.
*/
static void pik_autochop(Pik *p, PPoint *pFrom, PPoint *pTo){
  PElem *pElem = pik_find_chopper(p->list, pTo);
  if( pElem ){
    *pTo = pElem->type->xChop(p, pElem, pFrom);
  }
}

/* This routine runs after all attributes have been received
** on an element.
*/
static void pik_after_adding_attributes(Pik *p, PElem *pElem){
  int i;
  PPoint ofst;
  PNum dx, dy;

  if( p->nErr ) return;

  /* Position block elements */
  if( pElem->type->isLine==0 ){
    ofst = pik_elem_offset(p, pElem, pElem->eWith);
    dx = (pElem->with.x - ofst.x) - pElem->ptAt.x;
    dy = (pElem->with.y - ofst.y) - pElem->ptAt.y;
    if( dx!=0 || dy!=0 ){
      pik_elem_move(pElem, dx, dy);
    }
  }

  /* For a line object with no movement specified, a single movement
  ** of the default length in the current direction
  */
  if( pElem->type->isLine && p->nTPath<2 ){
    pik_next_rpath(p, 0);
    assert( p->nTPath==2 );
    switch( pElem->inDir ){
      default:        p->aTPath[1].x += pElem->w; break;
      case DIR_DOWN:  p->aTPath[1].y -= pElem->h; break;
      case DIR_LEFT:  p->aTPath[1].x -= pElem->w; break;
      case DIR_UP:    p->aTPath[1].y += pElem->h; break;
    }
    if( pElem->type->xInit==arcInit ){
      p->eDir = pElem->outDir = (pElem->inDir + (pElem->cw ? 1 : 3))%4;
      switch( pElem->outDir ){
        default:        p->aTPath[1].x += pElem->w; break;
        case DIR_DOWN:  p->aTPath[1].y -= pElem->h; break;
        case DIR_LEFT:  p->aTPath[1].x -= pElem->w; break;
        case DIR_UP:    p->aTPath[1].y += pElem->h; break;
      }
    }
  }

  /* Initialize the bounding box prior to running xCheck */
  pik_bbox_init(&pElem->bbox);

  /* Run object-specific code */
  if( pElem->type->xCheck!=0 ){
    pElem->type->xCheck(p,pElem);
    if( p->nErr ) return;
  }

  /* Compute final bounding box, entry and exit points, center
  ** point (ptAt) and path for the element
  */
  if( pElem->type->isLine ){
    pElem->aPath = malloc( sizeof(PPoint)*p->nTPath );
    if( pElem->aPath==0 ){
      pik_error(p, 0, 0);
      return;
    }else{
      pElem->nPath = p->nTPath;
      for(i=0; i<p->nTPath; i++){
        pElem->aPath[i] = p->aTPath[i];
      }
    }

    /* "chop" processing:
    ** If the line goes to the center of an object with an
    ** xChop method, then use the xChop method to trim the line.
    */
    if( pElem->bChop && pElem->nPath>=2 ){
      int n = pElem->nPath;
      pik_autochop(p, &pElem->aPath[n-2], &pElem->aPath[n-1]);
      pik_autochop(p, &pElem->aPath[1], &pElem->aPath[0]);
    }

    pElem->ptEnter = pElem->aPath[0];
    pElem->ptExit = pElem->aPath[pElem->nPath-1];

    /* Compute the center of the line based on the bounding box over
    ** the vertexes */
    for(i=0; i<pElem->nPath; i++){
      pik_bbox_add_xy(&pElem->bbox, pElem->aPath[i].x, pElem->aPath[i].y);
    }
    pElem->ptAt.x = (pElem->bbox.ne.x + pElem->bbox.sw.x)/2.0;
    pElem->ptAt.y = (pElem->bbox.ne.y + pElem->bbox.sw.y)/2.0;

    /* Reset the width and height of the object to be the width and height
    ** of the bounding box over vertexes */
    pElem->w = pElem->bbox.ne.x - pElem->bbox.sw.x;
    pElem->h = pElem->bbox.ne.y - pElem->bbox.sw.y;

    /* If this is a polygon (if it has the "close" attribute), then
    ** adjust the exit point */
    if( pElem->bClose ){
      /* "closed" lines work like block objects */
      pik_elem_set_exit(pElem, pElem->inDir);
    }else{
      /* For an open line, the "center" is half way between
      ** the .start and the .end */
    }
  }else{
    PNum w2 = pElem->w/2.0;
    PNum h2 = pElem->h/2.0;
    pElem->ptEnter = pElem->ptAt;
    pElem->ptExit = pElem->ptAt;
    switch( pElem->inDir ){
      default:         pElem->ptEnter.x -= w2;  break;
      case DIR_LEFT:   pElem->ptEnter.x += w2;  break;
      case DIR_UP:     pElem->ptEnter.y -= h2;  break;
      case DIR_DOWN:   pElem->ptEnter.y += h2;  break;
    }
    switch( pElem->outDir ){
      default:         pElem->ptExit.x += w2;  break;
      case DIR_LEFT:   pElem->ptExit.x -= w2;  break;
      case DIR_UP:     pElem->ptExit.y += h2;  break;
      case DIR_DOWN:   pElem->ptExit.y -= h2;  break;
    }
    pik_bbox_add_xy(&pElem->bbox, pElem->ptAt.x - w2, pElem->ptAt.y - h2);
    pik_bbox_add_xy(&pElem->bbox, pElem->ptAt.x + w2, pElem->ptAt.y + h2);
  }
  p->eDir = pElem->outDir;
}

/* Show basic information about each element as a comment in the
** generated HTML.  Used for testing and debugging.  Activated
** by the (undocumented) "debug = 1;"
** command.
*/
static void pik_elem_render(Pik *p, PElem *pElem){
  char *zDir;
  if( pElem==0 ) return;
  pik_append(p,"<!-- ", -1);
  if( pElem->zName ){
    pik_append_text(p, pElem->zName, -1, 0);
    pik_append(p, ": ", 2);
  }
  pik_append_text(p, pElem->type->zName, -1, 0);
  if( pElem->nTxt ){
    pik_append(p, " \"", 2);
    pik_append_text(p, pElem->aTxt[0].z+1, pElem->aTxt[0].n-2, 1);
    pik_append(p, "\"", 1);
  }
  pik_append_num(p, " w=", pElem->w);
  pik_append_num(p, " h=", pElem->h);
  pik_append_point(p, " center=", &pElem->ptAt);
  pik_append_point(p, " enter=", &pElem->ptEnter);
  switch( pElem->outDir ){
    default:        zDir = " right";  break;
    case DIR_LEFT:  zDir = " left";   break;
    case DIR_UP:    zDir = " up";     break;
    case DIR_DOWN:  zDir = " down";   break;
  }
  pik_append_point(p, " exit=", &pElem->ptExit);
  pik_append(p, zDir, -1);
  pik_append(p, " -->\n", -1);
}

/* Render a list of elements
*/
void pik_elist_render(Pik *p, PEList *pEList){
  int i;
  int iNextLayer = 0;
  int iThisLayer;
  int bMoreToDo;
  int mDebug = (int)pik_value(p, "debug", 5, 0);
  do{
    bMoreToDo = 0;
    iThisLayer = iNextLayer;
    iNextLayer = 0x7fffffff;
    for(i=0; i<pEList->n; i++){
      PElem *pElem = pEList->a[i];
      if( pElem->iLayer>iThisLayer ){
        if( pElem->iLayer<iNextLayer ) iNextLayer = pElem->iLayer;
        bMoreToDo = 1;
        continue; /* Defer until another round */
      }else if( pElem->iLayer<iThisLayer ){
        continue;
      }
      void (*xRender)(Pik*,PElem*);
      if( mDebug & 1 ) pik_elem_render(p, pElem);
      xRender = pElem->type->xRender;
      if( xRender ){
        xRender(p, pElem);
      }
      if( pElem->pSublist ){
        pik_elist_render(p, pElem->pSublist);
      }
    }
  }while( bMoreToDo );
}

/* Add all elements of the list pEList to the bounding box
*/
static void pik_bbox_add_elist(Pik *p, PEList *pEList, PNum wArrow){
  int i;
  for(i=0; i<pEList->n; i++){
    PElem *pElem = pEList->a[i];
    if( pElem->sw>0.0 ) pik_bbox_addbox(&p->bbox, &pElem->bbox);
    pik_append_txt(p, pElem, &p->bbox);
    if( pElem->pSublist ) pik_bbox_add_elist(p, pElem->pSublist, wArrow);


    /* Expand the bounding box to account for arrowheads on lines */
    if( pElem->type->isLine && pElem->nPath>0 ){
      if( pElem->larrow ){
        pik_bbox_addellipse(&p->bbox, pElem->aPath[0].x, pElem->aPath[0].y,
                            wArrow, wArrow);
      }
      if( pElem->rarrow ){
        int j = pElem->nPath-1;
        pik_bbox_addellipse(&p->bbox, pElem->aPath[j].x, pElem->aPath[j].y,
                            wArrow, wArrow);
      }
    }
  }
}

/* Recompute key layout parameters from variables. */
static void pik_compute_layout_settings(Pik *p){
  PNum thickness;  /* Line thickness */
  PNum wArrow;     /* Width of arrowheads */

  /* Set up rendering parameters */
  if( p->bLayoutVars ) return;
  thickness = pik_value(p,"thickness",9,0);
  if( thickness<=0.01 ) thickness = 0.01;
  wArrow = 0.5*pik_value(p,"arrowwid",8,0);
  p->wArrow = wArrow/thickness;
  p->hArrow = pik_value(p,"arrowht",7,0)/thickness;
  p->rScale = 144.0*pik_value(p,"scale",5,0);
  if( p->rScale<5.0 ) p->rScale = 5.0;
  p->fontScale = pik_value(p,"fontscale",9,0);
  if( p->fontScale<=0.0 ) p->fontScale = 1.0;
  p->fontScale *= p->rScale/144.0;
  p->charWidth = pik_value(p,"charwid",7,0)*p->fontScale;
  p->charHeight = pik_value(p,"charht",6,0)*p->fontScale;
  p->bLayoutVars = 1;
}

/* Render a list of elements.  Write the SVG into p->zOut.
** Delete the input element_list before returnning.
*/
static void pik_render(Pik *p, PEList *pEList){
  if( pEList==0 ) return;
  if( p->nErr==0 ){
    PNum thickness;  /* Stroke width */
    PNum margin;     /* Extra bounding box margin */
    PNum leftmargin; /* Extra bounding box area on the left */
    PNum w, h;       /* Drawing width and height */
    PNum wArrow;

    /* Set up rendering parameters */
    pik_compute_layout_settings(p);
    thickness = pik_value(p,"thickness",9,0);
    if( thickness<=0.01 ) thickness = 0.01;
    margin = pik_value(p,"margin",6,0);
    margin += thickness;
    leftmargin = pik_value(p,"leftmargin",10,0);
    wArrow = p->wArrow*thickness;

    /* Compute a bounding box over all objects so that we can know
    ** how big to declare the SVG canvas */
    pik_bbox_init(&p->bbox);
    pik_bbox_add_elist(p, pEList, wArrow);

    /* Expand the bounding box slightly to account for line thickness
    ** and the optional "margin = EXPR" setting. */
    p->bbox.ne.x += margin;
    p->bbox.ne.y += margin;
    p->bbox.sw.x -= margin + leftmargin;
    p->bbox.sw.y -= margin;

    /* Output the SVG */
    pik_append(p, "<svg",4);
    if( p->zClass ){
      pik_append(p, " class=\"", -1);
      pik_append(p, p->zClass, -1);
      pik_append(p, "\"", 1);
    }
    w = p->bbox.ne.x - p->bbox.sw.x;
    h = p->bbox.ne.y - p->bbox.sw.y;
    p->wSVG = (int)(p->rScale*w);
    p->hSVG = (int)(p->rScale*h);
    pik_append_dis(p, " viewBox=\"0 0 ",w,"");
    pik_append_dis(p, " ",h,"\">\n");
    if( (p->mFlags & PIKCHR_INCLUDE_SOURCE)!=0 ){
      /* emit original pikchr source code as metadata */
      /* FIXME: emit this only if a certain p->mFlags is set. */
      pik_append(p, "<metadata>\n", 11);
      pik_append(p, "<pikchr:pikchr xmlns:pikchr="
         "\"https://pikchr.org/home/doc/trunk/doc/grammar.md\">\n",
          -1);
      pik_append(p, "<pikchr:src><![CDATA[", 21);
      pik_append(p, p->zIn, (int)p->nIn);
      pik_append(p, "]]></pikchr:src>\n", 17);
      pik_append(p, "</pikchr:pikchr>\n", 17);
      pik_append(p, "</metadata>\n", 12);
    }
    pik_elist_render(p, pEList);
    pik_append(p,"</svg>\n", -1);
  }else{
    p->wSVG = -1;
    p->hSVG = -1;
  }
  pik_elist_free(p, pEList);
}



/*
** An array of this structure defines a list of keywords.
*/
typedef struct PikWord {
  char *zWord;             /* Text of the keyword */
  unsigned char nChar;     /* Length of keyword text in bytes */
  unsigned char eType;     /* Token code */
  unsigned char eCode;     /* Extra code for the token */
  unsigned char eEdge;     /* CP_* code for corner/edge keywords */
} PikWord;

/*
** Keywords
*/
static const PikWord pik_keywords[] = {
  { "above",      5,   T_ABOVE,     0,         0        },
  { "abs",        3,   T_FUNC1,     FN_ABS,    0        },
  { "aligned",    7,   T_ALIGNED,   0,         0        },
  { "and",        3,   T_AND,       0,         0        },
  { "as",         2,   T_AS,        0,         0        },
  { "assert",     6,   T_ASSERT,    0,         0        },
  { "at",         2,   T_AT,        0,         0        },
  { "behind",     6,   T_BEHIND,    0,         0        },
  { "below",      5,   T_BELOW,     0,         0        },
  { "between",    7,   T_BETWEEN,   0,         0        },
  { "big",        3,   T_BIG,       0,         0        },
  { "bold",       4,   T_BOLD,      0,         0        },
  { "bot",        3,   T_EDGEPT,    0,         CP_S     },
  { "bottom",     6,   T_BOTTOM,    0,         CP_S     },
  { "c",          1,   T_EDGEPT,    0,         CP_C     },
  { "ccw",        3,   T_CCW,       0,         0        },
  { "center",     6,   T_CENTER,    0,         CP_C     },
  { "chop",       4,   T_CHOP,      0,         0        },
  { "close",      5,   T_CLOSE,     0,         0        },
  { "color",      5,   T_COLOR,     0,         0        },
  { "cos",        3,   T_FUNC1,     FN_COS,    0        },
  { "cw",         2,   T_CW,        0,         0        },
  { "dashed",     6,   T_DASHED,    0,         0        },
  { "diameter",   8,   T_DIAMETER,  0,         0        },
  { "dotted",     6,   T_DOTTED,    0,         0        },
  { "down",       4,   T_DOWN,      DIR_DOWN,  0        },
  { "e",          1,   T_EDGEPT,    0,         CP_E     },
  { "east",       4,   T_EDGEPT,    0,         CP_E     },
  { "end",        3,   T_END,       0,         CP_END   },
  { "even",       4,   T_EVEN,      0,         0        },
  { "fill",       4,   T_FILL,      0,         0        },
  { "first",      5,   T_NTH,       0,         0        },
  { "fit",        3,   T_FIT,       0,         0        },
  { "from",       4,   T_FROM,      0,         0        },
  { "go",         2,   T_GO,        0,         0        },
  { "heading",    7,   T_HEADING,   0,         0        },
  { "height",     6,   T_HEIGHT,    0,         0        },
  { "ht",         2,   T_HEIGHT,    0,         0        },
  { "in",         2,   T_IN,        0,         0        },
  { "int",        3,   T_FUNC1,     FN_INT,    0        },
  { "invis",      5,   T_INVIS,     0,         0        },
  { "invisible",  9,   T_INVIS,     0,         0        },
  { "italic",     6,   T_ITALIC,    0,         0        },
  { "last",       4,   T_LAST,      0,         0        },
  { "left",       4,   T_LEFT,      DIR_LEFT,  CP_W     },
  { "ljust",      5,   T_LJUST,     0,         0        },
  { "max",        3,   T_FUNC2,     FN_MAX,    0        },
  { "min",        3,   T_FUNC2,     FN_MIN,    0        },
  { "n",          1,   T_EDGEPT,    0,         CP_N     },
  { "ne",         2,   T_EDGEPT,    0,         CP_NE    },
  { "north",      5,   T_EDGEPT,    0,         CP_N     },
  { "nw",         2,   T_EDGEPT,    0,         CP_NW    },
  { "of",         2,   T_OF,        0,         0        },
  { "previous",   8,   T_LAST,      0,         0,       },
  { "print",      5,   T_PRINT,     0,         0        },
  { "rad",        3,   T_RADIUS,    0,         0        },
  { "radius",     6,   T_RADIUS,    0,         0        },
  { "right",      5,   T_RIGHT,     DIR_RIGHT, CP_E     },
  { "rjust",      5,   T_RJUST,     0,         0        },
  { "s",          1,   T_EDGEPT,    0,         CP_S     },
  { "same",       4,   T_SAME,      0,         0        },
  { "se",         2,   T_EDGEPT,    0,         CP_SE    },
  { "sin",        3,   T_FUNC1,     FN_SIN,    0        },
  { "small",      5,   T_SMALL,     0,         0        },
  { "south",      5,   T_EDGEPT,    0,         CP_S     },
  { "sqrt",       4,   T_FUNC1,     FN_SQRT,   0        },
  { "start",      5,   T_START,     0,         CP_START },
  { "sw",         2,   T_EDGEPT,    0,         CP_SW    },
  { "t",          1,   T_TOP,       0,         CP_N     },
  { "the",        3,   T_THE,       0,         0        },
  { "then",       4,   T_THEN,      0,         0        },
  { "thick",      5,   T_THICK,     0,         0        },
  { "thickness",  9,   T_THICKNESS, 0,         0        },
  { "thin",       4,   T_THIN,      0,         0        },
  { "to",         2,   T_TO,        0,         0        },
  { "top",        3,   T_TOP,       0,         CP_N     },
  { "until",      5,   T_UNTIL,     0,         0        },
  { "up",         2,   T_UP,        DIR_UP,    0        },
  { "vertex",     6,   T_VERTEX,    0,         0        },
  { "w",          1,   T_EDGEPT,    0,         CP_W     },
  { "way",        3,   T_WAY,       0,         0        },
  { "west",       4,   T_EDGEPT,    0,         CP_W     },
  { "wid",        3,   T_WIDTH,     0,         0        },
  { "width",      5,   T_WIDTH,     0,         0        },
  { "with",       4,   T_WITH,      0,         0        },
  { "x",          1,   T_X,         0,         0        },
  { "y",          1,   T_Y,         0,         0        },
};

/*
** Search a PikWordlist for the given keyword.  Return a pointer to the
** element found.  Or return 0 if not found.
*/
static const PikWord *pik_find_word(
  const char *zIn,              /* Word to search for */
  int n,                        /* Length of zIn */
  const PikWord *aList,         /* List to search */
  int nList                     /* Number of entries in aList */
){
  int first = 0;
  int last = nList-1;
  while( first<=last ){
    int mid = (first + last)/2;
    int sz = aList[mid].nChar;
    int c = strncmp(zIn, aList[mid].zWord, sz<n ? sz : n);
    if( c==0 ){
      c = n - sz;
      if( c==0 ) return &aList[mid];
    }
    if( c<0 ){
      last = mid-1;
    }else{
      first = mid+1;
    }
  }
  return 0;
}

/*
** Set a symbolic debugger breakpoint on this routine to receive a
** breakpoint when the "#breakpoint" token is parsed.
*/
static void pik_breakpoint(const unsigned char *z){
  /* Prevent C compilers from optimizing out this routine. */
  if( z[2]=='X' ) exit(1);
}


/*
** Return the length of next token.  The token starts on
** the pToken->z character.  Fill in other fields of the
** pToken object as appropriate.
*/
static int pik_token_length(PToken *pToken){
  const unsigned char *z = (const unsigned char*)pToken->z;
  int i;
  unsigned char c, c2;
  switch( z[0] ){
    case '\\': {
      pToken->eType = T_WHITESPACE;
      for(i=1; z[i]=='\r' || z[i]==' ' || z[i]=='\t'; i++){}
      if( z[i]=='\n'  ) return i+1;
      pToken->eType = T_ERROR;
      return 1;
    }
    case ';':
    case '\n': {
      pToken->eType = T_EOL;
      return 1;
    }
    case '"': {
      for(i=1; (c = z[i])!=0; i++){
        if( c=='\\' ){ 
          if( z[i+1]==0 ) break;
          i++;
          continue;
        }
        if( c=='"' ){
          pToken->eType = T_STRING;
          return i+1;
        }
      }
      pToken->eType = T_ERROR;
      return i;
    }
    case ' ':
    case '\t':
    case '\f':
    case '\r': {
      for(i=1; (c = z[i])==' ' || c=='\t' || c=='\r' || c=='\t'; i++){}
      pToken->eType = T_WHITESPACE;
      return i;
    }
    case '#': {
      for(i=1; (c = z[i])!=0 && c!='\n'; i++){}
      pToken->eType = T_WHITESPACE;
      /* If the comment is "#breakpoint" then invoke the pik_breakpoint()
      ** routine.  The pik_breakpoint() routie is a no-op that serves as
      ** a convenient place to set a gdb breakpoint when debugging. */
      if( strncmp((const char*)z,"#breakpoint",11)==0 ) pik_breakpoint(z);
      return i;
    }
    case '/': {
      if( z[1]=='*' ){
        for(i=2; z[i]!=0 && (z[i]!='*' || z[i+1]!='/'); i++){}
        if( z[i]=='*' ){
          pToken->eType = T_WHITESPACE;
          return i+2;
        }else{
          pToken->eType = T_ERROR;
          return i;
        }
      }else if( z[1]=='/' ){
        for(i=2; z[i]!=0 && z[i]!='\n'; i++){}
        pToken->eType = T_WHITESPACE;
        return i;
      }else if( z[1]=='=' ){
        pToken->eType = T_ASSIGN;
        pToken->eCode = T_SLASH;
        return 2;
      }else{
        pToken->eType = T_SLASH;
        return 1;
      }
    }
    case '+': {
      if( z[1]=='=' ){
        pToken->eType = T_ASSIGN;
        pToken->eCode = T_PLUS;
        return 2;
      }
      pToken->eType = T_PLUS;
      return 1;
    }
    case '*': {
      if( z[1]=='=' ){
        pToken->eType = T_ASSIGN;
        pToken->eCode = T_STAR;
        return 2;
      }
      pToken->eType = T_STAR;
      return 1;
    }
    case '%': {   pToken->eType = T_PERCENT; return 1; }
    case '(': {   pToken->eType = T_LP;      return 1; }
    case ')': {   pToken->eType = T_RP;      return 1; }
    case '[': {   pToken->eType = T_LB;      return 1; }
    case ']': {   pToken->eType = T_RB;      return 1; }
    case ',': {   pToken->eType = T_COMMA;   return 1; }
    case ':': {   pToken->eType = T_COLON;   return 1; }
    case '>': {   pToken->eType = T_GT;      return 1; }
    case '=': {
       if( z[1]=='=' ){
         pToken->eType = T_EQ;
         return 2;
       }
       pToken->eType = T_ASSIGN;
       pToken->eCode = T_ASSIGN;
       return 1;
    }
    case '-': {
      if( z[1]=='>' ){
        pToken->eType = T_RARROW;
        return 2;
      }else if( z[1]=='=' ){
        pToken->eType = T_ASSIGN;
        pToken->eCode = T_MINUS;
        return 2;
      }else{
        pToken->eType = T_MINUS;
        return 1;
      }
    }
    case '<': { 
      if( z[1]=='-' ){
         if( z[2]=='>' ){
           pToken->eType = T_LRARROW;
           return 3;
         }else{
           pToken->eType = T_LARROW;
           return 2;
         }
      }else{
        pToken->eType = T_LT;
        return 1;
      }
    }
    default: {
      c = z[0];
      if( c=='.' ){
        unsigned char c1 = z[1];
        if( islower(c1) ){
          const PikWord *pFound;
          for(i=2; (c = z[i])>='a' && c<='z'; i++){}
          pFound = pik_find_word((const char*)z+1, i-1,
                                    pik_keywords, count(pik_keywords));
          if( pFound && (pFound->eEdge>0 ||
                         pFound->eType==T_EDGEPT ||
                         pFound->eType==T_START ||
                         pFound->eType==T_END )
          ){
            /* Dot followed by something that is a 2-D place value */
            pToken->eType = T_DOT_E;
          }else if( pFound && (pFound->eType==T_X || pFound->eType==T_Y) ){
            /* Dot followed by "x" or "y" */
            pToken->eType = T_DOT_XY;
          }else{
            /* Any other "dot" */
            pToken->eType = T_DOT_L;
          }
          return 1;
        }else if( isdigit(c1) ){
          i = 0;
          /* no-op.  Fall through to number handling */
        }else if( isupper(c1) ){
          for(i=2; (c = z[i])!=0 && (isalnum(c) || c=='_'); i++){}
          pToken->eType = T_DOT_U;
          return 1;
        }else{
          pToken->eType = T_ERROR;
          return 1;
        }
      }
      if( (c>='0' && c<='9') || c=='.' ){
        int nDigit;
        int isInt = 1;
        if( c!='.' ){
          nDigit = 1;
          for(i=1; (c = z[i])>='0' && c<='9'; i++){ nDigit++; }
          if( i==1 && (c=='x' || c=='X') ){
            for(i=2; (c = z[i])!=0 && isxdigit(c); i++){}
            pToken->eType = T_NUMBER;
            return i;
          }
        }else{
          isInt = 0;
          nDigit = 0;
        }
        if( c=='.' ){
          isInt = 0;
          for(i++; (c = z[i])>='0' && c<='9'; i++){ nDigit++; }
        }
        if( nDigit==0 ){
          pToken->eType = T_ERROR;
          return i;
        }
        if( c=='e' || c=='E' ){
          i++;
          c2 = z[i];
          if( c2=='+' || c2=='-' ){
            i++;
            c2 = z[i];
          }
          if( c2<'0' || c>'9' ){
            /* This is not an exp */
            i -= 2;
          }else{
            i++;
            isInt = 0;
            while( (c = z[i])>='0' && c<='9' ){ i++; }
          }
        }
        c2 = c ? z[i+1] : 0;
        if( isInt ){
          if( (c=='t' && c2=='h')
           || (c=='r' && c2=='d')
           || (c=='n' && c2=='d')
           || (c=='s' && c2=='t')
          ){
            pToken->eType = T_NTH;
            return i+2;
          }
        }
        if( (c=='i' && c2=='n')
         || (c=='c' && c2=='m')
         || (c=='m' && c2=='m')
         || (c=='p' && c2=='t')
         || (c=='p' && c2=='x')
         || (c=='p' && c2=='c')
        ){
          i += 2;
        }
        pToken->eType = T_NUMBER;
        return i;
      }else if( islower(c) || c=='_' || c=='$' || c=='@' ){
        const PikWord *pFound;
        for(i=1; (c =  z[i])!=0 && (isalnum(c) || c=='_'); i++){}
        pFound = pik_find_word((const char*)z, i,
                               pik_keywords, count(pik_keywords));
        if( pFound ){
          pToken->eType = pFound->eType;
          pToken->eCode = pFound->eCode;
          pToken->eEdge = pFound->eEdge;
          return i;
        }
        pToken->n = i;
        if( pik_find_class(pToken)!=0 ){
          pToken->eType = T_CLASSNAME;
        }else{
          pToken->eType = T_ID;
        }
        return i;
      }else if( c>='A' && c<='Z' ){
        for(i=1; (c =  z[i])!=0 && (isalnum(c) || c=='_'); i++){}
        pToken->eType = T_PLACENAME;
        return i;
      }else{
        pToken->eType = T_ERROR;
        return 1;
      }
    }
  }
}

/*
** Return a pointer to the next non-whitespace token after pThis.
** This is used to help form error messages.
*/
static PToken pik_next_semantic_token(PToken *pThis){
  PToken x;
  int sz;
  int i = pThis->n;
  memset(&x, 0, sizeof(x));
  x.z = pThis->z;
  while(1){
    x.z = pThis->z + i;
    sz = pik_token_length(&x);
    if( x.eType!=T_WHITESPACE ){
      x.n = sz;
      return x;
    }
    i += sz;
  }
}

/*
** Parse the PIKCHR script contained in zText[].  Return a rendering.  Or
** if an error is encountered, return the error text.  The error message
** is HTML formatted.  So regardless of what happens, the return text
** is safe to be insertd into an HTML output stream.
**
** If pnWidth and pnHeight are not NULL, then this routine writes the
** width and height of the <SVG> object into the integers that they
** point to.  A value of -1 is written if an error is seen.
**
** If zClass is not NULL, then it is a class name to be included in
** the <SVG> markup.
**
** The returned string is contained in memory obtained from malloc()
** and should be released by the caller.
*/
char *pikchr(
  const char *zText,     /* Input PIKCHR source text.  zero-terminated */
  const char *zClass,    /* Add class="%s" to <svg> markup */
  unsigned int mFlags,   /* Flags used to influence rendering behavior */
  int *pnWidth,          /* Write width of <svg> here, if not NULL */
  int *pnHeight          /* Write height here, if not NULL */
){
  int i;
  int sz;
  PToken token;
  Pik s;
  yyParser sParse;

  memset(&s, 0, sizeof(s));
  s.zIn = zText;
  s.nIn = (unsigned int)strlen(zText);
  s.eDir = DIR_RIGHT;
  s.zClass = zClass;
  s.mFlags = mFlags;
  pik_parserInit(&sParse, &s);
#if 0
  pik_parserTrace(stdout, "parser: ");
#endif
  for(i=0; zText[i] && s.nErr==0; i+=sz){
    token.eCode = 0;
    token.eEdge = 0;
    token.z = zText + i;
    sz = pik_token_length(&token);
    if( token.eType==T_WHITESPACE ){
      /* no-op */
    }else if( sz>1000 ){
      token.n = 1;
      pik_error(&s, &token, "token is too long - max length 1000 bytes");
      break;
    }else if( token.eType==T_ERROR ){
      token.n = (unsigned short)(sz & 0xffff);
      pik_error(&s, &token, "unrecognized token");
      break;
    }else{
#if 0
      printf("******** Token %s (%d): \"%.*s\" **************\n",
             yyTokenName[token.eType], token.eType,
             isspace(token.z[0]) ? 0 : token.n, token.z);
#endif
      token.n = (unsigned short)(sz & 0xffff);
      pik_parser(&sParse, token.eType, token);
    }
  }
  if( s.nErr==0 ){
    memset(&token,0,sizeof(token));
    token.z = zText;
    pik_parser(&sParse, 0, token);
  }
  pik_parserFinalize(&sParse);
  while( s.pVar ){
    PVar *pNext = s.pVar->pNext;
    free(s.pVar);
    s.pVar = pNext;
  }
  if( pnWidth ) *pnWidth = s.nErr ? -1 : s.wSVG;
  if( pnHeight ) *pnHeight = s.nErr ? -1 : s.hSVG;
  if( s.zOut ){
    s.zOut[s.nOut] = 0;
    s.zOut = realloc(s.zOut, s.nOut+1);
  }
  return s.zOut;
}

#if defined(PIKCHR_FUZZ)
#include <stdint.h>
int LLVMFuzzerTestOneInput(const uint8_t *aData, size_t nByte){
  int w,h;
  char *zIn, *zOut;
  zIn = malloc( nByte + 1 );
  if( zIn==0 ) return 0;
  memcpy(zIn, aData, nByte);
  zIn[nByte] = 0;
  zOut = pikchr(zIn, "pikchr", 0, &w, &h);
  free(zIn);
  free(zOut);
  return 0;
}
#endif /* PIKCHR_FUZZ */

#if defined(PIKCHR_SHELL)
/* Texting interface
**
** Generate HTML on standard output that displays both the original
** input text and the rendered SVG for all files named on the command
** line.
*/
int main(int argc, char **argv){
  int i;
  int bNoEcho = 0;         /* Do not show the text of the script */
  int mPikchrFlags = 0;    /* Flags passed into pikchr() */
  printf(
    "<!DOCTYPE html>\n"
    "<html lang=\"en-US\">\n"
    "<head>\n<title>PIKCHR Test</title>\n"
    "<meta charset=\"utf-8\">\n"
    "</head>\n"
    "<body>\n"
  );
  for(i=1; i<argc; i++){
    FILE *in;
    size_t sz;
    char *zIn;
    char *zOut;
    char *z, c;
    int j;
    int w, h;

    if( argv[i][0]=='-' ){
      char *z = argv[i];
      z++;
      if( z[0]=='-' ) z++;
      if( strcmp(z,"no-echo")==0 ){
        bNoEcho = 1;
      }else
      if( strcmp(z,"include-source")==0 ){
        mPikchrFlags |= PIKCHR_INCLUDE_SOURCE;
      }else
      {
        fprintf(stderr,"unknown option: \"%s\"\n", argv[i]);
        exit(1);
      }
      continue;
    }
    printf("<h1>File %s</h1>\n", argv[i]);
    in = fopen(argv[i], "rb");
    if( in==0 ){
      fprintf(stderr, "cannot open \"%s\" for reading\n", argv[i]);
      continue;
    }
    fseek(in, 0, SEEK_END);
    sz = ftell(in);
    rewind(in);
    zIn = malloc( sz+1 );
    if( zIn==0 ){
      fprintf(stderr, "cannot allocate space for file \"%s\"\n", argv[i]);
      fclose(in);
      continue;
    }
    sz = fread(zIn, 1, sz, in);
    fclose(in);
    zIn[sz] = 0;
    if( !bNoEcho ){
      printf("<p>Source text:</p>\n<blockquote><pre>\n");
      z = zIn;
      while( z[0]!=0 ){
        for(j=0; (c = z[j])!=0 && c!='<' && c!='>' && c!='&'; j++){}
        if( j ) printf("%.*s", j, z);
        z += j+1;
        j = -1;
        if( c=='<' ){
          printf("&lt;");
        }else if( c=='>' ){
          printf("&gt;");
        }else if( c=='&' ){
          printf("&amp;");
        }else if( c==0 ){
          break;
        }
      }
      printf("</pre></blockquote>\n");
    }
    zOut = pikchr(zIn, "pikchr", mPikchrFlags, &w, &h);
    free(zIn);
    if( zOut ){
      if( w<0 ){
        printf("<p>ERROR:</p>\n");
      }else if( bNoEcho==0 ){
        printf("<p>Output size: %d by %d</p>\n", w, h);
      }
      printf("<div style='border:3px solid lightgray;max-width:%dpx'>\n"
             "%s</div>\n",
             w, zOut);
      free(zOut);
    }
  }
  printf("</body></html>\n");
  return 0; 
}
#endif /* PIKCHR_SHELL */

#line 7094 "pikchr.c"