diff options
| author | cashmere <cashmere@autistici.org> | 2026-09-04 09:05:24 +0200 |
|---|---|---|
| committer | cashmere <cashmere@autistici.org> | 2026-09-04 09:05:24 +0200 |
| commit | 4f49b16079b27d7ce633619e4cddfdf2bffbc952 (patch) | |
| tree | c296c1e7f3c3e2f6859c6d3a1b1b208779b89679 /extsrc/pikchr.c | |
| download | relic-master.tar.gz | |
Diffstat (limited to 'extsrc/pikchr.c')
| -rw-r--r-- | extsrc/pikchr.c | 8350 |
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 as that is an HTML-ism and is not valid in XML. +** +** * The "&" character is changed into "&" 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, "<", 4); break; } + case '>': { pik_append(p, ">", 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, "&", 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, "\", -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 +** "<" 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, "→" }, /* Same as -> */ + { 12, T_RARROW, "→" }, /* Same as -> */ + { 6, T_LARROW, "←" }, /* Same as <- */ + { 11, T_LARROW, "←" }, /* Same as <- */ + { 16, T_LRARROW, "↔" }, /* 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("<"); + }else if( c=='>' ){ + printf(">"); + }else if( c=='&' ){ + printf("&"); + }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" |
