summaryrefslogtreecommitdiff
path: root/extsrc/pikchr.c
diff options
context:
space:
mode:
authorcashmere <cashmere@autistici.org>2026-09-04 09:05:24 +0200
committercashmere <cashmere@autistici.org>2026-09-04 09:05:24 +0200
commit4f49b16079b27d7ce633619e4cddfdf2bffbc952 (patch)
treec296c1e7f3c3e2f6859c6d3a1b1b208779b89679 /extsrc/pikchr.c
downloadrelic-master.tar.gz
initial commitHEADmaster
Diffstat (limited to 'extsrc/pikchr.c')
-rw-r--r--extsrc/pikchr.c8350
1 files changed, 8350 insertions, 0 deletions
diff --git a/extsrc/pikchr.c b/extsrc/pikchr.c
new file mode 100644
index 0000000..22933be
--- /dev/null
+++ b/extsrc/pikchr.c
@@ -0,0 +1,8350 @@
+/* This file is automatically generated by Lemon from input grammar
+** source file "pikchr.y".
+*/
+/*
+** 2000-05-29
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** Driver template for the LEMON parser generator.
+**
+** The "lemon" program processes an LALR(1) input grammar file, then uses
+** this template to construct a parser. The "lemon" program inserts text
+** at each "%%" line. Also, any "P-a-r-s-e" identifier prefix (without the
+** interstitial "-" characters) contained in this template is changed into
+** the value of the %name directive from the grammar. Otherwise, the content
+** of this template is copied straight through into the generate parser
+** source file.
+**
+** The following is the concatenation of all %include directives from the
+** input grammar file:
+*/
+/************ Begin %include sections from the grammar ************************/
+#line 1 "VERSION.h"
+#define MANIFEST_UUID "a7f1c35bc0448daf15e2bafa36e510c1517534e620c452ecb314ed57d974f081"
+#define MANIFEST_VERSION "[a7f1c35bc0]"
+#define MANIFEST_DATE "2026-04-03 10:29:56"
+#define MANIFEST_YEAR "2026"
+#define MANIFEST_ISODATE "20260403102956"
+#define MANIFEST_NUMERIC_DATE 20260403
+#define MANIFEST_NUMERIC_TIME 102956
+#define RELEASE_VERSION "1.0"
+#define RELEASE_VERSION_NUMBER 10000
+#define RELEASE_RESOURCE_VERSION 1,0,0,0
+#define COMPILER "gcc-13.3.0"
+#line 2 "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 "statements". Each statement is
+** parsed into a PObj object. These are stored on an extensible array
+** called PList. All parameters to each PObj are computed as the
+** object is parsed. (Hence, the parameters to a PObj may only refer
+** to prior statements.) Once the PObj is completely assembled, it is
+** added to the end of a PList and never changes thereafter - except,
+** PObj objects that are part of a "[...]" block might have their
+** absolute position shifted when the outer [...] block is positioned.
+** But apart from this repositioning, PObj objects are unchanged once
+** they are added to the list. The order of statements on a PList does
+** not change.
+**
+** After all input has been parsed, the top-level PList 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 statements must be
+** known in order to compute a bounding box on the output.
+**
+** Each PObj is on a "layer". (The common case is that all PObj'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 PList
+** and PObj 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 statements begin with a class name like "box" or "arrow" or "move".
+** There is a class named "text" which is used for statements 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 substatements, all gathered onto a separate PList object.
+**
+** Variables go into PVar objects that form a linked list.
+**
+** Each PObj 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
+
+/*
+** Typesafe version of ctype.h macros. Cygwin requires this, I'm told.
+*/
+#define IsUpper(X) isupper((unsigned char)(X))
+#define IsLower(X) islower((unsigned char)(X))
+#define ToLower(X) tolower((unsigned char)(X))
+#define IsDigit(X) isdigit((unsigned char)(X))
+#define IsXDigit(X) isxdigit((unsigned char)(X))
+#define IsSpace(X) isspace((unsigned char)(X))
+#define IsAlnum(X) isalnum((unsigned char)(X))
+
+
+/* Limit the number of tokens in a single script to avoid run-away
+** macro expansion attacks. See forum post
+** https://pikchr.org/home/forumpost/ef8684c6955a411a
+*/
+#ifndef PIKCHR_TOKEN_LIMIT
+# define PIKCHR_TOKEN_LIMIT 100000
+#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 PObj PObj; /* A single diagram object */
+typedef struct PList PList; /* A list of diagram objects */
+typedef struct PClass PClass; /* Description of statements 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 */
+typedef struct PMacro PMacro; /* A "define" macro */
+
+/* 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 PObj.ptAt */
+#define TP_ABOVE 0x0008 /* Position text above PObj.ptAt */
+#define TP_CENTER 0x0010 /* On the line */
+#define TP_BELOW 0x0020 /* Position text below PObj.ptAt */
+#define TP_BELOW2 0x0040 /* Position text way below PObj.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_MONO 0x4000 /* Monospace font family */
+#define TP_FMASK 0x7000 /* Mask for font style */
+#define TP_ALIGN 0x8000 /* 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 */
+};
+static const PPoint cZeroPoint = {0.0,0.0};
+
+/* 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_PARAMETER 253 /* $1, $2, ..., $9 */
+#define T_WHITESPACE 254 /* Whitespace or 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 PObj. These bits are
+** collected in PObj.mProp and PObj.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 */
+#define A_FIT 0x1000
+
+
+/* A single graphics object */
+struct PObj {
+ const PClass *type; /* Object type or class */
+ PToken errTok; /* Reference token for error messages */
+ PPoint ptAt; /* Reference point for the object */
+ PPoint ptEnter, ptExit; /* Entry and exit points */
+ PList *pSublist; /* Substructure for [...] objects */
+ char *zName; /* Name assigned to this statement */
+ 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 */
+ char bAltAutoFit; /* Always send both h and w into xFit() */
+ 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 */
+ PObj *pFrom, *pTo; /* End-point objects of a path */
+ PBox bbox; /* Bounding box */
+};
+
+/* A list of graphics objects */
+struct PList {
+ int n; /* Number of statements in the list */
+ int nAlloc; /* Allocated slots in a[] */
+ PObj **a; /* Pointers to individual objects */
+};
+
+/* A macro definition */
+struct PMacro {
+ PMacro *pNext; /* Next in the list */
+ PToken macroName; /* Name of the macro */
+ PToken macroBody; /* Body of the macro */
+ int inUse; /* Do not allow recursion */
+};
+
+/* 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 */
+ unsigned nToken; /* Number of tokens parsed */
+ PToken sIn; /* Input Pikchr-language text */
+ 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() */
+ PObj *cur; /* Object under construction */
+ PObj *lastRef; /* Last object references by name */
+ PList *list; /* Object list under construction */
+ PMacro *pMacros; /* List of all defined macros */
+ PVar *pVar; /* Application-defined variables */
+ PBox bbox; /* Bounding box around all statements */
+ /* 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> */
+ int fgcolor; /* foreground color value, or -1 for none */
+ int bgcolor; /* background color value, or -1 for none */
+ /* Paths for lines are constructed here first, then transferred into
+ ** the PObj 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 */
+ /* Error contexts */
+ unsigned int nCtx; /* Number of error contexts */
+ PToken aCtx[10]; /* Nested error contexts */
+};
+
+/* Include PIKCHR_PLAINTEXT_ERRORS among the bits of mFlags on the 3rd
+** argument to pikchr() in order to cause error message text to come out
+** as text/plain instead of as text/html
+*/
+#define PIKCHR_PLAINTEXT_ERRORS 0x0001
+
+/* Include PIKCHR_DARK_MODE among the mFlag bits to invert colors.
+*/
+#define PIKCHR_DARK_MODE 0x0002
+
+/*
+** 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*,PObj*); /* Initializer */
+ void (*xNumProp)(Pik*,PObj*,PToken*); /* Value change notification */
+ void (*xCheck)(Pik*,PObj*); /* Checks to do after parsing */
+ PPoint (*xChop)(Pik*,PObj*,PPoint*); /* Chopper */
+ PPoint (*xOffset)(Pik*,PObj*,int); /* Offset from .c to edge point */
+ void (*xFit)(Pik*,PObj*,PNum w,PNum h); /* Size to fit text */
+ void (*xRender)(Pik*,PObj*); /* 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*,int);
+static void pik_append_style(Pik*,PObj*,int);
+static void pik_append_txt(Pik*,PObj*, PBox*);
+static void pik_draw_arrowhead(Pik*,PPoint*pFrom,PPoint*pTo,PObj*);
+static void pik_chop(PPoint*pFrom,PPoint*pTo,PNum);
+static void pik_error(Pik*,PToken*,const char*);
+static void pik_elist_free(Pik*,PList*);
+static void pik_elem_free(Pik*,PObj*);
+static void pik_render(Pik*,PList*);
+static PList *pik_elist_append(Pik*,PList*,PObj*);
+static PObj *pik_elem_new(Pik*,PToken*,PToken*,PList*);
+static void pik_set_direction(Pik*,int);
+static void pik_elem_setname(Pik*,PObj*,PToken*);
+static int pik_round(PNum);
+static void pik_set_var(Pik*,PToken*,PNum,PToken*);
+static PNum pik_value(Pik*,const char*,int,int*);
+static int pik_value_int(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*,PObj*);
+static void pik_elem_move(PObj*,PNum dx, PNum dy);
+static void pik_elist_move(PList*,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*,PObj*);
+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*,PObj*,PToken*,PPoint*);
+static void pik_add_to(Pik*,PObj*,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 PObj *pik_find_nth(Pik*,PObj*,PToken*);
+static PObj *pik_find_byname(Pik*,PObj*,PToken*);
+static PPoint pik_place_of_elem(Pik*,PObj*,PToken*);
+static int pik_bbox_isempty(PBox*);
+static int pik_bbox_contains_point(PBox*,PPoint*);
+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, const int isMonospace);
+static void pik_size_to_fit(Pik*,PObj*,PToken*,int);
+static int pik_text_position(int,PToken*);
+static PNum pik_property_of(PObj*,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, PObj*, PToken*);
+static PPoint pik_nth_vertex(Pik *p, PToken *pNth, PToken *pErr, PObj *pObj);
+static PToken pik_next_semantic_token(PToken *pThis);
+static void pik_compute_layout_settings(Pik*);
+static void pik_behind(Pik*,PObj*);
+static PObj *pik_assert(Pik*,PNum,PToken*,PNum);
+static PObj *pik_position_assert(Pik*,PPoint*,PToken*,PPoint*);
+static PNum pik_dist(PPoint*,PPoint*);
+static void pik_add_macro(Pik*,PToken *pId,PToken *pCode);
+
+
+#line 549 "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_DEFINE 18
+#define T_CODEBLOCK 19
+#define T_FILL 20
+#define T_COLOR 21
+#define T_THICKNESS 22
+#define T_PRINT 23
+#define T_STRING 24
+#define T_COMMA 25
+#define T_ISODATE 26
+#define T_CLASSNAME 27
+#define T_LB 28
+#define T_RB 29
+#define T_UP 30
+#define T_DOWN 31
+#define T_LEFT 32
+#define T_RIGHT 33
+#define T_CLOSE 34
+#define T_CHOP 35
+#define T_FROM 36
+#define T_TO 37
+#define T_THEN 38
+#define T_HEADING 39
+#define T_GO 40
+#define T_AT 41
+#define T_WITH 42
+#define T_SAME 43
+#define T_AS 44
+#define T_FIT 45
+#define T_BEHIND 46
+#define T_UNTIL 47
+#define T_EVEN 48
+#define T_DOT_E 49
+#define T_HEIGHT 50
+#define T_WIDTH 51
+#define T_RADIUS 52
+#define T_DIAMETER 53
+#define T_DOTTED 54
+#define T_DASHED 55
+#define T_CW 56
+#define T_CCW 57
+#define T_LARROW 58
+#define T_RARROW 59
+#define T_LRARROW 60
+#define T_INVIS 61
+#define T_THICK 62
+#define T_THIN 63
+#define T_SOLID 64
+#define T_CENTER 65
+#define T_LJUST 66
+#define T_RJUST 67
+#define T_ABOVE 68
+#define T_BELOW 69
+#define T_ITALIC 70
+#define T_BOLD 71
+#define T_MONO 72
+#define T_ALIGNED 73
+#define T_BIG 74
+#define T_SMALL 75
+#define T_AND 76
+#define T_LT 77
+#define T_GT 78
+#define T_ON 79
+#define T_WAY 80
+#define T_BETWEEN 81
+#define T_THE 82
+#define T_NTH 83
+#define T_VERTEX 84
+#define T_TOP 85
+#define T_BOTTOM 86
+#define T_START 87
+#define T_END 88
+#define T_IN 89
+#define T_THIS 90
+#define T_DOT_U 91
+#define T_LAST 92
+#define T_NUMBER 93
+#define T_FUNC1 94
+#define T_FUNC2 95
+#define T_DIST 96
+#define T_DOT_XY 97
+#define T_X 98
+#define T_Y 99
+#define T_DOT_L 100
+#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
+** YYREALLOC Name of the realloc() function to use
+** YYFREE Name of the free() function to use
+** YYDYNSTACK True if stack space should be extended on heap
+** 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
+** YY_MIN_DSTRCTR Minimum symbol value that has a destructor
+** YY_MAX_DSTRCTR Maximum symbol value that has a destructor
+*/
+#ifndef INTERFACE
+# define INTERFACE 1
+#endif
+/************* Begin control #defines *****************************************/
+#define YYCODETYPE unsigned char
+#define YYNOCODE 138
+#define YYACTIONTYPE unsigned short int
+#define pik_parserTOKENTYPE PToken
+typedef union {
+ int yyinit;
+ pik_parserTOKENTYPE yy0;
+ PList* yy23;
+ PRel yy28;
+ PObj* yy54;
+ PNum yy129;
+ PPoint yy187;
+ short int yy272;
+} 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 YYREALLOC realloc
+#define YYFREE free
+#define YYDYNSTACK 0
+#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 164
+#define YYNRULE 156
+#define YYNRULE_WITH_ACTION 116
+#define YYNTOKEN 101
+#define YY_MAX_SHIFT 163
+#define YY_MIN_SHIFTREDUCE 287
+#define YY_MAX_SHIFTREDUCE 442
+#define YY_ERROR_ACTION 443
+#define YY_ACCEPT_ACTION 444
+#define YY_NO_ACTION 445
+#define YY_MIN_REDUCE 446
+#define YY_MAX_REDUCE 601
+#define YY_MIN_DSTRCTR 101
+#define YY_MAX_DSTRCTR 104
+/************* 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
+
+/* Macro to determine if stack space has the ability to grow using
+** heap memory.
+*/
+#if YYSTACKDEPTH<=0 || YYDYNSTACK
+# define YYGROWABLESTACK 1
+#else
+# define YYGROWABLESTACK 0
+#endif
+
+/* Guarantee a minimum number of initial stack slots.
+*/
+#if YYSTACKDEPTH<=0
+# undef YYSTACKDEPTH
+# define YYSTACKDEPTH 2 /* Need a minimum stack size */
+#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 (1305)
+static const YYACTIONTYPE yy_action[] = {
+ /* 0 */ 575, 495, 161, 119, 25, 452, 29, 74, 129, 148,
+ /* 10 */ 575, 64, 63, 62, 61, 453, 113, 120, 161, 119,
+ /* 20 */ 427, 428, 339, 357, 81, 121, 447, 454, 29, 575,
+ /* 30 */ 530, 13, 50, 450, 322, 323, 9, 8, 33, 149,
+ /* 40 */ 32, 7, 71, 127, 163, 335, 66, 28, 444, 27,
+ /* 50 */ 339, 339, 339, 339, 425, 426, 340, 341, 342, 343,
+ /* 60 */ 344, 345, 346, 347, 348, 474, 64, 63, 62, 61,
+ /* 70 */ 54, 51, 73, 306, 148, 474, 492, 161, 119, 297,
+ /* 80 */ 112, 113, 120, 161, 119, 427, 428, 339, 30, 81,
+ /* 90 */ 109, 447, 454, 29, 474, 528, 161, 119, 450, 322,
+ /* 100 */ 323, 9, 8, 33, 149, 32, 7, 71, 127, 163,
+ /* 110 */ 335, 66, 535, 36, 27, 339, 339, 339, 339, 425,
+ /* 120 */ 426, 340, 341, 342, 343, 344, 345, 346, 347, 348,
+ /* 130 */ 394, 435, 310, 59, 60, 64, 63, 62, 61, 313,
+ /* 140 */ 74, 376, 148, 69, 2, 533, 161, 119, 124, 113,
+ /* 150 */ 120, 161, 119, 80, 535, 31, 308, 79, 83, 107,
+ /* 160 */ 535, 441, 440, 535, 394, 435, 299, 59, 60, 120,
+ /* 170 */ 161, 119, 149, 463, 376, 376, 330, 84, 2, 122,
+ /* 180 */ 78, 78, 38, 156, 156, 156, 48, 37, 559, 328,
+ /* 190 */ 128, 152, 560, 561, 434, 441, 440, 350, 350, 350,
+ /* 200 */ 350, 350, 350, 350, 350, 350, 350, 350, 577, 77,
+ /* 210 */ 577, 35, 106, 46, 436, 437, 438, 439, 579, 375,
+ /* 220 */ 298, 117, 393, 155, 154, 153, 47, 4, 434, 69,
+ /* 230 */ 394, 435, 3, 59, 60, 411, 412, 413, 414, 398,
+ /* 240 */ 399, 376, 62, 61, 2, 108, 106, 5, 436, 437,
+ /* 250 */ 438, 439, 375, 375, 117, 117, 393, 155, 154, 153,
+ /* 260 */ 76, 441, 440, 67, 6, 142, 140, 64, 63, 62,
+ /* 270 */ 61, 380, 157, 424, 427, 428, 339, 379, 159, 45,
+ /* 280 */ 423, 72, 131, 148, 531, 161, 119, 1, 55, 125,
+ /* 290 */ 113, 120, 161, 119, 434, 147, 146, 64, 63, 62,
+ /* 300 */ 61, 397, 43, 11, 339, 339, 339, 339, 425, 426,
+ /* 310 */ 355, 65, 106, 149, 436, 437, 438, 439, 74, 375,
+ /* 320 */ 148, 117, 393, 155, 154, 153, 497, 113, 120, 161,
+ /* 330 */ 119, 22, 21, 12, 142, 140, 64, 63, 62, 61,
+ /* 340 */ 24, 356, 145, 141, 431, 64, 63, 62, 61, 391,
+ /* 350 */ 149, 448, 454, 29, 378, 158, 85, 55, 450, 394,
+ /* 360 */ 432, 138, 59, 60, 147, 146, 120, 161, 119, 163,
+ /* 370 */ 102, 43, 139, 42, 27, 430, 14, 15, 301, 302,
+ /* 380 */ 303, 446, 305, 16, 44, 74, 18, 148, 152, 19,
+ /* 390 */ 20, 36, 68, 496, 113, 120, 161, 119, 114, 359,
+ /* 400 */ 22, 21, 23, 142, 140, 64, 63, 62, 61, 24,
+ /* 410 */ 107, 145, 141, 431, 26, 57, 377, 149, 58, 118,
+ /* 420 */ 120, 161, 119, 392, 463, 384, 55, 64, 63, 62,
+ /* 430 */ 61, 382, 569, 147, 146, 160, 383, 435, 39, 70,
+ /* 440 */ 43, 106, 152, 445, 445, 88, 445, 445, 375, 445,
+ /* 450 */ 117, 393, 155, 154, 153, 120, 161, 119, 445, 17,
+ /* 460 */ 445, 10, 479, 479, 445, 445, 435, 441, 440, 22,
+ /* 470 */ 21, 445, 403, 64, 63, 62, 61, 152, 24, 445,
+ /* 480 */ 145, 141, 431, 133, 75, 126, 354, 445, 445, 123,
+ /* 490 */ 445, 404, 405, 406, 408, 80, 441, 440, 308, 79,
+ /* 500 */ 434, 411, 412, 413, 414, 394, 445, 445, 59, 60,
+ /* 510 */ 64, 63, 62, 61, 445, 445, 376, 445, 445, 42,
+ /* 520 */ 436, 437, 438, 439, 156, 156, 156, 394, 445, 434,
+ /* 530 */ 59, 60, 64, 63, 62, 61, 445, 445, 376, 445,
+ /* 540 */ 445, 42, 445, 394, 473, 391, 59, 60, 445, 436,
+ /* 550 */ 437, 438, 439, 49, 376, 445, 74, 42, 148, 445,
+ /* 560 */ 88, 445, 445, 445, 490, 113, 120, 161, 119, 445,
+ /* 570 */ 120, 161, 119, 132, 130, 394, 143, 475, 59, 60,
+ /* 580 */ 445, 473, 64, 63, 62, 61, 376, 106, 149, 42,
+ /* 590 */ 445, 445, 152, 445, 375, 391, 117, 393, 155, 154,
+ /* 600 */ 153, 394, 144, 52, 59, 60, 445, 445, 445, 106,
+ /* 610 */ 445, 445, 376, 445, 445, 42, 375, 445, 117, 393,
+ /* 620 */ 155, 154, 153, 445, 445, 106, 64, 63, 62, 61,
+ /* 630 */ 445, 445, 375, 445, 117, 393, 155, 154, 153, 394,
+ /* 640 */ 445, 445, 59, 60, 88, 445, 445, 53, 445, 445,
+ /* 650 */ 376, 445, 445, 42, 120, 161, 119, 106, 445, 445,
+ /* 660 */ 445, 110, 110, 445, 375, 445, 117, 393, 155, 154,
+ /* 670 */ 153, 394, 445, 445, 59, 60, 152, 107, 445, 445,
+ /* 680 */ 445, 445, 102, 106, 445, 42, 445, 120, 161, 119,
+ /* 690 */ 375, 451, 117, 393, 155, 154, 153, 394, 445, 445,
+ /* 700 */ 59, 60, 64, 63, 62, 61, 445, 445, 376, 152,
+ /* 710 */ 445, 40, 445, 394, 445, 396, 59, 60, 445, 445,
+ /* 720 */ 445, 106, 445, 445, 376, 88, 445, 41, 375, 445,
+ /* 730 */ 117, 393, 155, 154, 153, 120, 161, 119, 74, 445,
+ /* 740 */ 148, 445, 111, 111, 107, 445, 484, 113, 120, 161,
+ /* 750 */ 119, 445, 445, 106, 120, 161, 119, 152, 478, 445,
+ /* 760 */ 375, 86, 117, 393, 155, 154, 153, 445, 445, 445,
+ /* 770 */ 149, 120, 161, 119, 445, 445, 152, 445, 445, 106,
+ /* 780 */ 445, 64, 63, 62, 61, 445, 375, 445, 117, 393,
+ /* 790 */ 155, 154, 153, 152, 395, 106, 64, 63, 62, 61,
+ /* 800 */ 98, 445, 375, 445, 117, 393, 155, 154, 153, 445,
+ /* 810 */ 120, 161, 119, 445, 74, 445, 148, 56, 445, 74,
+ /* 820 */ 445, 148, 483, 113, 120, 161, 119, 480, 113, 120,
+ /* 830 */ 161, 119, 152, 74, 445, 148, 445, 89, 445, 445,
+ /* 840 */ 445, 134, 113, 120, 161, 119, 149, 120, 161, 119,
+ /* 850 */ 445, 149, 74, 445, 148, 445, 445, 445, 378, 158,
+ /* 860 */ 517, 113, 120, 161, 119, 149, 74, 445, 148, 152,
+ /* 870 */ 445, 74, 445, 148, 137, 113, 120, 161, 119, 525,
+ /* 880 */ 113, 120, 161, 119, 149, 74, 445, 148, 64, 63,
+ /* 890 */ 62, 61, 445, 527, 113, 120, 161, 119, 149, 445,
+ /* 900 */ 445, 391, 445, 149, 445, 445, 445, 445, 445, 445,
+ /* 910 */ 74, 445, 148, 445, 445, 162, 445, 149, 524, 113,
+ /* 920 */ 120, 161, 119, 118, 445, 74, 445, 148, 445, 445,
+ /* 930 */ 445, 445, 445, 526, 113, 120, 161, 119, 445, 74,
+ /* 940 */ 445, 148, 149, 445, 445, 445, 445, 523, 113, 120,
+ /* 950 */ 161, 119, 74, 445, 148, 445, 445, 149, 445, 445,
+ /* 960 */ 522, 113, 120, 161, 119, 445, 74, 445, 148, 445,
+ /* 970 */ 445, 149, 445, 445, 521, 113, 120, 161, 119, 74,
+ /* 980 */ 445, 148, 445, 445, 149, 445, 445, 520, 113, 120,
+ /* 990 */ 161, 119, 445, 74, 445, 148, 445, 445, 149, 445,
+ /* 1000 */ 445, 519, 113, 120, 161, 119, 445, 445, 445, 445,
+ /* 1010 */ 445, 149, 445, 445, 445, 445, 445, 445, 74, 445,
+ /* 1020 */ 148, 445, 445, 445, 445, 149, 150, 113, 120, 161,
+ /* 1030 */ 119, 74, 445, 148, 445, 445, 445, 445, 445, 151,
+ /* 1040 */ 113, 120, 161, 119, 445, 74, 445, 148, 445, 445,
+ /* 1050 */ 149, 445, 445, 136, 113, 120, 161, 119, 74, 445,
+ /* 1060 */ 148, 445, 445, 149, 445, 445, 135, 113, 120, 161,
+ /* 1070 */ 119, 445, 88, 445, 445, 445, 445, 149, 445, 445,
+ /* 1080 */ 445, 90, 120, 161, 119, 445, 445, 445, 445, 82,
+ /* 1090 */ 149, 120, 161, 119, 445, 87, 466, 445, 34, 99,
+ /* 1100 */ 445, 445, 445, 445, 152, 120, 161, 119, 100, 120,
+ /* 1110 */ 161, 119, 445, 152, 445, 445, 445, 445, 120, 161,
+ /* 1120 */ 119, 445, 445, 445, 101, 445, 445, 152, 445, 445,
+ /* 1130 */ 445, 152, 91, 445, 120, 161, 119, 103, 445, 445,
+ /* 1140 */ 152, 445, 120, 161, 119, 445, 445, 120, 161, 119,
+ /* 1150 */ 445, 92, 445, 445, 445, 445, 152, 445, 445, 445,
+ /* 1160 */ 93, 120, 161, 119, 152, 445, 104, 445, 445, 152,
+ /* 1170 */ 120, 161, 119, 445, 94, 445, 120, 161, 119, 445,
+ /* 1180 */ 445, 445, 445, 152, 120, 161, 119, 445, 445, 105,
+ /* 1190 */ 445, 445, 152, 445, 445, 445, 445, 95, 152, 120,
+ /* 1200 */ 161, 119, 96, 445, 445, 97, 152, 120, 161, 119,
+ /* 1210 */ 445, 445, 120, 161, 119, 120, 161, 119, 445, 445,
+ /* 1220 */ 445, 152, 445, 445, 445, 445, 445, 445, 445, 152,
+ /* 1230 */ 549, 445, 445, 548, 152, 445, 445, 152, 547, 445,
+ /* 1240 */ 120, 161, 119, 120, 161, 119, 546, 445, 120, 161,
+ /* 1250 */ 119, 445, 445, 445, 445, 445, 120, 161, 119, 445,
+ /* 1260 */ 445, 445, 152, 445, 445, 152, 445, 445, 445, 115,
+ /* 1270 */ 152, 445, 116, 445, 445, 445, 445, 445, 152, 120,
+ /* 1280 */ 161, 119, 120, 161, 119, 445, 445, 445, 445, 445,
+ /* 1290 */ 445, 445, 445, 445, 445, 445, 445, 445, 445, 445,
+ /* 1300 */ 445, 152, 445, 445, 152,
+};
+static const YYCODETYPE yy_lookahead[] = {
+ /* 0 */ 0, 115, 116, 117, 136, 103, 104, 105, 107, 107,
+ /* 10 */ 10, 4, 5, 6, 7, 113, 114, 115, 116, 117,
+ /* 20 */ 20, 21, 22, 17, 24, 101, 102, 103, 104, 29,
+ /* 30 */ 107, 25, 25, 109, 34, 35, 36, 37, 38, 137,
+ /* 40 */ 40, 41, 42, 43, 120, 45, 46, 109, 124, 125,
+ /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ /* 60 */ 60, 61, 62, 63, 64, 0, 4, 5, 6, 7,
+ /* 70 */ 4, 5, 105, 25, 107, 10, 115, 116, 117, 17,
+ /* 80 */ 113, 114, 115, 116, 117, 20, 21, 22, 128, 24,
+ /* 90 */ 101, 102, 103, 104, 29, 115, 116, 117, 109, 34,
+ /* 100 */ 35, 36, 37, 38, 137, 40, 41, 42, 43, 120,
+ /* 110 */ 45, 46, 49, 10, 125, 50, 51, 52, 53, 54,
+ /* 120 */ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
+ /* 130 */ 1, 2, 29, 4, 5, 4, 5, 6, 7, 8,
+ /* 140 */ 105, 12, 107, 3, 15, 115, 116, 117, 113, 114,
+ /* 150 */ 115, 116, 117, 24, 91, 130, 27, 28, 118, 105,
+ /* 160 */ 97, 32, 33, 100, 1, 2, 19, 4, 5, 115,
+ /* 170 */ 116, 117, 137, 119, 12, 12, 2, 118, 15, 1,
+ /* 180 */ 126, 127, 106, 20, 21, 22, 110, 111, 106, 2,
+ /* 190 */ 107, 137, 110, 111, 65, 32, 33, 65, 66, 67,
+ /* 200 */ 68, 69, 70, 71, 72, 73, 74, 75, 132, 133,
+ /* 210 */ 134, 131, 83, 39, 85, 86, 87, 88, 135, 90,
+ /* 220 */ 17, 92, 93, 94, 95, 96, 39, 15, 65, 89,
+ /* 230 */ 1, 2, 16, 4, 5, 30, 31, 32, 33, 98,
+ /* 240 */ 99, 12, 6, 7, 15, 83, 83, 41, 85, 86,
+ /* 250 */ 87, 88, 90, 90, 92, 92, 93, 94, 95, 96,
+ /* 260 */ 49, 32, 33, 44, 41, 2, 3, 4, 5, 6,
+ /* 270 */ 7, 27, 28, 42, 20, 21, 22, 27, 28, 16,
+ /* 280 */ 42, 105, 48, 107, 115, 116, 117, 13, 25, 113,
+ /* 290 */ 114, 115, 116, 117, 65, 32, 33, 4, 5, 6,
+ /* 300 */ 7, 17, 39, 25, 50, 51, 52, 53, 54, 55,
+ /* 310 */ 17, 100, 83, 137, 85, 86, 87, 88, 105, 90,
+ /* 320 */ 107, 92, 93, 94, 95, 96, 113, 114, 115, 116,
+ /* 330 */ 117, 68, 69, 76, 2, 3, 4, 5, 6, 7,
+ /* 340 */ 77, 17, 79, 80, 81, 4, 5, 6, 7, 17,
+ /* 350 */ 137, 102, 103, 104, 27, 28, 105, 25, 109, 1,
+ /* 360 */ 81, 80, 4, 5, 32, 33, 115, 116, 117, 120,
+ /* 370 */ 12, 39, 82, 15, 125, 81, 3, 36, 20, 21,
+ /* 380 */ 22, 0, 24, 3, 39, 105, 3, 107, 137, 3,
+ /* 390 */ 3, 10, 3, 113, 114, 115, 116, 117, 97, 78,
+ /* 400 */ 68, 69, 25, 2, 3, 4, 5, 6, 7, 77,
+ /* 410 */ 105, 79, 80, 81, 15, 15, 12, 137, 15, 92,
+ /* 420 */ 115, 116, 117, 17, 119, 29, 25, 4, 5, 6,
+ /* 430 */ 7, 29, 127, 32, 33, 91, 29, 2, 11, 3,
+ /* 440 */ 39, 83, 137, 138, 138, 105, 138, 138, 90, 138,
+ /* 450 */ 92, 93, 94, 95, 96, 115, 116, 117, 138, 36,
+ /* 460 */ 138, 121, 122, 123, 138, 138, 2, 32, 33, 68,
+ /* 470 */ 69, 138, 1, 4, 5, 6, 7, 137, 77, 138,
+ /* 480 */ 79, 80, 81, 12, 49, 14, 17, 138, 138, 18,
+ /* 490 */ 138, 20, 21, 22, 23, 24, 32, 33, 27, 28,
+ /* 500 */ 65, 30, 31, 32, 33, 1, 138, 138, 4, 5,
+ /* 510 */ 4, 5, 6, 7, 138, 138, 12, 138, 138, 15,
+ /* 520 */ 85, 86, 87, 88, 20, 21, 22, 1, 138, 65,
+ /* 530 */ 4, 5, 4, 5, 6, 7, 138, 138, 12, 138,
+ /* 540 */ 138, 15, 138, 1, 2, 17, 4, 5, 138, 85,
+ /* 550 */ 86, 87, 88, 25, 12, 138, 105, 15, 107, 138,
+ /* 560 */ 105, 138, 138, 138, 113, 114, 115, 116, 117, 138,
+ /* 570 */ 115, 116, 117, 47, 48, 1, 2, 122, 4, 5,
+ /* 580 */ 138, 39, 4, 5, 6, 7, 12, 83, 137, 15,
+ /* 590 */ 138, 138, 137, 138, 90, 17, 92, 93, 94, 95,
+ /* 600 */ 96, 1, 2, 25, 4, 5, 138, 138, 138, 83,
+ /* 610 */ 138, 138, 12, 138, 138, 15, 90, 138, 92, 93,
+ /* 620 */ 94, 95, 96, 138, 138, 83, 4, 5, 6, 7,
+ /* 630 */ 138, 138, 90, 138, 92, 93, 94, 95, 96, 1,
+ /* 640 */ 138, 138, 4, 5, 105, 138, 138, 25, 138, 138,
+ /* 650 */ 12, 138, 138, 15, 115, 116, 117, 83, 138, 138,
+ /* 660 */ 138, 122, 123, 138, 90, 138, 92, 93, 94, 95,
+ /* 670 */ 96, 1, 138, 138, 4, 5, 137, 105, 138, 138,
+ /* 680 */ 138, 138, 12, 83, 138, 15, 138, 115, 116, 117,
+ /* 690 */ 90, 119, 92, 93, 94, 95, 96, 1, 138, 138,
+ /* 700 */ 4, 5, 4, 5, 6, 7, 138, 138, 12, 137,
+ /* 710 */ 138, 15, 138, 1, 138, 17, 4, 5, 138, 138,
+ /* 720 */ 138, 83, 138, 138, 12, 105, 138, 15, 90, 138,
+ /* 730 */ 92, 93, 94, 95, 96, 115, 116, 117, 105, 138,
+ /* 740 */ 107, 138, 122, 123, 105, 138, 113, 114, 115, 116,
+ /* 750 */ 117, 138, 138, 83, 115, 116, 117, 137, 119, 138,
+ /* 760 */ 90, 105, 92, 93, 94, 95, 96, 138, 138, 138,
+ /* 770 */ 137, 115, 116, 117, 138, 138, 137, 138, 138, 83,
+ /* 780 */ 138, 4, 5, 6, 7, 138, 90, 138, 92, 93,
+ /* 790 */ 94, 95, 96, 137, 17, 83, 4, 5, 6, 7,
+ /* 800 */ 105, 138, 90, 138, 92, 93, 94, 95, 96, 138,
+ /* 810 */ 115, 116, 117, 138, 105, 138, 107, 25, 138, 105,
+ /* 820 */ 138, 107, 113, 114, 115, 116, 117, 113, 114, 115,
+ /* 830 */ 116, 117, 137, 105, 138, 107, 138, 105, 138, 138,
+ /* 840 */ 138, 113, 114, 115, 116, 117, 137, 115, 116, 117,
+ /* 850 */ 138, 137, 105, 138, 107, 138, 138, 138, 27, 28,
+ /* 860 */ 113, 114, 115, 116, 117, 137, 105, 138, 107, 137,
+ /* 870 */ 138, 105, 138, 107, 113, 114, 115, 116, 117, 113,
+ /* 880 */ 114, 115, 116, 117, 137, 105, 138, 107, 4, 5,
+ /* 890 */ 6, 7, 138, 113, 114, 115, 116, 117, 137, 138,
+ /* 900 */ 138, 17, 138, 137, 138, 138, 138, 138, 138, 138,
+ /* 910 */ 105, 138, 107, 138, 138, 84, 138, 137, 113, 114,
+ /* 920 */ 115, 116, 117, 92, 138, 105, 138, 107, 138, 138,
+ /* 930 */ 138, 138, 138, 113, 114, 115, 116, 117, 138, 105,
+ /* 940 */ 138, 107, 137, 138, 138, 138, 138, 113, 114, 115,
+ /* 950 */ 116, 117, 105, 138, 107, 138, 138, 137, 138, 138,
+ /* 960 */ 113, 114, 115, 116, 117, 138, 105, 138, 107, 138,
+ /* 970 */ 138, 137, 138, 138, 113, 114, 115, 116, 117, 105,
+ /* 980 */ 138, 107, 138, 138, 137, 138, 138, 113, 114, 115,
+ /* 990 */ 116, 117, 138, 105, 138, 107, 138, 138, 137, 138,
+ /* 1000 */ 138, 113, 114, 115, 116, 117, 138, 138, 138, 138,
+ /* 1010 */ 138, 137, 138, 138, 138, 138, 138, 138, 105, 138,
+ /* 1020 */ 107, 138, 138, 138, 138, 137, 113, 114, 115, 116,
+ /* 1030 */ 117, 105, 138, 107, 138, 138, 138, 138, 138, 113,
+ /* 1040 */ 114, 115, 116, 117, 138, 105, 138, 107, 138, 138,
+ /* 1050 */ 137, 138, 138, 113, 114, 115, 116, 117, 105, 138,
+ /* 1060 */ 107, 138, 138, 137, 138, 138, 113, 114, 115, 116,
+ /* 1070 */ 117, 138, 105, 138, 138, 138, 138, 137, 138, 138,
+ /* 1080 */ 138, 105, 115, 116, 117, 138, 138, 138, 138, 122,
+ /* 1090 */ 137, 115, 116, 117, 138, 105, 129, 138, 131, 105,
+ /* 1100 */ 138, 138, 138, 138, 137, 115, 116, 117, 105, 115,
+ /* 1110 */ 116, 117, 138, 137, 138, 138, 138, 138, 115, 116,
+ /* 1120 */ 117, 138, 138, 138, 105, 138, 138, 137, 138, 138,
+ /* 1130 */ 138, 137, 105, 138, 115, 116, 117, 105, 138, 138,
+ /* 1140 */ 137, 138, 115, 116, 117, 138, 138, 115, 116, 117,
+ /* 1150 */ 138, 105, 138, 138, 138, 138, 137, 138, 138, 138,
+ /* 1160 */ 105, 115, 116, 117, 137, 138, 105, 138, 138, 137,
+ /* 1170 */ 115, 116, 117, 138, 105, 138, 115, 116, 117, 138,
+ /* 1180 */ 138, 138, 138, 137, 115, 116, 117, 138, 138, 105,
+ /* 1190 */ 138, 138, 137, 138, 138, 138, 138, 105, 137, 115,
+ /* 1200 */ 116, 117, 105, 138, 138, 105, 137, 115, 116, 117,
+ /* 1210 */ 138, 138, 115, 116, 117, 115, 116, 117, 138, 138,
+ /* 1220 */ 138, 137, 138, 138, 138, 138, 138, 138, 138, 137,
+ /* 1230 */ 105, 138, 138, 105, 137, 138, 138, 137, 105, 138,
+ /* 1240 */ 115, 116, 117, 115, 116, 117, 105, 138, 115, 116,
+ /* 1250 */ 117, 138, 138, 138, 138, 138, 115, 116, 117, 138,
+ /* 1260 */ 138, 138, 137, 138, 138, 137, 138, 138, 138, 105,
+ /* 1270 */ 137, 138, 105, 138, 138, 138, 138, 138, 137, 115,
+ /* 1280 */ 116, 117, 115, 116, 117, 138, 138, 138, 138, 138,
+ /* 1290 */ 138, 138, 138, 138, 138, 138, 138, 138, 138, 138,
+ /* 1300 */ 138, 137, 138, 138, 137, 101, 101, 101, 101, 101,
+ /* 1310 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ /* 1320 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ /* 1330 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ /* 1340 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ /* 1350 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ /* 1360 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ /* 1370 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ /* 1380 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ /* 1390 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ /* 1400 */ 101, 101, 101, 101, 101, 101,
+};
+#define YY_SHIFT_COUNT (163)
+#define YY_SHIFT_MIN (0)
+#define YY_SHIFT_MAX (884)
+static const unsigned short int yy_shift_ofst[] = {
+ /* 0 */ 471, 129, 163, 229, 229, 229, 229, 229, 229, 229,
+ /* 10 */ 229, 229, 229, 229, 229, 229, 229, 229, 229, 229,
+ /* 20 */ 229, 229, 229, 229, 229, 229, 229, 358, 526, 638,
+ /* 30 */ 358, 471, 542, 542, 0, 65, 471, 670, 638, 670,
+ /* 40 */ 504, 504, 504, 574, 600, 638, 638, 638, 638, 638,
+ /* 50 */ 638, 696, 638, 638, 712, 638, 638, 638, 638, 638,
+ /* 60 */ 638, 638, 638, 638, 638, 254, 162, 162, 162, 162,
+ /* 70 */ 162, 435, 263, 332, 401, 464, 464, 205, 48, 1305,
+ /* 80 */ 1305, 1305, 1305, 132, 132, 528, 578, 62, 131, 341,
+ /* 90 */ 423, 293, 7, 469, 622, 698, 792, 777, 884, 506,
+ /* 100 */ 506, 506, 63, 506, 506, 506, 831, 506, 327, 103,
+ /* 110 */ 174, 187, 6, 66, 141, 236, 236, 244, 250, 140,
+ /* 120 */ 211, 381, 147, 178, 203, 216, 212, 219, 206, 223,
+ /* 130 */ 231, 238, 234, 274, 284, 278, 257, 324, 279, 281,
+ /* 140 */ 290, 294, 373, 380, 383, 345, 386, 387, 389, 301,
+ /* 150 */ 321, 377, 301, 399, 400, 403, 406, 396, 402, 407,
+ /* 160 */ 404, 344, 436, 427,
+};
+#define YY_REDUCE_COUNT (82)
+#define YY_REDUCE_MIN (-132)
+#define YY_REDUCE_MAX (1167)
+static const short yy_reduce_ofst[] = {
+ /* 0 */ -76, -98, -33, 35, 176, 213, 280, 451, 633, 709,
+ /* 10 */ 714, 728, 747, 761, 766, 780, 805, 820, 834, 847,
+ /* 20 */ 861, 874, 888, 913, 926, 940, 953, 54, 340, 967,
+ /* 30 */ 305, -11, 539, 620, 76, 76, 249, 639, 455, 572,
+ /* 40 */ 251, 656, 695, 732, 976, 990, 994, 1003, 1019, 1027,
+ /* 50 */ 1032, 1046, 1055, 1061, 1069, 1084, 1092, 1097, 1100, 1125,
+ /* 60 */ 1128, 1133, 1141, 1164, 1167, 82, -114, -39, -20, 30,
+ /* 70 */ 169, 83, -132, -132, -132, -99, -77, -62, -40, 25,
+ /* 80 */ 40, 59, 80,
+};
+static const YYACTIONTYPE yy_default[] = {
+ /* 0 */ 449, 443, 443, 443, 443, 443, 443, 443, 443, 443,
+ /* 10 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443,
+ /* 20 */ 443, 443, 443, 443, 443, 443, 443, 443, 473, 576,
+ /* 30 */ 443, 449, 580, 485, 581, 581, 449, 443, 443, 443,
+ /* 40 */ 443, 443, 443, 443, 443, 443, 443, 443, 477, 443,
+ /* 50 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443,
+ /* 60 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443,
+ /* 70 */ 443, 443, 443, 443, 443, 443, 443, 443, 455, 470,
+ /* 80 */ 508, 508, 576, 468, 493, 443, 443, 443, 471, 443,
+ /* 90 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 488,
+ /* 100 */ 486, 476, 459, 512, 511, 510, 443, 566, 443, 443,
+ /* 110 */ 443, 443, 443, 588, 443, 545, 544, 540, 443, 532,
+ /* 120 */ 529, 443, 443, 443, 443, 443, 443, 491, 443, 443,
+ /* 130 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443,
+ /* 140 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 592,
+ /* 150 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443,
+ /* 160 */ 443, 601, 443, 443,
+};
+/********** 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, /* DEFINE => nothing */
+ 0, /* CODEBLOCK => nothing */
+ 0, /* FILL => nothing */
+ 0, /* COLOR => nothing */
+ 0, /* THICKNESS => nothing */
+ 0, /* PRINT => nothing */
+ 0, /* STRING => nothing */
+ 0, /* COMMA => nothing */
+ 0, /* ISODATE => 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, /* SOLID => nothing */
+ 0, /* CENTER => nothing */
+ 0, /* LJUST => nothing */
+ 0, /* RJUST => nothing */
+ 0, /* ABOVE => nothing */
+ 0, /* BELOW => nothing */
+ 0, /* ITALIC => nothing */
+ 0, /* BOLD => nothing */
+ 0, /* MONO => 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, /* THIS => nothing */
+ 0, /* DOT_U => nothing */
+ 0, /* LAST => nothing */
+ 0, /* NUMBER => nothing */
+ 0, /* FUNC1 => nothing */
+ 0, /* FUNC2 => nothing */
+ 0, /* DIST => 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 */
+ yyStackEntry *yystackEnd; /* Last entry in the stack */
+ yyStackEntry *yystack; /* The parser stack */
+ yyStackEntry yystk0[YYSTACKDEPTH]; /* Initial stack space */
+};
+typedef struct yyParser yyParser;
+
+#include <assert.h>
+#ifndef NDEBUG
+#include <stdio.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 */ "DEFINE",
+ /* 19 */ "CODEBLOCK",
+ /* 20 */ "FILL",
+ /* 21 */ "COLOR",
+ /* 22 */ "THICKNESS",
+ /* 23 */ "PRINT",
+ /* 24 */ "STRING",
+ /* 25 */ "COMMA",
+ /* 26 */ "ISODATE",
+ /* 27 */ "CLASSNAME",
+ /* 28 */ "LB",
+ /* 29 */ "RB",
+ /* 30 */ "UP",
+ /* 31 */ "DOWN",
+ /* 32 */ "LEFT",
+ /* 33 */ "RIGHT",
+ /* 34 */ "CLOSE",
+ /* 35 */ "CHOP",
+ /* 36 */ "FROM",
+ /* 37 */ "TO",
+ /* 38 */ "THEN",
+ /* 39 */ "HEADING",
+ /* 40 */ "GO",
+ /* 41 */ "AT",
+ /* 42 */ "WITH",
+ /* 43 */ "SAME",
+ /* 44 */ "AS",
+ /* 45 */ "FIT",
+ /* 46 */ "BEHIND",
+ /* 47 */ "UNTIL",
+ /* 48 */ "EVEN",
+ /* 49 */ "DOT_E",
+ /* 50 */ "HEIGHT",
+ /* 51 */ "WIDTH",
+ /* 52 */ "RADIUS",
+ /* 53 */ "DIAMETER",
+ /* 54 */ "DOTTED",
+ /* 55 */ "DASHED",
+ /* 56 */ "CW",
+ /* 57 */ "CCW",
+ /* 58 */ "LARROW",
+ /* 59 */ "RARROW",
+ /* 60 */ "LRARROW",
+ /* 61 */ "INVIS",
+ /* 62 */ "THICK",
+ /* 63 */ "THIN",
+ /* 64 */ "SOLID",
+ /* 65 */ "CENTER",
+ /* 66 */ "LJUST",
+ /* 67 */ "RJUST",
+ /* 68 */ "ABOVE",
+ /* 69 */ "BELOW",
+ /* 70 */ "ITALIC",
+ /* 71 */ "BOLD",
+ /* 72 */ "MONO",
+ /* 73 */ "ALIGNED",
+ /* 74 */ "BIG",
+ /* 75 */ "SMALL",
+ /* 76 */ "AND",
+ /* 77 */ "LT",
+ /* 78 */ "GT",
+ /* 79 */ "ON",
+ /* 80 */ "WAY",
+ /* 81 */ "BETWEEN",
+ /* 82 */ "THE",
+ /* 83 */ "NTH",
+ /* 84 */ "VERTEX",
+ /* 85 */ "TOP",
+ /* 86 */ "BOTTOM",
+ /* 87 */ "START",
+ /* 88 */ "END",
+ /* 89 */ "IN",
+ /* 90 */ "THIS",
+ /* 91 */ "DOT_U",
+ /* 92 */ "LAST",
+ /* 93 */ "NUMBER",
+ /* 94 */ "FUNC1",
+ /* 95 */ "FUNC2",
+ /* 96 */ "DIST",
+ /* 97 */ "DOT_XY",
+ /* 98 */ "X",
+ /* 99 */ "Y",
+ /* 100 */ "DOT_L",
+ /* 101 */ "statement_list",
+ /* 102 */ "statement",
+ /* 103 */ "unnamed_statement",
+ /* 104 */ "basetype",
+ /* 105 */ "expr",
+ /* 106 */ "numproperty",
+ /* 107 */ "edge",
+ /* 108 */ "isodate",
+ /* 109 */ "direction",
+ /* 110 */ "dashproperty",
+ /* 111 */ "colorproperty",
+ /* 112 */ "locproperty",
+ /* 113 */ "position",
+ /* 114 */ "place",
+ /* 115 */ "object",
+ /* 116 */ "objectname",
+ /* 117 */ "nth",
+ /* 118 */ "textposition",
+ /* 119 */ "rvalue",
+ /* 120 */ "lvalue",
+ /* 121 */ "even",
+ /* 122 */ "relexpr",
+ /* 123 */ "optrelexpr",
+ /* 124 */ "document",
+ /* 125 */ "print",
+ /* 126 */ "prlist",
+ /* 127 */ "pritem",
+ /* 128 */ "prsep",
+ /* 129 */ "attribute_list",
+ /* 130 */ "savelist",
+ /* 131 */ "alist",
+ /* 132 */ "attribute",
+ /* 133 */ "go",
+ /* 134 */ "boolproperty",
+ /* 135 */ "withclause",
+ /* 136 */ "between",
+ /* 137 */ "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 ::= statement_list",
+ /* 1 */ "statement_list ::= statement",
+ /* 2 */ "statement_list ::= statement_list EOL statement",
+ /* 3 */ "statement ::=",
+ /* 4 */ "statement ::= direction",
+ /* 5 */ "statement ::= lvalue ASSIGN rvalue",
+ /* 6 */ "statement ::= PLACENAME COLON unnamed_statement",
+ /* 7 */ "statement ::= PLACENAME COLON position",
+ /* 8 */ "statement ::= unnamed_statement",
+ /* 9 */ "statement ::= print prlist",
+ /* 10 */ "statement ::= ASSERT LP expr EQ expr RP",
+ /* 11 */ "statement ::= ASSERT LP position EQ position RP",
+ /* 12 */ "statement ::= DEFINE ID CODEBLOCK",
+ /* 13 */ "rvalue ::= PLACENAME",
+ /* 14 */ "pritem ::= FILL",
+ /* 15 */ "pritem ::= COLOR",
+ /* 16 */ "pritem ::= THICKNESS",
+ /* 17 */ "pritem ::= rvalue",
+ /* 18 */ "pritem ::= STRING",
+ /* 19 */ "prsep ::= COMMA",
+ /* 20 */ "unnamed_statement ::= basetype attribute_list",
+ /* 21 */ "basetype ::= CLASSNAME",
+ /* 22 */ "basetype ::= STRING textposition",
+ /* 23 */ "basetype ::= LB savelist statement_list RB",
+ /* 24 */ "savelist ::=",
+ /* 25 */ "relexpr ::= expr",
+ /* 26 */ "relexpr ::= expr PERCENT",
+ /* 27 */ "optrelexpr ::=",
+ /* 28 */ "attribute_list ::= relexpr alist",
+ /* 29 */ "attribute ::= numproperty relexpr",
+ /* 30 */ "attribute ::= dashproperty expr",
+ /* 31 */ "attribute ::= dashproperty",
+ /* 32 */ "attribute ::= colorproperty rvalue",
+ /* 33 */ "attribute ::= go direction optrelexpr",
+ /* 34 */ "attribute ::= go direction even position",
+ /* 35 */ "attribute ::= CLOSE",
+ /* 36 */ "attribute ::= CHOP",
+ /* 37 */ "attribute ::= FROM position",
+ /* 38 */ "attribute ::= TO position",
+ /* 39 */ "attribute ::= THEN",
+ /* 40 */ "attribute ::= THEN optrelexpr HEADING expr",
+ /* 41 */ "attribute ::= THEN optrelexpr EDGEPT",
+ /* 42 */ "attribute ::= GO optrelexpr HEADING expr",
+ /* 43 */ "attribute ::= GO optrelexpr EDGEPT",
+ /* 44 */ "attribute ::= AT position",
+ /* 45 */ "attribute ::= SAME",
+ /* 46 */ "attribute ::= SAME AS object",
+ /* 47 */ "attribute ::= STRING textposition",
+ /* 48 */ "attribute ::= FIT",
+ /* 49 */ "attribute ::= BEHIND object",
+ /* 50 */ "withclause ::= DOT_E edge AT position",
+ /* 51 */ "withclause ::= edge AT position",
+ /* 52 */ "numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS",
+ /* 53 */ "boolproperty ::= CW",
+ /* 54 */ "boolproperty ::= CCW",
+ /* 55 */ "boolproperty ::= LARROW",
+ /* 56 */ "boolproperty ::= RARROW",
+ /* 57 */ "boolproperty ::= LRARROW",
+ /* 58 */ "boolproperty ::= INVIS",
+ /* 59 */ "boolproperty ::= THICK",
+ /* 60 */ "boolproperty ::= THIN",
+ /* 61 */ "boolproperty ::= SOLID",
+ /* 62 */ "textposition ::=",
+ /* 63 */ "textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL",
+ /* 64 */ "position ::= expr COMMA expr",
+ /* 65 */ "position ::= place PLUS expr COMMA expr",
+ /* 66 */ "position ::= place MINUS expr COMMA expr",
+ /* 67 */ "position ::= place PLUS LP expr COMMA expr RP",
+ /* 68 */ "position ::= place MINUS LP expr COMMA expr RP",
+ /* 69 */ "position ::= LP position COMMA position RP",
+ /* 70 */ "position ::= LP position RP",
+ /* 71 */ "position ::= expr between position AND position",
+ /* 72 */ "position ::= expr LT position COMMA position GT",
+ /* 73 */ "position ::= expr ABOVE position",
+ /* 74 */ "position ::= expr BELOW position",
+ /* 75 */ "position ::= expr LEFT OF position",
+ /* 76 */ "position ::= expr RIGHT OF position",
+ /* 77 */ "position ::= expr ON HEADING EDGEPT OF position",
+ /* 78 */ "position ::= expr HEADING EDGEPT OF position",
+ /* 79 */ "position ::= expr EDGEPT OF position",
+ /* 80 */ "position ::= expr ON HEADING expr FROM position",
+ /* 81 */ "position ::= expr HEADING expr FROM position",
+ /* 82 */ "place ::= edge OF object",
+ /* 83 */ "place2 ::= object",
+ /* 84 */ "place2 ::= object DOT_E edge",
+ /* 85 */ "place2 ::= NTH VERTEX OF object",
+ /* 86 */ "object ::= nth",
+ /* 87 */ "object ::= nth OF|IN object",
+ /* 88 */ "objectname ::= THIS",
+ /* 89 */ "objectname ::= PLACENAME",
+ /* 90 */ "objectname ::= objectname DOT_U PLACENAME",
+ /* 91 */ "nth ::= NTH CLASSNAME",
+ /* 92 */ "nth ::= NTH LAST CLASSNAME",
+ /* 93 */ "nth ::= LAST CLASSNAME",
+ /* 94 */ "nth ::= LAST",
+ /* 95 */ "nth ::= NTH LB RB",
+ /* 96 */ "nth ::= NTH LAST LB RB",
+ /* 97 */ "nth ::= LAST LB RB",
+ /* 98 */ "expr ::= expr PLUS expr",
+ /* 99 */ "expr ::= expr MINUS expr",
+ /* 100 */ "expr ::= expr STAR expr",
+ /* 101 */ "expr ::= expr SLASH expr",
+ /* 102 */ "expr ::= MINUS expr",
+ /* 103 */ "expr ::= PLUS expr",
+ /* 104 */ "expr ::= LP expr RP",
+ /* 105 */ "expr ::= LP FILL|COLOR|THICKNESS RP",
+ /* 106 */ "expr ::= NUMBER",
+ /* 107 */ "expr ::= ID",
+ /* 108 */ "expr ::= FUNC1 LP expr RP",
+ /* 109 */ "expr ::= FUNC2 LP expr COMMA expr RP",
+ /* 110 */ "expr ::= DIST LP position COMMA position RP",
+ /* 111 */ "expr ::= place2 DOT_XY X",
+ /* 112 */ "expr ::= place2 DOT_XY Y",
+ /* 113 */ "expr ::= object DOT_L numproperty",
+ /* 114 */ "expr ::= object DOT_L dashproperty",
+ /* 115 */ "expr ::= object DOT_L colorproperty",
+ /* 116 */ "lvalue ::= ID",
+ /* 117 */ "lvalue ::= FILL",
+ /* 118 */ "lvalue ::= COLOR",
+ /* 119 */ "lvalue ::= THICKNESS",
+ /* 120 */ "rvalue ::= expr",
+ /* 121 */ "print ::= PRINT",
+ /* 122 */ "prlist ::= pritem",
+ /* 123 */ "prlist ::= prlist prsep pritem",
+ /* 124 */ "direction ::= UP",
+ /* 125 */ "direction ::= DOWN",
+ /* 126 */ "direction ::= LEFT",
+ /* 127 */ "direction ::= RIGHT",
+ /* 128 */ "optrelexpr ::= relexpr",
+ /* 129 */ "attribute_list ::= alist",
+ /* 130 */ "alist ::=",
+ /* 131 */ "alist ::= alist attribute",
+ /* 132 */ "attribute ::= boolproperty",
+ /* 133 */ "attribute ::= WITH withclause",
+ /* 134 */ "go ::= GO",
+ /* 135 */ "go ::=",
+ /* 136 */ "even ::= UNTIL EVEN WITH",
+ /* 137 */ "even ::= EVEN WITH",
+ /* 138 */ "dashproperty ::= DOTTED",
+ /* 139 */ "dashproperty ::= DASHED",
+ /* 140 */ "colorproperty ::= FILL",
+ /* 141 */ "colorproperty ::= COLOR",
+ /* 142 */ "position ::= place",
+ /* 143 */ "between ::= WAY BETWEEN",
+ /* 144 */ "between ::= BETWEEN",
+ /* 145 */ "between ::= OF THE WAY BETWEEN",
+ /* 146 */ "place ::= place2",
+ /* 147 */ "edge ::= CENTER",
+ /* 148 */ "edge ::= EDGEPT",
+ /* 149 */ "edge ::= TOP",
+ /* 150 */ "edge ::= BOTTOM",
+ /* 151 */ "edge ::= START",
+ /* 152 */ "edge ::= END",
+ /* 153 */ "edge ::= RIGHT",
+ /* 154 */ "edge ::= LEFT",
+ /* 155 */ "object ::= objectname",
+};
+#endif /* NDEBUG */
+
+
+#if YYGROWABLESTACK
+/*
+** Try to increase the size of the parser stack. Return the number
+** of errors. Return 0 on success.
+*/
+static int yyGrowStack(yyParser *p){
+ int oldSize = 1 + (int)(p->yystackEnd - p->yystack);
+ int newSize;
+ int idx;
+ yyStackEntry *pNew;
+
+ newSize = oldSize*2 + 100;
+ idx = (int)(p->yytos - p->yystack);
+ if( p->yystack==p->yystk0 ){
+ pNew = YYREALLOC(0, newSize*sizeof(pNew[0]));
+ if( pNew==0 ) return 1;
+ memcpy(pNew, p->yystack, oldSize*sizeof(pNew[0]));
+ }else{
+ pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0]));
+ if( pNew==0 ) return 1;
+ }
+ p->yystack = pNew;
+ p->yytos = &p->yystack[idx];
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n",
+ yyTracePrompt, oldSize, newSize);
+ }
+#endif
+ p->yystackEnd = &p->yystack[newSize-1];
+ return 0;
+}
+#endif /* YYGROWABLESTACK */
+
+#if !YYGROWABLESTACK
+/* For builds that do no have a growable stack, yyGrowStack always
+** returns an error.
+*/
+# define yyGrowStack(X) 1
+#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
+ yypParser->yystack = yypParser->yystk0;
+ yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1];
+#ifndef YYNOERRORRECOVERY
+ yypParser->yyerrcnt = -1;
+#endif
+ yypParser->yytos = yypParser->yystack;
+ yypParser->yystack[0].stateno = 0;
+ yypParser->yystack[0].major = 0;
+}
+
+#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 101: /* statement_list */
+{
+#line 524 "pikchr.y"
+pik_elist_free(p,(yypminor->yy23));
+#line 1805 "pikchr.c"
+}
+ break;
+ case 102: /* statement */
+ case 103: /* unnamed_statement */
+ case 104: /* basetype */
+{
+#line 526 "pikchr.y"
+pik_elem_free(p,(yypminor->yy54));
+#line 1814 "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;
+
+ /* In-lined version of calling yy_pop_parser_stack() for each
+ ** element left in the stack */
+ yyStackEntry *yytos = pParser->yytos;
+ while( yytos>pParser->yystack ){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sPopping %s\n",
+ yyTracePrompt,
+ yyTokenName[yytos->major]);
+ }
+#endif
+ if( yytos->major>=YY_MIN_DSTRCTR ){
+ yy_destructor(pParser, yytos->major, &yytos->minor);
+ }
+ yytos--;
+ }
+
+#if YYGROWABLESTACK
+ if( pParser->yystack!=pParser->yystk0 ) YYFREE(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 559 "pikchr.y"
+
+ pik_error(p, 0, "parser stack overflow");
+#line 2052 "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
+ yytos = yypParser->yytos;
+ if( yytos>yypParser->yystackEnd ){
+ if( yyGrowStack(yypParser) ){
+ yypParser->yytos--;
+ yyStackOverflow(yypParser);
+ return;
+ }
+ yytos = yypParser->yytos;
+ assert( yytos <= yypParser->yystackEnd );
+ }
+ if( yyNewState > YY_MAX_SHIFT ){
+ yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
+ }
+ 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[] = {
+ 124, /* (0) document ::= statement_list */
+ 101, /* (1) statement_list ::= statement */
+ 101, /* (2) statement_list ::= statement_list EOL statement */
+ 102, /* (3) statement ::= */
+ 102, /* (4) statement ::= direction */
+ 102, /* (5) statement ::= lvalue ASSIGN rvalue */
+ 102, /* (6) statement ::= PLACENAME COLON unnamed_statement */
+ 102, /* (7) statement ::= PLACENAME COLON position */
+ 102, /* (8) statement ::= unnamed_statement */
+ 102, /* (9) statement ::= print prlist */
+ 102, /* (10) statement ::= ASSERT LP expr EQ expr RP */
+ 102, /* (11) statement ::= ASSERT LP position EQ position RP */
+ 102, /* (12) statement ::= DEFINE ID CODEBLOCK */
+ 119, /* (13) rvalue ::= PLACENAME */
+ 127, /* (14) pritem ::= FILL */
+ 127, /* (15) pritem ::= COLOR */
+ 127, /* (16) pritem ::= THICKNESS */
+ 127, /* (17) pritem ::= rvalue */
+ 127, /* (18) pritem ::= STRING */
+ 128, /* (19) prsep ::= COMMA */
+ 103, /* (20) unnamed_statement ::= basetype attribute_list */
+ 104, /* (21) basetype ::= CLASSNAME */
+ 104, /* (22) basetype ::= STRING textposition */
+ 104, /* (23) basetype ::= LB savelist statement_list RB */
+ 130, /* (24) savelist ::= */
+ 122, /* (25) relexpr ::= expr */
+ 122, /* (26) relexpr ::= expr PERCENT */
+ 123, /* (27) optrelexpr ::= */
+ 129, /* (28) attribute_list ::= relexpr alist */
+ 132, /* (29) attribute ::= numproperty relexpr */
+ 132, /* (30) attribute ::= dashproperty expr */
+ 132, /* (31) attribute ::= dashproperty */
+ 132, /* (32) attribute ::= colorproperty rvalue */
+ 132, /* (33) attribute ::= go direction optrelexpr */
+ 132, /* (34) attribute ::= go direction even position */
+ 132, /* (35) attribute ::= CLOSE */
+ 132, /* (36) attribute ::= CHOP */
+ 132, /* (37) attribute ::= FROM position */
+ 132, /* (38) attribute ::= TO position */
+ 132, /* (39) attribute ::= THEN */
+ 132, /* (40) attribute ::= THEN optrelexpr HEADING expr */
+ 132, /* (41) attribute ::= THEN optrelexpr EDGEPT */
+ 132, /* (42) attribute ::= GO optrelexpr HEADING expr */
+ 132, /* (43) attribute ::= GO optrelexpr EDGEPT */
+ 132, /* (44) attribute ::= AT position */
+ 132, /* (45) attribute ::= SAME */
+ 132, /* (46) attribute ::= SAME AS object */
+ 132, /* (47) attribute ::= STRING textposition */
+ 132, /* (48) attribute ::= FIT */
+ 132, /* (49) attribute ::= BEHIND object */
+ 135, /* (50) withclause ::= DOT_E edge AT position */
+ 135, /* (51) withclause ::= edge AT position */
+ 106, /* (52) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */
+ 134, /* (53) boolproperty ::= CW */
+ 134, /* (54) boolproperty ::= CCW */
+ 134, /* (55) boolproperty ::= LARROW */
+ 134, /* (56) boolproperty ::= RARROW */
+ 134, /* (57) boolproperty ::= LRARROW */
+ 134, /* (58) boolproperty ::= INVIS */
+ 134, /* (59) boolproperty ::= THICK */
+ 134, /* (60) boolproperty ::= THIN */
+ 134, /* (61) boolproperty ::= SOLID */
+ 118, /* (62) textposition ::= */
+ 118, /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */
+ 113, /* (64) position ::= expr COMMA expr */
+ 113, /* (65) position ::= place PLUS expr COMMA expr */
+ 113, /* (66) position ::= place MINUS expr COMMA expr */
+ 113, /* (67) position ::= place PLUS LP expr COMMA expr RP */
+ 113, /* (68) position ::= place MINUS LP expr COMMA expr RP */
+ 113, /* (69) position ::= LP position COMMA position RP */
+ 113, /* (70) position ::= LP position RP */
+ 113, /* (71) position ::= expr between position AND position */
+ 113, /* (72) position ::= expr LT position COMMA position GT */
+ 113, /* (73) position ::= expr ABOVE position */
+ 113, /* (74) position ::= expr BELOW position */
+ 113, /* (75) position ::= expr LEFT OF position */
+ 113, /* (76) position ::= expr RIGHT OF position */
+ 113, /* (77) position ::= expr ON HEADING EDGEPT OF position */
+ 113, /* (78) position ::= expr HEADING EDGEPT OF position */
+ 113, /* (79) position ::= expr EDGEPT OF position */
+ 113, /* (80) position ::= expr ON HEADING expr FROM position */
+ 113, /* (81) position ::= expr HEADING expr FROM position */
+ 114, /* (82) place ::= edge OF object */
+ 137, /* (83) place2 ::= object */
+ 137, /* (84) place2 ::= object DOT_E edge */
+ 137, /* (85) place2 ::= NTH VERTEX OF object */
+ 115, /* (86) object ::= nth */
+ 115, /* (87) object ::= nth OF|IN object */
+ 116, /* (88) objectname ::= THIS */
+ 116, /* (89) objectname ::= PLACENAME */
+ 116, /* (90) objectname ::= objectname DOT_U PLACENAME */
+ 117, /* (91) nth ::= NTH CLASSNAME */
+ 117, /* (92) nth ::= NTH LAST CLASSNAME */
+ 117, /* (93) nth ::= LAST CLASSNAME */
+ 117, /* (94) nth ::= LAST */
+ 117, /* (95) nth ::= NTH LB RB */
+ 117, /* (96) nth ::= NTH LAST LB RB */
+ 117, /* (97) nth ::= LAST LB RB */
+ 105, /* (98) expr ::= expr PLUS expr */
+ 105, /* (99) expr ::= expr MINUS expr */
+ 105, /* (100) expr ::= expr STAR expr */
+ 105, /* (101) expr ::= expr SLASH expr */
+ 105, /* (102) expr ::= MINUS expr */
+ 105, /* (103) expr ::= PLUS expr */
+ 105, /* (104) expr ::= LP expr RP */
+ 105, /* (105) expr ::= LP FILL|COLOR|THICKNESS RP */
+ 105, /* (106) expr ::= NUMBER */
+ 105, /* (107) expr ::= ID */
+ 105, /* (108) expr ::= FUNC1 LP expr RP */
+ 105, /* (109) expr ::= FUNC2 LP expr COMMA expr RP */
+ 105, /* (110) expr ::= DIST LP position COMMA position RP */
+ 105, /* (111) expr ::= place2 DOT_XY X */
+ 105, /* (112) expr ::= place2 DOT_XY Y */
+ 105, /* (113) expr ::= object DOT_L numproperty */
+ 105, /* (114) expr ::= object DOT_L dashproperty */
+ 105, /* (115) expr ::= object DOT_L colorproperty */
+ 120, /* (116) lvalue ::= ID */
+ 120, /* (117) lvalue ::= FILL */
+ 120, /* (118) lvalue ::= COLOR */
+ 120, /* (119) lvalue ::= THICKNESS */
+ 119, /* (120) rvalue ::= expr */
+ 125, /* (121) print ::= PRINT */
+ 126, /* (122) prlist ::= pritem */
+ 126, /* (123) prlist ::= prlist prsep pritem */
+ 109, /* (124) direction ::= UP */
+ 109, /* (125) direction ::= DOWN */
+ 109, /* (126) direction ::= LEFT */
+ 109, /* (127) direction ::= RIGHT */
+ 123, /* (128) optrelexpr ::= relexpr */
+ 129, /* (129) attribute_list ::= alist */
+ 131, /* (130) alist ::= */
+ 131, /* (131) alist ::= alist attribute */
+ 132, /* (132) attribute ::= boolproperty */
+ 132, /* (133) attribute ::= WITH withclause */
+ 133, /* (134) go ::= GO */
+ 133, /* (135) go ::= */
+ 121, /* (136) even ::= UNTIL EVEN WITH */
+ 121, /* (137) even ::= EVEN WITH */
+ 110, /* (138) dashproperty ::= DOTTED */
+ 110, /* (139) dashproperty ::= DASHED */
+ 111, /* (140) colorproperty ::= FILL */
+ 111, /* (141) colorproperty ::= COLOR */
+ 113, /* (142) position ::= place */
+ 136, /* (143) between ::= WAY BETWEEN */
+ 136, /* (144) between ::= BETWEEN */
+ 136, /* (145) between ::= OF THE WAY BETWEEN */
+ 114, /* (146) place ::= place2 */
+ 107, /* (147) edge ::= CENTER */
+ 107, /* (148) edge ::= EDGEPT */
+ 107, /* (149) edge ::= TOP */
+ 107, /* (150) edge ::= BOTTOM */
+ 107, /* (151) edge ::= START */
+ 107, /* (152) edge ::= END */
+ 107, /* (153) edge ::= RIGHT */
+ 107, /* (154) edge ::= LEFT */
+ 115, /* (155) 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 ::= statement_list */
+ -1, /* (1) statement_list ::= statement */
+ -3, /* (2) statement_list ::= statement_list EOL statement */
+ 0, /* (3) statement ::= */
+ -1, /* (4) statement ::= direction */
+ -3, /* (5) statement ::= lvalue ASSIGN rvalue */
+ -3, /* (6) statement ::= PLACENAME COLON unnamed_statement */
+ -3, /* (7) statement ::= PLACENAME COLON position */
+ -1, /* (8) statement ::= unnamed_statement */
+ -2, /* (9) statement ::= print prlist */
+ -6, /* (10) statement ::= ASSERT LP expr EQ expr RP */
+ -6, /* (11) statement ::= ASSERT LP position EQ position RP */
+ -3, /* (12) statement ::= DEFINE ID CODEBLOCK */
+ -1, /* (13) rvalue ::= PLACENAME */
+ -1, /* (14) pritem ::= FILL */
+ -1, /* (15) pritem ::= COLOR */
+ -1, /* (16) pritem ::= THICKNESS */
+ -1, /* (17) pritem ::= rvalue */
+ -1, /* (18) pritem ::= STRING */
+ -1, /* (19) prsep ::= COMMA */
+ -2, /* (20) unnamed_statement ::= basetype attribute_list */
+ -1, /* (21) basetype ::= CLASSNAME */
+ -2, /* (22) basetype ::= STRING textposition */
+ -4, /* (23) basetype ::= LB savelist statement_list RB */
+ 0, /* (24) savelist ::= */
+ -1, /* (25) relexpr ::= expr */
+ -2, /* (26) relexpr ::= expr PERCENT */
+ 0, /* (27) optrelexpr ::= */
+ -2, /* (28) attribute_list ::= relexpr alist */
+ -2, /* (29) attribute ::= numproperty relexpr */
+ -2, /* (30) attribute ::= dashproperty expr */
+ -1, /* (31) attribute ::= dashproperty */
+ -2, /* (32) attribute ::= colorproperty rvalue */
+ -3, /* (33) attribute ::= go direction optrelexpr */
+ -4, /* (34) attribute ::= go direction even position */
+ -1, /* (35) attribute ::= CLOSE */
+ -1, /* (36) attribute ::= CHOP */
+ -2, /* (37) attribute ::= FROM position */
+ -2, /* (38) attribute ::= TO position */
+ -1, /* (39) attribute ::= THEN */
+ -4, /* (40) attribute ::= THEN optrelexpr HEADING expr */
+ -3, /* (41) attribute ::= THEN optrelexpr EDGEPT */
+ -4, /* (42) attribute ::= GO optrelexpr HEADING expr */
+ -3, /* (43) attribute ::= GO optrelexpr EDGEPT */
+ -2, /* (44) attribute ::= AT position */
+ -1, /* (45) attribute ::= SAME */
+ -3, /* (46) attribute ::= SAME AS object */
+ -2, /* (47) attribute ::= STRING textposition */
+ -1, /* (48) attribute ::= FIT */
+ -2, /* (49) attribute ::= BEHIND object */
+ -4, /* (50) withclause ::= DOT_E edge AT position */
+ -3, /* (51) withclause ::= edge AT position */
+ -1, /* (52) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */
+ -1, /* (53) boolproperty ::= CW */
+ -1, /* (54) boolproperty ::= CCW */
+ -1, /* (55) boolproperty ::= LARROW */
+ -1, /* (56) boolproperty ::= RARROW */
+ -1, /* (57) boolproperty ::= LRARROW */
+ -1, /* (58) boolproperty ::= INVIS */
+ -1, /* (59) boolproperty ::= THICK */
+ -1, /* (60) boolproperty ::= THIN */
+ -1, /* (61) boolproperty ::= SOLID */
+ 0, /* (62) textposition ::= */
+ -2, /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */
+ -3, /* (64) position ::= expr COMMA expr */
+ -5, /* (65) position ::= place PLUS expr COMMA expr */
+ -5, /* (66) position ::= place MINUS expr COMMA expr */
+ -7, /* (67) position ::= place PLUS LP expr COMMA expr RP */
+ -7, /* (68) position ::= place MINUS LP expr COMMA expr RP */
+ -5, /* (69) position ::= LP position COMMA position RP */
+ -3, /* (70) position ::= LP position RP */
+ -5, /* (71) position ::= expr between position AND position */
+ -6, /* (72) position ::= expr LT position COMMA position GT */
+ -3, /* (73) position ::= expr ABOVE position */
+ -3, /* (74) position ::= expr BELOW position */
+ -4, /* (75) position ::= expr LEFT OF position */
+ -4, /* (76) position ::= expr RIGHT OF position */
+ -6, /* (77) position ::= expr ON HEADING EDGEPT OF position */
+ -5, /* (78) position ::= expr HEADING EDGEPT OF position */
+ -4, /* (79) position ::= expr EDGEPT OF position */
+ -6, /* (80) position ::= expr ON HEADING expr FROM position */
+ -5, /* (81) position ::= expr HEADING expr FROM position */
+ -3, /* (82) place ::= edge OF object */
+ -1, /* (83) place2 ::= object */
+ -3, /* (84) place2 ::= object DOT_E edge */
+ -4, /* (85) place2 ::= NTH VERTEX OF object */
+ -1, /* (86) object ::= nth */
+ -3, /* (87) object ::= nth OF|IN object */
+ -1, /* (88) objectname ::= THIS */
+ -1, /* (89) objectname ::= PLACENAME */
+ -3, /* (90) objectname ::= objectname DOT_U PLACENAME */
+ -2, /* (91) nth ::= NTH CLASSNAME */
+ -3, /* (92) nth ::= NTH LAST CLASSNAME */
+ -2, /* (93) nth ::= LAST CLASSNAME */
+ -1, /* (94) nth ::= LAST */
+ -3, /* (95) nth ::= NTH LB RB */
+ -4, /* (96) nth ::= NTH LAST LB RB */
+ -3, /* (97) nth ::= LAST LB RB */
+ -3, /* (98) expr ::= expr PLUS expr */
+ -3, /* (99) expr ::= expr MINUS expr */
+ -3, /* (100) expr ::= expr STAR expr */
+ -3, /* (101) expr ::= expr SLASH expr */
+ -2, /* (102) expr ::= MINUS expr */
+ -2, /* (103) expr ::= PLUS expr */
+ -3, /* (104) expr ::= LP expr RP */
+ -3, /* (105) expr ::= LP FILL|COLOR|THICKNESS RP */
+ -1, /* (106) expr ::= NUMBER */
+ -1, /* (107) expr ::= ID */
+ -4, /* (108) expr ::= FUNC1 LP expr RP */
+ -6, /* (109) expr ::= FUNC2 LP expr COMMA expr RP */
+ -6, /* (110) expr ::= DIST LP position COMMA position RP */
+ -3, /* (111) expr ::= place2 DOT_XY X */
+ -3, /* (112) expr ::= place2 DOT_XY Y */
+ -3, /* (113) expr ::= object DOT_L numproperty */
+ -3, /* (114) expr ::= object DOT_L dashproperty */
+ -3, /* (115) expr ::= object DOT_L colorproperty */
+ -1, /* (116) lvalue ::= ID */
+ -1, /* (117) lvalue ::= FILL */
+ -1, /* (118) lvalue ::= COLOR */
+ -1, /* (119) lvalue ::= THICKNESS */
+ -1, /* (120) rvalue ::= expr */
+ -1, /* (121) print ::= PRINT */
+ -1, /* (122) prlist ::= pritem */
+ -3, /* (123) prlist ::= prlist prsep pritem */
+ -1, /* (124) direction ::= UP */
+ -1, /* (125) direction ::= DOWN */
+ -1, /* (126) direction ::= LEFT */
+ -1, /* (127) direction ::= RIGHT */
+ -1, /* (128) optrelexpr ::= relexpr */
+ -1, /* (129) attribute_list ::= alist */
+ 0, /* (130) alist ::= */
+ -2, /* (131) alist ::= alist attribute */
+ -1, /* (132) attribute ::= boolproperty */
+ -2, /* (133) attribute ::= WITH withclause */
+ -1, /* (134) go ::= GO */
+ 0, /* (135) go ::= */
+ -3, /* (136) even ::= UNTIL EVEN WITH */
+ -2, /* (137) even ::= EVEN WITH */
+ -1, /* (138) dashproperty ::= DOTTED */
+ -1, /* (139) dashproperty ::= DASHED */
+ -1, /* (140) colorproperty ::= FILL */
+ -1, /* (141) colorproperty ::= COLOR */
+ -1, /* (142) position ::= place */
+ -2, /* (143) between ::= WAY BETWEEN */
+ -1, /* (144) between ::= BETWEEN */
+ -4, /* (145) between ::= OF THE WAY BETWEEN */
+ -1, /* (146) place ::= place2 */
+ -1, /* (147) edge ::= CENTER */
+ -1, /* (148) edge ::= EDGEPT */
+ -1, /* (149) edge ::= TOP */
+ -1, /* (150) edge ::= BOTTOM */
+ -1, /* (151) edge ::= START */
+ -1, /* (152) edge ::= END */
+ -1, /* (153) edge ::= RIGHT */
+ -1, /* (154) edge ::= LEFT */
+ -1, /* (155) 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;
+
+ 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 ::= statement_list */
+#line 563 "pikchr.y"
+{pik_render(p,yymsp[0].minor.yy23);}
+#line 2479 "pikchr.c"
+ break;
+ case 1: /* statement_list ::= statement */
+#line 566 "pikchr.y"
+{ yylhsminor.yy23 = pik_elist_append(p,0,yymsp[0].minor.yy54); }
+#line 2484 "pikchr.c"
+ yymsp[0].minor.yy23 = yylhsminor.yy23;
+ break;
+ case 2: /* statement_list ::= statement_list EOL statement */
+#line 568 "pikchr.y"
+{ yylhsminor.yy23 = pik_elist_append(p,yymsp[-2].minor.yy23,yymsp[0].minor.yy54); }
+#line 2490 "pikchr.c"
+ yymsp[-2].minor.yy23 = yylhsminor.yy23;
+ break;
+ case 3: /* statement ::= */
+#line 571 "pikchr.y"
+{ yymsp[1].minor.yy54 = 0; }
+#line 2496 "pikchr.c"
+ break;
+ case 4: /* statement ::= direction */
+#line 572 "pikchr.y"
+{ pik_set_direction(p,yymsp[0].minor.yy0.eCode); yylhsminor.yy54=0; }
+#line 2501 "pikchr.c"
+ yymsp[0].minor.yy54 = yylhsminor.yy54;
+ break;
+ case 5: /* statement ::= lvalue ASSIGN rvalue */
+#line 573 "pikchr.y"
+{pik_set_var(p,&yymsp[-2].minor.yy0,yymsp[0].minor.yy129,&yymsp[-1].minor.yy0); yylhsminor.yy54=0;}
+#line 2507 "pikchr.c"
+ yymsp[-2].minor.yy54 = yylhsminor.yy54;
+ break;
+ case 6: /* statement ::= PLACENAME COLON unnamed_statement */
+#line 575 "pikchr.y"
+{ yylhsminor.yy54 = yymsp[0].minor.yy54; pik_elem_setname(p,yymsp[0].minor.yy54,&yymsp[-2].minor.yy0); }
+#line 2513 "pikchr.c"
+ yymsp[-2].minor.yy54 = yylhsminor.yy54;
+ break;
+ case 7: /* statement ::= PLACENAME COLON position */
+#line 577 "pikchr.y"
+{ yylhsminor.yy54 = pik_elem_new(p,0,0,0);
+ if(yylhsminor.yy54){ yylhsminor.yy54->ptAt = yymsp[0].minor.yy187; pik_elem_setname(p,yylhsminor.yy54,&yymsp[-2].minor.yy0); }}
+#line 2520 "pikchr.c"
+ yymsp[-2].minor.yy54 = yylhsminor.yy54;
+ break;
+ case 8: /* statement ::= unnamed_statement */
+#line 579 "pikchr.y"
+{yylhsminor.yy54 = yymsp[0].minor.yy54;}
+#line 2526 "pikchr.c"
+ yymsp[0].minor.yy54 = yylhsminor.yy54;
+ break;
+ case 9: /* statement ::= print prlist */
+#line 580 "pikchr.y"
+{pik_append(p,"<br>\n",5); yymsp[-1].minor.yy54=0;}
+#line 2532 "pikchr.c"
+ break;
+ case 10: /* statement ::= ASSERT LP expr EQ expr RP */
+#line 585 "pikchr.y"
+{yymsp[-5].minor.yy54=pik_assert(p,yymsp[-3].minor.yy129,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy129);}
+#line 2537 "pikchr.c"
+ break;
+ case 11: /* statement ::= ASSERT LP position EQ position RP */
+#line 587 "pikchr.y"
+{yymsp[-5].minor.yy54=pik_position_assert(p,&yymsp[-3].minor.yy187,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy187);}
+#line 2542 "pikchr.c"
+ break;
+ case 12: /* statement ::= DEFINE ID CODEBLOCK */
+#line 588 "pikchr.y"
+{yymsp[-2].minor.yy54=0; pik_add_macro(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);}
+#line 2547 "pikchr.c"
+ break;
+ case 13: /* rvalue ::= PLACENAME */
+#line 599 "pikchr.y"
+{yylhsminor.yy129 = pik_lookup_color(p,&yymsp[0].minor.yy0);}
+#line 2552 "pikchr.c"
+ yymsp[0].minor.yy129 = yylhsminor.yy129;
+ break;
+ case 14: /* pritem ::= FILL */
+ case 15: /* pritem ::= COLOR */ yytestcase(yyruleno==15);
+ case 16: /* pritem ::= THICKNESS */ yytestcase(yyruleno==16);
+#line 604 "pikchr.y"
+{pik_append_num(p,"",pik_value(p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.n,0));}
+#line 2560 "pikchr.c"
+ break;
+ case 17: /* pritem ::= rvalue */
+#line 607 "pikchr.y"
+{pik_append_num(p,"",yymsp[0].minor.yy129);}
+#line 2565 "pikchr.c"
+ break;
+ case 18: /* pritem ::= STRING */
+#line 608 "pikchr.y"
+{pik_append_text(p,yymsp[0].minor.yy0.z+1,yymsp[0].minor.yy0.n-2,0);}
+#line 2570 "pikchr.c"
+ break;
+ case 19: /* prsep ::= COMMA */
+#line 609 "pikchr.y"
+{pik_append(p, " ", 1);}
+#line 2575 "pikchr.c"
+ break;
+ case 20: /* unnamed_statement ::= basetype attribute_list */
+#line 614 "pikchr.y"
+{yylhsminor.yy54 = yymsp[-1].minor.yy54; pik_after_adding_attributes(p,yylhsminor.yy54);}
+#line 2580 "pikchr.c"
+ yymsp[-1].minor.yy54 = yylhsminor.yy54;
+ break;
+ case 21: /* basetype ::= CLASSNAME */
+#line 616 "pikchr.y"
+{yylhsminor.yy54 = pik_elem_new(p,&yymsp[0].minor.yy0,0,0); }
+#line 2586 "pikchr.c"
+ yymsp[0].minor.yy54 = yylhsminor.yy54;
+ break;
+ case 22: /* basetype ::= STRING textposition */
+#line 618 "pikchr.y"
+{yymsp[-1].minor.yy0.eCode = yymsp[0].minor.yy272; yylhsminor.yy54 = pik_elem_new(p,0,&yymsp[-1].minor.yy0,0); }
+#line 2592 "pikchr.c"
+ yymsp[-1].minor.yy54 = yylhsminor.yy54;
+ break;
+ case 23: /* basetype ::= LB savelist statement_list RB */
+#line 620 "pikchr.y"
+{ p->list = yymsp[-2].minor.yy23; yymsp[-3].minor.yy54 = pik_elem_new(p,0,0,yymsp[-1].minor.yy23); if(yymsp[-3].minor.yy54) yymsp[-3].minor.yy54->errTok = yymsp[0].minor.yy0; }
+#line 2598 "pikchr.c"
+ break;
+ case 24: /* savelist ::= */
+#line 625 "pikchr.y"
+{yymsp[1].minor.yy23 = p->list; p->list = 0;}
+#line 2603 "pikchr.c"
+ break;
+ case 25: /* relexpr ::= expr */
+#line 632 "pikchr.y"
+{yylhsminor.yy28.rAbs = yymsp[0].minor.yy129; yylhsminor.yy28.rRel = 0;}
+#line 2608 "pikchr.c"
+ yymsp[0].minor.yy28 = yylhsminor.yy28;
+ break;
+ case 26: /* relexpr ::= expr PERCENT */
+#line 633 "pikchr.y"
+{yylhsminor.yy28.rAbs = 0; yylhsminor.yy28.rRel = yymsp[-1].minor.yy129/100;}
+#line 2614 "pikchr.c"
+ yymsp[-1].minor.yy28 = yylhsminor.yy28;
+ break;
+ case 27: /* optrelexpr ::= */
+#line 635 "pikchr.y"
+{yymsp[1].minor.yy28.rAbs = 0; yymsp[1].minor.yy28.rRel = 1.0;}
+#line 2620 "pikchr.c"
+ break;
+ case 28: /* attribute_list ::= relexpr alist */
+#line 637 "pikchr.y"
+{pik_add_direction(p,0,&yymsp[-1].minor.yy28);}
+#line 2625 "pikchr.c"
+ break;
+ case 29: /* attribute ::= numproperty relexpr */
+#line 641 "pikchr.y"
+{ pik_set_numprop(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy28); }
+#line 2630 "pikchr.c"
+ break;
+ case 30: /* attribute ::= dashproperty expr */
+#line 642 "pikchr.y"
+{ pik_set_dashed(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy129); }
+#line 2635 "pikchr.c"
+ break;
+ case 31: /* attribute ::= dashproperty */
+#line 643 "pikchr.y"
+{ pik_set_dashed(p,&yymsp[0].minor.yy0,0); }
+#line 2640 "pikchr.c"
+ break;
+ case 32: /* attribute ::= colorproperty rvalue */
+#line 644 "pikchr.y"
+{ pik_set_clrprop(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy129); }
+#line 2645 "pikchr.c"
+ break;
+ case 33: /* attribute ::= go direction optrelexpr */
+#line 645 "pikchr.y"
+{ pik_add_direction(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy28);}
+#line 2650 "pikchr.c"
+ break;
+ case 34: /* attribute ::= go direction even position */
+#line 646 "pikchr.y"
+{pik_evenwith(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy187);}
+#line 2655 "pikchr.c"
+ break;
+ case 35: /* attribute ::= CLOSE */
+#line 647 "pikchr.y"
+{ pik_close_path(p,&yymsp[0].minor.yy0); }
+#line 2660 "pikchr.c"
+ break;
+ case 36: /* attribute ::= CHOP */
+#line 648 "pikchr.y"
+{ p->cur->bChop = 1; }
+#line 2665 "pikchr.c"
+ break;
+ case 37: /* attribute ::= FROM position */
+#line 649 "pikchr.y"
+{ pik_set_from(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy187); }
+#line 2670 "pikchr.c"
+ break;
+ case 38: /* attribute ::= TO position */
+#line 650 "pikchr.y"
+{ pik_add_to(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy187); }
+#line 2675 "pikchr.c"
+ break;
+ case 39: /* attribute ::= THEN */
+#line 651 "pikchr.y"
+{ pik_then(p, &yymsp[0].minor.yy0, p->cur); }
+#line 2680 "pikchr.c"
+ break;
+ case 40: /* attribute ::= THEN optrelexpr HEADING expr */
+ case 42: /* attribute ::= GO optrelexpr HEADING expr */ yytestcase(yyruleno==42);
+#line 653 "pikchr.y"
+{pik_move_hdg(p,&yymsp[-2].minor.yy28,&yymsp[-1].minor.yy0,yymsp[0].minor.yy129,0,&yymsp[-3].minor.yy0);}
+#line 2686 "pikchr.c"
+ break;
+ case 41: /* attribute ::= THEN optrelexpr EDGEPT */
+ case 43: /* attribute ::= GO optrelexpr EDGEPT */ yytestcase(yyruleno==43);
+#line 654 "pikchr.y"
+{pik_move_hdg(p,&yymsp[-1].minor.yy28,0,0,&yymsp[0].minor.yy0,&yymsp[-2].minor.yy0);}
+#line 2692 "pikchr.c"
+ break;
+ case 44: /* attribute ::= AT position */
+#line 659 "pikchr.y"
+{ pik_set_at(p,0,&yymsp[0].minor.yy187,&yymsp[-1].minor.yy0); }
+#line 2697 "pikchr.c"
+ break;
+ case 45: /* attribute ::= SAME */
+#line 661 "pikchr.y"
+{pik_same(p,0,&yymsp[0].minor.yy0);}
+#line 2702 "pikchr.c"
+ break;
+ case 46: /* attribute ::= SAME AS object */
+#line 662 "pikchr.y"
+{pik_same(p,yymsp[0].minor.yy54,&yymsp[-2].minor.yy0);}
+#line 2707 "pikchr.c"
+ break;
+ case 47: /* attribute ::= STRING textposition */
+#line 663 "pikchr.y"
+{pik_add_txt(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy272);}
+#line 2712 "pikchr.c"
+ break;
+ case 48: /* attribute ::= FIT */
+#line 664 "pikchr.y"
+{pik_size_to_fit(p,0,&yymsp[0].minor.yy0,3); }
+#line 2717 "pikchr.c"
+ break;
+ case 49: /* attribute ::= BEHIND object */
+#line 665 "pikchr.y"
+{pik_behind(p,yymsp[0].minor.yy54);}
+#line 2722 "pikchr.c"
+ break;
+ case 50: /* withclause ::= DOT_E edge AT position */
+ case 51: /* withclause ::= edge AT position */ yytestcase(yyruleno==51);
+#line 673 "pikchr.y"
+{ pik_set_at(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy187,&yymsp[-1].minor.yy0); }
+#line 2728 "pikchr.c"
+ break;
+ case 52: /* numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */
+#line 677 "pikchr.y"
+{yylhsminor.yy0 = yymsp[0].minor.yy0;}
+#line 2733 "pikchr.c"
+ yymsp[0].minor.yy0 = yylhsminor.yy0;
+ break;
+ case 53: /* boolproperty ::= CW */
+#line 688 "pikchr.y"
+{p->cur->cw = 1;}
+#line 2739 "pikchr.c"
+ break;
+ case 54: /* boolproperty ::= CCW */
+#line 689 "pikchr.y"
+{p->cur->cw = 0;}
+#line 2744 "pikchr.c"
+ break;
+ case 55: /* boolproperty ::= LARROW */
+#line 690 "pikchr.y"
+{p->cur->larrow=1; p->cur->rarrow=0; }
+#line 2749 "pikchr.c"
+ break;
+ case 56: /* boolproperty ::= RARROW */
+#line 691 "pikchr.y"
+{p->cur->larrow=0; p->cur->rarrow=1; }
+#line 2754 "pikchr.c"
+ break;
+ case 57: /* boolproperty ::= LRARROW */
+#line 692 "pikchr.y"
+{p->cur->larrow=1; p->cur->rarrow=1; }
+#line 2759 "pikchr.c"
+ break;
+ case 58: /* boolproperty ::= INVIS */
+#line 693 "pikchr.y"
+{p->cur->sw = -0.00001;}
+#line 2764 "pikchr.c"
+ break;
+ case 59: /* boolproperty ::= THICK */
+#line 694 "pikchr.y"
+{p->cur->sw *= 1.5;}
+#line 2769 "pikchr.c"
+ break;
+ case 60: /* boolproperty ::= THIN */
+#line 695 "pikchr.y"
+{p->cur->sw *= 0.67;}
+#line 2774 "pikchr.c"
+ break;
+ case 61: /* boolproperty ::= SOLID */
+#line 696 "pikchr.y"
+{p->cur->sw = pik_value(p,"thickness",9,0);
+ p->cur->dotted = p->cur->dashed = 0.0;}
+#line 2780 "pikchr.c"
+ break;
+ case 62: /* textposition ::= */
+#line 699 "pikchr.y"
+{yymsp[1].minor.yy272 = 0;}
+#line 2785 "pikchr.c"
+ break;
+ case 63: /* textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */
+#line 702 "pikchr.y"
+{yylhsminor.yy272 = (short int)pik_text_position(yymsp[-1].minor.yy272,&yymsp[0].minor.yy0);}
+#line 2790 "pikchr.c"
+ yymsp[-1].minor.yy272 = yylhsminor.yy272;
+ break;
+ case 64: /* position ::= expr COMMA expr */
+#line 705 "pikchr.y"
+{yylhsminor.yy187.x=yymsp[-2].minor.yy129; yylhsminor.yy187.y=yymsp[0].minor.yy129;}
+#line 2796 "pikchr.c"
+ yymsp[-2].minor.yy187 = yylhsminor.yy187;
+ break;
+ case 65: /* position ::= place PLUS expr COMMA expr */
+#line 707 "pikchr.y"
+{yylhsminor.yy187.x=yymsp[-4].minor.yy187.x+yymsp[-2].minor.yy129; yylhsminor.yy187.y=yymsp[-4].minor.yy187.y+yymsp[0].minor.yy129;}
+#line 2802 "pikchr.c"
+ yymsp[-4].minor.yy187 = yylhsminor.yy187;
+ break;
+ case 66: /* position ::= place MINUS expr COMMA expr */
+#line 708 "pikchr.y"
+{yylhsminor.yy187.x=yymsp[-4].minor.yy187.x-yymsp[-2].minor.yy129; yylhsminor.yy187.y=yymsp[-4].minor.yy187.y-yymsp[0].minor.yy129;}
+#line 2808 "pikchr.c"
+ yymsp[-4].minor.yy187 = yylhsminor.yy187;
+ break;
+ case 67: /* position ::= place PLUS LP expr COMMA expr RP */
+#line 710 "pikchr.y"
+{yylhsminor.yy187.x=yymsp[-6].minor.yy187.x+yymsp[-3].minor.yy129; yylhsminor.yy187.y=yymsp[-6].minor.yy187.y+yymsp[-1].minor.yy129;}
+#line 2814 "pikchr.c"
+ yymsp[-6].minor.yy187 = yylhsminor.yy187;
+ break;
+ case 68: /* position ::= place MINUS LP expr COMMA expr RP */
+#line 712 "pikchr.y"
+{yylhsminor.yy187.x=yymsp[-6].minor.yy187.x-yymsp[-3].minor.yy129; yylhsminor.yy187.y=yymsp[-6].minor.yy187.y-yymsp[-1].minor.yy129;}
+#line 2820 "pikchr.c"
+ yymsp[-6].minor.yy187 = yylhsminor.yy187;
+ break;
+ case 69: /* position ::= LP position COMMA position RP */
+#line 713 "pikchr.y"
+{yymsp[-4].minor.yy187.x=yymsp[-3].minor.yy187.x; yymsp[-4].minor.yy187.y=yymsp[-1].minor.yy187.y;}
+#line 2826 "pikchr.c"
+ break;
+ case 70: /* position ::= LP position RP */
+#line 714 "pikchr.y"
+{yymsp[-2].minor.yy187=yymsp[-1].minor.yy187;}
+#line 2831 "pikchr.c"
+ break;
+ case 71: /* position ::= expr between position AND position */
+#line 716 "pikchr.y"
+{yylhsminor.yy187 = pik_position_between(yymsp[-4].minor.yy129,yymsp[-2].minor.yy187,yymsp[0].minor.yy187);}
+#line 2836 "pikchr.c"
+ yymsp[-4].minor.yy187 = yylhsminor.yy187;
+ break;
+ case 72: /* position ::= expr LT position COMMA position GT */
+#line 718 "pikchr.y"
+{yylhsminor.yy187 = pik_position_between(yymsp[-5].minor.yy129,yymsp[-3].minor.yy187,yymsp[-1].minor.yy187);}
+#line 2842 "pikchr.c"
+ yymsp[-5].minor.yy187 = yylhsminor.yy187;
+ break;
+ case 73: /* position ::= expr ABOVE position */
+#line 719 "pikchr.y"
+{yylhsminor.yy187=yymsp[0].minor.yy187; yylhsminor.yy187.y += yymsp[-2].minor.yy129;}
+#line 2848 "pikchr.c"
+ yymsp[-2].minor.yy187 = yylhsminor.yy187;
+ break;
+ case 74: /* position ::= expr BELOW position */
+#line 720 "pikchr.y"
+{yylhsminor.yy187=yymsp[0].minor.yy187; yylhsminor.yy187.y -= yymsp[-2].minor.yy129;}
+#line 2854 "pikchr.c"
+ yymsp[-2].minor.yy187 = yylhsminor.yy187;
+ break;
+ case 75: /* position ::= expr LEFT OF position */
+#line 721 "pikchr.y"
+{yylhsminor.yy187=yymsp[0].minor.yy187; yylhsminor.yy187.x -= yymsp[-3].minor.yy129;}
+#line 2860 "pikchr.c"
+ yymsp[-3].minor.yy187 = yylhsminor.yy187;
+ break;
+ case 76: /* position ::= expr RIGHT OF position */
+#line 722 "pikchr.y"
+{yylhsminor.yy187=yymsp[0].minor.yy187; yylhsminor.yy187.x += yymsp[-3].minor.yy129;}
+#line 2866 "pikchr.c"
+ yymsp[-3].minor.yy187 = yylhsminor.yy187;
+ break;
+ case 77: /* position ::= expr ON HEADING EDGEPT OF position */
+#line 724 "pikchr.y"
+{yylhsminor.yy187 = pik_position_at_hdg(yymsp[-5].minor.yy129,&yymsp[-2].minor.yy0,yymsp[0].minor.yy187);}
+#line 2872 "pikchr.c"
+ yymsp[-5].minor.yy187 = yylhsminor.yy187;
+ break;
+ case 78: /* position ::= expr HEADING EDGEPT OF position */
+#line 726 "pikchr.y"
+{yylhsminor.yy187 = pik_position_at_hdg(yymsp[-4].minor.yy129,&yymsp[-2].minor.yy0,yymsp[0].minor.yy187);}
+#line 2878 "pikchr.c"
+ yymsp[-4].minor.yy187 = yylhsminor.yy187;
+ break;
+ case 79: /* position ::= expr EDGEPT OF position */
+#line 728 "pikchr.y"
+{yylhsminor.yy187 = pik_position_at_hdg(yymsp[-3].minor.yy129,&yymsp[-2].minor.yy0,yymsp[0].minor.yy187);}
+#line 2884 "pikchr.c"
+ yymsp[-3].minor.yy187 = yylhsminor.yy187;
+ break;
+ case 80: /* position ::= expr ON HEADING expr FROM position */
+#line 730 "pikchr.y"
+{yylhsminor.yy187 = pik_position_at_angle(yymsp[-5].minor.yy129,yymsp[-2].minor.yy129,yymsp[0].minor.yy187);}
+#line 2890 "pikchr.c"
+ yymsp[-5].minor.yy187 = yylhsminor.yy187;
+ break;
+ case 81: /* position ::= expr HEADING expr FROM position */
+#line 732 "pikchr.y"
+{yylhsminor.yy187 = pik_position_at_angle(yymsp[-4].minor.yy129,yymsp[-2].minor.yy129,yymsp[0].minor.yy187);}
+#line 2896 "pikchr.c"
+ yymsp[-4].minor.yy187 = yylhsminor.yy187;
+ break;
+ case 82: /* place ::= edge OF object */
+#line 744 "pikchr.y"
+{yylhsminor.yy187 = pik_place_of_elem(p,yymsp[0].minor.yy54,&yymsp[-2].minor.yy0);}
+#line 2902 "pikchr.c"
+ yymsp[-2].minor.yy187 = yylhsminor.yy187;
+ break;
+ case 83: /* place2 ::= object */
+#line 745 "pikchr.y"
+{yylhsminor.yy187 = pik_place_of_elem(p,yymsp[0].minor.yy54,0);}
+#line 2908 "pikchr.c"
+ yymsp[0].minor.yy187 = yylhsminor.yy187;
+ break;
+ case 84: /* place2 ::= object DOT_E edge */
+#line 746 "pikchr.y"
+{yylhsminor.yy187 = pik_place_of_elem(p,yymsp[-2].minor.yy54,&yymsp[0].minor.yy0);}
+#line 2914 "pikchr.c"
+ yymsp[-2].minor.yy187 = yylhsminor.yy187;
+ break;
+ case 85: /* place2 ::= NTH VERTEX OF object */
+#line 747 "pikchr.y"
+{yylhsminor.yy187 = pik_nth_vertex(p,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,yymsp[0].minor.yy54);}
+#line 2920 "pikchr.c"
+ yymsp[-3].minor.yy187 = yylhsminor.yy187;
+ break;
+ case 86: /* object ::= nth */
+#line 759 "pikchr.y"
+{yylhsminor.yy54 = pik_find_nth(p,0,&yymsp[0].minor.yy0);}
+#line 2926 "pikchr.c"
+ yymsp[0].minor.yy54 = yylhsminor.yy54;
+ break;
+ case 87: /* object ::= nth OF|IN object */
+#line 760 "pikchr.y"
+{yylhsminor.yy54 = pik_find_nth(p,yymsp[0].minor.yy54,&yymsp[-2].minor.yy0);}
+#line 2932 "pikchr.c"
+ yymsp[-2].minor.yy54 = yylhsminor.yy54;
+ break;
+ case 88: /* objectname ::= THIS */
+#line 762 "pikchr.y"
+{yymsp[0].minor.yy54 = p->cur;}
+#line 2938 "pikchr.c"
+ break;
+ case 89: /* objectname ::= PLACENAME */
+#line 763 "pikchr.y"
+{yylhsminor.yy54 = pik_find_byname(p,0,&yymsp[0].minor.yy0);}
+#line 2943 "pikchr.c"
+ yymsp[0].minor.yy54 = yylhsminor.yy54;
+ break;
+ case 90: /* objectname ::= objectname DOT_U PLACENAME */
+#line 765 "pikchr.y"
+{yylhsminor.yy54 = pik_find_byname(p,yymsp[-2].minor.yy54,&yymsp[0].minor.yy0);}
+#line 2949 "pikchr.c"
+ yymsp[-2].minor.yy54 = yylhsminor.yy54;
+ break;
+ case 91: /* nth ::= NTH CLASSNAME */
+#line 767 "pikchr.y"
+{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-1].minor.yy0); }
+#line 2955 "pikchr.c"
+ yymsp[-1].minor.yy0 = yylhsminor.yy0;
+ break;
+ case 92: /* nth ::= NTH LAST CLASSNAME */
+#line 768 "pikchr.y"
+{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-2].minor.yy0); }
+#line 2961 "pikchr.c"
+ yymsp[-2].minor.yy0 = yylhsminor.yy0;
+ break;
+ case 93: /* nth ::= LAST CLASSNAME */
+#line 769 "pikchr.y"
+{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.eCode = -1;}
+#line 2967 "pikchr.c"
+ break;
+ case 94: /* nth ::= LAST */
+#line 770 "pikchr.y"
+{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -1;}
+#line 2972 "pikchr.c"
+ yymsp[0].minor.yy0 = yylhsminor.yy0;
+ break;
+ case 95: /* nth ::= NTH LB RB */
+#line 771 "pikchr.y"
+{yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-2].minor.yy0);}
+#line 2978 "pikchr.c"
+ yymsp[-2].minor.yy0 = yylhsminor.yy0;
+ break;
+ case 96: /* nth ::= NTH LAST LB RB */
+#line 772 "pikchr.y"
+{yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-3].minor.yy0);}
+#line 2984 "pikchr.c"
+ yymsp[-3].minor.yy0 = yylhsminor.yy0;
+ break;
+ case 97: /* nth ::= LAST LB RB */
+#line 773 "pikchr.y"
+{yymsp[-2].minor.yy0=yymsp[-1].minor.yy0; yymsp[-2].minor.yy0.eCode = -1; }
+#line 2990 "pikchr.c"
+ break;
+ case 98: /* expr ::= expr PLUS expr */
+#line 775 "pikchr.y"
+{yylhsminor.yy129=yymsp[-2].minor.yy129+yymsp[0].minor.yy129;}
+#line 2995 "pikchr.c"
+ yymsp[-2].minor.yy129 = yylhsminor.yy129;
+ break;
+ case 99: /* expr ::= expr MINUS expr */
+#line 776 "pikchr.y"
+{yylhsminor.yy129=yymsp[-2].minor.yy129-yymsp[0].minor.yy129;}
+#line 3001 "pikchr.c"
+ yymsp[-2].minor.yy129 = yylhsminor.yy129;
+ break;
+ case 100: /* expr ::= expr STAR expr */
+#line 777 "pikchr.y"
+{yylhsminor.yy129=yymsp[-2].minor.yy129*yymsp[0].minor.yy129;}
+#line 3007 "pikchr.c"
+ yymsp[-2].minor.yy129 = yylhsminor.yy129;
+ break;
+ case 101: /* expr ::= expr SLASH expr */
+#line 778 "pikchr.y"
+{
+ if( yymsp[0].minor.yy129==0.0 ){ pik_error(p, &yymsp[-1].minor.yy0, "division by zero"); yylhsminor.yy129 = 0.0; }
+ else{ yylhsminor.yy129 = yymsp[-2].minor.yy129/yymsp[0].minor.yy129; }
+}
+#line 3016 "pikchr.c"
+ yymsp[-2].minor.yy129 = yylhsminor.yy129;
+ break;
+ case 102: /* expr ::= MINUS expr */
+#line 782 "pikchr.y"
+{yymsp[-1].minor.yy129=-yymsp[0].minor.yy129;}
+#line 3022 "pikchr.c"
+ break;
+ case 103: /* expr ::= PLUS expr */
+#line 783 "pikchr.y"
+{yymsp[-1].minor.yy129=yymsp[0].minor.yy129;}
+#line 3027 "pikchr.c"
+ break;
+ case 104: /* expr ::= LP expr RP */
+#line 784 "pikchr.y"
+{yymsp[-2].minor.yy129=yymsp[-1].minor.yy129;}
+#line 3032 "pikchr.c"
+ break;
+ case 105: /* expr ::= LP FILL|COLOR|THICKNESS RP */
+#line 785 "pikchr.y"
+{yymsp[-2].minor.yy129=pik_get_var(p,&yymsp[-1].minor.yy0);}
+#line 3037 "pikchr.c"
+ break;
+ case 106: /* expr ::= NUMBER */
+#line 786 "pikchr.y"
+{yylhsminor.yy129=pik_atof(&yymsp[0].minor.yy0);}
+#line 3042 "pikchr.c"
+ yymsp[0].minor.yy129 = yylhsminor.yy129;
+ break;
+ case 107: /* expr ::= ID */
+#line 787 "pikchr.y"
+{yylhsminor.yy129=pik_get_var(p,&yymsp[0].minor.yy0);}
+#line 3048 "pikchr.c"
+ yymsp[0].minor.yy129 = yylhsminor.yy129;
+ break;
+ case 108: /* expr ::= FUNC1 LP expr RP */
+#line 788 "pikchr.y"
+{yylhsminor.yy129 = pik_func(p,&yymsp[-3].minor.yy0,yymsp[-1].minor.yy129,0.0);}
+#line 3054 "pikchr.c"
+ yymsp[-3].minor.yy129 = yylhsminor.yy129;
+ break;
+ case 109: /* expr ::= FUNC2 LP expr COMMA expr RP */
+#line 789 "pikchr.y"
+{yylhsminor.yy129 = pik_func(p,&yymsp[-5].minor.yy0,yymsp[-3].minor.yy129,yymsp[-1].minor.yy129);}
+#line 3060 "pikchr.c"
+ yymsp[-5].minor.yy129 = yylhsminor.yy129;
+ break;
+ case 110: /* expr ::= DIST LP position COMMA position RP */
+#line 790 "pikchr.y"
+{yymsp[-5].minor.yy129 = pik_dist(&yymsp[-3].minor.yy187,&yymsp[-1].minor.yy187);}
+#line 3066 "pikchr.c"
+ break;
+ case 111: /* expr ::= place2 DOT_XY X */
+#line 791 "pikchr.y"
+{yylhsminor.yy129 = yymsp[-2].minor.yy187.x;}
+#line 3071 "pikchr.c"
+ yymsp[-2].minor.yy129 = yylhsminor.yy129;
+ break;
+ case 112: /* expr ::= place2 DOT_XY Y */
+#line 792 "pikchr.y"
+{yylhsminor.yy129 = yymsp[-2].minor.yy187.y;}
+#line 3077 "pikchr.c"
+ yymsp[-2].minor.yy129 = yylhsminor.yy129;
+ break;
+ case 113: /* expr ::= object DOT_L numproperty */
+ case 114: /* expr ::= object DOT_L dashproperty */ yytestcase(yyruleno==114);
+ case 115: /* expr ::= object DOT_L colorproperty */ yytestcase(yyruleno==115);
+#line 793 "pikchr.y"
+{yylhsminor.yy129=pik_property_of(yymsp[-2].minor.yy54,&yymsp[0].minor.yy0);}
+#line 3085 "pikchr.c"
+ yymsp[-2].minor.yy129 = yylhsminor.yy129;
+ break;
+ default:
+ /* (116) lvalue ::= ID */ yytestcase(yyruleno==116);
+ /* (117) lvalue ::= FILL */ yytestcase(yyruleno==117);
+ /* (118) lvalue ::= COLOR */ yytestcase(yyruleno==118);
+ /* (119) lvalue ::= THICKNESS */ yytestcase(yyruleno==119);
+ /* (120) rvalue ::= expr */ yytestcase(yyruleno==120);
+ /* (121) print ::= PRINT */ yytestcase(yyruleno==121);
+ /* (122) prlist ::= pritem (OPTIMIZED OUT) */ assert(yyruleno!=122);
+ /* (123) prlist ::= prlist prsep pritem */ yytestcase(yyruleno==123);
+ /* (124) direction ::= UP */ yytestcase(yyruleno==124);
+ /* (125) direction ::= DOWN */ yytestcase(yyruleno==125);
+ /* (126) direction ::= LEFT */ yytestcase(yyruleno==126);
+ /* (127) direction ::= RIGHT */ yytestcase(yyruleno==127);
+ /* (128) optrelexpr ::= relexpr (OPTIMIZED OUT) */ assert(yyruleno!=128);
+ /* (129) attribute_list ::= alist */ yytestcase(yyruleno==129);
+ /* (130) alist ::= */ yytestcase(yyruleno==130);
+ /* (131) alist ::= alist attribute */ yytestcase(yyruleno==131);
+ /* (132) attribute ::= boolproperty (OPTIMIZED OUT) */ assert(yyruleno!=132);
+ /* (133) attribute ::= WITH withclause */ yytestcase(yyruleno==133);
+ /* (134) go ::= GO */ yytestcase(yyruleno==134);
+ /* (135) go ::= */ yytestcase(yyruleno==135);
+ /* (136) even ::= UNTIL EVEN WITH */ yytestcase(yyruleno==136);
+ /* (137) even ::= EVEN WITH */ yytestcase(yyruleno==137);
+ /* (138) dashproperty ::= DOTTED */ yytestcase(yyruleno==138);
+ /* (139) dashproperty ::= DASHED */ yytestcase(yyruleno==139);
+ /* (140) colorproperty ::= FILL */ yytestcase(yyruleno==140);
+ /* (141) colorproperty ::= COLOR */ yytestcase(yyruleno==141);
+ /* (142) position ::= place */ yytestcase(yyruleno==142);
+ /* (143) between ::= WAY BETWEEN */ yytestcase(yyruleno==143);
+ /* (144) between ::= BETWEEN */ yytestcase(yyruleno==144);
+ /* (145) between ::= OF THE WAY BETWEEN */ yytestcase(yyruleno==145);
+ /* (146) place ::= place2 */ yytestcase(yyruleno==146);
+ /* (147) edge ::= CENTER */ yytestcase(yyruleno==147);
+ /* (148) edge ::= EDGEPT */ yytestcase(yyruleno==148);
+ /* (149) edge ::= TOP */ yytestcase(yyruleno==149);
+ /* (150) edge ::= BOTTOM */ yytestcase(yyruleno==150);
+ /* (151) edge ::= START */ yytestcase(yyruleno==151);
+ /* (152) edge ::= END */ yytestcase(yyruleno==152);
+ /* (153) edge ::= RIGHT */ yytestcase(yyruleno==153);
+ /* (154) edge ::= LEFT */ yytestcase(yyruleno==154);
+ /* (155) object ::= objectname */ yytestcase(yyruleno==155);
+ 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 551 "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 3196 "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
+
+ while(1){ /* Exit by "break" */
+ assert( yypParser->yytos>=yypParser->yystack );
+ assert( yyact==yypParser->yytos->stateno );
+ yyact = yy_find_shift_action((YYCODETYPE)yymajor,yyact);
+ if( yyact >= YY_MIN_REDUCE ){
+ unsigned int yyruleno = yyact - YY_MIN_REDUCE; /* Reduce by this rule */
+#ifndef NDEBUG
+ assert( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) );
+ if( yyTraceFILE ){
+ int 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",
+ yypParser->yytos[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( yypParser->yytos>=yypParser->yystackEnd ){
+ if( yyGrowStack(yypParser) ){
+ yyStackOverflow(yypParser);
+ break;
+ }
+ }
+ }
+ yyact = yy_reduce(yypParser,yyruleno,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);
+ if( yyact<=YY_MAX_SHIFTREDUCE ) break;
+ 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
+ }
+ }
+#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 798 "pikchr.y"
+
+
+
+/* Chart of the 148 official CSS color names with their
+** corresponding RGB values thru Color Module Level 4:
+** https://developer.mozilla.org/en-US/docs/Web/CSS/color_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 },
+ { "DarkGrey", 0xa9a9a9 },
+ { "DarkKhaki", 0xbdb76b },
+ { "DarkMagenta", 0x8b008b },
+ { "DarkOliveGreen", 0x556b2f },
+ { "DarkOrange", 0xff8c00 },
+ { "DarkOrchid", 0x9932cc },
+ { "DarkRed", 0x8b0000 },
+ { "DarkSalmon", 0xe9967a },
+ { "DarkSeaGreen", 0x8fbc8f },
+ { "DarkSlateBlue", 0x483d8b },
+ { "DarkSlateGray", 0x2f4f4f },
+ { "DarkSlateGrey", 0x2f4f4f },
+ { "DarkTurquoise", 0x00ced1 },
+ { "DarkViolet", 0x9400d3 },
+ { "DeepPink", 0xff1493 },
+ { "DeepSkyBlue", 0x00bfff },
+ { "DimGray", 0x696969 },
+ { "DimGrey", 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 },
+ { "Grey", 0x808080 },
+ { "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 },
+ { "LightGrey", 0xd3d3d3 },
+ { "LightPink", 0xffb6c1 },
+ { "LightSalmon", 0xffa07a },
+ { "LightSeaGreen", 0x20b2aa },
+ { "LightSkyBlue", 0x87cefa },
+ { "LightSlateGray", 0x778899 },
+ { "LightSlateGrey", 0x778899 },
+ { "LightSteelBlue", 0xb0c4de },
+ { "LightYellow", 0xffffe0 },
+ { "Lime", 0x00ff00 },
+ { "LimeGreen", 0x32cd32 },
+ { "Linen", 0xfaf0e6 },
+ { "Magenta", 0xff00ff },
+ { "Maroon", 0x800000 },
+ { "MediumAquamarine", 0x66cdaa },
+ { "MediumBlue", 0x0000cd },
+ { "MediumOrchid", 0xba55d3 },
+ { "MediumPurple", 0x9370db },
+ { "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 },
+ { "RebeccaPurple", 0x663399 },
+ { "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 },
+ { "SlateGrey", 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 },
+ { "diamondht", 0.75 },
+ { "diamondwid", 1.0 },
+ { "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, PObj *pObj){
+ pObj->w = pik_value(p, "arcrad",6,0);
+ pObj->h = pObj->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){
+ 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*dy;
+ m.y += 0.5*dx;
+ }else{
+ m.x += 0.5*dy;
+ m.y -= 0.5*dx;
+ }
+ return m;
+}
+static void arcCheck(Pik *p, PObj *pObj){
+ PPoint f, m, t;
+ PNum sw;
+ int i;
+ if( p->nTPath>2 ){
+ pik_error(p, &pObj->errTok, "arc geometry error");
+ return;
+ }
+ f = p->aTPath[0];
+ t = p->aTPath[1];
+ m = arcControlPoint(pObj->cw, f, t);
+ sw = pObj->sw;
+ for(i=1; i<16; i++){
+ PNum t1, t2, a, b, c, x, y;
+ t1 = 0.0625*i;
+ t2 = 1.0 - t1;
+ a = t2*t2;
+ b = 2*t1*t2;
+ c = t1*t1;
+ x = a*f.x + b*m.x + c*t.x;
+ y = a*f.y + b*m.y + c*t.y;
+ pik_bbox_addellipse(&pObj->bbox, x, y, sw, sw);
+ }
+}
+static void arcRender(Pik *p, PObj *pObj){
+ PPoint f, m, t;
+ if( pObj->nPath<2 ) return;
+ if( pObj->sw<0.0 ) return;
+ f = pObj->aPath[0];
+ t = pObj->aPath[1];
+ m = arcControlPoint(pObj->cw,f,t);
+ if( pObj->larrow ){
+ pik_draw_arrowhead(p,&m,&f,pObj);
+ }
+ if( pObj->rarrow ){
+ pik_draw_arrowhead(p,&m,&t,pObj);
+ }
+ 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,pObj,0);
+ pik_append(p,"\" />\n", -1);
+
+ pik_append_txt(p, pObj, 0);
+}
+
+
+/* Methods for the "arrow" class */
+static void arrowInit(Pik *p, PObj *pObj){
+ pObj->w = pik_value(p, "linewid",7,0);
+ pObj->h = pik_value(p, "lineht",6,0);
+ pObj->rad = pik_value(p, "linerad",7,0);
+ pObj->rarrow = 1;
+}
+
+/* Methods for the "box" class */
+static void boxInit(Pik *p, PObj *pObj){
+ pObj->w = pik_value(p, "boxwid",6,0);
+ pObj->h = pik_value(p, "boxht",5,0);
+ pObj->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, PObj *pObj, int cp){
+ PPoint pt = cZeroPoint;
+ PNum w2 = 0.5*pObj->w;
+ PNum h2 = 0.5*pObj->h;
+ PNum rad = pObj->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;
+ }
+ switch( cp ){
+ case CP_C: 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;
+ default: assert(0);
+ }
+ UNUSED_PARAMETER(p);
+ return pt;
+}
+static PPoint boxChop(Pik *p, PObj *pObj, PPoint *pPt){
+ PNum dx, dy;
+ int cp = CP_C;
+ PPoint chop = pObj->ptAt;
+ if( pObj->w<=0.0 ) return chop;
+ if( pObj->h<=0.0 ) return chop;
+ dx = (pPt->x - pObj->ptAt.x)*pObj->h/pObj->w;
+ dy = (pPt->y - pObj->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 = pObj->type->xOffset(p,pObj,cp);
+ chop.x += pObj->ptAt.x;
+ chop.y += pObj->ptAt.y;
+ return chop;
+}
+static void boxFit(Pik *p, PObj *pObj, PNum w, PNum h){
+ if( w>0 ) pObj->w = w;
+ if( h>0 ) pObj->h = h;
+ UNUSED_PARAMETER(p);
+}
+static void boxRender(Pik *p, PObj *pObj){
+ PNum w2 = 0.5*pObj->w;
+ PNum h2 = 0.5*pObj->h;
+ PNum rad = pObj->rad;
+ PPoint pt = pObj->ptAt;
+ if( pObj->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,pObj,3);
+ pik_append(p,"\" />\n", -1);
+ }
+ pik_append_txt(p, pObj, 0);
+}
+
+/* Methods for the "circle" class */
+static void circleInit(Pik *p, PObj *pObj){
+ pObj->w = pik_value(p, "circlerad",9,0)*2;
+ pObj->h = pObj->w;
+ pObj->rad = 0.5*pObj->w;
+}
+static void circleNumProp(Pik *p, PObj *pObj, 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_DIAMETER:
+ case T_RADIUS:
+ pObj->w = pObj->h = 2.0*pObj->rad;
+ break;
+ case T_WIDTH:
+ pObj->h = pObj->w;
+ pObj->rad = 0.5*pObj->w;
+ break;
+ case T_HEIGHT:
+ pObj->w = pObj->h;
+ pObj->rad = 0.5*pObj->w;
+ break;
+ }
+ UNUSED_PARAMETER(p);
+}
+static PPoint circleChop(Pik *p, PObj *pObj, PPoint *pPt){
+ PPoint chop;
+ PNum dx = pPt->x - pObj->ptAt.x;
+ PNum dy = pPt->y - pObj->ptAt.y;
+ PNum dist = hypot(dx,dy);
+ if( dist<pObj->rad || dist<=0 ) return pObj->ptAt;
+ chop.x = pObj->ptAt.x + dx*pObj->rad/dist;
+ chop.y = pObj->ptAt.y + dy*pObj->rad/dist;
+ UNUSED_PARAMETER(p);
+ return chop;
+}
+static void circleFit(Pik *p, PObj *pObj, PNum w, PNum h){
+ PNum mx = 0.0;
+ if( w>0 ) mx = w;
+ if( h>mx ) mx = h;
+ if( w*h>0 && (w*w + h*h) > mx*mx ){
+ mx = hypot(w,h);
+ }
+ if( mx>0.0 ){
+ pObj->rad = 0.5*mx;
+ pObj->w = pObj->h = mx;
+ }
+ UNUSED_PARAMETER(p);
+}
+
+static void circleRender(Pik *p, PObj *pObj){
+ PNum r = pObj->rad;
+ PPoint pt = pObj->ptAt;
+ if( pObj->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,pObj,3);
+ pik_append(p,"\" />\n", -1);
+ }
+ pik_append_txt(p, pObj, 0);
+}
+
+/* Methods for the "cylinder" class */
+static void cylinderInit(Pik *p, PObj *pObj){
+ pObj->w = pik_value(p, "cylwid",6,0);
+ pObj->h = pik_value(p, "cylht",5,0);
+ pObj->rad = pik_value(p, "cylrad",6,0); /* Minor radius of ellipses */
+}
+static void cylinderFit(Pik *p, PObj *pObj, PNum w, PNum h){
+ if( w>0 ) pObj->w = w;
+ if( h>0 ) pObj->h = h + 0.25*pObj->rad + pObj->sw;
+ UNUSED_PARAMETER(p);
+}
+static void cylinderRender(Pik *p, PObj *pObj){
+ PNum w2 = 0.5*pObj->w;
+ PNum h2 = 0.5*pObj->h;
+ PNum rad = pObj->rad;
+ PPoint pt = pObj->ptAt;
+ if( pObj->sw>=0.0 ){
+ if( rad>h2 ){
+ rad = h2;
+ }else if( rad<0 ){
+ rad = 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,pObj,3);
+ pik_append(p,"\" />\n", -1);
+ }
+ pik_append_txt(p, pObj, 0);
+}
+static PPoint cylinderOffset(Pik *p, PObj *pObj, int cp){
+ PPoint pt = cZeroPoint;
+ PNum w2 = pObj->w*0.5;
+ PNum h1 = pObj->h*0.5;
+ PNum h2 = h1 - pObj->rad;
+ switch( cp ){
+ case CP_C: 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;
+ default: assert(0);
+ }
+ UNUSED_PARAMETER(p);
+ return pt;
+}
+
+/* Methods for the "dot" class */
+static void dotInit(Pik *p, PObj *pObj){
+ pObj->rad = pik_value(p, "dotrad",6,0);
+ pObj->h = pObj->w = pObj->rad*6;
+ pObj->fill = pObj->color;
+}
+static void dotNumProp(Pik *p, PObj *pObj, PToken *pId){
+ switch( pId->eType ){
+ case T_COLOR:
+ pObj->fill = pObj->color;
+ break;
+ case T_FILL:
+ pObj->color = pObj->fill;
+ break;
+ }
+ UNUSED_PARAMETER(p);
+}
+static void dotCheck(Pik *p, PObj *pObj){
+ pObj->w = pObj->h = 0;
+ pik_bbox_addellipse(&pObj->bbox, pObj->ptAt.x, pObj->ptAt.y,
+ pObj->rad, pObj->rad);
+ UNUSED_PARAMETER(p);
+}
+static PPoint dotOffset(Pik *p, PObj *pObj, int cp){
+ UNUSED_PARAMETER(p);
+ UNUSED_PARAMETER(pObj);
+ UNUSED_PARAMETER(cp);
+ return cZeroPoint;
+}
+static void dotRender(Pik *p, PObj *pObj){
+ PNum r = pObj->rad;
+ PPoint pt = pObj->ptAt;
+ if( pObj->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,pObj,2);
+ pik_append(p,"\" />\n", -1);
+ }
+ pik_append_txt(p, pObj, 0);
+}
+
+/* Methods for the "diamond" class */
+static void diamondInit(Pik *p, PObj *pObj){
+ pObj->w = pik_value(p, "diamondwid",10,0);
+ pObj->h = pik_value(p, "diamondht",9,0);
+ pObj->bAltAutoFit = 1;
+}
+/* Return offset from the center of the box to the compass point
+** given by parameter cp */
+static PPoint diamondOffset(Pik *p, PObj *pObj, int cp){
+ PPoint pt = cZeroPoint;
+ PNum w2 = 0.5*pObj->w;
+ PNum w4 = 0.25*pObj->w;
+ PNum h2 = 0.5*pObj->h;
+ PNum h4 = 0.25*pObj->h;
+ switch( cp ){
+ case CP_C: break;
+ case CP_N: pt.x = 0.0; pt.y = h2; break;
+ case CP_NE: pt.x = w4; pt.y = h4; break;
+ case CP_E: pt.x = w2; pt.y = 0.0; break;
+ case CP_SE: pt.x = w4; pt.y = -h4; break;
+ case CP_S: pt.x = 0.0; pt.y = -h2; break;
+ case CP_SW: pt.x = -w4; pt.y = -h4; break;
+ case CP_W: pt.x = -w2; pt.y = 0.0; break;
+ case CP_NW: pt.x = -w4; pt.y = h4; break;
+ default: assert(0);
+ }
+ UNUSED_PARAMETER(p);
+ return pt;
+}
+static void diamondFit(Pik *p, PObj *pObj, PNum w, PNum h){
+ if( pObj->w<=0 ) pObj->w = w*1.5;
+ if( pObj->h<=0 ) pObj->h = h*1.5;
+ if( pObj->w>0 && pObj->h>0 ){
+ PNum x = pObj->w*h/pObj->h + w;
+ PNum y = pObj->h*x/pObj->w;
+ pObj->w = x;
+ pObj->h = y;
+ }
+ UNUSED_PARAMETER(p);
+}
+static void diamondRender(Pik *p, PObj *pObj){
+ PNum w2 = 0.5*pObj->w;
+ PNum h2 = 0.5*pObj->h;
+ PPoint pt = pObj->ptAt;
+ if( pObj->sw>=0.0 ){
+ pik_append_xy(p,"<path d=\"M", pt.x-w2,pt.y);
+ pik_append_xy(p,"L", pt.x,pt.y-h2);
+ pik_append_xy(p,"L", pt.x+w2,pt.y);
+ pik_append_xy(p,"L", pt.x,pt.y+h2);
+ pik_append(p,"Z\" ",-1);
+ pik_append_style(p,pObj,3);
+ pik_append(p,"\" />\n", -1);
+ }
+ pik_append_txt(p, pObj, 0);
+}
+
+
+/* Methods for the "ellipse" class */
+static void ellipseInit(Pik *p, PObj *pObj){
+ pObj->w = pik_value(p, "ellipsewid",10,0);
+ pObj->h = pik_value(p, "ellipseht",9,0);
+}
+static PPoint ellipseChop(Pik *p, PObj *pObj, PPoint *pPt){
+ PPoint chop;
+ PNum s, dq, dist;
+ PNum dx = pPt->x - pObj->ptAt.x;
+ PNum dy = pPt->y - pObj->ptAt.y;
+ if( pObj->w<=0.0 ) return pObj->ptAt;
+ if( pObj->h<=0.0 ) return pObj->ptAt;
+ s = pObj->h/pObj->w;
+ dq = dx*s;
+ dist = hypot(dq,dy);
+ if( dist<pObj->h ) return pObj->ptAt;
+ chop.x = pObj->ptAt.x + 0.5*dq*pObj->h/(dist*s);
+ chop.y = pObj->ptAt.y + 0.5*dy*pObj->h/dist;
+ UNUSED_PARAMETER(p);
+ return chop;
+}
+static PPoint ellipseOffset(Pik *p, PObj *pObj, int cp){
+ PPoint pt = cZeroPoint;
+ PNum w = pObj->w*0.5;
+ PNum w2 = w*0.70710678118654747608;
+ PNum h = pObj->h*0.5;
+ PNum h2 = h*0.70710678118654747608;
+ switch( cp ){
+ case CP_C: 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;
+ default: assert(0);
+ }
+ UNUSED_PARAMETER(p);
+ return pt;
+}
+static void ellipseRender(Pik *p, PObj *pObj){
+ PNum w = pObj->w;
+ PNum h = pObj->h;
+ PPoint pt = pObj->ptAt;
+ if( pObj->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,pObj,3);
+ pik_append(p,"\" />\n", -1);
+ }
+ pik_append_txt(p, pObj, 0);
+}
+
+/* Methods for the "file" object */
+static void fileInit(Pik *p, PObj *pObj){
+ pObj->w = pik_value(p, "filewid",7,0);
+ pObj->h = pik_value(p, "fileht",6,0);
+ pObj->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, PObj *pObj, int cp){
+ PPoint pt = cZeroPoint;
+ PNum w2 = 0.5*pObj->w;
+ PNum h2 = 0.5*pObj->h;
+ PNum rx = pObj->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: 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;
+ default: assert(0);
+ }
+ UNUSED_PARAMETER(p);
+ return pt;
+}
+static void fileFit(Pik *p, PObj *pObj, PNum w, PNum h){
+ if( w>0 ) pObj->w = w;
+ if( h>0 ) pObj->h = h + 2*pObj->rad;
+ UNUSED_PARAMETER(p);
+}
+static void fileRender(Pik *p, PObj *pObj){
+ PNum w2 = 0.5*pObj->w;
+ PNum h2 = 0.5*pObj->h;
+ PNum rad = pObj->rad;
+ PPoint pt = pObj->ptAt;
+ PNum mn = w2<h2 ? w2 : h2;
+ if( rad>mn ) rad = mn;
+ if( rad<mn*0.25 ) rad = mn*0.25;
+ if( pObj->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,pObj,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,pObj,0);
+ pik_append(p,"\" />\n",-1);
+ }
+ pik_append_txt(p, pObj, 0);
+}
+
+
+/* Methods for the "line" class */
+static void lineInit(Pik *p, PObj *pObj){
+ pObj->w = pik_value(p, "linewid",7,0);
+ pObj->h = pik_value(p, "lineht",6,0);
+ pObj->rad = pik_value(p, "linerad",7,0);
+}
+static PPoint lineOffset(Pik *p, PObj *pObj, 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 && !pObj->bClose ){
+ PPoint out;
+ out.x = 0.5*(pObj->ptEnter.x + pObj->ptExit.x) - pObj->ptAt.x;
+ out.y = 0.5*(pObj->ptEnter.x + pObj->ptExit.y) - pObj->ptAt.y;
+ return out;
+ }
+#endif
+ return boxOffset(p,pObj,cp);
+}
+static void lineRender(Pik *p, PObj *pObj){
+ int i;
+ if( pObj->sw>0.0 ){
+ const char *z = "<path d=\"M";
+ int n = pObj->nPath;
+ if( pObj->larrow ){
+ pik_draw_arrowhead(p,&pObj->aPath[1],&pObj->aPath[0],pObj);
+ }
+ if( pObj->rarrow ){
+ pik_draw_arrowhead(p,&pObj->aPath[n-2],&pObj->aPath[n-1],pObj);
+ }
+ for(i=0; i<pObj->nPath; i++){
+ pik_append_xy(p,z,pObj->aPath[i].x,pObj->aPath[i].y);
+ z = "L";
+ }
+ if( pObj->bClose ){
+ pik_append(p,"Z",1);
+ }else{
+ pObj->fill = -1.0;
+ }
+ pik_append(p,"\" ",-1);
+ pik_append_style(p,pObj,pObj->bClose?3:0);
+ pik_append(p,"\" />\n", -1);
+ }
+ pik_append_txt(p, pObj, 0);
+}
+
+/* Methods for the "move" class */
+static void moveInit(Pik *p, PObj *pObj){
+ pObj->w = pik_value(p, "movewid",7,0);
+ pObj->h = pObj->w;
+ pObj->fill = -1.0;
+ pObj->color = -1.0;
+ pObj->sw = -1.0;
+}
+static void moveRender(Pik *p, PObj *pObj){
+ /* No-op */
+ UNUSED_PARAMETER(p);
+ UNUSED_PARAMETER(pObj);
+}
+
+/* Methods for the "oval" class */
+static void ovalInit(Pik *p, PObj *pObj){
+ pObj->h = pik_value(p, "ovalht",6,0);
+ pObj->w = pik_value(p, "ovalwid",7,0);
+ pObj->rad = 0.5*(pObj->h<pObj->w?pObj->h:pObj->w);
+}
+static void ovalNumProp(Pik *p, PObj *pObj, PToken *pId){
+ UNUSED_PARAMETER(p);
+ UNUSED_PARAMETER(pId);
+ /* Always adjust the radius to be half of the smaller of
+ ** the width and height. */
+ pObj->rad = 0.5*(pObj->h<pObj->w?pObj->h:pObj->w);
+}
+static void ovalFit(Pik *p, PObj *pObj, PNum w, PNum h){
+ UNUSED_PARAMETER(p);
+ if( w>0 ) pObj->w = w;
+ if( h>0 ) pObj->h = h;
+ if( pObj->w<pObj->h ) pObj->w = pObj->h;
+ pObj->rad = 0.5*(pObj->h<pObj->w?pObj->h:pObj->w);
+}
+
+
+
+/* Methods for the "spline" class */
+static void splineInit(Pik *p, PObj *pObj){
+ pObj->w = pik_value(p, "linewid",7,0);
+ pObj->h = pik_value(p, "lineht",6,0);
+ pObj->rad = 1000;
+}
+/* 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, PObj *pObj, PNum r){
+ int i;
+ int n = pObj->nPath;
+ const PPoint *a = pObj->aPath;
+ PPoint m;
+ PPoint an = a[n-1];
+ int isMid = 0;
+ int iLast = pObj->bClose ? n : n-1;
+
+ 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<iLast; i++){
+ an = i<n-1 ? a[i+1] : a[0];
+ m = radiusMidpoint(an,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],an,r, &isMid);
+ pik_append_xy(p," L ",m.x,m.y);
+ }
+ }
+ pik_append_xy(p," L ",an.x,an.y);
+ if( pObj->bClose ){
+ pik_append(p,"Z",1);
+ }else{
+ pObj->fill = -1.0;
+ }
+ pik_append(p,"\" ",-1);
+ pik_append_style(p,pObj,pObj->bClose?3:0);
+ pik_append(p,"\" />\n", -1);
+}
+static void splineRender(Pik *p, PObj *pObj){
+ if( pObj->sw>0.0 ){
+ int n = pObj->nPath;
+ PNum r = pObj->rad;
+ if( n<3 || r<=0.0 ){
+ lineRender(p,pObj);
+ return;
+ }
+ if( pObj->larrow ){
+ pik_draw_arrowhead(p,&pObj->aPath[1],&pObj->aPath[0],pObj);
+ }
+ if( pObj->rarrow ){
+ pik_draw_arrowhead(p,&pObj->aPath[n-2],&pObj->aPath[n-1],pObj);
+ }
+ radiusPath(p,pObj,pObj->rad);
+ }
+ pik_append_txt(p, pObj, 0);
+}
+
+
+/* Methods for the "text" class */
+static void textInit(Pik *p, PObj *pObj){
+ pik_value(p, "textwid",7,0);
+ pik_value(p, "textht",6,0);
+ pObj->sw = 0.0;
+}
+static PPoint textOffset(Pik *p, PObj *pObj, int cp){
+ /* Automatically slim-down the width and height of text
+ ** statements so that the bounding box tightly encloses the text,
+ ** then get boxOffset() to do the offset computation.
+ */
+ pik_size_to_fit(p, pObj, &pObj->errTok,3);
+ return boxOffset(p, pObj, cp);
+}
+static void textRender(Pik *p, PObj *pObj){
+ pik_append_txt(p, pObj, 0);
+}
+
+
+/* Methods for the "sublist" class */
+static void sublistInit(Pik *p, PObj *pObj){
+ PList *pList = pObj->pSublist;
+ int i;
+ UNUSED_PARAMETER(p);
+ pik_bbox_init(&pObj->bbox);
+ for(i=0; i<pList->n; i++){
+ pik_bbox_addbox(&pObj->bbox, &pList->a[i]->bbox);
+ }
+ pObj->w = pObj->bbox.ne.x - pObj->bbox.sw.x;
+ pObj->h = pObj->bbox.ne.y - pObj->bbox.sw.y;
+ pObj->ptAt.x = 0.5*(pObj->bbox.ne.x + pObj->bbox.sw.x);
+ pObj->ptAt.y = 0.5*(pObj->bbox.ne.y + pObj->bbox.sw.y);
+ pObj->mCalc |= A_WIDTH|A_HEIGHT|A_RADIUS;
+}
+
+
+/*
+** The following array holds all the different kinds of objects.
+** The special [] object is 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 */ "diamond",
+ /* isline */ 0,
+ /* eJust */ 0,
+ /* xInit */ diamondInit,
+ /* xNumProp */ 0,
+ /* xCheck */ 0,
+ /* xChop */ boxChop,
+ /* xOffset */ diamondOffset,
+ /* xFit */ diamondFit,
+ /* xRender */ diamondRender
+ },
+ { /* 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 */ boxFit,
+ /* 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 */ textRender
+ },
+};
+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, PObj *pObj){
+ PNum dx = t->x - f->x;
+ PNum dy = t->y - f->y;
+ PNum dist = hypot(dx,dy);
+ PNum h = p->hArrow * pObj->sw;
+ PNum w = p->wArrow * pObj->sw;
+ PNum e1, ddx, ddy;
+ PNum bx, by;
+ if( pObj->color<0.0 ) return;
+ if( pObj->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:",pObj->color,"\"/>\n",0);
+ pik_chop(f,t,h/2);
+}
+
+/*
+** Compute the relative offset to an edge location from the reference for a
+** an statement.
+*/
+static PPoint pik_elem_offset(Pik *p, PObj *pObj, int cp){
+ return pObj->type->xOffset(p, pObj, 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 = nNew;
+ }
+ memcpy(p->zOut+p->nOut, zText, n);
+ p->nOut += n;
+ p->zOut[p->nOut] = 0;
+}
+
+/*
+** Given a string and its length, returns true if the string begins
+** with a construct which syntactically matches an HTML entity escape
+** sequence (without checking for whether it's a known entity). Always
+** returns false if zText[0] is false or n<4. Entities match the
+** equivalent of the regexes `&#[0-9]{2,};` and
+** `&[a-zA-Z][a-zA-Z0-9]+;`.
+*/
+static int pik_isentity(char const * zText, int n){
+ int i = 0;
+ if( n<4 || '&'!=zText[0] ) return 0;
+ n--;
+ zText++;
+ if( '#'==zText[0] ){
+ zText++;
+ n--;
+ for(i=0; i<n; i++){
+ if( i>1 && ';'==zText[i] ) return 1;
+ else if( zText[i]<'0' || zText[i]>'9' ) return 0;
+ /* Note that &#nn; values nn<32d are not legal entities. */
+ }
+ }else{
+ for(i=0; i<n; i++){
+ if( i>1 && ';'==zText[i] ) return 1;
+ else if( i>0 && zText[i]>='0' && zText[i]<='9' ){
+ continue;
+ }else if( zText[i]<'A' || zText[i]>'z'
+ || (zText[i]>'Z' && zText[i]<'a') ) return 0;
+ }
+ }
+ return 0;
+}
+
+/*
+** Append text to zOut with HTML characters escaped.
+**
+** * The space character is changed into non-breaking space (U+00a0)
+** 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 = 0;
+ 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, "\302\240;", 2); break; }
+ case '&':
+ if( pik_isentity(zText+i, n-i) ){ pik_append(p, "&", 1); }
+ else { pik_append(p, "&amp;", 5); }
+ }
+ i++;
+ n -= i;
+ zText += i;
+ i = 0;
+ }
+}
+
+/*
+** Append error message text. This is either a raw append, or an append
+** with HTML escapes, depending on whether the PIKCHR_PLAINTEXT_ERRORS flag
+** is set.
+*/
+static void pik_append_errtxt(Pik *p, const char *zText, int n){
+ if( p->mFlags & PIKCHR_PLAINTEXT_ERRORS ){
+ pik_append(p, zText, n);
+ }else{
+ pik_append_text(p, zText, n, 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);
+}
+
+/*
+** Invert the RGB color so that it is appropriate for dark mode.
+** Variable x hold the initial color. The color is intended for use
+** as a background color if isBg is true, and as a foreground color
+** if isBg is false.
+*/
+static int pik_color_to_dark_mode(int x, int isBg){
+ int r, g, b;
+ int mn, mx;
+ x = 0xffffff - x;
+ r = (x>>16) & 0xff;
+ g = (x>>8) & 0xff;
+ b = x & 0xff;
+ mx = r;
+ if( g>mx ) mx = g;
+ if( b>mx ) mx = b;
+ mn = r;
+ if( g<mn ) mn = g;
+ if( b<mn ) mn = b;
+ r = mn + (mx-r);
+ g = mn + (mx-g);
+ b = mn + (mx-b);
+ if( isBg ){
+ if( mx>127 ){
+ r = (127*r)/mx;
+ g = (127*g)/mx;
+ b = (127*b)/mx;
+ }
+ }else{
+ if( mn<128 && mx>mn ){
+ r = 127 + ((r-mn)*128)/(mx-mn);
+ g = 127 + ((g-mn)*128)/(mx-mn);
+ b = 127 + ((b-mn)*128)/(mx-mn);
+ }
+ }
+ return r*0x10000 + g*0x100 + b;
+}
+
+/* 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%g%s", z1, 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%g%s", z1, 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%g,%g", z1, p->rScale*x, 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);
+}
+
+/* Append a color specification to the output.
+**
+** In PIKCHR_DARK_MODE, the color is inverted. The "bg" flags indicates that
+** the color is intended for use as a background color if true, or as a
+** foreground color if false. The distinction only matters for color
+** inversions in PIKCHR_DARK_MODE.
+*/
+static void pik_append_clr(Pik *p,const char *z1,PNum v,const char *z2,int bg){
+ char buf[200];
+ int x = pik_round(v);
+ int r, g, b;
+ if( x==0 && p->fgcolor>0 && !bg ){
+ x = p->fgcolor;
+ }else if( bg && x>=0xffffff && p->bgcolor>0 ){
+ x = p->bgcolor;
+ }else if( p->mFlags & PIKCHR_DARK_MODE ){
+ x = pik_color_to_dark_mode(x,bg);
+ }
+ r = (x>>16) & 0xff;
+ g = (x>>8) & 0xff;
+ 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%g %g 0 0 0 %g %g",
+ p->rScale*r1, p->rScale*r2,
+ p->rScale*x, 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.
+**
+** eFill is non-zero to fill in the background, or 0 if no fill should
+** occur. Non-zero values of eFill determine the "bg" flag to pik_append_clr()
+** for cases when pObj->fill==pObj->color
+**
+** 1 fill is background, and color is foreground.
+** 2 fill and color are both foreground. (Used by "dot" objects)
+** 3 fill and color are both background. (Used by most other objs)
+*/
+static void pik_append_style(Pik *p, PObj *pObj, int eFill){
+ int clrIsBg = 0;
+ pik_append(p, " style=\"", -1);
+ if( pObj->fill>=0 && eFill ){
+ int fillIsBg = 1;
+ if( pObj->fill==pObj->color ){
+ if( eFill==2 ) fillIsBg = 0;
+ if( eFill==3 ) clrIsBg = 1;
+ }
+ pik_append_clr(p, "fill:", pObj->fill, ";", fillIsBg);
+ }else{
+ pik_append(p,"fill:none;",-1);
+ }
+ if( pObj->sw>=0.0 && pObj->color>=0.0 ){
+ PNum sw = pObj->sw;
+ pik_append_dis(p, "stroke-width:", sw, ";");
+ if( pObj->nPath>2 && pObj->rad<=pObj->sw ){
+ pik_append(p, "stroke-linejoin:round;", -1);
+ }
+ pik_append_clr(p, "stroke:",pObj->color,";",clrIsBg);
+ if( pObj->dotted>0.0 ){
+ PNum v = pObj->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( pObj->dashed>0.0 ){
+ PNum v = pObj->dashed;
+ pik_append_dis(p,"stroke-dasharray:",v,"");
+ pik_append_dis(p,",",v,";");
+ }
+ }
+}
+
+/*
+** Compute the vertical locations for all text items in the
+** object pObj. In other words, set every pObj->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(PObj *pObj){
+ int n, i;
+ PToken *aTxt;
+ n = pObj->nTxt;
+ if( n==0 ) return;
+ aTxt = pObj->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++];
+ }
+ }
+ }
+}
+
+/* Return the font scaling factor associated with the input text attribute.
+*/
+static PNum pik_font_scale(PToken *t){
+ PNum scale = 1.0;
+ if( t->eCode & TP_BIG ) scale *= 1.25;
+ if( t->eCode & TP_SMALL ) scale *= 0.8;
+ if( t->eCode & TP_XTRA ) scale *= scale;
+ return scale;
+}
+
+/* Append multiple <text> SVG elements for the text fields of the PObj.
+** Parameters:
+**
+** p The Pik object into which we are rendering
+**
+** pObj 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, PObj *pObj, PBox *pBox){
+ PNum jw; /* Justification margin relative to center */
+ PNum ha2 = 0.0; /* Height of the top row of text */
+ PNum ha1 = 0.0; /* Height of the second "above" row */
+ PNum hc = 0.0; /* Height of the center row */
+ PNum hb1 = 0.0; /* Height of the first "below" row of text */
+ PNum hb2 = 0.0; /* Height of the second "below" row */
+ PNum yBase = 0.0;
+ PNum sw = pObj->sw>=0.0 ? pObj->sw : 0;
+ int n, i, nz;
+ PNum x, y, orig_y, s;
+ const char *z;
+ PToken *aTxt;
+ unsigned allMask = 0;
+
+ if( p->nErr ) return;
+ if( pObj->nTxt==0 ) return;
+ aTxt = pObj->aTxt;
+ n = pObj->nTxt;
+ pik_txt_vertical_layout(pObj);
+ x = pObj->ptAt.x;
+ for(i=0; i<n; i++) allMask |= pObj->aTxt[i].eCode;
+ if( pObj->type->isLine ){
+ hc = sw*1.5;
+ }else if( pObj->rad>0.0 && pObj->type->xInit==cylinderInit ){
+ yBase = -0.75*pObj->rad;
+ }
+ if( allMask & TP_CENTER ){
+ for(i=0; i<n; i++){
+ if( pObj->aTxt[i].eCode & TP_CENTER ){
+ s = pik_font_scale(pObj->aTxt+i);
+ if( hc<s*p->charHeight ) hc = s*p->charHeight;
+ }
+ }
+ }
+ if( allMask & TP_ABOVE ){
+ for(i=0; i<n; i++){
+ if( pObj->aTxt[i].eCode & TP_ABOVE ){
+ s = pik_font_scale(pObj->aTxt+i)*p->charHeight;
+ if( ha1<s ) ha1 = s;
+ }
+ }
+ if( allMask & TP_ABOVE2 ){
+ for(i=0; i<n; i++){
+ if( pObj->aTxt[i].eCode & TP_ABOVE2 ){
+ s = pik_font_scale(pObj->aTxt+i)*p->charHeight;
+ if( ha2<s ) ha2 = s;
+ }
+ }
+ }
+ }
+ if( allMask & TP_BELOW ){
+ for(i=0; i<n; i++){
+ if( pObj->aTxt[i].eCode & TP_BELOW ){
+ s = pik_font_scale(pObj->aTxt+i)*p->charHeight;
+ if( hb1<s ) hb1 = s;
+ }
+ }
+ if( allMask & TP_BELOW2 ){
+ for(i=0; i<n; i++){
+ if( pObj->aTxt[i].eCode & TP_BELOW2 ){
+ s = pik_font_scale(pObj->aTxt+i)*p->charHeight;
+ if( hb2<s ) hb2 = s;
+ }
+ }
+ }
+ }
+ if( pObj->type->eJust==1 ){
+ jw = 0.5*(pObj->w - 0.5*(p->charWidth + sw));
+ }else{
+ jw = 0.0;
+ }
+ for(i=0; i<n; i++){
+ PToken *t = &aTxt[i];
+ PNum xtraFontScale = pik_font_scale(t);
+ PNum nx = 0;
+ orig_y = pObj->ptAt.y;
+ y = yBase;
+ if( t->eCode & TP_ABOVE2 ) y += 0.5*hc + ha1 + 0.5*ha2;
+ if( t->eCode & TP_ABOVE ) y += 0.5*hc + 0.5*ha1;
+ if( t->eCode & TP_BELOW ) y -= 0.5*hc + 0.5*hb1;
+ if( t->eCode & TP_BELOW2 ) y -= 0.5*hc + hb1 + 0.5*hb2;
+ 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, t->eCode & TP_MONO)*p->charWidth*xtraFontScale*0.01;
+ PNum ch = p->charHeight*0.5*xtraFontScale;
+ PNum x0, y0, x1, y1; /* Boundary of text relative to pObj->ptAt */
+ if( (t->eCode & (TP_BOLD|TP_MONO))==TP_BOLD ){
+ cw *= 1.1;
+ }
+ if( t->eCode & TP_RJUST ){
+ x0 = nx;
+ y0 = y-ch;
+ x1 = nx-cw;
+ y1 = y+ch;
+ }else if( t->eCode & TP_LJUST ){
+ x0 = nx;
+ y0 = y-ch;
+ x1 = nx+cw;
+ y1 = y+ch;
+ }else{
+ x0 = nx+cw/2;
+ y0 = y+ch;
+ x1 = nx-cw/2;
+ y1 = y-ch;
+ }
+ if( (t->eCode & TP_ALIGN)!=0 && pObj->nPath>=2 ){
+ int nn = pObj->nPath;
+ PNum dx = pObj->aPath[nn-1].x - pObj->aPath[0].x;
+ PNum dy = pObj->aPath[nn-1].y - pObj->aPath[0].y;
+ if( dx!=0 || dy!=0 ){
+ PNum dist = hypot(dx,dy);
+ PNum tt;
+ dx /= dist;
+ dy /= dist;
+ tt = dx*x0 - dy*y0;
+ y0 = dy*x0 - dx*y0;
+ x0 = tt;
+ tt = dx*x1 - dy*y1;
+ y1 = dy*x1 - dx*y1;
+ x1 = tt;
+ }
+ }
+ pik_bbox_add_xy(pBox, x+x0, orig_y+y0);
+ pik_bbox_add_xy(pBox, x+x1, orig_y+y1);
+ continue;
+ }
+ nx += x;
+ y += orig_y;
+
+ 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( t->eCode & TP_MONO ){
+ pik_append(p, " font-family=\"monospace\"", -1);
+ }
+ if( pObj->color>=0.0 ){
+ pik_append_clr(p, " fill=\"", pObj->color, "\"",0);
+ }
+ 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 && pObj->nPath>=2 ){
+ int nn = pObj->nPath;
+ PNum dx = pObj->aPath[nn-1].x - pObj->aPath[0].x;
+ PNum dy = pObj->aPath[nn-1].y - pObj->aPath[0].y;
+ if( dx!=0 || dy!=0 ){
+ 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);
+ if( t->n>=2 && t->z[0]=='"' ){
+ z = t->z+1;
+ nz = t->n-2;
+ }else{
+ z = t->z;
+ nz = t->n;
+ }
+ while( nz>0 ){
+ int j;
+ for(j=0; j<nz && z[j]!='\\'; j++){}
+ if( j ) pik_append_text(p, z, j, 0x3);
+ if( j<nz && (j+1==nz || z[j+1]=='\\') ){
+ pik_append(p, "&#92;", -1);
+ j++;
+ }
+ nz -= j+1;
+ z += j+1;
+ }
+ pik_append(p, "</text>\n", -1);
+ }
+}
+
+/*
+** Append text (that will go inside of a <pre>...</pre>) that
+** shows the context of an error token.
+*/
+static void pik_error_context(Pik *p, PToken *pErr, int nContext){
+ int iErrPt; /* Index of first byte of error from start of input */
+ int iErrCol; /* Column of the error token on its line */
+ int iStart; /* Start position of the error context */
+ int iEnd; /* End position of the error context */
+ int iLineno; /* Line number of the error */
+ int iFirstLineno; /* Line number of start of error context */
+ int i; /* Loop counter */
+ int iBump = 0; /* Bump the location of the error cursor */
+ char zLineno[24]; /* Buffer in which to generate line numbers */
+
+ iErrPt = (int)(pErr->z - p->sIn.z);
+ if( iErrPt>=(int)p->sIn.n ){
+ iErrPt = p->sIn.n-1;
+ iBump = 1;
+ }else{
+ while( iErrPt>0 && (p->sIn.z[iErrPt]=='\n' || p->sIn.z[iErrPt]=='\r') ){
+ iErrPt--;
+ iBump = 1;
+ }
+ }
+ iLineno = 1;
+ for(i=0; i<iErrPt; i++){
+ if( p->sIn.z[i]=='\n' ){
+ iLineno++;
+ }
+ }
+ iStart = 0;
+ iFirstLineno = 1;
+ while( iFirstLineno+nContext<iLineno ){
+ while( p->sIn.z[iStart]!='\n' ){ iStart++; }
+ iStart++;
+ iFirstLineno++;
+ }
+ for(iEnd=iErrPt; p->sIn.z[iEnd]!=0 && p->sIn.z[iEnd]!='\n'; iEnd++){}
+ i = iStart;
+ while( iFirstLineno<=iLineno ){
+ snprintf(zLineno,sizeof(zLineno)-1,"/* %4d */ ", iFirstLineno++);
+ zLineno[sizeof(zLineno)-1] = 0;
+ pik_append(p, zLineno, -1);
+ for(i=iStart; p->sIn.z[i]!=0 && p->sIn.z[i]!='\n'; i++){}
+ pik_append_errtxt(p, p->sIn.z+iStart, i-iStart);
+ iStart = i+1;
+ pik_append(p, "\n", 1);
+ }
+ for(iErrCol=0, i=iErrPt; i>0 && p->sIn.z[i]!='\n'; iErrCol++, i--){}
+ for(i=0; i<iErrCol+11+iBump; i++){ pik_append(p, " ", 1); }
+ for(i=0; i<(int)pErr->n; i++) pik_append(p, "^", 1);
+ pik_append(p, "\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;
+ if( p==0 ) return;
+ if( p->nErr ) return;
+ p->nErr++;
+ if( zMsg==0 ){
+ if( p->mFlags & PIKCHR_PLAINTEXT_ERRORS ){
+ pik_append(p, "\nOut of memory\n", -1);
+ }else{
+ pik_append(p, "\n<div><p>Out of memory</p></div>\n", -1);
+ }
+ return;
+ }
+ if( pErr==0 ){
+ pik_append(p, "\n", 1);
+ pik_append_errtxt(p, zMsg, -1);
+ return;
+ }
+ if( (p->mFlags & PIKCHR_PLAINTEXT_ERRORS)==0 ){
+ pik_append(p, "<div><pre>\n", -1);
+ }
+ pik_error_context(p, pErr, 5);
+ pik_append(p, "ERROR: ", -1);
+ pik_append_errtxt(p, zMsg, -1);
+ pik_append(p, "\n", 1);
+ for(i=p->nCtx-1; i>=0; i--){
+ pik_append(p, "Called from:\n", -1);
+ pik_error_context(p, &p->aCtx[i], 0);
+ }
+ if( (p->mFlags & PIKCHR_PLAINTEXT_ERRORS)==0 ){
+ pik_append(p, "</pre></div>\n", -1);
+ }
+}
+
+/*
+** Process an "assert( e1 == e2 )" statement. Always return NULL.
+*/
+static PObj *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 PObj *pik_position_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 objects */
+static void pik_elist_free(Pik *p, PList *pList){
+ int i;
+ if( pList==0 ) return;
+ for(i=0; i<pList->n; i++){
+ pik_elem_free(p, pList->a[i]);
+ }
+ free(pList->a);
+ free(pList);
+ return;
+}
+
+/* Free a single object, and its substructure */
+static void pik_elem_free(Pik *p, PObj *pObj){
+ if( pObj==0 ) return;
+ free(pObj->zName);
+ pik_elist_free(p, pObj->pSublist);
+ free(pObj->aPath);
+ free(pObj);
+}
+
+/* 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;
+}
+
+/*
+** Compute the distance between two points
+*/
+static PNum pik_dist(PPoint *pA, PPoint *pB){
+ PNum dx, dy;
+ dx = pB->x - pA->x;
+ dy = pB->y - pA->y;
+ return hypot(dx,dy);
+}
+
+/* Return true if a bounding box is empty.
+*/
+static int pik_bbox_isempty(PBox *p){
+ return p->sw.x>p->ne.x;
+}
+
+/* Return true if point pPt is contained within the bounding box pBox
+*/
+static int pik_bbox_contains_point(PBox *pBox, PPoint *pPt){
+ if( pik_bbox_isempty(pBox) ) return 0;
+ if( pPt->x < pBox->sw.x ) return 0;
+ if( pPt->x > pBox->ne.x ) return 0;
+ if( pPt->y < pBox->sw.y ) return 0;
+ if( pPt->y > pBox->ne.y ) return 0;
+ return 1;
+}
+
+/* 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 object onto the end of an object list. The
+** object list is created if it does not already exist. Return
+** the new object list.
+*/
+static PList *pik_elist_append(Pik *p, PList *pList, PObj *pObj){
+ if( pObj==0 ) return pList;
+ if( pList==0 ){
+ pList = malloc(sizeof(*pList));
+ if( pList==0 ){
+ pik_error(p, 0, 0);
+ pik_elem_free(p, pObj);
+ return 0;
+ }
+ memset(pList, 0, sizeof(*pList));
+ }
+ if( pList->n>=pList->nAlloc ){
+ int nNew = (pList->n+5)*2;
+ PObj **pNew = realloc(pList->a, sizeof(PObj*)*nNew);
+ if( pNew==0 ){
+ pik_error(p, 0, 0);
+ pik_elem_free(p, pObj);
+ return pList;
+ }
+ pList->nAlloc = nNew;
+ pList->a = pNew;
+ }
+ pList->a[pList->n++] = pObj;
+ p->list = pList;
+ return pList;
+}
+
+/* Convert an object 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 PObj object.
+**
+** If pId!=0 then pId is an identifier that defines the object 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 PObj *pik_elem_new(Pik *p, PToken *pId, PToken *pStr,PList *pSublist){
+ PObj *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;
+ pNew->eWith = CP_C;
+ }else{
+ PObj *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 = pik_value_int(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 ){
+ const PClass *pClass;
+ pNew->errTok = *pId;
+ 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 object type");
+ pik_elem_free(p, pNew);
+ return 0;
+ }
+ pNew->type = &noopClass;
+ pNew->ptExit = pNew->ptEnter = pNew->ptAt;
+ return pNew;
+}
+
+/*
+** If the ID token in the argument is the name of a macro, return
+** the PMacro object for that macro
+*/
+static PMacro *pik_find_macro(Pik *p, PToken *pId){
+ PMacro *pMac;
+ for(pMac = p->pMacros; pMac; pMac=pMac->pNext){
+ if( pMac->macroName.n==pId->n
+ && strncmp(pMac->macroName.z,pId->z,pId->n)==0
+ ){
+ return pMac;
+ }
+ }
+ return 0;
+}
+
+/* Add a new macro
+*/
+static void pik_add_macro(
+ Pik *p, /* Current Pikchr diagram */
+ PToken *pId, /* The ID token that defines the macro name */
+ PToken *pCode /* Macro body inside of {...} */
+){
+ PMacro *pNew = pik_find_macro(p, pId);
+ if( pNew==0 ){
+ pNew = malloc( sizeof(*pNew) );
+ if( pNew==0 ){
+ pik_error(p, 0, 0);
+ return;
+ }
+ pNew->pNext = p->pMacros;
+ p->pMacros = pNew;
+ pNew->macroName = *pId;
+ }
+ pNew->macroBody.z = pCode->z+1;
+ pNew->macroBody.n = pCode->n-2;
+ pNew->inUse = 0;
+}
+
+
+/*
+** Set the output direction and exit point for an object
+*/
+static void pik_elem_set_exit(PObj *pObj, int eDir){
+ assert( ValidDir(eDir) );
+ pObj->outDir = eDir;
+ if( !pObj->type->isLine || pObj->bClose ){
+ pObj->ptExit = pObj->ptAt;
+ switch( pObj->outDir ){
+ default: pObj->ptExit.x += pObj->w*0.5; break;
+ case DIR_LEFT: pObj->ptExit.x -= pObj->w*0.5; break;
+ case DIR_UP: pObj->ptExit.y += pObj->h*0.5; break;
+ case DIR_DOWN: pObj->ptExit.y -= pObj->h*0.5; break;
+ }
+ }
+}
+
+/* Change the layout direction.
+*/
+static void pik_set_direction(Pik *p, int eDir){
+ assert( ValidDir(eDir) );
+ p->eDir = (unsigned char)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 object (and within its
+** substructure) by dx, dy
+*/
+static void pik_elem_move(PObj *pObj, PNum dx, PNum dy){
+ int i;
+ pObj->ptAt.x += dx;
+ pObj->ptAt.y += dy;
+ pObj->ptEnter.x += dx;
+ pObj->ptEnter.y += dy;
+ pObj->ptExit.x += dx;
+ pObj->ptExit.y += dy;
+ pObj->bbox.ne.x += dx;
+ pObj->bbox.ne.y += dy;
+ pObj->bbox.sw.x += dx;
+ pObj->bbox.sw.y += dy;
+ for(i=0; i<pObj->nPath; i++){
+ pObj->aPath[i].x += dx;
+ pObj->aPath[i].y += dy;
+ }
+ if( pObj->pSublist ){
+ pik_elist_move(pObj->pSublist, dx, dy);
+ }
+}
+static void pik_elist_move(PList *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 pObj so that the same object or conflicting
+** objects 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) */
+ PObj *pObj, /* The object under construction */
+ PToken *pId, /* Make the error point to this token */
+ int mThis /* Value we are trying to set */
+){
+ if( pObj->mProp & mThis ){
+ pik_error(p, pId, "value is already set");
+ return 1;
+ }
+ if( pObj->mCalc & mThis ){
+ pik_error(p, pId, "value already fixed by prior constraints");
+ return 1;
+ }
+ pObj->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){
+ PObj *pObj = p->cur;
+ switch( pId->eType ){
+ case T_HEIGHT:
+ if( pik_param_ok(p, pObj, pId, A_HEIGHT) ) return;
+ pObj->h = pObj->h*pVal->rRel + pVal->rAbs;
+ break;
+ case T_WIDTH:
+ if( pik_param_ok(p, pObj, pId, A_WIDTH) ) return;
+ pObj->w = pObj->w*pVal->rRel + pVal->rAbs;
+ break;
+ case T_RADIUS:
+ if( pik_param_ok(p, pObj, pId, A_RADIUS) ) return;
+ pObj->rad = pObj->rad*pVal->rRel + pVal->rAbs;
+ break;
+ case T_DIAMETER:
+ if( pik_param_ok(p, pObj, pId, A_RADIUS) ) return;
+ pObj->rad = pObj->rad*pVal->rRel + 0.5*pVal->rAbs; /* diam it 2x rad */
+ break;
+ case T_THICKNESS:
+ if( pik_param_ok(p, pObj, pId, A_THICKNESS) ) return;
+ pObj->sw = pObj->sw*pVal->rRel + pVal->rAbs;
+ break;
+ }
+ if( pObj->type->xNumProp ){
+ pObj->type->xNumProp(p, pObj, pId);
+ }
+ return;
+}
+
+/*
+** Set a color property. The argument is an RGB value.
+*/
+void pik_set_clrprop(Pik *p, PToken *pId, PNum rClr){
+ PObj *pObj = p->cur;
+ switch( pId->eType ){
+ case T_FILL:
+ if( pik_param_ok(p, pObj, pId, A_FILL) ) return;
+ pObj->fill = rClr;
+ break;
+ case T_COLOR:
+ if( pik_param_ok(p, pObj, pId, A_COLOR) ) return;
+ pObj->color = rClr;
+ break;
+ }
+ if( pObj->type->xNumProp ){
+ pObj->type->xNumProp(p, pObj, 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){
+ PObj *pObj = p->cur;
+ PNum v;
+ switch( pId->eType ){
+ case T_DOTTED: {
+ v = pVal==0 ? pik_value(p,"dashwid",7,0) : *pVal;
+ pObj->dotted = v;
+ pObj->dashed = 0.0;
+ break;
+ }
+ case T_DASHED: {
+ v = pVal==0 ? pik_value(p,"dashwid",7,0) : *pVal;
+ pObj->dashed = v;
+ pObj->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, PObj *pObj){
+ int n;
+ if( !pObj->type->isLine ){
+ pik_error(p, pToken, "use with line-oriented objects only");
+ return;
+ }
+ n = p->nTPath - 1;
+ if( n<1 && (pObj->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 object. "up 0.5", or "left 3", or "down"
+** or "down 50%".
+*/
+static void pik_add_direction(Pik *p, PToken *pDir, PRel *pVal){
+ PObj *pObj = p->cur;
+ int n;
+ int dir;
+ if( !pObj->type->isLine ){
+ if( pDir ){
+ pik_error(p, pDir, "use with line-oriented objects only");
+ }else{
+ PToken x = pik_next_semantic_token(&pObj->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 + pObj->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 + pObj->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 + pObj->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 + pObj->w*pVal->rRel;
+ p->mTPath |= 1;
+ break;
+ }
+ pObj->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 */
+){
+ PObj *pObj = p->cur;
+ int n;
+ PNum rDist = pDist->rAbs + pik_value(p,"linewid",7,0)*pDist->rRel;
+ if( !pObj->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 ){
+ rHdg = fmod(rHdg,360.0);
+ }else if( pEdgept->eEdge==CP_C ){
+ pik_error(p, pEdgept, "syntax error");
+ return;
+ }else{
+ rHdg = pik_hdg_angle[pEdgept->eEdge];
+ }
+ if( rHdg<=45.0 ){
+ pObj->outDir = DIR_UP;
+ }else if( rHdg<=135.0 ){
+ pObj->outDir = DIR_RIGHT;
+ }else if( rHdg<=225.0 ){
+ pObj->outDir = DIR_DOWN;
+ }else if( rHdg<=315.0 ){
+ pObj->outDir = DIR_LEFT;
+ }else{
+ pObj->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){
+ PObj *pObj = p->cur;
+ int n;
+ if( !pObj->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;
+ }
+ pObj->outDir = pDir->eCode;
+}
+
+/* If the last referenced object is centered at point pPt then return
+** a pointer to that object. If there is no prior object reference,
+** or if the points are not the same, return NULL.
+**
+** This is a side-channel hack used to find the objects at which a
+** line begins and ends. For example, in
+**
+** arrow from OBJ1 to OBJ2 chop
+**
+** The arrow object is normally just handed the coordinates of the
+** centers for OBJ1 and OBJ2. But we also want to know the specific
+** object named in case there are multiple objects centered at the
+** same point.
+**
+** See forum post 1d46e3a0bc
+*/
+static PObj *pik_last_ref_object(Pik *p, PPoint *pPt){
+ PObj *pRes = 0;
+ if( p->lastRef==0 ) return 0;
+ if( p->lastRef->ptAt.x==pPt->x
+ && p->lastRef->ptAt.y==pPt->y
+ ){
+ pRes = p->lastRef;
+ }
+ p->lastRef = 0;
+ return pRes;
+}
+
+/* Set the "from" of an object
+*/
+static void pik_set_from(Pik *p, PObj *pObj, PToken *pTk, PPoint *pPt){
+ if( !pObj->type->isLine ){
+ pik_error(p, pTk, "use \"at\" to position this object");
+ return;
+ }
+ if( pObj->mProp & A_FROM ){
+ pik_error(p, pTk, "line start location already fixed");
+ return;
+ }
+ if( pObj->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;
+ pObj->mProp |= A_FROM;
+ pObj->pFrom = pik_last_ref_object(p, pPt);
+}
+
+/* Set the "to" of an object
+*/
+static void pik_add_to(Pik *p, PObj *pObj, PToken *pTk, PPoint *pPt){
+ int n = p->nTPath-1;
+ if( !pObj->type->isLine ){
+ pik_error(p, pTk, "use \"at\" to position this object");
+ return;
+ }
+ if( pObj->bClose ){
+ pik_error(p, pTk, "polygon is closed");
+ return;
+ }
+ pik_reset_samepath(p);
+ if( n==0 || p->mTPath==3 || p->thenFlag ){
+ n = pik_next_rpath(p, pTk);
+ }
+ p->aTPath[n] = *pPt;
+ p->mTPath = 3;
+ pObj->pTo = pik_last_ref_object(p, pPt);
+}
+
+static void pik_close_path(Pik *p, PToken *pErr){
+ PObj *pObj = p->cur;
+ if( p->nTPath<3 ){
+ pik_error(p, pErr,
+ "need at least 3 vertexes in order to close the polygon");
+ return;
+ }
+ if( pObj->bClose ){
+ pik_error(p, pErr, "polygon already closed");
+ return;
+ }
+ pObj->bClose = 1;
+}
+
+/* Lower the layer of the current object so that it is behind the
+** given object.
+*/
+static void pik_behind(Pik *p, PObj *pOther){
+ PObj *pObj = p->cur;
+ if( p->nErr==0 && pObj->iLayer>=pOther->iLayer ){
+ pObj->iLayer = pOther->iLayer - 1;
+ }
+}
+
+
+/* Set the "at" of an object
+*/
+static void pik_set_at(Pik *p, PToken *pEdge, PPoint *pAt, PToken *pErrTok){
+ PObj *pObj;
+ static unsigned char eDirToCp[] = { CP_E, CP_S, CP_W, CP_N };
+ if( p->nErr ) return;
+ pObj = p->cur;
+
+ if( pObj->type->isLine ){
+ pik_error(p, pErrTok, "use \"from\" and \"to\" to position this object");
+ return;
+ }
+ if( pObj->mProp & A_AT ){
+ pik_error(p, pErrTok, "location fixed by prior \"at\"");
+ return;
+ }
+ pObj->mProp |= A_AT;
+ pObj->eWith = pEdge ? pEdge->eEdge : CP_C;
+ if( pObj->eWith>=CP_END ){
+ int dir = pObj->eWith==CP_END ? pObj->outDir : (pObj->inDir+2)%4;
+ pObj->eWith = eDirToCp[dir];
+ }
+ pObj->with = *pAt;
+}
+
+/*
+** Try to add a text attribute to an object
+*/
+static void pik_add_txt(Pik *p, PToken *pTxt, int iPos){
+ PObj *pObj = p->cur;
+ PToken *pT;
+ if( pObj->nTxt >= count(pObj->aTxt) ){
+ pik_error(p, pTxt, "too many text terms");
+ return;
+ }
+ pT = &pObj->aTxt[pObj->nTxt++];
+ *pT = *pTxt;
+ pT->eCode = (short)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_MONO: iRes |= TP_MONO; 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;
+}
+
+/*
+** Table of scale-factor estimates for variable-width characters.
+** Actual character widths vary by font. These numbers are only
+** guesses. And this table only provides data for ASCII.
+**
+** 100 means normal width.
+*/
+static const unsigned char awChar[] = {
+ /* Skip initial 32 control characters */
+ /* ' ' */ 45,
+ /* '!' */ 55,
+ /* '"' */ 62,
+ /* '#' */ 115,
+ /* '$' */ 90,
+ /* '%' */ 132,
+ /* '&' */ 125,
+ /* '\''*/ 40,
+
+ /* '(' */ 55,
+ /* ')' */ 55,
+ /* '*' */ 71,
+ /* '+' */ 115,
+ /* ',' */ 45,
+ /* '-' */ 48,
+ /* '.' */ 45,
+ /* '/' */ 50,
+
+ /* '0' */ 91,
+ /* '1' */ 91,
+ /* '2' */ 91,
+ /* '3' */ 91,
+ /* '4' */ 91,
+ /* '5' */ 91,
+ /* '6' */ 91,
+ /* '7' */ 91,
+
+ /* '8' */ 91,
+ /* '9' */ 91,
+ /* ':' */ 50,
+ /* ';' */ 50,
+ /* '<' */ 120,
+ /* '=' */ 120,
+ /* '>' */ 120,
+ /* '?' */ 78,
+
+ /* '@' */ 142,
+ /* 'A' */ 102,
+ /* 'B' */ 105,
+ /* 'C' */ 110,
+ /* 'D' */ 115,
+ /* 'E' */ 105,
+ /* 'F' */ 98,
+ /* 'G' */ 105,
+
+ /* 'H' */ 125,
+ /* 'I' */ 58,
+ /* 'J' */ 58,
+ /* 'K' */ 107,
+ /* 'L' */ 95,
+ /* 'M' */ 145,
+ /* 'N' */ 125,
+ /* 'O' */ 115,
+
+ /* 'P' */ 95,
+ /* 'Q' */ 115,
+ /* 'R' */ 107,
+ /* 'S' */ 95,
+ /* 'T' */ 97,
+ /* 'U' */ 118,
+ /* 'V' */ 102,
+ /* 'W' */ 150,
+
+ /* 'X' */ 100,
+ /* 'Y' */ 93,
+ /* 'Z' */ 100,
+ /* '[' */ 58,
+ /* '\\'*/ 50,
+ /* ']' */ 58,
+ /* '^' */ 119,
+ /* '_' */ 72,
+
+ /* '`' */ 72,
+ /* 'a' */ 86,
+ /* 'b' */ 92,
+ /* 'c' */ 80,
+ /* 'd' */ 92,
+ /* 'e' */ 85,
+ /* 'f' */ 52,
+ /* 'g' */ 92,
+
+ /* 'h' */ 92,
+ /* 'i' */ 47,
+ /* 'j' */ 47,
+ /* 'k' */ 88,
+ /* 'l' */ 48,
+ /* 'm' */ 135,
+ /* 'n' */ 92,
+ /* 'o' */ 86,
+
+ /* 'p' */ 92,
+ /* 'q' */ 92,
+ /* 'r' */ 69,
+ /* 's' */ 75,
+ /* 't' */ 58,
+ /* 'u' */ 92,
+ /* 'v' */ 80,
+ /* 'w' */ 121,
+
+ /* 'x' */ 81,
+ /* 'y' */ 80,
+ /* 'z' */ 76,
+ /* '{' */ 91,
+ /* '|'*/ 49,
+ /* '}' */ 91,
+ /* '~' */ 118,
+};
+
+/* Return an estimate of the width of the displayed characters
+** in a character string. The returned value is 100 times the
+** average character width.
+**
+** Omit "\" used to escape characters. And count entities like
+** "&lt;" as a single character. Multi-byte UTF8 characters count
+** as a single character.
+**
+** Unless using a monospaced font, attempt to scale the answer by
+** the actual characters seen. Wide characters count more than
+** narrow characters. But the widths are only guesses.
+**
+*/
+static int pik_text_length(const PToken *pToken, const int isMonospace){
+ const int stdAvg=100, monoAvg=82;
+ int n = pToken->n;
+ const char *z = pToken->z;
+ int cnt, j;
+ for(j=1, cnt=0; j<n-1; j++){
+ char c = z[j];
+ if( c=='\\' && z[j+1]!='&' ){
+ c = z[++j];
+ }else if( c=='&' ){
+ int k;
+ for(k=j+1; k<j+7 && z[k]!=0 && z[k]!=';'; k++){}
+ if( z[k]==';' ) j = k;
+ cnt += (isMonospace ? monoAvg : stdAvg) * 3 / 2;
+ continue;
+ }
+ if( (c & 0xc0)==0xc0 ){
+ while( j+1<n-1 && (z[j+1]&0xc0)==0x80 ){ j++; }
+ cnt += isMonospace ? monoAvg : stdAvg;
+ continue;
+ }
+ if( isMonospace ){
+ cnt += monoAvg;
+ }else if( c >= 0x20 && c <= 0x7e ){
+ cnt += awChar[c-0x20];
+ }else{
+ cnt += stdAvg;
+ }
+ }
+ 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.
+**
+** The eWhich parameter is:
+**
+** 1: Fit horizontally only
+** 2: Fit vertically only
+** 3: Fit both ways
+*/
+static void pik_size_to_fit(Pik *p, PObj *pObj, PToken *pFit, int eWhich){
+ PNum w, h;
+ PBox bbox;
+ if( p->nErr ) return;
+ if( pObj==0 ) pObj = p->cur;
+
+ if( pObj->nTxt==0 ){
+ pik_error(0, pFit, "no text to fit to");
+ return;
+ }
+ if( pObj->type->xFit==0 ) return;
+ pik_bbox_init(&bbox);
+ pik_compute_layout_settings(p);
+ pik_append_txt(p, pObj, &bbox);
+ if( (eWhich & 1)!=0 || pObj->bAltAutoFit ){
+ w = (bbox.ne.x - bbox.sw.x) + p->charWidth;
+ }else{
+ w = 0;
+ }
+ if( (eWhich & 2)!=0 || pObj->bAltAutoFit ){
+ PNum h1, h2;
+ h1 = (bbox.ne.y - pObj->ptAt.y);
+ h2 = (pObj->ptAt.y - bbox.sw.y);
+ h = 2.0*( h1<h2 ? h2 : h1 ) + 0.5*p->charHeight;
+ }else{
+ h = 0;
+ }
+ pObj->type->xFit(p, pObj, w, h);
+ pObj->mProp |= A_FIT;
+}
+
+/* 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 */
+}
+
+/*
+** Round a PNum into the nearest integer
+*/
+static int pik_round(PNum v){
+ if( isnan(v) ) return 0;
+ if( v < -2147483647 ) return (-2147483647-1);
+ if( v >= 2147483647 ) return 2147483647;
+ return (int)v;
+}
+
+/*
+** 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;
+}
+static int pik_value_int(Pik *p, const char *z, int n, int *pMiss){
+ return pik_round(pik_value(p,z,n,pMiss));
+}
+
+/*
+** 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 -99.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 -99.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>-90.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 (short int)i;
+}
+
+/* Search for the NTH object.
+**
+** If pBasis is not NULL then it should be a [] object. Use the
+** sublist of that [] object for the search. If pBasis is not a []
+** object, 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 PObj *pik_find_nth(Pik *p, PObj *pBasis, PToken *pNth){
+ PList *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--){
+ PObj *pObj = pList->a[i];
+ if( pClass && pObj->type!=pClass ) continue;
+ n++;
+ if( n==0 ){ return pObj; }
+ }
+ }else{
+ for(i=0; i<pList->n; i++){
+ PObj *pObj = pList->a[i];
+ if( pClass && pObj->type!=pClass ) continue;
+ n--;
+ if( n==0 ){ return pObj; }
+ }
+ }
+ pik_error(p, pNth, "no such object");
+ return 0;
+}
+
+/* Search for an object by name.
+**
+** Search in pBasis->pSublist if pBasis is not NULL. If pBasis is NULL
+** then search in p->list.
+*/
+static PObj *pik_find_byname(Pik *p, PObj *pBasis, PToken *pName){
+ PList *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--){
+ PObj *pObj = pList->a[i];
+ if( pObj->zName && pik_token_eq(pName,pObj->zName)==0 ){
+ p->lastRef = pObj;
+ return pObj;
+ }
+ }
+ /* 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--){
+ PObj *pObj = pList->a[i];
+ for(j=0; j<pObj->nTxt; j++){
+ if( pObj->aTxt[j].n==pName->n+2
+ && memcmp(pObj->aTxt[j].z+1,pName->z,pName->n)==0 ){
+ p->lastRef = pObj;
+ return pObj;
+ }
+ }
+ }
+ 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 object of the same
+** type if pOther is NULL.
+*/
+static void pik_same(Pik *p, PObj *pOther, PToken *pErrTok){
+ PObj *pObj = 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==pObj->type ) break;
+ }
+ if( i<0 ){
+ pik_error(p, pErrTok, "no prior objects of the same type");
+ return;
+ }
+ }
+ if( pOther->nPath && pObj->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( !pObj->type->isLine ){
+ pObj->w = pOther->w;
+ pObj->h = pOther->h;
+ }
+ pObj->rad = pOther->rad;
+ pObj->sw = pOther->sw;
+ pObj->dashed = pOther->dashed;
+ pObj->dotted = pOther->dotted;
+ pObj->fill = pOther->fill;
+ pObj->color = pOther->color;
+ pObj->cw = pOther->cw;
+ pObj->larrow = pOther->larrow;
+ pObj->rarrow = pOther->rarrow;
+ pObj->bClose = pOther->bClose;
+ pObj->bChop = pOther->bChop;
+ pObj->iLayer = pOther->iLayer;
+}
+
+
+/* Return a "Place" associated with object pObj. 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, PObj *pObj, PToken *pEdge){
+ PPoint pt = cZeroPoint;
+ const PClass *pClass;
+ if( pObj==0 ) return pt;
+ if( pEdge==0 ){
+ return pObj->ptAt;
+ }
+ pClass = pObj->type;
+ if( pEdge->eType==T_EDGEPT || (pEdge->eEdge>0 && pEdge->eEdge<CP_END) ){
+ pt = pClass->xOffset(p, pObj, pEdge->eEdge);
+ pt.x += pObj->ptAt.x;
+ pt.y += pObj->ptAt.y;
+ return pt;
+ }
+ if( pEdge->eType==T_START ){
+ return pObj->ptEnter;
+ }else{
+ return pObj->ptExit;
+ }
+}
+
+/* Do a linear interpolation of two positions.
+*/
+static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2){
+ PPoint out;
+ 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, PObj *pObj){
+ static const PPoint zero = {0, 0};
+ 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(PObj *pObj, PToken *pProp){
+ PNum v = 0.0;
+ if( pObj ){
+ switch( pProp->eType ){
+ case T_HEIGHT: v = pObj->h; break;
+ case T_WIDTH: v = pObj->w; break;
+ case T_RADIUS: v = pObj->rad; break;
+ case T_DIAMETER: v = pObj->rad*2.0; break;
+ case T_THICKNESS: v = pObj->sw; break;
+ case T_DASHED: v = pObj->dashed; break;
+ case T_DOTTED: v = pObj->dotted; break;
+ case T_FILL: v = pObj->fill; break;
+ case T_COLOR: v = pObj->color; break;
+ case T_X: v = pObj->ptAt.x; break;
+ case T_Y: v = pObj->ptAt.y; break;
+ case T_TOP: v = pObj->bbox.ne.y; break;
+ case T_BOTTOM: v = pObj->bbox.sw.y; break;
+ case T_LEFT: v = pObj->bbox.sw.x; break;
+ case T_RIGHT: v = pObj->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 = x<0.0 ? -x : x; 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 object
+*/
+static void pik_elem_setname(Pik *p, PObj *pObj, PToken *pName){
+ if( pObj==0 ) return;
+ if( pName==0 ) return;
+ free(pObj->zName);
+ pObj->zName = malloc(pName->n+1);
+ if( pObj->zName==0 ){
+ pik_error(p,0,0);
+ }else{
+ memcpy(pObj->zName,pName->z,pName->n);
+ pObj->zName[pName->n] = 0;
+ }
+ return;
+}
+
+/*
+** Search for object located at *pCenter that has an xChop method and
+** that does not enclose point pOther.
+**
+** Return a pointer to the object, or NULL if not found.
+*/
+static PObj *pik_find_chopper(PList *pList, PPoint *pCenter, PPoint *pOther){
+ int i;
+ if( pList==0 ) return 0;
+ for(i=pList->n-1; i>=0; i--){
+ PObj *pObj = pList->a[i];
+ if( pObj->type->xChop!=0
+ && pObj->ptAt.x==pCenter->x
+ && pObj->ptAt.y==pCenter->y
+ && !pik_bbox_contains_point(&pObj->bbox, pOther)
+ ){
+ return pObj;
+ }else if( pObj->pSublist ){
+ pObj = pik_find_chopper(pObj->pSublist,pCenter,pOther);
+ if( pObj ) return pObj;
+ }
+ }
+ return 0;
+}
+
+/*
+** There is a line traveling from pFrom to pTo.
+**
+** If pObj is not null and is a choppable object, then chop at
+** the boundary of pObj - where the line crosses the boundary
+** of pObj.
+**
+** If pObj is NULL or has no xChop method, then search for some
+** other object centered at pTo that is choppable and use it
+** instead.
+*/
+static void pik_autochop(Pik *p, PPoint *pFrom, PPoint *pTo, PObj *pObj){
+ if( pObj==0 || pObj->type->xChop==0 ){
+ pObj = pik_find_chopper(p->list, pTo, pFrom);
+ }
+ if( pObj ){
+ *pTo = pObj->type->xChop(p, pObj, pFrom);
+ }
+}
+
+/* This routine runs after all attributes have been received
+** on an object.
+*/
+static void pik_after_adding_attributes(Pik *p, PObj *pObj){
+ int i;
+ PPoint ofst;
+ PNum dx, dy;
+
+ if( p->nErr ) return;
+
+ /* Position block objects */
+ if( pObj->type->isLine==0 ){
+ /* A height or width less than or equal to zero means "autofit".
+ ** Change the height or width to be big enough to contain the text,
+ */
+ if( pObj->h<=0.0 ){
+ if( pObj->nTxt==0 ){
+ pObj->h = 0.0;
+ }else if( pObj->w<=0.0 ){
+ pik_size_to_fit(p, pObj, &pObj->errTok, 3);
+ }else{
+ pik_size_to_fit(p, pObj, &pObj->errTok, 2);
+ }
+ }
+ if( pObj->w<=0.0 ){
+ if( pObj->nTxt==0 ){
+ pObj->w = 0.0;
+ }else{
+ pik_size_to_fit(p, pObj, &pObj->errTok, 1);
+ }
+ }
+ ofst = pik_elem_offset(p, pObj, pObj->eWith);
+ dx = (pObj->with.x - ofst.x) - pObj->ptAt.x;
+ dy = (pObj->with.y - ofst.y) - pObj->ptAt.y;
+ if( dx!=0 || dy!=0 ){
+ pik_elem_move(pObj, dx, dy);
+ }
+ }
+
+ /* For a line object with no movement specified, a single movement
+ ** of the default length in the current direction
+ */
+ if( pObj->type->isLine && p->nTPath<2 ){
+ pik_next_rpath(p, 0);
+ assert( p->nTPath==2 );
+ switch( pObj->inDir ){
+ default: p->aTPath[1].x += pObj->w; break;
+ case DIR_DOWN: p->aTPath[1].y -= pObj->h; break;
+ case DIR_LEFT: p->aTPath[1].x -= pObj->w; break;
+ case DIR_UP: p->aTPath[1].y += pObj->h; break;
+ }
+ if( pObj->type->xInit==arcInit ){
+ pObj->outDir = (pObj->inDir + (pObj->cw ? 1 : 3))%4;
+ p->eDir = (unsigned char)pObj->outDir;
+ switch( pObj->outDir ){
+ default: p->aTPath[1].x += pObj->w; break;
+ case DIR_DOWN: p->aTPath[1].y -= pObj->h; break;
+ case DIR_LEFT: p->aTPath[1].x -= pObj->w; break;
+ case DIR_UP: p->aTPath[1].y += pObj->h; break;
+ }
+ }
+ }
+
+ /* Initialize the bounding box prior to running xCheck */
+ pik_bbox_init(&pObj->bbox);
+
+ /* Run object-specific code */
+ if( pObj->type->xCheck!=0 ){
+ pObj->type->xCheck(p,pObj);
+ if( p->nErr ) return;
+ }
+
+ /* Compute final bounding box, entry and exit points, center
+ ** point (ptAt) and path for the object
+ */
+ if( pObj->type->isLine ){
+ pObj->aPath = malloc( sizeof(PPoint)*p->nTPath );
+ if( pObj->aPath==0 ){
+ pik_error(p, 0, 0);
+ return;
+ }else{
+ pObj->nPath = p->nTPath;
+ for(i=0; i<p->nTPath; i++){
+ pObj->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( pObj->bChop && pObj->nPath>=2 ){
+ int n = pObj->nPath;
+ pik_autochop(p, &pObj->aPath[n-2], &pObj->aPath[n-1], pObj->pTo);
+ pik_autochop(p, &pObj->aPath[1], &pObj->aPath[0], pObj->pFrom);
+ }
+
+ pObj->ptEnter = pObj->aPath[0];
+ pObj->ptExit = pObj->aPath[pObj->nPath-1];
+
+ /* Compute the center of the line based on the bounding box over
+ ** the vertexes. This is a difference from PIC. In Pikchr, the
+ ** center of a line is the center of its bounding box. In PIC, the
+ ** center of a line is halfway between its .start and .end. For
+ ** straight lines, this is the same point, but for multi-segment
+ ** lines the result is usually diferent */
+ for(i=0; i<pObj->nPath; i++){
+ pik_bbox_add_xy(&pObj->bbox, pObj->aPath[i].x, pObj->aPath[i].y);
+ }
+ pObj->ptAt.x = (pObj->bbox.ne.x + pObj->bbox.sw.x)/2.0;
+ pObj->ptAt.y = (pObj->bbox.ne.y + pObj->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 */
+ pObj->w = pObj->bbox.ne.x - pObj->bbox.sw.x;
+ pObj->h = pObj->bbox.ne.y - pObj->bbox.sw.y;
+
+ /* If this is a polygon (if it has the "close" attribute), then
+ ** adjust the exit point */
+ if( pObj->bClose ){
+ /* For "closed" lines, the .end is one of the .e, .s, .w, or .n
+ ** points of the bounding box, as with block objects. */
+ pik_elem_set_exit(pObj, pObj->inDir);
+ }
+ }else{
+ PNum w2 = pObj->w/2.0;
+ PNum h2 = pObj->h/2.0;
+ pObj->ptEnter = pObj->ptAt;
+ pObj->ptExit = pObj->ptAt;
+ switch( pObj->inDir ){
+ default: pObj->ptEnter.x -= w2; break;
+ case DIR_LEFT: pObj->ptEnter.x += w2; break;
+ case DIR_UP: pObj->ptEnter.y -= h2; break;
+ case DIR_DOWN: pObj->ptEnter.y += h2; break;
+ }
+ switch( pObj->outDir ){
+ default: pObj->ptExit.x += w2; break;
+ case DIR_LEFT: pObj->ptExit.x -= w2; break;
+ case DIR_UP: pObj->ptExit.y += h2; break;
+ case DIR_DOWN: pObj->ptExit.y -= h2; break;
+ }
+ pik_bbox_add_xy(&pObj->bbox, pObj->ptAt.x - w2, pObj->ptAt.y - h2);
+ pik_bbox_add_xy(&pObj->bbox, pObj->ptAt.x + w2, pObj->ptAt.y + h2);
+ }
+ p->eDir = (unsigned char)pObj->outDir;
+}
+
+/* Show basic information about each object 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, PObj *pObj){
+ char *zDir;
+ if( pObj==0 ) return;
+ pik_append(p,"<!-- ", -1);
+ if( pObj->zName ){
+ pik_append_text(p, pObj->zName, -1, 0);
+ pik_append(p, ": ", 2);
+ }
+ pik_append_text(p, pObj->type->zName, -1, 0);
+ if( pObj->nTxt ){
+ pik_append(p, " \"", 2);
+ pik_append_text(p, pObj->aTxt[0].z+1, pObj->aTxt[0].n-2, 1);
+ pik_append(p, "\"", 1);
+ }
+ pik_append_num(p, " w=", pObj->w);
+ pik_append_num(p, " h=", pObj->h);
+ pik_append_point(p, " center=", &pObj->ptAt);
+ pik_append_point(p, " enter=", &pObj->ptEnter);
+ switch( pObj->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=", &pObj->ptExit);
+ pik_append(p, zDir, -1);
+ pik_append(p, " -->\n", -1);
+}
+
+/* Render a list of objects
+*/
+void pik_elist_render(Pik *p, PList *pList){
+ int i;
+ int iNextLayer = 0;
+ int iThisLayer;
+ int bMoreToDo;
+ int miss = 0;
+ int mDebug = pik_value_int(p, "debug", 5, 0);
+ PNum colorLabel;
+ do{
+ bMoreToDo = 0;
+ iThisLayer = iNextLayer;
+ iNextLayer = 0x7fffffff;
+ for(i=0; i<pList->n; i++){
+ PObj *pObj = pList->a[i];
+ void (*xRender)(Pik*,PObj*);
+ if( pObj->iLayer>iThisLayer ){
+ if( pObj->iLayer<iNextLayer ) iNextLayer = pObj->iLayer;
+ bMoreToDo = 1;
+ continue; /* Defer until another round */
+ }else if( pObj->iLayer<iThisLayer ){
+ continue;
+ }
+ if( mDebug & 1 ) pik_elem_render(p, pObj);
+ xRender = pObj->type->xRender;
+ if( xRender ){
+ xRender(p, pObj);
+ }
+ if( pObj->pSublist ){
+ pik_elist_render(p, pObj->pSublist);
+ }
+ }
+ }while( bMoreToDo );
+
+ /* If the color_debug_label value is defined, then go through
+ ** and paint a dot at every label location */
+ colorLabel = pik_value(p, "debug_label_color", 17, &miss);
+ if( miss==0 && colorLabel>=0.0 ){
+ PObj dot;
+ memset(&dot, 0, sizeof(dot));
+ dot.type = &noopClass;
+ dot.rad = 0.015;
+ dot.sw = 0.015;
+ dot.fill = colorLabel;
+ dot.color = colorLabel;
+ dot.nTxt = 1;
+ dot.aTxt[0].eCode = TP_ABOVE;
+ for(i=0; i<pList->n; i++){
+ PObj *pObj = pList->a[i];
+ if( pObj->zName==0 ) continue;
+ dot.ptAt = pObj->ptAt;
+ dot.aTxt[0].z = pObj->zName;
+ dot.aTxt[0].n = (int)strlen(pObj->zName);
+ dotRender(p, &dot);
+ }
+ }
+}
+
+/* Add all objects of the list pList to the bounding box
+*/
+static void pik_bbox_add_elist(Pik *p, PList *pList, PNum wArrow){
+ int i;
+ for(i=0; i<pList->n; i++){
+ PObj *pObj = pList->a[i];
+ if( pObj->sw>=0.0 ) pik_bbox_addbox(&p->bbox, &pObj->bbox);
+ pik_append_txt(p, pObj, &p->bbox);
+ if( pObj->pSublist ) pik_bbox_add_elist(p, pObj->pSublist, wArrow);
+
+
+ /* Expand the bounding box to account for arrowheads on lines */
+ if( pObj->type->isLine && pObj->nPath>0 ){
+ if( pObj->larrow ){
+ pik_bbox_addellipse(&p->bbox, pObj->aPath[0].x, pObj->aPath[0].y,
+ wArrow, wArrow);
+ }
+ if( pObj->rarrow ){
+ int j = pObj->nPath-1;
+ pik_bbox_addellipse(&p->bbox, pObj->aPath[j].x, pObj->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->fontScale = pik_value(p,"fontscale",9,0);
+ if( p->fontScale<=0.0 ) p->fontScale = 1.0;
+ 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 objects. Write the SVG into p->zOut.
+** Delete the input object_list before returnning.
+*/
+static void pik_render(Pik *p, PList *pList){
+ if( pList==0 ) return;
+ if( p->nErr==0 ){
+ PNum thickness; /* Stroke width */
+ PNum margin; /* Extra bounding box margin */
+ PNum w, h; /* Drawing width and height */
+ PNum wArrow;
+ PNum pikScale; /* Value of the "scale" variable */
+ int miss = 0;
+
+ /* 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;
+ wArrow = p->wArrow*thickness;
+ miss = 0;
+ p->fgcolor = pik_value_int(p,"fgcolor",7,&miss);
+ if( miss ){
+ PToken t;
+ t.z = "fgcolor";
+ t.n = 7;
+ p->fgcolor = pik_round(pik_lookup_color(0, &t));
+ }
+ miss = 0;
+ p->bgcolor = pik_value_int(p,"bgcolor",7,&miss);
+ if( miss ){
+ PToken t;
+ t.z = "bgcolor";
+ t.n = 7;
+ p->bgcolor = pik_round(pik_lookup_color(0, &t));
+ }
+
+ /* 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, pList, wArrow);
+
+ /* Expand the bounding box slightly to account for line thickness
+ ** and the optional "margin = EXPR" setting. */
+ p->bbox.ne.x += margin + pik_value(p,"rightmargin",11,0);
+ p->bbox.ne.y += margin + pik_value(p,"topmargin",9,0);
+ p->bbox.sw.x -= margin + pik_value(p,"leftmargin",10,0);
+ p->bbox.sw.y -= margin + pik_value(p,"bottommargin",12,0);
+
+ /* Output the SVG */
+ pik_append(p, "<svg xmlns='http://www.w3.org/2000/svg'"
+ " style='font-size:initial;'",-1);
+ 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 = pik_round(p->rScale*w);
+ p->hSVG = pik_round(p->rScale*h);
+ pikScale = pik_value(p,"scale",5,0);
+ if( pikScale>=0.001 && pikScale<=1000.0
+ && (pikScale<0.99 || pikScale>1.01)
+ ){
+ p->wSVG = pik_round(p->wSVG*pikScale);
+ p->hSVG = pik_round(p->hSVG*pikScale);
+ pik_append_num(p, " width=\"", p->wSVG);
+ pik_append_num(p, "\" height=\"", p->hSVG);
+ pik_append(p, "\"", 1);
+ }
+ pik_append_dis(p, " viewBox=\"0 0 ",w,"");
+ pik_append_dis(p, " ",h,"\"");
+ pik_append(p, " data-pikchr-date=\"" MANIFEST_ISODATE "\">\n", -1);
+ pik_elist_render(p, pList);
+ pik_append(p,"</svg>\n", -1);
+ }else{
+ p->wSVG = -1;
+ p->hSVG = -1;
+ }
+ pik_elist_free(p, pList);
+}
+
+
+
+/*
+** 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 },
+ { "define", 6, T_DEFINE, 0, 0 },
+ { "diameter", 8, T_DIAMETER, 0, 0 },
+ { "dist", 4, T_DIST, 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 },
+ { "mono", 4, T_MONO, 0, 0 },
+ { "monospace", 9, T_MONO, 0, 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 },
+ { "pikchr_date",11, T_ISODATE, 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 },
+ { "solid", 5, T_SOLID, 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 },
+ { "this", 4, T_THIS, 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
+** keyword entry 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, int bAllowCodeBlock){
+ 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=='\f'; 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;
+ }
+ }
+ case 0xe2: {
+ if( z[1]==0x86 ){
+ if( z[2]==0x90 ){
+ pToken->eType = T_LARROW; /* <- */
+ return 3;
+ }
+ if( z[2]==0x92 ){
+ pToken->eType = T_RARROW; /* -> */
+ return 3;
+ }
+ if( z[2]==0x94 ){
+ pToken->eType = T_LRARROW; /* <-> */
+ return 3;
+ }
+ }
+ pToken->eType = T_ERROR;
+ return 1;
+ }
+ case '{': {
+ int len, depth;
+ i = 1;
+ if( bAllowCodeBlock ){
+ depth = 1;
+ while( z[i] && depth>0 ){
+ PToken x;
+ x.z = (char*)(z+i);
+ len = pik_token_length(&x, 0);
+ if( len==1 ){
+ if( z[i]=='{' ) depth++;
+ if( z[i]=='}' ) depth--;
+ }
+ i += len;
+ }
+ }else{
+ depth = 0;
+ }
+ if( depth ){
+ pToken->eType = T_ERROR;
+ return 1;
+ }
+ pToken->eType = T_CODEBLOCK;
+ return i;
+ }
+ case '&': {
+ static const struct {
+ int nByte; /* Number of bytes in zEntity */
+ int eCode; /* Corresponding token code */
+ const char *zEntity; /* Name of the HTML entity */
+ } aEntity[] = {
+ /* 123456789 1234567 */
+ { 6, T_RARROW, "&rarr;" }, /* Same as -> */
+ { 12, T_RARROW, "&rightarrow;" }, /* Same as -> */
+ { 6, T_LARROW, "&larr;" }, /* Same as <- */
+ { 11, T_LARROW, "&leftarrow;" }, /* Same as <- */
+ { 16, T_LRARROW, "&leftrightarrow;" }, /* Same as <-> */
+ };
+ unsigned int i;
+ for(i=0; i<sizeof(aEntity)/sizeof(aEntity[0]); i++){
+ if( strncmp((const char*)z,aEntity[i].zEntity,aEntity[i].nByte)==0 ){
+ pToken->eType = aEntity[i].eCode;
+ return aEntity[i].nByte;
+ }
+ }
+ pToken->eType = T_ERROR;
+ 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;
+ i = 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' ){
+ int iBefore = i;
+ i++;
+ c2 = z[i];
+ if( c2=='+' || c2=='-' ){
+ i++;
+ c2 = z[i];
+ }
+ if( c2<'0' || c>'9' ){
+ /* This is not an exp */
+ i = iBefore;
+ }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) ){
+ 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 if( c=='$' && z[1]>='1' && z[1]<='9' && !IsDigit(z[2]) ){
+ pToken->eType = T_PARAMETER;
+ pToken->eCode = z[1] - '1';
+ return 2;
+ }else if( c=='_' || c=='$' || c=='@' ){
+ for(i=1; (c = z[i])!=0 && (IsAlnum(c) || c=='_'); i++){}
+ pToken->eType = T_ID;
+ 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, 1);
+ if( x.eType!=T_WHITESPACE ){
+ x.n = sz;
+ return x;
+ }
+ i += sz;
+ }
+}
+
+/* Parser arguments to a macro invocation
+**
+** (arg1, arg2, ...)
+**
+** Arguments are comma-separated, except that commas within string
+** literals or with (...), {...}, or [...] do not count. The argument
+** list begins and ends with parentheses. There can be at most 9
+** arguments.
+**
+** Return the number of bytes in the argument list.
+*/
+static unsigned int pik_parse_macro_args(
+ Pik *p,
+ const char *z, /* Start of the argument list */
+ int n, /* Available bytes */
+ PToken *args, /* Fill in with the arguments */
+ PToken *pOuter /* Arguments of the next outer context, or NULL */
+){
+ int nArg = 0;
+ int i, j, sz;
+ int iStart;
+ int depth = 0;
+ PToken x;
+ if( z[0]!='(' ) return 0;
+ args[0].z = z+1;
+ iStart = 1;
+ for(i=1; i<n && z[i]!=')'; i+=sz){
+ x.z = z+i;
+ sz = pik_token_length(&x, 0);
+ if( sz!=1 ) continue;
+ if( z[i]==',' && depth<=0 ){
+ args[nArg].n = i - iStart;
+ if( nArg==8 ){
+ x.z = z;
+ x.n = 1;
+ pik_error(p, &x, "too many macro arguments - max 9");
+ return 0;
+ }
+ nArg++;
+ args[nArg].z = z+i+1;
+ iStart = i+1;
+ depth = 0;
+ }else if( z[i]=='(' || z[i]=='{' || z[i]=='[' ){
+ depth++;
+ }else if( z[i]==')' || z[i]=='}' || z[i]==']' ){
+ depth--;
+ }
+ }
+ if( z[i]==')' ){
+ args[nArg].n = i - iStart;
+ /* Remove leading and trailing whitespace from each argument (including
+ ** backslash-escaped newlines). If what remains is one of $1, $2, ... $9
+ ** then transfer the corresponding argument from the outer context */
+ for(j=0; j<=nArg; j++){
+ PToken *t = &args[j];
+ while( (t->n>0 && IsSpace(t->z[0]))
+ || (t->n>1 && t->z[0]=='\\' && IsSpace(t->z[1]))
+ ){
+ t->z++;
+ t->n--;
+ }
+ while( t->n>0 && IsSpace(t->z[t->n-1]) ){
+ t->n--;
+ if( t->n>0 && t->z[t->n-1]=='\\' ) t->n--;
+ }
+ if( t->n==2 && t->z[0]=='$' && t->z[1]>='1' && t->z[1]<='9' ){
+ if( pOuter ) *t = pOuter[t->z[1]-'1'];
+ else t->n = 0;
+ }
+ }
+ return i+1;
+ }
+ x.z = z;
+ x.n = 1;
+ pik_error(p, &x, "unterminated macro argument list");
+ return 0;
+}
+
+/*
+** Split up the content of a PToken into multiple tokens and
+** send each to the parser.
+*/
+void pik_tokenize(Pik *p, PToken *pIn, yyParser *pParser, PToken *aParam){
+ unsigned int i;
+ int sz = 0;
+ PToken token;
+ PMacro *pMac;
+ for(i=0; i<pIn->n && pIn->z[i] && p->nErr==0; i+=sz){
+ token.eCode = 0;
+ token.eEdge = 0;
+ token.z = pIn->z + i;
+ sz = pik_token_length(&token, 1);
+ if( token.eType==T_WHITESPACE ){
+ /* no-op */
+ }else if( sz>50000 ){
+ token.n = 1;
+ pik_error(p, &token, "token is too long - max length 50000 bytes");
+ break;
+ }else if( token.eType==T_ERROR ){
+ token.n = (unsigned short)(sz & 0xffff);
+ pik_error(p, &token, "unrecognized token");
+ break;
+ }else if( sz+i>pIn->n ){
+ token.n = pIn->n - i;
+ pik_error(p, &token, "syntax error");
+ break;
+ }else if( token.eType==T_PARAMETER ){
+ /* Substitute a parameter into the input stream */
+ if( aParam==0 || aParam[token.eCode].n==0 ){
+ continue;
+ }
+ token.n = (unsigned short)(sz & 0xffff);
+ if( p->nCtx>=count(p->aCtx) ){
+ pik_error(p, &token, "macros nested too deep");
+ }else{
+ p->aCtx[p->nCtx++] = token;
+ pik_tokenize(p, &aParam[token.eCode], pParser, 0);
+ p->nCtx--;
+ }
+ }else if( token.eType==T_ID
+ && (token.n = (unsigned short)(sz & 0xffff),
+ (pMac = pik_find_macro(p,&token))!=0)
+ ){
+ PToken args[9];
+ unsigned int j = i+sz;
+ if( pMac->inUse ){
+ pik_error(p, &pMac->macroName, "recursive macro definition");
+ break;
+ }
+ token.n = (short int)(sz & 0xffff);
+ if( p->nCtx>=count(p->aCtx) ){
+ pik_error(p, &token, "macros nested too deep");
+ break;
+ }
+ pMac->inUse = 1;
+ memset(args, 0, sizeof(args));
+ p->aCtx[p->nCtx++] = token;
+ sz += pik_parse_macro_args(p, pIn->z+j, pIn->n-j, args, aParam);
+ pik_tokenize(p, &pMac->macroBody, pParser, args);
+ p->nCtx--;
+ pMac->inUse = 0;
+ }else{
+#if 0
+ printf("******** Token %s (%d): \"%.*s\" **************\n",
+ yyTokenName[token.eType], token.eType,
+ (int)(IsSpace(token.z[0]) ? 0 : sz), token.z);
+#endif
+ token.n = (unsigned short)(sz & 0xffff);
+ if( p->nToken++ > PIKCHR_TOKEN_LIMIT ){
+ pik_error(p, &token, "script is too complex");
+ break;
+ }
+ if( token.eType==T_ISODATE ){
+ token.z = "\"" MANIFEST_ISODATE "\"";
+ token.n = sizeof(MANIFEST_ISODATE)+1;
+ token.eType = T_STRING;
+ }
+ pik_parser(pParser, token.eType, token);
+ }
+ }
+}
+
+/*
+** Return the version name.
+*/
+const char *pikchr_version(void)
+ /* Emscripten workaround, else it chokes on the inlined version */;
+
+const char *pikchr_version(void){
+ return RELEASE_VERSION " " MANIFEST_ISODATE;
+}
+
+/*
+** 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 */
+){
+ Pik s;
+ yyParser sParse;
+
+ memset(&s, 0, sizeof(s));
+ s.sIn.z = zText;
+ s.sIn.n = (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
+ pik_tokenize(&s, &s.sIn, &sParse, 0);
+ if( s.nErr==0 ){
+ PToken token;
+ memset(&token,0,sizeof(token));
+ token.z = zText + (s.sIn.n>0 ? s.sIn.n-1 : 0);
+ token.n = 1;
+ pik_parser(&sParse, 0, token);
+ }
+ pik_parserFinalize(&sParse);
+ if( s.zOut==0 && s.nErr==0 ){
+ pik_append(&s, "<!-- empty pikchr diagram -->\n", -1);
+ }
+ while( s.pVar ){
+ PVar *pNext = s.pVar->pNext;
+ free(s.pVar);
+ s.pVar = pNext;
+ }
+ while( s.pMacros ){
+ PMacro *pNext = s.pMacros->pNext;
+ free(s.pMacros);
+ s.pMacros = 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;
+ unsigned int mFlags = nByte & 3;
+ zIn = malloc( nByte + 1 );
+ if( zIn==0 ) return 0;
+ memcpy(zIn, aData, nByte);
+ zIn[nByte] = 0;
+ zOut = pikchr(zIn, "pikchr", mFlags, &w, &h);
+ free(zIn);
+ free(zOut);
+ return 0;
+}
+#endif /* PIKCHR_FUZZ */
+
+#if defined(PIKCHR_SHELL)
+/* Print a usage comment for the shell and exit. */
+static void usage(const char *argv0){
+ fprintf(stderr, "usage: %s [OPTIONS] FILE ...\n", argv0);
+ fprintf(stderr,
+ "Convert Pikchr input files into SVG. Filename \"-\" means stdin.\n"
+ "All output goes to stdout.\n"
+ "Options:\n"
+ " --dark-mode Generate \"dark mode\" output\n"
+ " --dont-stop Process all files even if earlier files have errors\n"
+ " --svg-only Emit raw SVG without the HTML wrapper\n"
+ );
+ exit(1);
+}
+
+/* Send text to standard output, but escape HTML markup */
+static void print_escape_html(const char *z){
+ int j;
+ char c;
+ 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;
+ }
+ }
+}
+
+/* Read the content of file zFilename into memory obtained from malloc()
+** Return the memory. If something goes wrong (ex: the file does not exist
+** or cannot be opened) put an error message on stderr and return NULL.
+**
+** If the filename is "-" read stdin.
+*/
+static char *readFile(const char *zFilename){
+ FILE *in;
+ size_t n;
+ size_t nUsed = 0;
+ size_t nAlloc = 0;
+ char *z = 0, *zNew = 0;
+ in = strcmp(zFilename,"-")==0 ? stdin : fopen(zFilename, "rb");
+ if( in==0 ){
+ fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename);
+ return 0;
+ }
+ while(1){
+ if( nUsed+2>=nAlloc ){
+ nAlloc = nAlloc*2 + 4000;
+ zNew = realloc(z, nAlloc);
+ }
+ if( zNew==0 ){
+ free(z);
+ fprintf(stderr, "out of memory trying to allocate %lld bytes\n",
+ (long long int)nAlloc);
+ exit(1);
+ }
+ z = zNew;
+ n = fread(z+nUsed, 1, nAlloc-nUsed-1, in);
+ if( n<=0 ){
+ break;
+ }
+ nUsed += n;
+ }
+ if( in!=stdin ) fclose(in);
+ z[nUsed] = 0;
+ return z;
+}
+
+
+/* Testing 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 bSvgOnly = 0; /* Output SVG only. No HTML wrapper */
+ int bDontStop = 0; /* Continue in spite of errors */
+ int exitCode = 0; /* What to return */
+ int mFlags = 0; /* mFlags argument to pikchr() */
+ const char *zStyle = ""; /* Extra styling */
+ const char *zHtmlHdr =
+ "<!DOCTYPE html>\n"
+ "<html lang=\"en-US\">\n"
+ "<head>\n<title>PIKCHR Test</title>\n"
+ "<style>\n"
+ " .hidden {\n"
+ " position: absolute !important;\n"
+ " opacity: 0 !important;\n"
+ " pointer-events: none !important;\n"
+ " display: none !important;\n"
+ " }\n"
+ "</style>\n"
+ "<script>\n"
+ " function toggleHidden(id){\n"
+ " for(var c of document.getElementById(id).children){\n"
+ " c.classList.toggle('hidden');\n"
+ " }\n"
+ " }\n"
+ "</script>\n"
+ "<meta charset=\"utf-8\">\n"
+ "</head>\n"
+ "<body>\n"
+ ;
+ if( argc<2 ) usage(argv[0]);
+ for(i=1; i<argc; i++){
+ char *zIn;
+ char *zOut;
+ int w, h;
+
+ if( argv[i][0]=='-' && argv[i][1]!=0 ){
+ char *z = argv[i];
+ z++;
+ if( z[0]=='-' ) z++;
+ if( strcmp(z,"dont-stop")==0 ){
+ bDontStop = 1;
+ }else
+ if( strcmp(z,"dark-mode")==0 ){
+ zStyle = "color:white;background-color:black;";
+ mFlags |= PIKCHR_DARK_MODE;
+ }else
+ if( strcmp(z,"svg-only")==0 ){
+ if( zHtmlHdr==0 ){
+ fprintf(stderr, "the \"%s\" option must come first\n",argv[i]);
+ exit(1);
+ }
+ bSvgOnly = 1;
+ mFlags |= PIKCHR_PLAINTEXT_ERRORS;
+ }else
+ if( strcmp(z,"version")==0 || strcmp(z,"v")==0 ){
+ printf("pikchr %s\n", pikchr_version());
+ return 0;
+ }else
+ {
+ fprintf(stderr,"unknown option: \"%s\"\n", argv[i]);
+ usage(argv[0]);
+ }
+ continue;
+ }
+ zIn = readFile(argv[i]);
+ if( zIn==0 ) continue;
+ zOut = pikchr(zIn, "pikchr", mFlags, &w, &h);
+ if( w<0 && !bDontStop ) exitCode = 1;
+ if( zOut==0 ){
+ fprintf(stderr, "pikchr() returns NULL. Out of memory?\n");
+ if( !bDontStop ) exit(1);
+ }else if( bSvgOnly ){
+ printf("%s\n", zOut);
+ }else{
+ if( zHtmlHdr ){
+ printf("%s", zHtmlHdr);
+ zHtmlHdr = 0;
+ }
+ printf("<h1>File %s</h1>\n", argv[i]);
+ if( w<0 ){
+ printf("<p>ERROR</p>\n%s\n", zOut);
+ }else{
+ printf("<div id=\"svg-%d\" onclick=\"toggleHidden('svg-%d')\">\n",i,i);
+ printf("<div style='border:3px solid lightgray;max-width:%dpx;%s'>\n",
+ w,zStyle);
+ printf("%s</div>\n", zOut);
+ printf("<pre class='hidden'>");
+ print_escape_html(zIn);
+ printf("</pre>\n</div>\n");
+ }
+ }
+ free(zOut);
+ free(zIn);
+ }
+ if( !bSvgOnly ){
+ printf("</body></html>\n");
+ }
+ return exitCode ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+#endif /* PIKCHR_SHELL */
+
+#ifdef PIKCHR_TCL
+#include <tcl.h>
+/* Compatability between Tcl8.6 and Tcl9.0 */
+#if TCL_MAJOR_VERSION==9
+# define CONST const
+#elif !defined(Tcl_Size)
+ typedef int Tcl_Size;
+#endif
+
+/*
+** An interface to TCL
+**
+** TCL command: pikchr $INPUTTEXT
+**
+** Returns a list of 3 elements which are the output text, the width, and
+** the height.
+**
+** Register the "pikchr" command by invoking Pikchr_Init(Tcl_Interp*). Or
+** compile this source file as a shared library and load it using the
+** "load" command of Tcl.
+**
+** Compile this source-code file into a shared library using a command
+** similar to this:
+**
+** gcc -c pikchr.so -DPIKCHR_TCL -shared pikchr.c
+*/
+static int pik_tcl_command(
+ ClientData clientData, /* Not Used */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ int w, h; /* Width and height of the pikchr */
+ const char *zIn; /* Source text input */
+ char *zOut; /* SVG output text */
+ Tcl_Obj *pRes; /* The result TCL object */
+
+ (void)clientData;
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "PIKCHR_SOURCE_TEXT");
+ return TCL_ERROR;
+ }
+ zIn = Tcl_GetString(objv[1]);
+ w = h = 0;
+ zOut = pikchr(zIn, "pikchr", 0, &w, &h);
+ if( zOut==0 ){
+ return TCL_ERROR; /* Out of memory */
+ }
+ pRes = Tcl_NewObj();
+ Tcl_ListObjAppendElement(0, pRes, Tcl_NewStringObj(zOut, -1));
+ free(zOut);
+ Tcl_ListObjAppendElement(0, pRes, Tcl_NewIntObj(w));
+ Tcl_ListObjAppendElement(0, pRes, Tcl_NewIntObj(h));
+ Tcl_SetObjResult(interp, pRes);
+ return TCL_OK;
+}
+
+#ifndef PACKAGE_NAME
+# define PACKAGE_NAME "pikchr"
+#endif
+#ifndef PACKAGE_VERSION
+# define PACKAGE_VERSION "1.0"
+#endif
+
+/* Invoke this routine to register the "pikchr" command with the interpreter
+** given in the argument */
+int Pikchr_Init(Tcl_Interp *interp){
+ Tcl_CreateObjCommand(interp, "pikchr", pik_tcl_command, 0, 0);
+ Tcl_PkgProvide(interp, PACKAGE_NAME, PACKAGE_VERSION);
+ return TCL_OK;
+}
+
+
+#endif /* PIKCHR_TCL */
+
+
+#line 8350 "pikchr.c"