/* This file is automatically generated by Lemon from input grammar
** source file "pikchr.y". */
/*
** Zero-Clause BSD license:
**
** Copyright (C) 2020-09-01 by D. Richard Hipp <drh@sqlite.org>
**
** Permission to use, copy, modify, and/or distribute this software for
** any purpose with or without fee is hereby granted.
**
****************************************************************************
**
** This software translates a PIC-inspired diagram language into SVG.
**
** PIKCHR (pronounced like "picture") is *mostly* backwards compatible
** with legacy PIC, though some features of legacy PIC are removed
** (for example, the "sh" command is removed for security) and
** many enhancements are added.
**
** PIKCHR is designed for use in an internet facing web environment.
** In particular, PIKCHR is designed to safely generate benign SVG from
** source text that provided by a hostile agent.
**
** This code was originally written by D. Richard Hipp using documentation
** from prior PIC implementations but without reference to prior code.
** All of the code in this project is original.
**
** This file implements a C-language subroutine that accepts a string
** of PIKCHR language text and generates a second string of SVG output that
** renders the drawing defined by the input. Space to hold the returned
** string is obtained from malloc() and should be freed by the caller.
** NULL might be returned if there is a memory allocation error.
**
** If there are errors in the PIKCHR input, the output will consist of an
** error message and the original PIKCHR input text (inside of <pre>...</pre>).
**
** The subroutine implemented by this file is intended to be stand-alone.
** It uses no external routines other than routines commonly found in
** the standard C library.
**
****************************************************************************
** COMPILING:
**
** The original source text is a mixture of C99 and "Lemon"
** (See https://sqlite.org/src/file/doc/lemon.html). Lemon is an LALR(1)
** parser generator program, similar to Yacc. The grammar of the
** input language is specified in Lemon. C-code is attached. Lemon
** runs to generate a single output file ("pikchr.c") which is then
** compiled to generate the Pikchr library. This header comment is
** preserved in the Lemon output, so you might be reading this in either
** the generated "pikchr.c" file that is output by Lemon, or in the
** "pikchr.y" source file that is input into Lemon. If you make changes,
** you should change the input source file "pikchr.y", not the
** Lemon-generated output file.
**
** Basic compilation steps:
**
** lemon pikchr.y
** cc pikchr.c -o pikchr.o
**
** Add -DPIKCHR_SHELL to add a main() routine that reads input files
** and sends them through Pikchr, for testing. Add -DPIKCHR_FUZZ for
** -fsanitizer=fuzzer testing.
**
****************************************************************************
** IMPLEMENTATION NOTES (for people who want to understand the internal
** operation of this software, perhaps to extend the code or to fix bugs):
**
** Each call to pikchr() uses a single instance of the Pik structure to
** track its internal state. The Pik structure lives for the duration
** of the pikchr() call.
**
** The input is a sequence of objects or "elements". Each element is
** parsed into a PElem object. These are stored on an extensible array
** called PEList. All parameters to each PElem are computed as the
** object is parsed. (Hence, the parameters to a PElem may only refer
** to prior elements.) Once the PElem is completely assembled, it is
** added to the end of a PEList and never changes thereafter - except,
** PElem objects that are part of a "[...]" block might have their
** absolute position shifted when the outer [...] block is positioned.
** But apart from this repositioning, PElem objects are unchanged once
** they are added to the list. The order of elements on a PEList does
** not change.
**
** After all input has been parsed, the top-level PEList is walked to
** generate output. Sub-lists resulting from [...] blocks are scanned
** as they are encountered. All input must be collected and parsed ahead
** of output generation because the size and position of elements must be
** known in order to compute a bounding box on the output.
**
** Each PElem is on a "layer". (The common case is that all PElem's are
** on a single layer, but multiple layers are possible.) A separate pass
** is made through the list for each layer.
**
** After all output is generated, the Pik object and all the PEList
** and PElem objects are deallocated and the generated output string is
** returned. Upon any error, the Pik.nErr flag is set, processing quickly
** stops, and the stack unwinds. No attempt is made to continue reading
** input after an error.
**
** Most elements begin with a class name like "box" or "arrow" or "move".
** There is a class named "text" which is used for elements that begin
** with a string literal. You can also specify the "text" class.
** A Sublist ("[...]") is a single object that contains a pointer to
** its subelements, all gathered onto a separate PEList object.
**
** Variables go into PVar objects that form a linked list.
**
** Each PElem has zero or one names. Input constructs that attempt
** to assign a new name from an older name, for example:
**
** Abc: Abc + (0.5cm, 0)
**
** Statements like these generate a new "noop" object at the specified
** place and with the given name. As place-names are searched by scanning
** the list in reverse order, this has the effect of overriding the "Abc"
** name when referenced by subsequent objects.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <assert.h>
#define count(X) (sizeof(X)/sizeof(X[0]))
#ifndef M_PI
# define M_PI 3.1415926535897932385
#endif
/* Tag intentionally unused parameters with this macro to prevent
** compiler warnings with -Wextra */
#define UNUSED_PARAMETER(X) (void)(X)
typedef struct Pik Pik; /* Complete parsing context */
typedef struct PToken PToken; /* A single token */
typedef struct PElem PElem; /* A single diagram object or "element" */
typedef struct PEList PEList; /* A list of elements */
typedef struct PClass PClass; /* Description of elements types */
typedef double PNum; /* Numeric value */
typedef struct PRel PRel; /* Absolute or percentage value */
typedef struct PPoint PPoint; /* A position in 2-D space */
typedef struct PVar PVar; /* script-defined variable */
typedef struct PBox PBox; /* A bounding box */
/* Compass points */
#define CP_N 1
#define CP_NE 2
#define CP_E 3
#define CP_SE 4
#define CP_S 5
#define CP_SW 6
#define CP_W 7
#define CP_NW 8
#define CP_C 9 /* .center or .c */
#define CP_END 10 /* .end */
#define CP_START 11 /* .start */
/* Heading angles corresponding to compass points */
static const PNum pik_hdg_angle[] = {
/* none */ 0.0,
/* N */ 0.0,
/* NE */ 45.0,
/* E */ 90.0,
/* SE */ 135.0,
/* S */ 180.0,
/* SW */ 225.0,
/* W */ 270.0,
/* NW */ 315.0,
/* C */ 0.0,
};
/* Built-in functions */
#define FN_ABS 0
#define FN_COS 1
#define FN_INT 2
#define FN_MAX 3
#define FN_MIN 4
#define FN_SIN 5
#define FN_SQRT 6
/* Text position and style flags. Stored in PToken.eCode so limited
** to 15 bits. */
#define TP_LJUST 0x0001 /* left justify...... */
#define TP_RJUST 0x0002 /* ...Right justify */
#define TP_JMASK 0x0003 /* Mask for justification bits */
#define TP_ABOVE2 0x0004 /* Position text way above PElem.ptAt */
#define TP_ABOVE 0x0008 /* Position text above PElem.ptAt */
#define TP_CENTER 0x0010 /* On the line */
#define TP_BELOW 0x0020 /* Position text below PElem.ptAt */
#define TP_BELOW2 0x0040 /* Position text way below PElem.ptAt */
#define TP_VMASK 0x007c /* Mask for text positioning flags */
#define TP_BIG 0x0100 /* Larger font */
#define TP_SMALL 0x0200 /* Smaller font */
#define TP_XTRA 0x0400 /* Amplify TP_BIG or TP_SMALL */
#define TP_SZMASK 0x0700 /* Font size mask */
#define TP_ITALIC 0x1000 /* Italic font */
#define TP_BOLD 0x2000 /* Bold font */
#define TP_FMASK 0x3000 /* Mask for font style */
#define TP_ALIGN 0x4000 /* Rotate to align with the line */
/* An object to hold a position in 2-D space */
struct PPoint {
PNum x, y; /* X and Y coordinates */
};
/* A bounding box */
struct PBox {
PPoint sw, ne; /* Lower-left and top-right corners */
};
/* An Absolute or a relative distance. The absolute distance
** is stored in rAbs and the relative distance is stored in rRel.
** Usually, one or the other will be 0.0. When using a PRel to
** update an existing value, the computation is usually something
** like this:
**
** value = PRel.rAbs + value*PRel.rRel
**
*/
struct PRel {
PNum rAbs; /* Absolute value */
PNum rRel; /* Value relative to current value */
};
/* A variable created by the ID = EXPR construct of the PIKCHR script
**
** PIKCHR (and PIC) scripts do not use many varaibles, so it is reasonable
** to store them all on a linked list.
*/
struct PVar {
const char *zName; /* Name of the variable */
PNum val; /* Value of the variable */
PVar *pNext; /* Next variable in a list of them all */
};
/* A single token in the parser input stream
*/
struct PToken {
const char *z; /* Pointer to the token text */
unsigned int n; /* Length of the token in bytes */
short int eCode; /* Auxiliary code */
unsigned char eType; /* The numeric parser code */
unsigned char eEdge; /* Corner value for corner keywords */
};
/* Return negative, zero, or positive if pToken is less than, equal to
** or greater than the zero-terminated string z[]
*/
static int pik_token_eq(PToken *pToken, const char *z){
int c = strncmp(pToken->z,z,pToken->n);
if( c==0 && z[pToken->n]!=0 ) c = -1;
return c;
}
/* Extra token types not generated by LEMON but needed by the
** tokenizer
*/
#define T_WHITESPACE 254 /* Whitespace of comments */
#define T_ERROR 255 /* Any text that is not a valid token */
/* Directions of movement */
#define DIR_RIGHT 0
#define DIR_DOWN 1
#define DIR_LEFT 2
#define DIR_UP 3
#define ValidDir(X) ((X)>=0 && (X)<=3)
#define IsUpDown(X) (((X)&1)==1)
#define IsLeftRight(X) (((X)&1)==0)
/* Bitmask for the various attributes for PElem. These bits are
** collected in PElem.mProp and PElem.mCalc to check for constraint
** errors. */
#define A_WIDTH 0x0001
#define A_HEIGHT 0x0002
#define A_RADIUS 0x0004
#define A_THICKNESS 0x0008
#define A_DASHED 0x0010 /* Includes "dotted" */
#define A_FILL 0x0020
#define A_COLOR 0x0040
#define A_ARROW 0x0080
#define A_FROM 0x0100
#define A_CW 0x0200
#define A_AT 0x0400
#define A_TO 0x0800 /* one or more movement attributes */
/* A single element */
struct PElem {
const PClass *type; /* Element type */
PToken errTok; /* Reference token for error messages */
PPoint ptAt; /* Reference point for the object */
PPoint ptEnter, ptExit; /* Entry and exit points */
PEList *pSublist; /* Substructure for [...] elements */
char *zName; /* Name assigned to this element */
PNum w; /* "width" property */
PNum h; /* "height" property */
PNum rad; /* "radius" property */
PNum sw; /* "thickness" property. (Mnemonic: "stroke width")*/
PNum dotted; /* "dotted" property. <=0.0 for off */
PNum dashed; /* "dashed" property. <=0.0 for off */
PNum fill; /* "fill" property. Negative for off */
PNum color; /* "color" property */
PPoint with; /* Position constraint from WITH clause */
char eWith; /* Type of heading point on WITH clause */
char cw; /* True for clockwise arc */
char larrow; /* Arrow at beginning (<- or <->) */
char rarrow; /* Arrow at end (-> or <->) */
char bClose; /* True if "close" is seen */
char bChop; /* True if "chop" is seen */
unsigned char nTxt; /* Number of text values */
unsigned mProp; /* Masks of properties set so far */
unsigned mCalc; /* Values computed from other constraints */
PToken aTxt[5]; /* Text with .eCode holding TP flags */
int iLayer; /* Rendering order */
int inDir, outDir; /* Entry and exit directions */
int nPath; /* Number of path points */
PPoint *aPath; /* Array of path points */
PBox bbox; /* Bounding box */
};
/* A list of elements */
struct PEList {
int n; /* Number of elements in the list */
int nAlloc; /* Allocated slots in a[] */
PElem **a; /* Pointers to individual elements */
};
/* Each call to the pikchr() subroutine uses an instance of the following
** object to pass around context to all of its subroutines.
*/
struct Pik {
unsigned nErr; /* Number of errors seen */
const char *zIn; /* Input PIKCHR-language text. zero-terminated */
unsigned int nIn; /* Number of bytes in zIn */
char *zOut; /* Result accumulates here */
unsigned int nOut; /* Bytes written to zOut[] so far */
unsigned int nOutAlloc; /* Space allocated to zOut[] */
unsigned char eDir; /* Current direction */
unsigned int mFlags; /* Flags passed to pikchr() */
PElem *cur; /* Element under construction */
PEList *list; /* Element list under construction */
PVar *pVar; /* Application-defined variables */
PBox bbox; /* Bounding box around all elements */
/* Cache of layout values. <=0.0 for unknown... */
PNum rScale; /* Multiply to convert inches to pixels */
PNum fontScale; /* Scale fonts by this percent */
PNum charWidth; /* Character width */
PNum charHeight; /* Character height */
PNum wArrow; /* Width of arrowhead at the fat end */
PNum hArrow; /* Ht of arrowhead - dist from tip to fat end */
char bLayoutVars; /* True if cache is valid */
char thenFlag; /* True if "then" seen */
char samePath; /* aTPath copied by "same" */
const char *zClass; /* Class name for the <svg> */
int wSVG, hSVG; /* Width and height of the <svg> */
/* Paths for lines are constructed here first, then transferred into
** the PElem object at the end: */
int nTPath; /* Number of entries on aTPath[] */
int mTPath; /* For last entry, 1: x set, 2: y set */
PPoint aTPath[1000]; /* Path under construction */
};
/*
** Flag values for Pik.mFlags (to be picked up by makeheaders on systems
** that use makeheaders.
*/
#undef INTERFACE
#define INTERFACE 1
#if INTERFACE
#define PIKCHR_INCLUDE_SOURCE 0x0001 /* Include Pikchr src in SVG output */
#endif /* INTERFACE */
/*
** The behavior of an object class is defined by an instance of
** this structure. This is the "virtual method" table.
*/
struct PClass {
const char *zName; /* Name of class */
char isLine; /* True if a line class */
char eJust; /* Use box-style text justification */
void (*xInit)(Pik*,PElem*); /* Initializer */
void (*xNumProp)(Pik*,PElem*,PToken*); /* Value change notification */
void (*xCheck)(Pik*,PElem*); /* Checks to after parsing */
PPoint (*xChop)(Pik*,PElem*,PPoint*); /* Chopper */
PPoint (*xOffset)(Pik*,PElem*,int); /* Offset from .c to edge point */
void (*xFit)(Pik*,PElem*,PNum w,PNum h); /* Size to fit text */
void (*xRender)(Pik*,PElem*); /* Render */
};
/* Forward declarations */
static void pik_append(Pik*, const char*,int);
static void pik_append_text(Pik*,const char*,int,int);
static void pik_append_num(Pik*,const char*,PNum);
static void pik_append_point(Pik*,const char*,PPoint*);
static void pik_append_x(Pik*,const char*,PNum,const char*);
static void pik_append_y(Pik*,const char*,PNum,const char*);
static void pik_append_xy(Pik*,const char*,PNum,PNum);
static void pik_append_dis(Pik*,const char*,PNum,const char*);
static void pik_append_arc(Pik*,PNum,PNum,PNum,PNum);
static void pik_append_clr(Pik*,const char*,PNum,const char*);
static void pik_append_style(Pik*,PElem*,int);
static void pik_append_txt(Pik*,PElem*, PBox*);
static void pik_draw_arrowhead(Pik*,PPoint*pFrom,PPoint*pTo,PElem*);
static void pik_chop(PPoint*pFrom,PPoint*pTo,PNum);
static void pik_error(Pik*,PToken*,const char*);
static void pik_elist_free(Pik*,PEList*);
static void pik_elem_free(Pik*,PElem*);
static void pik_render(Pik*,PEList*);
static PEList *pik_elist_append(Pik*,PEList*,PElem*);
static PElem *pik_elem_new(Pik*,PToken*,PToken*,PEList*);
static void pik_set_direction(Pik*,int);
static void pik_elem_setname(Pik*,PElem*,PToken*);
static void pik_set_var(Pik*,PToken*,PNum,PToken*);
static PNum pik_value(Pik*,const char*,int,int*);
static PNum pik_lookup_color(Pik*,PToken*);
static PNum pik_get_var(Pik*,PToken*);
static PNum pik_atof(PToken*);
static void pik_after_adding_attributes(Pik*,PElem*);
static void pik_elem_move(PElem*,PNum dx, PNum dy);
static void pik_elist_move(PEList*,PNum dx, PNum dy);
static void pik_set_numprop(Pik*,PToken*,PRel*);
static void pik_set_clrprop(Pik*,PToken*,PNum);
static void pik_set_dashed(Pik*,PToken*,PNum*);
static void pik_then(Pik*,PToken*,PElem*);
static void pik_add_direction(Pik*,PToken*,PRel*);
static void pik_move_hdg(Pik*,PRel*,PToken*,PNum,PToken*,PToken*);
static void pik_evenwith(Pik*,PToken*,PPoint*);
static void pik_set_from(Pik*,PElem*,PToken*,PPoint*);
static void pik_add_to(Pik*,PElem*,PToken*,PPoint*);
static void pik_close_path(Pik*,PToken*);
static void pik_set_at(Pik*,PToken*,PPoint*,PToken*);
static short int pik_nth_value(Pik*,PToken*);
static PElem *pik_find_nth(Pik*,PElem*,PToken*);
static PElem *pik_find_byname(Pik*,PElem*,PToken*);
static PPoint pik_place_of_elem(Pik*,PElem*,PToken*);
static int pik_bbox_isempty(PBox*);
static void pik_bbox_init(PBox*);
static void pik_bbox_addbox(PBox*,PBox*);
static void pik_bbox_add_xy(PBox*,PNum,PNum);
static void pik_bbox_addellipse(PBox*,PNum x,PNum y,PNum rx,PNum ry);
static void pik_add_txt(Pik*,PToken*,int);
static int pik_text_length(const PToken *pToken);
static void pik_size_to_fit(Pik*,PToken*);
static int pik_text_position(int,PToken*);
static PNum pik_property_of(PElem*,PToken*);
static PNum pik_func(Pik*,PToken*,PNum,PNum);
static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2);
static PPoint pik_position_at_angle(PNum dist, PNum r, PPoint pt);
static PPoint pik_position_at_hdg(PNum dist, PToken *pD, PPoint pt);
static void pik_same(Pik *p, PElem*, PToken*);
static PPoint pik_nth_vertex(Pik *p, PToken *pNth, PToken *pErr, PElem *pElem);
static PToken pik_next_semantic_token(PToken *pThis);
static void pik_compute_layout_settings(Pik*);
static void pik_behind(Pik*,PElem*);
static PElem *pik_assert(Pik*,PNum,PToken*,PNum);
static PElem *pik_place_assert(Pik*,PPoint*,PToken*,PPoint*);
#line 487 "pikchr.c"
/**************** End of %include directives **********************************/
/* These constants specify the various numeric values for terminal symbols.
***************** Begin token definitions *************************************/
#ifndef T_ID
#define T_ID 1
#define T_EDGEPT 2
#define T_OF 3
#define T_PLUS 4
#define T_MINUS 5
#define T_STAR 6
#define T_SLASH 7
#define T_PERCENT 8
#define T_UMINUS 9
#define T_EOL 10
#define T_ASSIGN 11
#define T_PLACENAME 12
#define T_COLON 13
#define T_ASSERT 14
#define T_LP 15
#define T_EQ 16
#define T_RP 17
#define T_FILL 18
#define T_COLOR 19
#define T_THICKNESS 20
#define T_PRINT 21
#define T_STRING 22
#define T_COMMA 23
#define T_CLASSNAME 24
#define T_LB 25
#define T_RB 26
#define T_UP 27
#define T_DOWN 28
#define T_LEFT 29
#define T_RIGHT 30
#define T_CLOSE 31
#define T_CHOP 32
#define T_FROM 33
#define T_TO 34
#define T_THEN 35
#define T_HEADING 36
#define T_GO 37
#define T_AT 38
#define T_WITH 39
#define T_SAME 40
#define T_AS 41
#define T_FIT 42
#define T_BEHIND 43
#define T_UNTIL 44
#define T_EVEN 45
#define T_DOT_E 46
#define T_HEIGHT 47
#define T_WIDTH 48
#define T_RADIUS 49
#define T_DIAMETER 50
#define T_DOTTED 51
#define T_DASHED 52
#define T_CW 53
#define T_CCW 54
#define T_LARROW 55
#define T_RARROW 56
#define T_LRARROW 57
#define T_INVIS 58
#define T_THICK 59
#define T_THIN 60
#define T_CENTER 61
#define T_LJUST 62
#define T_RJUST 63
#define T_ABOVE 64
#define T_BELOW 65
#define T_ITALIC 66
#define T_BOLD 67
#define T_ALIGNED 68
#define T_BIG 69
#define T_SMALL 70
#define T_AND 71
#define T_LT 72
#define T_GT 73
#define T_ON 74
#define T_WAY 75
#define T_BETWEEN 76
#define T_THE 77
#define T_NTH 78
#define T_VERTEX 79
#define T_TOP 80
#define T_BOTTOM 81
#define T_START 82
#define T_END 83
#define T_IN 84
#define T_DOT_U 85
#define T_LAST 86
#define T_NUMBER 87
#define T_FUNC1 88
#define T_FUNC2 89
#define T_DOT_XY 90
#define T_X 91
#define T_Y 92
#define T_DOT_L 93
#endif
/**************** End token definitions ***************************************/
/* The next sections is a series of control #defines.
** various aspects of the generated parser.
** YYCODETYPE is the data type used to store the integer codes
** that represent terminal and non-terminal symbols.
** "unsigned char" is used if there are fewer than
** 256 symbols. Larger types otherwise.
** YYNOCODE is a number of type YYCODETYPE that is not used for
** any terminal or nonterminal symbol.
** YYFALLBACK If defined, this indicates that one or more tokens
** (also known as: "terminal symbols") have fall-back
** values which should be used if the original symbol
** would not parse. This permits keywords to sometimes
** be used as identifiers, for example.
** YYACTIONTYPE is the data type used for "action codes" - numbers
** that indicate what to do in response to the next
** token.
** pik_parserTOKENTYPE is the data type used for minor type for terminal
** symbols. Background: A "minor type" is a semantic
** value associated with a terminal or non-terminal
** symbols. For example, for an "ID" terminal symbol,
** the minor type might be the name of the identifier.
** Each non-terminal can have a different minor type.
** Terminal symbols all have the same minor type, though.
** This macros defines the minor type for terminal
** symbols.
** YYMINORTYPE is the data type used for all minor types.
** This is typically a union of many types, one of
** which is pik_parserTOKENTYPE. The entry in the union
** for terminal symbols is called "yy0".
** YYSTACKDEPTH is the maximum depth of the parser's stack. If
** zero the stack is dynamically sized using realloc()
** pik_parserARG_SDECL A static variable declaration for the %extra_argument
** pik_parserARG_PDECL A parameter declaration for the %extra_argument
** pik_parserARG_PARAM Code to pass %extra_argument as a subroutine parameter
** pik_parserARG_STORE Code to store %extra_argument into yypParser
** pik_parserARG_FETCH Code to extract %extra_argument from yypParser
** pik_parserCTX_* As pik_parserARG_ except for %extra_context
** YYERRORSYMBOL is the code number of the error symbol. If not
** defined, then do no error processing.
** YYNSTATE the combined number of states.
** YYNRULE the number of rules in the grammar
** YYNTOKEN Number of terminal symbols
** YY_MAX_SHIFT Maximum value for shift actions
** YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions
** YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions
** YY_ERROR_ACTION The yy_action[] code for syntax error
** YY_ACCEPT_ACTION The yy_action[] code for accept
** YY_NO_ACTION The yy_action[] code for no-op
** YY_MIN_REDUCE Minimum value for reduce actions
** YY_MAX_REDUCE Maximum value for reduce actions
*/
#ifndef INTERFACE
# define INTERFACE 1
#endif
/************* Begin control #defines *****************************************/
#define YYCODETYPE unsigned char
#define YYNOCODE 130
#define YYACTIONTYPE unsigned short int
#define pik_parserTOKENTYPE PToken
typedef union {
int yyinit;
pik_parserTOKENTYPE yy0;
PRel yy60;
PEList* yy72;
PNum yy73;
int yy74;
PPoint yy139;
PElem* yy254;
} YYMINORTYPE;
#ifndef YYSTACKDEPTH
#define YYSTACKDEPTH 100
#endif
#define pik_parserARG_SDECL
#define pik_parserARG_PDECL
#define pik_parserARG_PARAM
#define pik_parserARG_FETCH
#define pik_parserARG_STORE
#define pik_parserCTX_SDECL Pik *p;
#define pik_parserCTX_PDECL ,Pik *p
#define pik_parserCTX_PARAM ,p
#define pik_parserCTX_FETCH Pik *p=yypParser->p;
#define pik_parserCTX_STORE yypParser->p=p;
#define YYFALLBACK 1
#define YYNSTATE 157
#define YYNRULE 151
#define YYNRULE_WITH_ACTION 111
#define YYNTOKEN 94
#define YY_MAX_SHIFT 156
#define YY_MIN_SHIFTREDUCE 276
#define YY_MAX_SHIFTREDUCE 426
#define YY_ERROR_ACTION 427
#define YY_ACCEPT_ACTION 428
#define YY_NO_ACTION 429
#define YY_MIN_REDUCE 430
#define YY_MAX_REDUCE 580
/************* End control #defines *******************************************/
#define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])))
/* Define the yytestcase() macro to be a no-op if is not already defined
** otherwise.
**
** Applications can choose to define yytestcase() in the %include section
** to a macro that can assist in verifying code coverage. For production
** code the yytestcase() macro should be turned off. But it is useful
** for testing.
*/
#ifndef yytestcase
# define yytestcase(X)
#endif
/* Next are the tables used to determine what action to take based on the
** current state and lookahead token. These tables are used to implement
** functions that take a state number and lookahead value and return an
** action integer.
**
** Suppose the action integer is N. Then the action is determined as
** follows
**
** 0 <= N <= YY_MAX_SHIFT Shift N. That is, push the lookahead
** token onto the stack and goto state N.
**
** N between YY_MIN_SHIFTREDUCE Shift to an arbitrary state then
** and YY_MAX_SHIFTREDUCE reduce by rule N-YY_MIN_SHIFTREDUCE.
**
** N == YY_ERROR_ACTION A syntax error has occurred.
**
** N == YY_ACCEPT_ACTION The parser accepts its input.
**
** N == YY_NO_ACTION No such action. Denotes unused
** slots in the yy_action[] table.
**
** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE
** and YY_MAX_REDUCE
**
** The action table is constructed as a single large table named yy_action[].
** Given state S and lookahead X, the action is computed as either:
**
** (A) N = yy_action[ yy_shift_ofst[S] + X ]
** (B) N = yy_default[S]
**
** The (A) formula is preferred. The B formula is used instead if
** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X.
**
** The formulas above are for computing the action when the lookahead is
** a terminal symbol. If the lookahead is a non-terminal (as occurs after
** a reduce action) then the yy_reduce_ofst[] array is used in place of
** the yy_shift_ofst[] array.
**
** The following are the tables generated in this section:
**
** yy_action[] A single table containing all actions.
** yy_lookahead[] A table containing the lookahead for each entry in
** yy_action. Used to detect hash collisions.
** yy_shift_ofst[] For each state, the offset into yy_action for
** shifting terminals.
** yy_reduce_ofst[] For each state, the offset into yy_action for
** shifting non-terminals after a reduce.
** yy_default[] Default action for each state.
**
*********** Begin parsing tables **********************************************/
#define YY_ACTTAB_COUNT (1159)
static const YYACTIONTYPE yy_action[] = {
/* 0 */ 554, 430, 21, 436, 25, 71, 126, 142, 50, 46,
/* 10 */ 554, 33, 437, 111, 118, 154, 117, 125, 411, 412,
/* 20 */ 327, 538, 78, 512, 539, 540, 554, 62, 61, 60,
/* 30 */ 59, 310, 311, 6, 5, 30, 143, 29, 4, 69,
/* 40 */ 124, 24, 323, 64, 558, 294, 45, 327, 327, 327,
/* 50 */ 327, 409, 410, 328, 329, 330, 331, 332, 333, 334,
/* 60 */ 335, 457, 62, 61, 60, 59, 119, 431, 438, 25,
/* 70 */ 33, 457, 67, 434, 27, 286, 478, 154, 117, 411,
/* 80 */ 412, 327, 28, 78, 156, 344, 298, 457, 428, 23,
/* 90 */ 318, 8, 310, 311, 6, 5, 30, 80, 29, 4,
/* 100 */ 69, 124, 81, 323, 64, 475, 154, 117, 327, 327,
/* 110 */ 327, 327, 409, 410, 328, 329, 330, 331, 332, 333,
/* 120 */ 334, 335, 379, 419, 41, 57, 58, 73, 62, 61,
/* 130 */ 60, 59, 32, 362, 382, 383, 20, 287, 379, 419,
/* 140 */ 40, 57, 58, 77, 316, 296, 76, 60, 59, 362,
/* 150 */ 425, 424, 20, 67, 337, 337, 337, 337, 337, 337,
/* 160 */ 337, 337, 337, 337, 366, 150, 425, 424, 387, 362,
/* 170 */ 364, 151, 62, 61, 60, 59, 301, 34, 42, 146,
/* 180 */ 26, 123, 418, 365, 152, 388, 389, 390, 392, 77,
/* 190 */ 65, 296, 76, 73, 395, 396, 397, 398, 418, 104,
/* 200 */ 2, 420, 421, 422, 423, 142, 7, 115, 378, 149,
/* 210 */ 148, 121, 120, 154, 117, 104, 3, 420, 421, 422,
/* 220 */ 423, 128, 408, 115, 378, 149, 148, 379, 419, 407,
/* 230 */ 57, 58, 116, 343, 121, 106, 416, 71, 362, 142,
/* 240 */ 63, 56, 414, 115, 480, 111, 118, 154, 117, 36,
/* 250 */ 132, 133, 52, 35, 9, 425, 424, 11, 136, 134,
/* 260 */ 62, 61, 60, 59, 62, 61, 60, 59, 143, 395,
/* 270 */ 396, 397, 398, 377, 556, 74, 556, 377, 13, 51,
/* 280 */ 85, 39, 142, 43, 14, 141, 140, 418, 122, 118,
/* 290 */ 154, 117, 38, 364, 151, 136, 134, 62, 61, 60,
/* 300 */ 59, 15, 66, 112, 104, 346, 420, 421, 422, 423,
/* 310 */ 18, 143, 115, 378, 149, 148, 51, 516, 1, 54,
/* 320 */ 17, 16, 141, 140, 55, 510, 154, 117, 19, 38,
/* 330 */ 139, 135, 415, 379, 370, 363, 57, 58, 62, 61,
/* 340 */ 60, 59, 368, 369, 103, 68, 153, 56, 155, 37,
/* 350 */ 289, 290, 291, 429, 293, 116, 516, 17, 16, 379,
/* 360 */ 429, 516, 57, 58, 516, 19, 429, 139, 135, 415,
/* 370 */ 362, 379, 456, 56, 57, 58, 429, 429, 62, 61,
/* 380 */ 60, 59, 362, 429, 379, 56, 429, 57, 58, 429,
/* 390 */ 379, 377, 429, 57, 58, 362, 429, 47, 56, 429,
/* 400 */ 429, 103, 129, 127, 56, 419, 456, 515, 154, 117,
/* 410 */ 104, 62, 61, 60, 59, 362, 429, 429, 115, 378,
/* 420 */ 149, 148, 429, 379, 137, 429, 57, 58, 513, 154,
/* 430 */ 117, 429, 425, 424, 362, 429, 104, 56, 379, 138,
/* 440 */ 10, 57, 58, 429, 115, 378, 149, 148, 104, 362,
/* 450 */ 429, 379, 56, 429, 57, 58, 115, 378, 149, 148,
/* 460 */ 429, 104, 362, 429, 418, 44, 429, 104, 429, 115,
/* 470 */ 378, 149, 148, 429, 429, 115, 378, 149, 148, 429,
/* 480 */ 429, 104, 429, 420, 421, 422, 423, 379, 429, 115,
/* 490 */ 57, 58, 62, 61, 60, 59, 429, 429, 362, 429,
/* 500 */ 104, 48, 429, 105, 429, 429, 429, 429, 115, 378,
/* 510 */ 149, 148, 118, 154, 117, 104, 461, 411, 412, 327,
/* 520 */ 429, 12, 105, 115, 378, 149, 148, 419, 104, 429,
/* 530 */ 429, 118, 154, 117, 147, 446, 115, 378, 149, 148,
/* 540 */ 429, 429, 75, 75, 429, 429, 327, 327, 327, 327,
/* 550 */ 409, 410, 429, 147, 425, 424, 429, 429, 71, 429,
/* 560 */ 142, 429, 429, 429, 104, 479, 111, 118, 154, 117,
/* 570 */ 86, 72, 115, 378, 149, 148, 429, 429, 429, 118,
/* 580 */ 154, 117, 429, 71, 429, 142, 418, 429, 429, 143,
/* 590 */ 473, 111, 118, 154, 117, 429, 429, 62, 61, 60,
/* 600 */ 59, 147, 429, 429, 429, 420, 421, 422, 423, 71,
/* 610 */ 342, 142, 429, 429, 143, 429, 467, 111, 118, 154,
/* 620 */ 117, 429, 71, 429, 142, 429, 429, 429, 429, 466,
/* 630 */ 111, 118, 154, 117, 429, 71, 429, 142, 429, 429,
/* 640 */ 143, 429, 499, 111, 118, 154, 117, 71, 429, 142,
/* 650 */ 87, 429, 429, 143, 131, 111, 118, 154, 117, 118,
/* 660 */ 154, 117, 429, 71, 429, 142, 143, 429, 429, 429,
/* 670 */ 507, 111, 118, 154, 117, 429, 429, 429, 143, 429,
/* 680 */ 429, 147, 71, 429, 142, 429, 429, 429, 429, 509,
/* 690 */ 111, 118, 154, 117, 143, 62, 61, 60, 59, 429,
/* 700 */ 429, 62, 61, 60, 59, 429, 429, 71, 341, 142,
/* 710 */ 429, 429, 429, 143, 506, 111, 118, 154, 117, 71,
/* 720 */ 49, 142, 62, 61, 60, 59, 508, 111, 118, 154,
/* 730 */ 117, 429, 71, 429, 142, 381, 429, 429, 143, 505,
/* 740 */ 111, 118, 154, 117, 71, 419, 142, 429, 429, 429,
/* 750 */ 143, 504, 111, 118, 154, 117, 429, 71, 429, 142,
/* 760 */ 429, 429, 429, 143, 503, 111, 118, 154, 117, 71,
/* 770 */ 429, 142, 425, 424, 429, 143, 502, 111, 118, 154,
/* 780 */ 117, 429, 429, 71, 429, 142, 429, 429, 143, 429,
/* 790 */ 501, 111, 118, 154, 117, 429, 62, 61, 60, 59,
/* 800 */ 143, 429, 429, 429, 418, 429, 429, 429, 71, 380,
/* 810 */ 142, 429, 429, 429, 143, 144, 111, 118, 154, 117,
/* 820 */ 71, 429, 142, 420, 421, 422, 423, 145, 111, 118,
/* 830 */ 154, 117, 429, 70, 429, 142, 429, 429, 429, 143,
/* 840 */ 110, 111, 118, 154, 117, 71, 429, 142, 429, 429,
/* 850 */ 429, 143, 130, 111, 118, 154, 117, 429, 71, 429,
/* 860 */ 142, 429, 92, 429, 143, 463, 111, 118, 154, 117,
/* 870 */ 429, 118, 154, 117, 92, 429, 143, 22, 462, 462,
/* 880 */ 429, 429, 429, 118, 154, 117, 432, 438, 25, 143,
/* 890 */ 79, 105, 434, 147, 429, 429, 429, 449, 429, 31,
/* 900 */ 118, 154, 117, 156, 446, 147, 429, 429, 23, 429,
/* 910 */ 429, 429, 548, 107, 431, 438, 25, 429, 92, 429,
/* 920 */ 434, 429, 147, 429, 429, 429, 429, 118, 154, 117,
/* 930 */ 92, 156, 429, 429, 108, 108, 23, 429, 429, 118,
/* 940 */ 154, 117, 92, 429, 429, 429, 109, 109, 429, 147,
/* 950 */ 429, 118, 154, 117, 105, 429, 429, 429, 458, 84,
/* 960 */ 429, 147, 429, 118, 154, 117, 97, 435, 118, 154,
/* 970 */ 117, 98, 429, 147, 429, 118, 154, 117, 88, 429,
/* 980 */ 118, 154, 117, 82, 429, 147, 429, 118, 154, 117,
/* 990 */ 147, 429, 118, 154, 117, 429, 99, 147, 429, 429,
/* 1000 */ 429, 89, 147, 429, 429, 118, 154, 117, 429, 147,
/* 1010 */ 118, 154, 117, 90, 147, 62, 61, 60, 59, 83,
/* 1020 */ 429, 429, 118, 154, 117, 429, 100, 147, 118, 154,
/* 1030 */ 117, 429, 147, 429, 53, 118, 154, 117, 91, 62,
/* 1040 */ 61, 60, 59, 429, 147, 101, 429, 118, 154, 117,
/* 1050 */ 147, 102, 377, 429, 118, 154, 117, 147, 429, 429,
/* 1060 */ 118, 154, 117, 93, 429, 429, 429, 429, 94, 147,
/* 1070 */ 429, 429, 118, 154, 117, 95, 147, 118, 154, 117,
/* 1080 */ 96, 429, 147, 429, 118, 154, 117, 530, 429, 118,
/* 1090 */ 154, 117, 529, 429, 147, 429, 118, 154, 117, 147,
/* 1100 */ 429, 118, 154, 117, 528, 429, 147, 429, 429, 429,
/* 1110 */ 527, 147, 429, 118, 154, 117, 429, 429, 147, 118,
/* 1120 */ 154, 117, 113, 147, 429, 429, 429, 114, 429, 429,
/* 1130 */ 429, 118, 154, 117, 429, 147, 118, 154, 117, 429,
/* 1140 */ 429, 147, 429, 429, 429, 429, 429, 429, 429, 429,
/* 1150 */ 429, 429, 429, 147, 429, 429, 429, 429, 147,
};
static const YYCODETYPE yy_lookahead[] = {
/* 0 */ 0, 0, 128, 96, 97, 98, 100, 100, 4, 5,
/* 10 */ 10, 10, 105, 106, 107, 108, 109, 100, 18, 19,
/* 20 */ 20, 99, 22, 100, 102, 103, 26, 4, 5, 6,
/* 30 */ 7, 31, 32, 33, 34, 35, 129, 37, 38, 39,
/* 40 */ 40, 101, 42, 43, 127, 23, 23, 47, 48, 49,
/* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
/* 60 */ 60, 0, 4, 5, 6, 7, 94, 95, 96, 97,
/* 70 */ 10, 10, 3, 101, 120, 17, 107, 108, 109, 18,
/* 80 */ 19, 20, 122, 22, 112, 17, 26, 26, 116, 117,
/* 90 */ 2, 23, 31, 32, 33, 34, 35, 110, 37, 38,
/* 100 */ 39, 40, 110, 42, 43, 107, 108, 109, 47, 48,
/* 110 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
/* 120 */ 59, 60, 1, 2, 36, 4, 5, 46, 4, 5,
/* 130 */ 6, 7, 123, 12, 91, 92, 15, 17, 1, 2,
/* 140 */ 16, 4, 5, 22, 2, 24, 25, 6, 7, 12,
/* 150 */ 29, 30, 15, 84, 61, 62, 63, 64, 65, 66,
/* 160 */ 67, 68, 69, 70, 24, 25, 29, 30, 1, 12,
/* 170 */ 24, 25, 4, 5, 6, 7, 8, 16, 36, 12,
/* 180 */ 15, 14, 61, 24, 25, 18, 19, 20, 21, 22,
/* 190 */ 41, 24, 25, 46, 27, 28, 29, 30, 61, 78,
/* 200 */ 38, 80, 81, 82, 83, 100, 71, 86, 87, 88,
/* 210 */ 89, 106, 107, 108, 109, 78, 38, 80, 81, 82,
/* 220 */ 83, 45, 39, 86, 87, 88, 89, 1, 2, 39,
/* 230 */ 4, 5, 86, 17, 129, 78, 76, 98, 12, 100,
/* 240 */ 93, 15, 76, 86, 105, 106, 107, 108, 109, 99,
/* 250 */ 75, 77, 102, 103, 3, 29, 30, 3, 2, 3,
/* 260 */ 4, 5, 6, 7, 4, 5, 6, 7, 129, 27,
/* 270 */ 28, 29, 30, 17, 124, 125, 126, 17, 3, 23,
/* 280 */ 98, 36, 100, 23, 3, 29, 30, 61, 106, 107,
/* 290 */ 108, 109, 36, 24, 25, 2, 3, 4, 5, 6,
/* 300 */ 7, 3, 3, 90, 78, 73, 80, 81, 82, 83,
/* 310 */ 23, 129, 86, 87, 88, 89, 23, 46, 13, 15,
/* 320 */ 64, 65, 29, 30, 15, 107, 108, 109, 72, 36,
/* 330 */ 74, 75, 76, 1, 26, 12, 4, 5, 4, 5,
/* 340 */ 6, 7, 26, 26, 12, 3, 85, 15, 79, 11,
/* 350 */ 18, 19, 20, 130, 22, 86, 85, 64, 65, 1,
/* 360 */ 130, 90, 4, 5, 93, 72, 130, 74, 75, 76,
/* 370 */ 12, 1, 2, 15, 4, 5, 130, 130, 4, 5,
/* 380 */ 6, 7, 12, 130, 1, 15, 130, 4, 5, 130,
/* 390 */ 1, 17, 130, 4, 5, 12, 130, 23, 15, 130,
/* 400 */ 130, 12, 44, 45, 15, 2, 36, 107, 108, 109,
/* 410 */ 78, 4, 5, 6, 7, 12, 130, 130, 86, 87,
/* 420 */ 88, 89, 130, 1, 2, 130, 4, 5, 107, 108,
/* 430 */ 109, 130, 29, 30, 12, 130, 78, 15, 1, 2,
/* 440 */ 33, 4, 5, 130, 86, 87, 88, 89, 78, 12,
/* 450 */ 130, 1, 15, 130, 4, 5, 86, 87, 88, 89,
/* 460 */ 130, 78, 12, 130, 61, 15, 130, 78, 130, 86,
/* 470 */ 87, 88, 89, 130, 130, 86, 87, 88, 89, 130,
/* 480 */ 130, 78, 130, 80, 81, 82, 83, 1, 130, 86,
/* 490 */ 4, 5, 4, 5, 6, 7, 130, 130, 12, 130,
/* 500 */ 78, 15, 130, 98, 130, 130, 130, 130, 86, 87,
/* 510 */ 88, 89, 107, 108, 109, 78, 111, 18, 19, 20,
/* 520 */ 130, 33, 98, 86, 87, 88, 89, 2, 78, 130,
/* 530 */ 130, 107, 108, 109, 129, 111, 86, 87, 88, 89,
/* 540 */ 130, 130, 118, 119, 130, 130, 47, 48, 49, 50,
/* 550 */ 51, 52, 130, 129, 29, 30, 130, 130, 98, 130,
/* 560 */ 100, 130, 130, 130, 78, 105, 106, 107, 108, 109,
/* 570 */ 98, 46, 86, 87, 88, 89, 130, 130, 130, 107,
/* 580 */ 108, 109, 130, 98, 130, 100, 61, 130, 130, 129,
/* 590 */ 105, 106, 107, 108, 109, 130, 130, 4, 5, 6,
/* 600 */ 7, 129, 130, 130, 130, 80, 81, 82, 83, 98,
/* 610 */ 17, 100, 130, 130, 129, 130, 105, 106, 107, 108,
/* 620 */ 109, 130, 98, 130, 100, 130, 130, 130, 130, 105,
/* 630 */ 106, 107, 108, 109, 130, 98, 130, 100, 130, 130,
/* 640 */ 129, 130, 105, 106, 107, 108, 109, 98, 130, 100,
/* 650 */ 98, 130, 130, 129, 105, 106, 107, 108, 109, 107,
/* 660 */ 108, 109, 130, 98, 130, 100, 129, 130, 130, 130,
/* 670 */ 105, 106, 107, 108, 109, 130, 130, 130, 129, 130,
/* 680 */ 130, 129, 98, 130, 100, 130, 130, 130, 130, 105,
/* 690 */ 106, 107, 108, 109, 129, 4, 5, 6, 7, 130,
/* 700 */ 130, 4, 5, 6, 7, 130, 130, 98, 17, 100,
/* 710 */ 130, 130, 130, 129, 105, 106, 107, 108, 109, 98,
/* 720 */ 23, 100, 4, 5, 6, 7, 105, 106, 107, 108,
/* 730 */ 109, 130, 98, 130, 100, 17, 130, 130, 129, 105,
/* 740 */ 106, 107, 108, 109, 98, 2, 100, 130, 130, 130,
/* 750 */ 129, 105, 106, 107, 108, 109, 130, 98, 130, 100,
/* 760 */ 130, 130, 130, 129, 105, 106, 107, 108, 109, 98,
/* 770 */ 130, 100, 29, 30, 130, 129, 105, 106, 107, 108,
/* 780 */ 109, 130, 130, 98, 130, 100, 130, 130, 129, 130,
/* 790 */ 105, 106, 107, 108, 109, 130, 4, 5, 6, 7,
/* 800 */ 129, 130, 130, 130, 61, 130, 130, 130, 98, 17,
/* 810 */ 100, 130, 130, 130, 129, 105, 106, 107, 108, 109,
/* 820 */ 98, 130, 100, 80, 81, 82, 83, 105, 106, 107,
/* 830 */ 108, 109, 130, 98, 130, 100, 130, 130, 130, 129,
/* 840 */ 105, 106, 107, 108, 109, 98, 130, 100, 130, 130,
/* 850 */ 130, 129, 105, 106, 107, 108, 109, 130, 98, 130,
/* 860 */ 100, 130, 98, 130, 129, 105, 106, 107, 108, 109,
/* 870 */ 130, 107, 108, 109, 98, 130, 129, 113, 114, 115,
/* 880 */ 130, 130, 130, 107, 108, 109, 95, 96, 97, 129,
/* 890 */ 114, 98, 101, 129, 130, 130, 130, 121, 130, 123,
/* 900 */ 107, 108, 109, 112, 111, 129, 130, 130, 117, 130,
/* 910 */ 130, 130, 119, 94, 95, 96, 97, 130, 98, 130,
/* 920 */ 101, 130, 129, 130, 130, 130, 130, 107, 108, 109,
/* 930 */ 98, 112, 130, 130, 114, 115, 117, 130, 130, 107,
/* 940 */ 108, 109, 98, 130, 130, 130, 114, 115, 130, 129,
/* 950 */ 130, 107, 108, 109, 98, 130, 130, 130, 114, 98,
/* 960 */ 130, 129, 130, 107, 108, 109, 98, 111, 107, 108,
/* 970 */ 109, 98, 130, 129, 130, 107, 108, 109, 98, 130,
/* 980 */ 107, 108, 109, 98, 130, 129, 130, 107, 108, 109,
/* 990 */ 129, 130, 107, 108, 109, 130, 98, 129, 130, 130,
/* 1000 */ 130, 98, 129, 130, 130, 107, 108, 109, 130, 129,
/* 1010 */ 107, 108, 109, 98, 129, 4, 5, 6, 7, 98,
/* 1020 */ 130, 130, 107, 108, 109, 130, 98, 129, 107, 108,
/* 1030 */ 109, 130, 129, 130, 23, 107, 108, 109, 98, 4,
/* 1040 */ 5, 6, 7, 130, 129, 98, 130, 107, 108, 109,
/* 1050 */ 129, 98, 17, 130, 107, 108, 109, 129, 130, 130,
/* 1060 */ 107, 108, 109, 98, 130, 130, 130, 130, 98, 129,
/* 1070 */ 130, 130, 107, 108, 109, 98, 129, 107, 108, 109,
/* 1080 */ 98, 130, 129, 130, 107, 108, 109, 98, 130, 107,
/* 1090 */ 108, 109, 98, 130, 129, 130, 107, 108, 109, 129,
/* 1100 */ 130, 107, 108, 109, 98, 130, 129, 130, 130, 130,
/* 1110 */ 98, 129, 130, 107, 108, 109, 130, 130, 129, 107,
/* 1120 */ 108, 109, 98, 129, 130, 130, 130, 98, 130, 130,
/* 1130 */ 130, 107, 108, 109, 130, 129, 107, 108, 109, 130,
/* 1140 */ 130, 129, 130, 130, 130, 130, 130, 130, 130, 130,
/* 1150 */ 130, 130, 130, 129, 130, 130, 130, 130, 129, 94,
/* 1160 */ 94, 94, 94, 94, 94, 94, 94, 94, 94, 94,
/* 1170 */ 94, 94, 94, 94, 94, 94, 94, 94, 94, 94,
/* 1180 */ 94, 94, 94, 94, 94, 94, 94, 94, 94, 94,
/* 1190 */ 94, 94, 94, 94, 94, 94, 94, 94, 94, 94,
/* 1200 */ 94, 94, 94, 94, 94, 94, 94, 94, 94, 94,
/* 1210 */ 94, 94, 94, 94, 94, 94, 94, 94, 94, 94,
/* 1220 */ 94, 94, 94, 94, 94, 94, 94, 94, 94, 94,
/* 1230 */ 94, 94, 94, 94, 94, 94, 94, 94, 94, 94,
/* 1240 */ 94, 94, 94, 94, 94, 94, 94, 94, 94, 94,
/* 1250 */ 94, 94, 94,
};
#define YY_SHIFT_COUNT (156)
#define YY_SHIFT_MIN (0)
#define YY_SHIFT_MAX (1035)
static const unsigned short int yy_shift_ofst[] = {
/* 0 */ 167, 121, 137, 137, 137, 137, 137, 137, 137, 137,
/* 10 */ 137, 137, 137, 137, 137, 137, 137, 137, 137, 137,
/* 20 */ 137, 137, 137, 332, 358, 383, 226, 332, 167, 370,
/* 30 */ 370, 0, 61, 167, 403, 389, 383, 389, 422, 437,
/* 40 */ 383, 383, 383, 383, 383, 383, 450, 383, 383, 383,
/* 50 */ 486, 383, 383, 383, 383, 383, 383, 383, 383, 383,
/* 60 */ 383, 383, 383, 499, 157, 157, 157, 157, 157, 525,
/* 70 */ 256, 293, 743, 743, 242, 22, 1159, 1159, 1159, 1159,
/* 80 */ 93, 93, 260, 374, 58, 124, 407, 488, 593, 23,
/* 90 */ 691, 697, 168, 718, 1011, 792, 1035, 334, 334, 334,
/* 100 */ 334, 334, 334, 271, 269, 334, 146, 60, 88, 142,
/* 110 */ 68, 4, 43, 141, 141, 140, 159, 69, 147, 1,
/* 120 */ 81, 120, 161, 165, 149, 162, 178, 183, 190, 176,
/* 130 */ 135, 216, 160, 175, 174, 166, 251, 254, 275, 245,
/* 140 */ 281, 298, 299, 213, 232, 287, 305, 213, 304, 309,
/* 150 */ 308, 316, 317, 323, 261, 342, 338,
};
#define YY_REDUCE_COUNT (79)
#define YY_REDUCE_MIN (-126)
#define YY_REDUCE_MAX (1029)
static const short yy_reduce_ofst[] = {
/* 0 */ -28, -93, 139, 460, 485, 511, 524, 537, 549, 565,
/* 10 */ 584, 609, 621, 634, 646, 659, 671, 685, 710, 722,
/* 20 */ 735, 747, 760, 424, 764, 776, 182, 793, 819, 820,
/* 30 */ 832, 150, 150, 791, 105, 405, 844, 856, 472, 552,
/* 40 */ 861, 868, 873, 880, 885, 898, 903, 915, 921, 928,
/* 50 */ 940, 947, 953, 965, 970, 977, 982, 989, 994, 1006,
/* 60 */ 1012, 1024, 1029, -78, -31, -2, 218, 300, 321, -83,
/* 70 */ -126, -126, -94, -77, -60, -46, -40, -13, -8, 9,
};
static const YYACTIONTYPE yy_default[] = {
/* 0 */ 433, 427, 427, 427, 427, 427, 427, 427, 427, 427,
/* 10 */ 427, 427, 427, 427, 427, 427, 427, 427, 427, 427,
/* 20 */ 427, 427, 427, 427, 456, 555, 427, 427, 433, 559,
/* 30 */ 468, 560, 560, 433, 427, 427, 427, 427, 427, 427,
/* 40 */ 427, 427, 427, 427, 427, 427, 427, 427, 427, 427,
/* 50 */ 427, 427, 460, 427, 427, 427, 427, 427, 427, 427,
/* 60 */ 427, 427, 427, 427, 427, 427, 427, 427, 427, 427,
/* 70 */ 427, 427, 427, 427, 427, 439, 453, 490, 490, 555,
/* 80 */ 451, 476, 427, 427, 427, 427, 427, 427, 427, 427,
/* 90 */ 427, 427, 454, 427, 427, 427, 427, 471, 469, 494,
/* 100 */ 493, 492, 459, 442, 427, 545, 427, 427, 427, 427,
/* 110 */ 427, 567, 427, 526, 525, 521, 427, 514, 511, 427,
/* 120 */ 511, 427, 427, 427, 474, 427, 427, 427, 427, 427,
/* 130 */ 427, 427, 427, 427, 427, 427, 427, 427, 427, 427,
/* 140 */ 427, 427, 427, 571, 427, 427, 427, 427, 427, 427,
/* 150 */ 427, 427, 427, 427, 580, 427, 427,
};
/********** End of lemon-generated parsing tables *****************************/
/* The next table maps tokens (terminal symbols) into fallback tokens.
** If a construct like the following:
**
** %fallback ID X Y Z.
**
** appears in the grammar, then ID becomes a fallback token for X, Y,
** and Z. Whenever one of the tokens X, Y, or Z is input to the parser
** but it does not parse, the type of the token is changed to ID and
** the parse is retried before an error is thrown.
**
** This feature can be used, for example, to cause some keywords in a language
** to revert to identifiers if they keyword does not apply in the context where
** it appears.
*/
#ifdef YYFALLBACK
static const YYCODETYPE yyFallback[] = {
0, /* $ => nothing */
0, /* ID => nothing */
1, /* EDGEPT => ID */
0, /* OF => nothing */
0, /* PLUS => nothing */
0, /* MINUS => nothing */
0, /* STAR => nothing */
0, /* SLASH => nothing */
0, /* PERCENT => nothing */
0, /* UMINUS => nothing */
0, /* EOL => nothing */
0, /* ASSIGN => nothing */
0, /* PLACENAME => nothing */
0, /* COLON => nothing */
0, /* ASSERT => nothing */
0, /* LP => nothing */
0, /* EQ => nothing */
0, /* RP => nothing */
0, /* FILL => nothing */
0, /* COLOR => nothing */
0, /* THICKNESS => nothing */
0, /* PRINT => nothing */
0, /* STRING => nothing */
0, /* COMMA => nothing */
0, /* CLASSNAME => nothing */
0, /* LB => nothing */
0, /* RB => nothing */
0, /* UP => nothing */
0, /* DOWN => nothing */
0, /* LEFT => nothing */
0, /* RIGHT => nothing */
0, /* CLOSE => nothing */
0, /* CHOP => nothing */
0, /* FROM => nothing */
0, /* TO => nothing */
0, /* THEN => nothing */
0, /* HEADING => nothing */
0, /* GO => nothing */
0, /* AT => nothing */
0, /* WITH => nothing */
0, /* SAME => nothing */
0, /* AS => nothing */
0, /* FIT => nothing */
0, /* BEHIND => nothing */
0, /* UNTIL => nothing */
0, /* EVEN => nothing */
0, /* DOT_E => nothing */
0, /* HEIGHT => nothing */
0, /* WIDTH => nothing */
0, /* RADIUS => nothing */
0, /* DIAMETER => nothing */
0, /* DOTTED => nothing */
0, /* DASHED => nothing */
0, /* CW => nothing */
0, /* CCW => nothing */
0, /* LARROW => nothing */
0, /* RARROW => nothing */
0, /* LRARROW => nothing */
0, /* INVIS => nothing */
0, /* THICK => nothing */
0, /* THIN => nothing */
0, /* CENTER => nothing */
0, /* LJUST => nothing */
0, /* RJUST => nothing */
0, /* ABOVE => nothing */
0, /* BELOW => nothing */
0, /* ITALIC => nothing */
0, /* BOLD => nothing */
0, /* ALIGNED => nothing */
0, /* BIG => nothing */
0, /* SMALL => nothing */
0, /* AND => nothing */
0, /* LT => nothing */
0, /* GT => nothing */
0, /* ON => nothing */
0, /* WAY => nothing */
0, /* BETWEEN => nothing */
0, /* THE => nothing */
0, /* NTH => nothing */
0, /* VERTEX => nothing */
0, /* TOP => nothing */
0, /* BOTTOM => nothing */
0, /* START => nothing */
0, /* END => nothing */
0, /* IN => nothing */
0, /* DOT_U => nothing */
0, /* LAST => nothing */
0, /* NUMBER => nothing */
0, /* FUNC1 => nothing */
0, /* FUNC2 => nothing */
0, /* DOT_XY => nothing */
0, /* X => nothing */
0, /* Y => nothing */
0, /* DOT_L => nothing */
};
#endif /* YYFALLBACK */
/* The following structure represents a single element of the
** parser's stack. Information stored includes:
**
** + The state number for the parser at this level of the stack.
**
** + The value of the token stored at this level of the stack.
** (In other words, the "major" token.)
**
** + The semantic value stored at this level of the stack. This is
** the information used by the action routines in the grammar.
** It is sometimes called the "minor" token.
**
** After the "shift" half of a SHIFTREDUCE action, the stateno field
** actually contains the reduce action for the second half of the
** SHIFTREDUCE.
*/
struct yyStackEntry {
YYACTIONTYPE stateno; /* The state-number, or reduce action in SHIFTREDUCE */
YYCODETYPE major; /* The major token value. This is the code
** number for the token at this stack level */
YYMINORTYPE minor; /* The user-supplied minor token value. This
** is the value of the token */
};
typedef struct yyStackEntry yyStackEntry;
/* The state of the parser is completely contained in an instance of
** the following structure */
struct yyParser {
yyStackEntry *yytos; /* Pointer to top element of the stack */
#ifdef YYTRACKMAXSTACKDEPTH
int yyhwm; /* High-water mark of the stack */
#endif
#ifndef YYNOERRORRECOVERY
int yyerrcnt; /* Shifts left before out of the error */
#endif
pik_parserARG_SDECL /* A place to hold %extra_argument */
pik_parserCTX_SDECL /* A place to hold %extra_context */
#if YYSTACKDEPTH<=0
int yystksz; /* Current side of the stack */
yyStackEntry *yystack; /* The parser's stack */
yyStackEntry yystk0; /* First stack entry */
#else
yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */
yyStackEntry *yystackEnd; /* Last entry in the stack */
#endif
};
typedef struct yyParser yyParser;
#ifndef NDEBUG
#include <stdio.h>
#include <assert.h>
static FILE *yyTraceFILE = 0;
static char *yyTracePrompt = 0;
#endif /* NDEBUG */
#ifndef NDEBUG
/*
** Turn parser tracing on by giving a stream to which to write the trace
** and a prompt to preface each trace message. Tracing is turned off
** by making either argument NULL
**
** Inputs:
** <ul>
** <li> A FILE* to which trace output should be written.
** If NULL, then tracing is turned off.
** <li> A prefix string written at the beginning of every
** line of trace output. If NULL, then tracing is
** turned off.
** </ul>
**
** Outputs:
** None.
*/
void pik_parserTrace(FILE *TraceFILE, char *zTracePrompt){
yyTraceFILE = TraceFILE;
yyTracePrompt = zTracePrompt;
if( yyTraceFILE==0 ) yyTracePrompt = 0;
else if( yyTracePrompt==0 ) yyTraceFILE = 0;
}
#endif /* NDEBUG */
#if defined(YYCOVERAGE) || !defined(NDEBUG)
/* For tracing shifts, the names of all terminals and nonterminals
** are required. The following table supplies these names */
static const char *const yyTokenName[] = {
/* 0 */ "$",
/* 1 */ "ID",
/* 2 */ "EDGEPT",
/* 3 */ "OF",
/* 4 */ "PLUS",
/* 5 */ "MINUS",
/* 6 */ "STAR",
/* 7 */ "SLASH",
/* 8 */ "PERCENT",
/* 9 */ "UMINUS",
/* 10 */ "EOL",
/* 11 */ "ASSIGN",
/* 12 */ "PLACENAME",
/* 13 */ "COLON",
/* 14 */ "ASSERT",
/* 15 */ "LP",
/* 16 */ "EQ",
/* 17 */ "RP",
/* 18 */ "FILL",
/* 19 */ "COLOR",
/* 20 */ "THICKNESS",
/* 21 */ "PRINT",
/* 22 */ "STRING",
/* 23 */ "COMMA",
/* 24 */ "CLASSNAME",
/* 25 */ "LB",
/* 26 */ "RB",
/* 27 */ "UP",
/* 28 */ "DOWN",
/* 29 */ "LEFT",
/* 30 */ "RIGHT",
/* 31 */ "CLOSE",
/* 32 */ "CHOP",
/* 33 */ "FROM",
/* 34 */ "TO",
/* 35 */ "THEN",
/* 36 */ "HEADING",
/* 37 */ "GO",
/* 38 */ "AT",
/* 39 */ "WITH",
/* 40 */ "SAME",
/* 41 */ "AS",
/* 42 */ "FIT",
/* 43 */ "BEHIND",
/* 44 */ "UNTIL",
/* 45 */ "EVEN",
/* 46 */ "DOT_E",
/* 47 */ "HEIGHT",
/* 48 */ "WIDTH",
/* 49 */ "RADIUS",
/* 50 */ "DIAMETER",
/* 51 */ "DOTTED",
/* 52 */ "DASHED",
/* 53 */ "CW",
/* 54 */ "CCW",
/* 55 */ "LARROW",
/* 56 */ "RARROW",
/* 57 */ "LRARROW",
/* 58 */ "INVIS",
/* 59 */ "THICK",
/* 60 */ "THIN",
/* 61 */ "CENTER",
/* 62 */ "LJUST",
/* 63 */ "RJUST",
/* 64 */ "ABOVE",
/* 65 */ "BELOW",
/* 66 */ "ITALIC",
/* 67 */ "BOLD",
/* 68 */ "ALIGNED",
/* 69 */ "BIG",
/* 70 */ "SMALL",
/* 71 */ "AND",
/* 72 */ "LT",
/* 73 */ "GT",
/* 74 */ "ON",
/* 75 */ "WAY",
/* 76 */ "BETWEEN",
/* 77 */ "THE",
/* 78 */ "NTH",
/* 79 */ "VERTEX",
/* 80 */ "TOP",
/* 81 */ "BOTTOM",
/* 82 */ "START",
/* 83 */ "END",
/* 84 */ "IN",
/* 85 */ "DOT_U",
/* 86 */ "LAST",
/* 87 */ "NUMBER",
/* 88 */ "FUNC1",
/* 89 */ "FUNC2",
/* 90 */ "DOT_XY",
/* 91 */ "X",
/* 92 */ "Y",
/* 93 */ "DOT_L",
/* 94 */ "element_list",
/* 95 */ "element",
/* 96 */ "unnamed_element",
/* 97 */ "basetype",
/* 98 */ "expr",
/* 99 */ "numproperty",
/* 100 */ "edge",
/* 101 */ "direction",
/* 102 */ "dashproperty",
/* 103 */ "colorproperty",
/* 104 */ "locproperty",
/* 105 */ "position",
/* 106 */ "place",
/* 107 */ "object",
/* 108 */ "objectname",
/* 109 */ "nth",
/* 110 */ "textposition",
/* 111 */ "rvalue",
/* 112 */ "lvalue",
/* 113 */ "even",
/* 114 */ "relexpr",
/* 115 */ "optrelexpr",
/* 116 */ "document",
/* 117 */ "print",
/* 118 */ "prlist",
/* 119 */ "pritem",
/* 120 */ "prsep",
/* 121 */ "attribute_list",
/* 122 */ "savelist",
/* 123 */ "alist",
/* 124 */ "attribute",
/* 125 */ "go",
/* 126 */ "boolproperty",
/* 127 */ "withclause",
/* 128 */ "between",
/* 129 */ "place2",
};
#endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */
#ifndef NDEBUG
/* For tracing reduce actions, the names of all rules are required.
*/
static const char *const yyRuleName[] = {
/* 0 */ "document ::= element_list",
/* 1 */ "element_list ::= element",
/* 2 */ "element_list ::= element_list EOL element",
/* 3 */ "element ::=",
/* 4 */ "element ::= direction",
/* 5 */ "element ::= lvalue ASSIGN rvalue",
/* 6 */ "element ::= PLACENAME COLON unnamed_element",
/* 7 */ "element ::= PLACENAME COLON position",
/* 8 */ "element ::= unnamed_element",
/* 9 */ "element ::= print prlist",
/* 10 */ "element ::= ASSERT LP expr EQ expr RP",
/* 11 */ "element ::= ASSERT LP place EQ place RP",
/* 12 */ "rvalue ::= PLACENAME",
/* 13 */ "pritem ::= FILL",
/* 14 */ "pritem ::= COLOR",
/* 15 */ "pritem ::= THICKNESS",
/* 16 */ "pritem ::= rvalue",
/* 17 */ "pritem ::= STRING",
/* 18 */ "prsep ::= COMMA",
/* 19 */ "unnamed_element ::= basetype attribute_list",
/* 20 */ "basetype ::= CLASSNAME",
/* 21 */ "basetype ::= STRING textposition",
/* 22 */ "basetype ::= LB savelist element_list RB",
/* 23 */ "savelist ::=",
/* 24 */ "relexpr ::= expr",
/* 25 */ "relexpr ::= expr PERCENT",
/* 26 */ "optrelexpr ::=",
/* 27 */ "attribute_list ::= relexpr alist",
/* 28 */ "attribute ::= numproperty relexpr",
/* 29 */ "attribute ::= dashproperty expr",
/* 30 */ "attribute ::= dashproperty",
/* 31 */ "attribute ::= colorproperty rvalue",
/* 32 */ "attribute ::= go direction optrelexpr",
/* 33 */ "attribute ::= go direction even position",
/* 34 */ "attribute ::= CLOSE",
/* 35 */ "attribute ::= CHOP",
/* 36 */ "attribute ::= FROM position",
/* 37 */ "attribute ::= TO position",
/* 38 */ "attribute ::= THEN",
/* 39 */ "attribute ::= THEN optrelexpr HEADING expr",
/* 40 */ "attribute ::= THEN optrelexpr EDGEPT",
/* 41 */ "attribute ::= GO optrelexpr HEADING expr",
/* 42 */ "attribute ::= GO optrelexpr EDGEPT",
/* 43 */ "attribute ::= AT position",
/* 44 */ "attribute ::= SAME",
/* 45 */ "attribute ::= SAME AS object",
/* 46 */ "attribute ::= STRING textposition",
/* 47 */ "attribute ::= FIT",
/* 48 */ "attribute ::= BEHIND object",
/* 49 */ "withclause ::= DOT_E edge AT position",
/* 50 */ "withclause ::= edge AT position",
/* 51 */ "numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS",
/* 52 */ "boolproperty ::= CW",
/* 53 */ "boolproperty ::= CCW",
/* 54 */ "boolproperty ::= LARROW",
/* 55 */ "boolproperty ::= RARROW",
/* 56 */ "boolproperty ::= LRARROW",
/* 57 */ "boolproperty ::= INVIS",
/* 58 */ "boolproperty ::= THICK",
/* 59 */ "boolproperty ::= THIN",
/* 60 */ "textposition ::=",
/* 61 */ "textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|ALIGNED|BIG|SMALL",
/* 62 */ "position ::= expr COMMA expr",
/* 63 */ "position ::= place PLUS expr COMMA expr",
/* 64 */ "position ::= place MINUS expr COMMA expr",
/* 65 */ "position ::= place PLUS LP expr COMMA expr RP",
/* 66 */ "position ::= place MINUS LP expr COMMA expr RP",
/* 67 */ "position ::= LP position COMMA position RP",
/* 68 */ "position ::= LP position RP",
/* 69 */ "position ::= expr between position AND position",
/* 70 */ "position ::= expr LT position COMMA position GT",
/* 71 */ "position ::= expr ABOVE position",
/* 72 */ "position ::= expr BELOW position",
/* 73 */ "position ::= expr LEFT OF position",
/* 74 */ "position ::= expr RIGHT OF position",
/* 75 */ "position ::= expr ON HEADING EDGEPT OF position",
/* 76 */ "position ::= expr HEADING EDGEPT OF position",
/* 77 */ "position ::= expr EDGEPT OF position",
/* 78 */ "position ::= expr ON HEADING expr FROM position",
/* 79 */ "position ::= expr HEADING expr FROM position",
/* 80 */ "place ::= edge OF object",
/* 81 */ "place2 ::= object",
/* 82 */ "place2 ::= object DOT_E edge",
/* 83 */ "place2 ::= NTH VERTEX OF object",
/* 84 */ "object ::= nth",
/* 85 */ "object ::= nth OF|IN object",
/* 86 */ "objectname ::= PLACENAME",
/* 87 */ "objectname ::= objectname DOT_U PLACENAME",
/* 88 */ "nth ::= NTH CLASSNAME",
/* 89 */ "nth ::= NTH LAST CLASSNAME",
/* 90 */ "nth ::= LAST CLASSNAME",
/* 91 */ "nth ::= LAST",
/* 92 */ "nth ::= NTH LB RB",
/* 93 */ "nth ::= NTH LAST LB RB",
/* 94 */ "nth ::= LAST LB RB",
/* 95 */ "expr ::= expr PLUS expr",
/* 96 */ "expr ::= expr MINUS expr",
/* 97 */ "expr ::= expr STAR expr",
/* 98 */ "expr ::= expr SLASH expr",
/* 99 */ "expr ::= MINUS expr",
/* 100 */ "expr ::= PLUS expr",
/* 101 */ "expr ::= LP expr RP",
/* 102 */ "expr ::= NUMBER",
/* 103 */ "expr ::= ID",
/* 104 */ "expr ::= FUNC1 LP expr RP",
/* 105 */ "expr ::= FUNC2 LP expr COMMA expr RP",
/* 106 */ "expr ::= place2 DOT_XY X",
/* 107 */ "expr ::= place2 DOT_XY Y",
/* 108 */ "expr ::= object DOT_L numproperty",
/* 109 */ "expr ::= object DOT_L dashproperty",
/* 110 */ "expr ::= object DOT_L colorproperty",
/* 111 */ "lvalue ::= ID",
/* 112 */ "lvalue ::= FILL",
/* 113 */ "lvalue ::= COLOR",
/* 114 */ "lvalue ::= THICKNESS",
/* 115 */ "rvalue ::= expr",
/* 116 */ "print ::= PRINT",
/* 117 */ "prlist ::= pritem",
/* 118 */ "prlist ::= prlist prsep pritem",
/* 119 */ "direction ::= UP",
/* 120 */ "direction ::= DOWN",
/* 121 */ "direction ::= LEFT",
/* 122 */ "direction ::= RIGHT",
/* 123 */ "optrelexpr ::= relexpr",
/* 124 */ "attribute_list ::= alist",
/* 125 */ "alist ::=",
/* 126 */ "alist ::= alist attribute",
/* 127 */ "attribute ::= boolproperty",
/* 128 */ "attribute ::= WITH withclause",
/* 129 */ "go ::= GO",
/* 130 */ "go ::=",
/* 131 */ "even ::= UNTIL EVEN WITH",
/* 132 */ "even ::= EVEN WITH",
/* 133 */ "dashproperty ::= DOTTED",
/* 134 */ "dashproperty ::= DASHED",
/* 135 */ "colorproperty ::= FILL",
/* 136 */ "colorproperty ::= COLOR",
/* 137 */ "position ::= place",
/* 138 */ "between ::= WAY BETWEEN",
/* 139 */ "between ::= BETWEEN",
/* 140 */ "between ::= OF THE WAY BETWEEN",
/* 141 */ "place ::= place2",
/* 142 */ "edge ::= CENTER",
/* 143 */ "edge ::= EDGEPT",
/* 144 */ "edge ::= TOP",
/* 145 */ "edge ::= BOTTOM",
/* 146 */ "edge ::= START",
/* 147 */ "edge ::= END",
/* 148 */ "edge ::= RIGHT",
/* 149 */ "edge ::= LEFT",
/* 150 */ "object ::= objectname",
};
#endif /* NDEBUG */
#if YYSTACKDEPTH<=0
/*
** Try to increase the size of the parser stack. Return the number
** of errors. Return 0 on success.
*/
static int yyGrowStack(yyParser *p){
int newSize;
int idx;
yyStackEntry *pNew;
newSize = p->yystksz*2 + 100;
idx = p->yytos ? (int)(p->yytos - p->yystack) : 0;
if( p->yystack==&p->yystk0 ){
pNew = malloc(newSize*sizeof(pNew[0]));
if( pNew ) pNew[0] = p->yystk0;
}else{
pNew = realloc(p->yystack, newSize*sizeof(pNew[0]));
}
if( pNew ){
p->yystack = pNew;
p->yytos = &p->yystack[idx];
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n",
yyTracePrompt, p->yystksz, newSize);
}
#endif
p->yystksz = newSize;
}
return pNew==0;
}
#endif
/* Datatype of the argument to the memory allocated passed as the
** second argument to pik_parserAlloc() below. This can be changed by
** putting an appropriate #define in the %include section of the input
** grammar.
*/
#ifndef YYMALLOCARGTYPE
# define YYMALLOCARGTYPE size_t
#endif
/* Initialize a new parser that has already been allocated.
*/
void pik_parserInit(void *yypRawParser pik_parserCTX_PDECL){
yyParser *yypParser = (yyParser*)yypRawParser;
pik_parserCTX_STORE
#ifdef YYTRACKMAXSTACKDEPTH
yypParser->yyhwm = 0;
#endif
#if YYSTACKDEPTH<=0
yypParser->yytos = NULL;
yypParser->yystack = NULL;
yypParser->yystksz = 0;
if( yyGrowStack(yypParser) ){
yypParser->yystack = &yypParser->yystk0;
yypParser->yystksz = 1;
}
#endif
#ifndef YYNOERRORRECOVERY
yypParser->yyerrcnt = -1;
#endif
yypParser->yytos = yypParser->yystack;
yypParser->yystack[0].stateno = 0;
yypParser->yystack[0].major = 0;
#if YYSTACKDEPTH>0
yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1];
#endif
}
#ifndef pik_parser_ENGINEALWAYSONSTACK
/*
** This function allocates a new parser.
** The only argument is a pointer to a function which works like
** malloc.
**
** Inputs:
** A pointer to the function used to allocate memory.
**
** Outputs:
** A pointer to a parser. This pointer is used in subsequent calls
** to pik_parser and pik_parserFree.
*/
void *pik_parserAlloc(void *(*mallocProc)(YYMALLOCARGTYPE) pik_parserCTX_PDECL){
yyParser *yypParser;
yypParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) );
if( yypParser ){
pik_parserCTX_STORE
pik_parserInit(yypParser pik_parserCTX_PARAM);
}
return (void*)yypParser;
}
#endif /* pik_parser_ENGINEALWAYSONSTACK */
/* The following function deletes the "minor type" or semantic value
** associated with a symbol. The symbol can be either a terminal
** or nonterminal. "yymajor" is the symbol code, and "yypminor" is
** a pointer to the value to be deleted. The code used to do the
** deletions is derived from the %destructor and/or %token_destructor
** directives of the input grammar.
*/
static void yy_destructor(
yyParser *yypParser, /* The parser */
YYCODETYPE yymajor, /* Type code for object to destroy */
YYMINORTYPE *yypminor /* The object to be destroyed */
){
pik_parserARG_FETCH
pik_parserCTX_FETCH
switch( yymajor ){
/* Here is inserted the actions which take place when a
** terminal or non-terminal is destroyed. This can happen
** when the symbol is popped from the stack during a
** reduce or during error processing or when a parser is
** being destroyed before it is finished parsing.
**
** Note: during a reduce, the only symbols destroyed are those
** which appear on the RHS of the rule, but which are *not* used
** inside the C code.
*/
/********* Begin destructor definitions ***************************************/
case 94: /* element_list */
{
#line 476 "pikchr.y"
pik_elist_free(p,(yypminor->yy72));
#line 1664 "pikchr.c"
}
break;
case 95: /* element */
case 96: /* unnamed_element */
case 97: /* basetype */
{
#line 478 "pikchr.y"
pik_elem_free(p,(yypminor->yy254));
#line 1673 "pikchr.c"
}
break;
/********* End destructor definitions *****************************************/
default: break; /* If no destructor action specified: do nothing */
}
}
/*
** Pop the parser's stack once.
**
** If there is a destructor routine associated with the token which
** is popped from the stack, then call it.
*/
static void yy_pop_parser_stack(yyParser *pParser){
yyStackEntry *yytos;
assert( pParser->yytos!=0 );
assert( pParser->yytos > pParser->yystack );
yytos = pParser->yytos--;
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE,"%sPopping %s\n",
yyTracePrompt,
yyTokenName[yytos->major]);
}
#endif
yy_destructor(pParser, yytos->major, &yytos->minor);
}
/*
** Clear all secondary memory allocations from the parser
*/
void pik_parserFinalize(void *p){
yyParser *pParser = (yyParser*)p;
while( pParser->yytos>pParser->yystack ) yy_pop_parser_stack(pParser);
#if YYSTACKDEPTH<=0
if( pParser->yystack!=&pParser->yystk0 ) free(pParser->yystack);
#endif
}
#ifndef pik_parser_ENGINEALWAYSONSTACK
/*
** Deallocate and destroy a parser. Destructors are called for
** all stack elements before shutting the parser down.
**
** If the YYPARSEFREENEVERNULL macro exists (for example because it
** is defined in a %include section of the input grammar) then it is
** assumed that the input pointer is never NULL.
*/
void pik_parserFree(
void *p, /* The parser to be deleted */
void (*freeProc)(void*) /* Function used to reclaim memory */
){
#ifndef YYPARSEFREENEVERNULL
if( p==0 ) return;
#endif
pik_parserFinalize(p);
(*freeProc)(p);
}
#endif /* pik_parser_ENGINEALWAYSONSTACK */
/*
** Return the peak depth of the stack for a parser.
*/
#ifdef YYTRACKMAXSTACKDEPTH
int pik_parserStackPeak(void *p){
yyParser *pParser = (yyParser*)p;
return pParser->yyhwm;
}
#endif
/* This array of booleans keeps track of the parser statement
** coverage. The element yycoverage[X][Y] is set when the parser
** is in state X and has a lookahead token Y. In a well-tested
** systems, every element of this matrix should end up being set.
*/
#if defined(YYCOVERAGE)
static unsigned char yycoverage[YYNSTATE][YYNTOKEN];
#endif
/*
** Write into out a description of every state/lookahead combination that
**
** (1) has not been used by the parser, and
** (2) is not a syntax error.
**
** Return the number of missed state/lookahead combinations.
*/
#if defined(YYCOVERAGE)
int pik_parserCoverage(FILE *out){
int stateno, iLookAhead, i;
int nMissed = 0;
for(stateno=0; stateno<YYNSTATE; stateno++){
i = yy_shift_ofst[stateno];
for(iLookAhead=0; iLookAhead<YYNTOKEN; iLookAhead++){
if( yy_lookahead[i+iLookAhead]!=iLookAhead ) continue;
if( yycoverage[stateno][iLookAhead]==0 ) nMissed++;
if( out ){
fprintf(out,"State %d lookahead %s %s\n", stateno,
yyTokenName[iLookAhead],
yycoverage[stateno][iLookAhead] ? "ok" : "missed");
}
}
}
return nMissed;
}
#endif
/*
** Find the appropriate action for a parser given the terminal
** look-ahead token iLookAhead.
*/
static YYACTIONTYPE yy_find_shift_action(
YYCODETYPE iLookAhead, /* The look-ahead token */
YYACTIONTYPE stateno /* Current state number */
){
int i;
if( stateno>YY_MAX_SHIFT ) return stateno;
assert( stateno <= YY_SHIFT_COUNT );
#if defined(YYCOVERAGE)
yycoverage[stateno][iLookAhead] = 1;
#endif
do{
i = yy_shift_ofst[stateno];
assert( i>=0 );
assert( i<=YY_ACTTAB_COUNT );
assert( i+YYNTOKEN<=(int)YY_NLOOKAHEAD );
assert( iLookAhead!=YYNOCODE );
assert( iLookAhead < YYNTOKEN );
i += iLookAhead;
assert( i<(int)YY_NLOOKAHEAD );
if( yy_lookahead[i]!=iLookAhead ){
#ifdef YYFALLBACK
YYCODETYPE iFallback; /* Fallback token */
assert( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0]) );
iFallback = yyFallback[iLookAhead];
if( iFallback!=0 ){
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
}
#endif
assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */
iLookAhead = iFallback;
continue;
}
#endif
#ifdef YYWILDCARD
{
int j = i - iLookAhead + YYWILDCARD;
assert( j<(int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])) );
if( yy_lookahead[j]==YYWILDCARD && iLookAhead>0 ){
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
yyTracePrompt, yyTokenName[iLookAhead],
yyTokenName[YYWILDCARD]);
}
#endif /* NDEBUG */
return yy_action[j];
}
}
#endif /* YYWILDCARD */
return yy_default[stateno];
}else{
assert( i>=0 && i<(int)(sizeof(yy_action)/sizeof(yy_action[0])) );
return yy_action[i];
}
}while(1);
}
/*
** Find the appropriate action for a parser given the non-terminal
** look-ahead token iLookAhead.
*/
static YYACTIONTYPE yy_find_reduce_action(
YYACTIONTYPE stateno, /* Current state number */
YYCODETYPE iLookAhead /* The look-ahead token */
){
int i;
#ifdef YYERRORSYMBOL
if( stateno>YY_REDUCE_COUNT ){
return yy_default[stateno];
}
#else
assert( stateno<=YY_REDUCE_COUNT );
#endif
i = yy_reduce_ofst[stateno];
assert( iLookAhead!=YYNOCODE );
i += iLookAhead;
#ifdef YYERRORSYMBOL
if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
return yy_default[stateno];
}
#else
assert( i>=0 && i<YY_ACTTAB_COUNT );
assert( yy_lookahead[i]==iLookAhead );
#endif
return yy_action[i];
}
/*
** The following routine is called if the stack overflows.
*/
static void yyStackOverflow(yyParser *yypParser){
pik_parserARG_FETCH
pik_parserCTX_FETCH
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
}
#endif
while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
/* Here code is inserted which will execute if the parser
** stack every overflows */
/******** Begin %stack_overflow code ******************************************/
#line 510 "pikchr.y"
pik_error(p, 0, "parser stack overflow");
#line 1894 "pikchr.c"
/******** End %stack_overflow code ********************************************/
pik_parserARG_STORE /* Suppress warning about unused %extra_argument var */
pik_parserCTX_STORE
}
/*
** Print tracing information for a SHIFT action
*/
#ifndef NDEBUG
static void yyTraceShift(yyParser *yypParser, int yyNewState, const char *zTag){
if( yyTraceFILE ){
if( yyNewState<YYNSTATE ){
fprintf(yyTraceFILE,"%s%s '%s', go to state %d\n",
yyTracePrompt, zTag, yyTokenName[yypParser->yytos->major],
yyNewState);
}else{
fprintf(yyTraceFILE,"%s%s '%s', pending reduce %d\n",
yyTracePrompt, zTag, yyTokenName[yypParser->yytos->major],
yyNewState - YY_MIN_REDUCE);
}
}
}
#else
# define yyTraceShift(X,Y,Z)
#endif
/*
** Perform a shift action.
*/
static void yy_shift(
yyParser *yypParser, /* The parser to be shifted */
YYACTIONTYPE yyNewState, /* The new state to shift in */
YYCODETYPE yyMajor, /* The major token to shift in */
pik_parserTOKENTYPE yyMinor /* The minor token to shift in */
){
yyStackEntry *yytos;
yypParser->yytos++;
#ifdef YYTRACKMAXSTACKDEPTH
if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
yypParser->yyhwm++;
assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) );
}
#endif
#if YYSTACKDEPTH>0
if( yypParser->yytos>yypParser->yystackEnd ){
yypParser->yytos--;
yyStackOverflow(yypParser);
return;
}
#else
if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz] ){
if( yyGrowStack(yypParser) ){
yypParser->yytos--;
yyStackOverflow(yypParser);
return;
}
}
#endif
if( yyNewState > YY_MAX_SHIFT ){
yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
}
yytos = yypParser->yytos;
yytos->stateno = yyNewState;
yytos->major = yyMajor;
yytos->minor.yy0 = yyMinor;
yyTraceShift(yypParser, yyNewState, "Shift");
}
/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side
** of that rule */
static const YYCODETYPE yyRuleInfoLhs[] = {
116, /* (0) document ::= element_list */
94, /* (1) element_list ::= element */
94, /* (2) element_list ::= element_list EOL element */
95, /* (3) element ::= */
95, /* (4) element ::= direction */
95, /* (5) element ::= lvalue ASSIGN rvalue */
95, /* (6) element ::= PLACENAME COLON unnamed_element */
95, /* (7) element ::= PLACENAME COLON position */
95, /* (8) element ::= unnamed_element */
95, /* (9) element ::= print prlist */
95, /* (10) element ::= ASSERT LP expr EQ expr RP */
95, /* (11) element ::= ASSERT LP place EQ place RP */
111, /* (12) rvalue ::= PLACENAME */
119, /* (13) pritem ::= FILL */
119, /* (14) pritem ::= COLOR */
119, /* (15) pritem ::= THICKNESS */
119, /* (16) pritem ::= rvalue */
119, /* (17) pritem ::= STRING */
120, /* (18) prsep ::= COMMA */
96, /* (19) unnamed_element ::= basetype attribute_list */
97, /* (20) basetype ::= CLASSNAME */
97, /* (21) basetype ::= STRING textposition */
97, /* (22) basetype ::= LB savelist element_list RB */
122, /* (23) savelist ::= */
114, /* (24) relexpr ::= expr */
114, /* (25) relexpr ::= expr PERCENT */
115, /* (26) optrelexpr ::= */
121, /* (27) attribute_list ::= relexpr alist */
124, /* (28) attribute ::= numproperty relexpr */
124, /* (29) attribute ::= dashproperty expr */
124, /* (30) attribute ::= dashproperty */
124, /* (31) attribute ::= colorproperty rvalue */
124, /* (32) attribute ::= go direction optrelexpr */
124, /* (33) attribute ::= go direction even position */
124, /* (34) attribute ::= CLOSE */
124, /* (35) attribute ::= CHOP */
124, /* (36) attribute ::= FROM position */
124, /* (37) attribute ::= TO position */
124, /* (38) attribute ::= THEN */
124, /* (39) attribute ::= THEN optrelexpr HEADING expr */
124, /* (40) attribute ::= THEN optrelexpr EDGEPT */
124, /* (41) attribute ::= GO optrelexpr HEADING expr */
124, /* (42) attribute ::= GO optrelexpr EDGEPT */
124, /* (43) attribute ::= AT position */
124, /* (44) attribute ::= SAME */
124, /* (45) attribute ::= SAME AS object */
124, /* (46) attribute ::= STRING textposition */
124, /* (47) attribute ::= FIT */
124, /* (48) attribute ::= BEHIND object */
127, /* (49) withclause ::= DOT_E edge AT position */
127, /* (50) withclause ::= edge AT position */
99, /* (51) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */
126, /* (52) boolproperty ::= CW */
126, /* (53) boolproperty ::= CCW */
126, /* (54) boolproperty ::= LARROW */
126, /* (55) boolproperty ::= RARROW */
126, /* (56) boolproperty ::= LRARROW */
126, /* (57) boolproperty ::= INVIS */
126, /* (58) boolproperty ::= THICK */
126, /* (59) boolproperty ::= THIN */
110, /* (60) textposition ::= */
110, /* (61) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|ALIGNED|BIG|SMALL */
105, /* (62) position ::= expr COMMA expr */
105, /* (63) position ::= place PLUS expr COMMA expr */
105, /* (64) position ::= place MINUS expr COMMA expr */
105, /* (65) position ::= place PLUS LP expr COMMA expr RP */
105, /* (66) position ::= place MINUS LP expr COMMA expr RP */
105, /* (67) position ::= LP position COMMA position RP */
105, /* (68) position ::= LP position RP */
105, /* (69) position ::= expr between position AND position */
105, /* (70) position ::= expr LT position COMMA position GT */
105, /* (71) position ::= expr ABOVE position */
105, /* (72) position ::= expr BELOW position */
105, /* (73) position ::= expr LEFT OF position */
105, /* (74) position ::= expr RIGHT OF position */
105, /* (75) position ::= expr ON HEADING EDGEPT OF position */
105, /* (76) position ::= expr HEADING EDGEPT OF position */
105, /* (77) position ::= expr EDGEPT OF position */
105, /* (78) position ::= expr ON HEADING expr FROM position */
105, /* (79) position ::= expr HEADING expr FROM position */
106, /* (80) place ::= edge OF object */
129, /* (81) place2 ::= object */
129, /* (82) place2 ::= object DOT_E edge */
129, /* (83) place2 ::= NTH VERTEX OF object */
107, /* (84) object ::= nth */
107, /* (85) object ::= nth OF|IN object */
108, /* (86) objectname ::= PLACENAME */
108, /* (87) objectname ::= objectname DOT_U PLACENAME */
109, /* (88) nth ::= NTH CLASSNAME */
109, /* (89) nth ::= NTH LAST CLASSNAME */
109, /* (90) nth ::= LAST CLASSNAME */
109, /* (91) nth ::= LAST */
109, /* (92) nth ::= NTH LB RB */
109, /* (93) nth ::= NTH LAST LB RB */
109, /* (94) nth ::= LAST LB RB */
98, /* (95) expr ::= expr PLUS expr */
98, /* (96) expr ::= expr MINUS expr */
98, /* (97) expr ::= expr STAR expr */
98, /* (98) expr ::= expr SLASH expr */
98, /* (99) expr ::= MINUS expr */
98, /* (100) expr ::= PLUS expr */
98, /* (101) expr ::= LP expr RP */
98, /* (102) expr ::= NUMBER */
98, /* (103) expr ::= ID */
98, /* (104) expr ::= FUNC1 LP expr RP */
98, /* (105) expr ::= FUNC2 LP expr COMMA expr RP */
98, /* (106) expr ::= place2 DOT_XY X */
98, /* (107) expr ::= place2 DOT_XY Y */
98, /* (108) expr ::= object DOT_L numproperty */
98, /* (109) expr ::= object DOT_L dashproperty */
98, /* (110) expr ::= object DOT_L colorproperty */
112, /* (111) lvalue ::= ID */
112, /* (112) lvalue ::= FILL */
112, /* (113) lvalue ::= COLOR */
112, /* (114) lvalue ::= THICKNESS */
111, /* (115) rvalue ::= expr */
117, /* (116) print ::= PRINT */
118, /* (117) prlist ::= pritem */
118, /* (118) prlist ::= prlist prsep pritem */
101, /* (119) direction ::= UP */
101, /* (120) direction ::= DOWN */
101, /* (121) direction ::= LEFT */
101, /* (122) direction ::= RIGHT */
115, /* (123) optrelexpr ::= relexpr */
121, /* (124) attribute_list ::= alist */
123, /* (125) alist ::= */
123, /* (126) alist ::= alist attribute */
124, /* (127) attribute ::= boolproperty */
124, /* (128) attribute ::= WITH withclause */
125, /* (129) go ::= GO */
125, /* (130) go ::= */
113, /* (131) even ::= UNTIL EVEN WITH */
113, /* (132) even ::= EVEN WITH */
102, /* (133) dashproperty ::= DOTTED */
102, /* (134) dashproperty ::= DASHED */
103, /* (135) colorproperty ::= FILL */
103, /* (136) colorproperty ::= COLOR */
105, /* (137) position ::= place */
128, /* (138) between ::= WAY BETWEEN */
128, /* (139) between ::= BETWEEN */
128, /* (140) between ::= OF THE WAY BETWEEN */
106, /* (141) place ::= place2 */
100, /* (142) edge ::= CENTER */
100, /* (143) edge ::= EDGEPT */
100, /* (144) edge ::= TOP */
100, /* (145) edge ::= BOTTOM */
100, /* (146) edge ::= START */
100, /* (147) edge ::= END */
100, /* (148) edge ::= RIGHT */
100, /* (149) edge ::= LEFT */
107, /* (150) object ::= objectname */
};
/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number
** of symbols on the right-hand side of that rule. */
static const signed char yyRuleInfoNRhs[] = {
-1, /* (0) document ::= element_list */
-1, /* (1) element_list ::= element */
-3, /* (2) element_list ::= element_list EOL element */
0, /* (3) element ::= */
-1, /* (4) element ::= direction */
-3, /* (5) element ::= lvalue ASSIGN rvalue */
-3, /* (6) element ::= PLACENAME COLON unnamed_element */
-3, /* (7) element ::= PLACENAME COLON position */
-1, /* (8) element ::= unnamed_element */
-2, /* (9) element ::= print prlist */
-6, /* (10) element ::= ASSERT LP expr EQ expr RP */
-6, /* (11) element ::= ASSERT LP place EQ place RP */
-1, /* (12) rvalue ::= PLACENAME */
-1, /* (13) pritem ::= FILL */
-1, /* (14) pritem ::= COLOR */
-1, /* (15) pritem ::= THICKNESS */
-1, /* (16) pritem ::= rvalue */
-1, /* (17) pritem ::= STRING */
-1, /* (18) prsep ::= COMMA */
-2, /* (19) unnamed_element ::= basetype attribute_list */
-1, /* (20) basetype ::= CLASSNAME */
-2, /* (21) basetype ::= STRING textposition */
-4, /* (22) basetype ::= LB savelist element_list RB */
0, /* (23) savelist ::= */
-1, /* (24) relexpr ::= expr */
-2, /* (25) relexpr ::= expr PERCENT */
0, /* (26) optrelexpr ::= */
-2, /* (27) attribute_list ::= relexpr alist */
-2, /* (28) attribute ::= numproperty relexpr */
-2, /* (29) attribute ::= dashproperty expr */
-1, /* (30) attribute ::= dashproperty */
-2, /* (31) attribute ::= colorproperty rvalue */
-3, /* (32) attribute ::= go direction optrelexpr */
-4, /* (33) attribute ::= go direction even position */
-1, /* (34) attribute ::= CLOSE */
-1, /* (35) attribute ::= CHOP */
-2, /* (36) attribute ::= FROM position */
-2, /* (37) attribute ::= TO position */
-1, /* (38) attribute ::= THEN */
-4, /* (39) attribute ::= THEN optrelexpr HEADING expr */
-3, /* (40) attribute ::= THEN optrelexpr EDGEPT */
-4, /* (41) attribute ::= GO optrelexpr HEADING expr */
-3, /* (42) attribute ::= GO optrelexpr EDGEPT */
-2, /* (43) attribute ::= AT position */
-1, /* (44) attribute ::= SAME */
-3, /* (45) attribute ::= SAME AS object */
-2, /* (46) attribute ::= STRING textposition */
-1, /* (47) attribute ::= FIT */
-2, /* (48) attribute ::= BEHIND object */
-4, /* (49) withclause ::= DOT_E edge AT position */
-3, /* (50) withclause ::= edge AT position */
-1, /* (51) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */
-1, /* (52) boolproperty ::= CW */
-1, /* (53) boolproperty ::= CCW */
-1, /* (54) boolproperty ::= LARROW */
-1, /* (55) boolproperty ::= RARROW */
-1, /* (56) boolproperty ::= LRARROW */
-1, /* (57) boolproperty ::= INVIS */
-1, /* (58) boolproperty ::= THICK */
-1, /* (59) boolproperty ::= THIN */
0, /* (60) textposition ::= */
-2, /* (61) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|ALIGNED|BIG|SMALL */
-3, /* (62) position ::= expr COMMA expr */
-5, /* (63) position ::= place PLUS expr COMMA expr */
-5, /* (64) position ::= place MINUS expr COMMA expr */
-7, /* (65) position ::= place PLUS LP expr COMMA expr RP */
-7, /* (66) position ::= place MINUS LP expr COMMA expr RP */
-5, /* (67) position ::= LP position COMMA position RP */
-3, /* (68) position ::= LP position RP */
-5, /* (69) position ::= expr between position AND position */
-6, /* (70) position ::= expr LT position COMMA position GT */
-3, /* (71) position ::= expr ABOVE position */
-3, /* (72) position ::= expr BELOW position */
-4, /* (73) position ::= expr LEFT OF position */
-4, /* (74) position ::= expr RIGHT OF position */
-6, /* (75) position ::= expr ON HEADING EDGEPT OF position */
-5, /* (76) position ::= expr HEADING EDGEPT OF position */
-4, /* (77) position ::= expr EDGEPT OF position */
-6, /* (78) position ::= expr ON HEADING expr FROM position */
-5, /* (79) position ::= expr HEADING expr FROM position */
-3, /* (80) place ::= edge OF object */
-1, /* (81) place2 ::= object */
-3, /* (82) place2 ::= object DOT_E edge */
-4, /* (83) place2 ::= NTH VERTEX OF object */
-1, /* (84) object ::= nth */
-3, /* (85) object ::= nth OF|IN object */
-1, /* (86) objectname ::= PLACENAME */
-3, /* (87) objectname ::= objectname DOT_U PLACENAME */
-2, /* (88) nth ::= NTH CLASSNAME */
-3, /* (89) nth ::= NTH LAST CLASSNAME */
-2, /* (90) nth ::= LAST CLASSNAME */
-1, /* (91) nth ::= LAST */
-3, /* (92) nth ::= NTH LB RB */
-4, /* (93) nth ::= NTH LAST LB RB */
-3, /* (94) nth ::= LAST LB RB */
-3, /* (95) expr ::= expr PLUS expr */
-3, /* (96) expr ::= expr MINUS expr */
-3, /* (97) expr ::= expr STAR expr */
-3, /* (98) expr ::= expr SLASH expr */
-2, /* (99) expr ::= MINUS expr */
-2, /* (100) expr ::= PLUS expr */
-3, /* (101) expr ::= LP expr RP */
-1, /* (102) expr ::= NUMBER */
-1, /* (103) expr ::= ID */
-4, /* (104) expr ::= FUNC1 LP expr RP */
-6, /* (105) expr ::= FUNC2 LP expr COMMA expr RP */
-3, /* (106) expr ::= place2 DOT_XY X */
-3, /* (107) expr ::= place2 DOT_XY Y */
-3, /* (108) expr ::= object DOT_L numproperty */
-3, /* (109) expr ::= object DOT_L dashproperty */
-3, /* (110) expr ::= object DOT_L colorproperty */
-1, /* (111) lvalue ::= ID */
-1, /* (112) lvalue ::= FILL */
-1, /* (113) lvalue ::= COLOR */
-1, /* (114) lvalue ::= THICKNESS */
-1, /* (115) rvalue ::= expr */
-1, /* (116) print ::= PRINT */
-1, /* (117) prlist ::= pritem */
-3, /* (118) prlist ::= prlist prsep pritem */
-1, /* (119) direction ::= UP */
-1, /* (120) direction ::= DOWN */
-1, /* (121) direction ::= LEFT */
-1, /* (122) direction ::= RIGHT */
-1, /* (123) optrelexpr ::= relexpr */
-1, /* (124) attribute_list ::= alist */
0, /* (125) alist ::= */
-2, /* (126) alist ::= alist attribute */
-1, /* (127) attribute ::= boolproperty */
-2, /* (128) attribute ::= WITH withclause */
-1, /* (129) go ::= GO */
0, /* (130) go ::= */
-3, /* (131) even ::= UNTIL EVEN WITH */
-2, /* (132) even ::= EVEN WITH */
-1, /* (133) dashproperty ::= DOTTED */
-1, /* (134) dashproperty ::= DASHED */
-1, /* (135) colorproperty ::= FILL */
-1, /* (136) colorproperty ::= COLOR */
-1, /* (137) position ::= place */
-2, /* (138) between ::= WAY BETWEEN */
-1, /* (139) between ::= BETWEEN */
-4, /* (140) between ::= OF THE WAY BETWEEN */
-1, /* (141) place ::= place2 */
-1, /* (142) edge ::= CENTER */
-1, /* (143) edge ::= EDGEPT */
-1, /* (144) edge ::= TOP */
-1, /* (145) edge ::= BOTTOM */
-1, /* (146) edge ::= START */
-1, /* (147) edge ::= END */
-1, /* (148) edge ::= RIGHT */
-1, /* (149) edge ::= LEFT */
-1, /* (150) object ::= objectname */
};
static void yy_accept(yyParser*); /* Forward Declaration */
/*
** Perform a reduce action and the shift that must immediately
** follow the reduce.
**
** The yyLookahead and yyLookaheadToken parameters provide reduce actions
** access to the lookahead token (if any). The yyLookahead will be YYNOCODE
** if the lookahead token has already been consumed. As this procedure is
** only called from one place, optimizing compilers will in-line it, which
** means that the extra parameters have no performance impact.
*/
static YYACTIONTYPE yy_reduce(
yyParser *yypParser, /* The parser */
unsigned int yyruleno, /* Number of the rule by which to reduce */
int yyLookahead, /* Lookahead token, or YYNOCODE if none */
pik_parserTOKENTYPE yyLookaheadToken /* Value of the lookahead token */
pik_parserCTX_PDECL /* %extra_context */
){
int yygoto; /* The next state */
YYACTIONTYPE yyact; /* The next action */
yyStackEntry *yymsp; /* The top of the parser's stack */
int yysize; /* Amount to pop the stack */
pik_parserARG_FETCH
(void)yyLookahead;
(void)yyLookaheadToken;
yymsp = yypParser->yytos;
#ifndef NDEBUG
if( yyTraceFILE && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
yysize = yyRuleInfoNRhs[yyruleno];
if( yysize ){
fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n",
yyTracePrompt,
yyruleno, yyRuleName[yyruleno],
yyruleno<YYNRULE_WITH_ACTION ? "" : " without external action",
yymsp[yysize].stateno);
}else{
fprintf(yyTraceFILE, "%sReduce %d [%s]%s.\n",
yyTracePrompt, yyruleno, yyRuleName[yyruleno],
yyruleno<YYNRULE_WITH_ACTION ? "" : " without external action");
}
}
#endif /* NDEBUG */
/* Check that the stack is large enough to grow by a single entry
** if the RHS of the rule is empty. This ensures that there is room
** enough on the stack to push the LHS value */
if( yyRuleInfoNRhs[yyruleno]==0 ){
#ifdef YYTRACKMAXSTACKDEPTH
if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
yypParser->yyhwm++;
assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack));
}
#endif
#if YYSTACKDEPTH>0
if( yypParser->yytos>=yypParser->yystackEnd ){
yyStackOverflow(yypParser);
/* The call to yyStackOverflow() above pops the stack until it is
** empty, causing the main parser loop to exit. So the return value
** is never used and does not matter. */
return 0;
}
#else
if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){
if( yyGrowStack(yypParser) ){
yyStackOverflow(yypParser);
/* The call to yyStackOverflow() above pops the stack until it is
** empty, causing the main parser loop to exit. So the return value
** is never used and does not matter. */
return 0;
}
yymsp = yypParser->yytos;
}
#endif
}
switch( yyruleno ){
/* Beginning here are the reduction cases. A typical example
** follows:
** case 0:
** #line <lineno> <grammarfile>
** { ... } // User supplied code
** #line <lineno> <thisfile>
** break;
*/
/********** Begin reduce actions **********************************************/
YYMINORTYPE yylhsminor;
case 0: /* document ::= element_list */
#line 514 "pikchr.y"
{pik_render(p,yymsp[0].minor.yy72);}
#line 2365 "pikchr.c"
break;
case 1: /* element_list ::= element */
#line 517 "pikchr.y"
{ yylhsminor.yy72 = pik_elist_append(p,0,yymsp[0].minor.yy254); }
#line 2370 "pikchr.c"
yymsp[0].minor.yy72 = yylhsminor.yy72;
break;
case 2: /* element_list ::= element_list EOL element */
#line 519 "pikchr.y"
{ yylhsminor.yy72 = pik_elist_append(p,yymsp[-2].minor.yy72,yymsp[0].minor.yy254); }
#line 2376 "pikchr.c"
yymsp[-2].minor.yy72 = yylhsminor.yy72;
break;
case 3: /* element ::= */
#line 522 "pikchr.y"
{ yymsp[1].minor.yy254 = 0; }
#line 2382 "pikchr.c"
break;
case 4: /* element ::= direction */
#line 523 "pikchr.y"
{ pik_set_direction(p,yymsp[0].minor.yy0.eCode); yylhsminor.yy254=0; }
#line 2387 "pikchr.c"
yymsp[0].minor.yy254 = yylhsminor.yy254;
break;
case 5: /* element ::= lvalue ASSIGN rvalue */
#line 524 "pikchr.y"
{pik_set_var(p,&yymsp[-2].minor.yy0,yymsp[0].minor.yy73,&yymsp[-1].minor.yy0); yylhsminor.yy254=0;}
#line 2393 "pikchr.c"
yymsp[-2].minor.yy254 = yylhsminor.yy254;
break;
case 6: /* element ::= PLACENAME COLON unnamed_element */
#line 526 "pikchr.y"
{ yylhsminor.yy254 = yymsp[0].minor.yy254; pik_elem_setname(p,yymsp[0].minor.yy254,&yymsp[-2].minor.yy0); }
#line 2399 "pikchr.c"
yymsp[-2].minor.yy254 = yylhsminor.yy254;
break;
case 7: /* element ::= PLACENAME COLON position */
#line 528 "pikchr.y"
{ yylhsminor.yy254 = pik_elem_new(p,0,0,0);
if(yylhsminor.yy254){ yylhsminor.yy254->ptAt = yymsp[0].minor.yy139; pik_elem_setname(p,yylhsminor.yy254,&yymsp[-2].minor.yy0); }}
#line 2406 "pikchr.c"
yymsp[-2].minor.yy254 = yylhsminor.yy254;
break;
case 8: /* element ::= unnamed_element */
#line 530 "pikchr.y"
{yylhsminor.yy254 = yymsp[0].minor.yy254;}
#line 2412 "pikchr.c"
yymsp[0].minor.yy254 = yylhsminor.yy254;
break;
case 9: /* element ::= print prlist */
#line 531 "pikchr.y"
{pik_append(p,"<br>\n",5); yymsp[-1].minor.yy254=0;}
#line 2418 "pikchr.c"
break;
case 10: /* element ::= ASSERT LP expr EQ expr RP */
#line 536 "pikchr.y"
{yymsp[-5].minor.yy254=pik_assert(p,yymsp[-3].minor.yy73,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy73);}
#line 2423 "pikchr.c"
break;
case 11: /* element ::= ASSERT LP place EQ place RP */
#line 538 "pikchr.y"
{yymsp[-5].minor.yy254=pik_place_assert(p,&yymsp[-3].minor.yy139,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy139);}
#line 2428 "pikchr.c"
break;
case 12: /* rvalue ::= PLACENAME */
#line 549 "pikchr.y"
{yylhsminor.yy73 = pik_lookup_color(p,&yymsp[0].minor.yy0);}
#line 2433 "pikchr.c"
yymsp[0].minor.yy73 = yylhsminor.yy73;
break;
case 13: /* pritem ::= FILL */
case 14: /* pritem ::= COLOR */ yytestcase(yyruleno==14);
case 15: /* pritem ::= THICKNESS */ yytestcase(yyruleno==15);
#line 554 "pikchr.y"
{pik_append_num(p,"",pik_value(p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.n,0));}
#line 2441 "pikchr.c"
break;
case 16: /* pritem ::= rvalue */
#line 557 "pikchr.y"
{pik_append_num(p,"",yymsp[0].minor.yy73);}
#line 2446 "pikchr.c"
break;
case 17: /* pritem ::= STRING */
#line 558 "pikchr.y"
{pik_append_text(p,yymsp[0].minor.yy0.z+1,yymsp[0].minor.yy0.n-2,0);}
#line 2451 "pikchr.c"
break;
case 18: /* prsep ::= COMMA */
#line 559 "pikchr.y"
{pik_append(p, " ", 1);}
#line 2456 "pikchr.c"
break;
case 19: /* unnamed_element ::= basetype attribute_list */
#line 562 "pikchr.y"
{yylhsminor.yy254 = yymsp[-1].minor.yy254; pik_after_adding_attributes(p,yylhsminor.yy254);}
#line 2461 "pikchr.c"
yymsp[-1].minor.yy254 = yylhsminor.yy254;
break;
case 20: /* basetype ::= CLASSNAME */
#line 564 "pikchr.y"
{yylhsminor.yy254 = pik_elem_new(p,&yymsp[0].minor.yy0,0,0); }
#line 2467 "pikchr.c"
yymsp[0].minor.yy254 = yylhsminor.yy254;
break;
case 21: /* basetype ::= STRING textposition */
#line 566 "pikchr.y"
{yymsp[-1].minor.yy0.eCode = yymsp[0].minor.yy74; yylhsminor.yy254 = pik_elem_new(p,0,&yymsp[-1].minor.yy0,0); }
#line 2473 "pikchr.c"
yymsp[-1].minor.yy254 = yylhsminor.yy254;
break;
case 22: /* basetype ::= LB savelist element_list RB */
#line 568 "pikchr.y"
{ p->list = yymsp[-2].minor.yy72; yymsp[-3].minor.yy254 = pik_elem_new(p,0,0,yymsp[-1].minor.yy72); if(yymsp[-3].minor.yy254) yymsp[-3].minor.yy254->errTok = yymsp[0].minor.yy0; }
#line 2479 "pikchr.c"
break;
case 23: /* savelist ::= */
#line 573 "pikchr.y"
{yymsp[1].minor.yy72 = p->list; p->list = 0;}
#line 2484 "pikchr.c"
break;
case 24: /* relexpr ::= expr */
#line 580 "pikchr.y"
{yylhsminor.yy60.rAbs = yymsp[0].minor.yy73; yylhsminor.yy60.rRel = 0;}
#line 2489 "pikchr.c"
yymsp[0].minor.yy60 = yylhsminor.yy60;
break;
case 25: /* relexpr ::= expr PERCENT */
#line 581 "pikchr.y"
{yylhsminor.yy60.rAbs = 0; yylhsminor.yy60.rRel = yymsp[-1].minor.yy73/100;}
#line 2495 "pikchr.c"
yymsp[-1].minor.yy60 = yylhsminor.yy60;
break;
case 26: /* optrelexpr ::= */
#line 583 "pikchr.y"
{yymsp[1].minor.yy60.rAbs = 0; yymsp[1].minor.yy60.rRel = 1.0;}
#line 2501 "pikchr.c"
break;
case 27: /* attribute_list ::= relexpr alist */
#line 585 "pikchr.y"
{pik_add_direction(p,0,&yymsp[-1].minor.yy60);}
#line 2506 "pikchr.c"
break;
case 28: /* attribute ::= numproperty relexpr */
#line 589 "pikchr.y"
{ pik_set_numprop(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy60); }
#line 2511 "pikchr.c"
break;
case 29: /* attribute ::= dashproperty expr */
#line 590 "pikchr.y"
{ pik_set_dashed(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy73); }
#line 2516 "pikchr.c"
break;
case 30: /* attribute ::= dashproperty */
#line 591 "pikchr.y"
{ pik_set_dashed(p,&yymsp[0].minor.yy0,0); }
#line 2521 "pikchr.c"
break;
case 31: /* attribute ::= colorproperty rvalue */
#line 592 "pikchr.y"
{ pik_set_clrprop(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy73); }
#line 2526 "pikchr.c"
break;
case 32: /* attribute ::= go direction optrelexpr */
#line 593 "pikchr.y"
{ pik_add_direction(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy60);}
#line 2531 "pikchr.c"
break;
case 33: /* attribute ::= go direction even position */
#line 594 "pikchr.y"
{pik_evenwith(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy139);}
#line 2536 "pikchr.c"
break;
case 34: /* attribute ::= CLOSE */
#line 595 "pikchr.y"
{ pik_close_path(p,&yymsp[0].minor.yy0); }
#line 2541 "pikchr.c"
break;
case 35: /* attribute ::= CHOP */
#line 596 "pikchr.y"
{ p->cur->bChop = 1; }
#line 2546 "pikchr.c"
break;
case 36: /* attribute ::= FROM position */
#line 597 "pikchr.y"
{ pik_set_from(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy139); }
#line 2551 "pikchr.c"
break;
case 37: /* attribute ::= TO position */
#line 598 "pikchr.y"
{ pik_add_to(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy139); }
#line 2556 "pikchr.c"
break;
case 38: /* attribute ::= THEN */
#line 599 "pikchr.y"
{ pik_then(p, &yymsp[0].minor.yy0, p->cur); }
#line 2561 "pikchr.c"
break;
case 39: /* attribute ::= THEN optrelexpr HEADING expr */
case 41: /* attribute ::= GO optrelexpr HEADING expr */ yytestcase(yyruleno==41);
#line 601 "pikchr.y"
{pik_move_hdg(p,&yymsp[-2].minor.yy60,&yymsp[-1].minor.yy0,yymsp[0].minor.yy73,0,&yymsp[-3].minor.yy0);}
#line 2567 "pikchr.c"
break;
case 40: /* attribute ::= THEN optrelexpr EDGEPT */
case 42: /* attribute ::= GO optrelexpr EDGEPT */ yytestcase(yyruleno==42);
#line 602 "pikchr.y"
{pik_move_hdg(p,&yymsp[-1].minor.yy60,0,0,&yymsp[0].minor.yy0,&yymsp[-2].minor.yy0);}
#line 2573 "pikchr.c"
break;
case 43: /* attribute ::= AT position */
#line 607 "pikchr.y"
{ pik_set_at(p,0,&yymsp[0].minor.yy139,&yymsp[-1].minor.yy0); }
#line 2578 "pikchr.c"
break;
case 44: /* attribute ::= SAME */
#line 609 "pikchr.y"
{pik_same(p,0,&yymsp[0].minor.yy0);}
#line 2583 "pikchr.c"
break;
case 45: /* attribute ::= SAME AS object */
#line 610 "pikchr.y"
{pik_same(p,yymsp[0].minor.yy254,&yymsp[-2].minor.yy0);}
#line 2588 "pikchr.c"
break;
case 46: /* attribute ::= STRING textposition */
#line 611 "pikchr.y"
{pik_add_txt(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy74);}
#line 2593 "pikchr.c"
break;
case 47: /* attribute ::= FIT */
#line 612 "pikchr.y"
{pik_size_to_fit(p,&yymsp[0].minor.yy0); }
#line 2598 "pikchr.c"
break;
case 48: /* attribute ::= BEHIND object */
#line 613 "pikchr.y"
{pik_behind(p,yymsp[0].minor.yy254);}
#line 2603 "pikchr.c"
break;
case 49: /* withclause ::= DOT_E edge AT position */
case 50: /* withclause ::= edge AT position */ yytestcase(yyruleno==50);
#line 621 "pikchr.y"
{ pik_set_at(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy139,&yymsp[-1].minor.yy0); }
#line 2609 "pikchr.c"
break;
case 51: /* numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */
#line 625 "pikchr.y"
{yylhsminor.yy0 = yymsp[0].minor.yy0;}
#line 2614 "pikchr.c"
yymsp[0].minor.yy0 = yylhsminor.yy0;
break;
case 52: /* boolproperty ::= CW */
#line 636 "pikchr.y"
{p->cur->cw = 1;}
#line 2620 "pikchr.c"
break;
case 53: /* boolproperty ::= CCW */
#line 637 "pikchr.y"
{p->cur->cw = 0;}
#line 2625 "pikchr.c"
break;
case 54: /* boolproperty ::= LARROW */
#line 638 "pikchr.y"
{p->cur->larrow=1; p->cur->rarrow=0; }
#line 2630 "pikchr.c"
break;
case 55: /* boolproperty ::= RARROW */
#line 639 "pikchr.y"
{p->cur->larrow=0; p->cur->rarrow=1; }
#line 2635 "pikchr.c"
break;
case 56: /* boolproperty ::= LRARROW */
#line 640 "pikchr.y"
{p->cur->larrow=1; p->cur->rarrow=1; }
#line 2640 "pikchr.c"
break;
case 57: /* boolproperty ::= INVIS */
#line 641 "pikchr.y"
{p->cur->sw = 0.0;}
#line 2645 "pikchr.c"
break;
case 58: /* boolproperty ::= THICK */
#line 642 "pikchr.y"
{p->cur->sw *= 1.5;}
#line 2650 "pikchr.c"
break;
case 59: /* boolproperty ::= THIN */
#line 643 "pikchr.y"
{p->cur->sw *= 0.67;}
#line 2655 "pikchr.c"
break;
case 60: /* textposition ::= */
#line 645 "pikchr.y"
{yymsp[1].minor.yy74 = 0;}
#line 2660 "pikchr.c"
break;
case 61: /* textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|ALIGNED|BIG|SMALL */
#line 648 "pikchr.y"
{yylhsminor.yy74 = pik_text_position(yymsp[-1].minor.yy74,&yymsp[0].minor.yy0);}
#line 2665 "pikchr.c"
yymsp[-1].minor.yy74 = yylhsminor.yy74;
break;
case 62: /* position ::= expr COMMA expr */
#line 651 "pikchr.y"
{yylhsminor.yy139.x=yymsp[-2].minor.yy73; yylhsminor.yy139.y=yymsp[0].minor.yy73;}
#line 2671 "pikchr.c"
yymsp[-2].minor.yy139 = yylhsminor.yy139;
break;
case 63: /* position ::= place PLUS expr COMMA expr */
#line 653 "pikchr.y"
{yylhsminor.yy139.x=yymsp[-4].minor.yy139.x+yymsp[-2].minor.yy73; yylhsminor.yy139.y=yymsp[-4].minor.yy139.y+yymsp[0].minor.yy73;}
#line 2677 "pikchr.c"
yymsp[-4].minor.yy139 = yylhsminor.yy139;
break;
case 64: /* position ::= place MINUS expr COMMA expr */
#line 654 "pikchr.y"
{yylhsminor.yy139.x=yymsp[-4].minor.yy139.x-yymsp[-2].minor.yy73; yylhsminor.yy139.y=yymsp[-4].minor.yy139.y-yymsp[0].minor.yy73;}
#line 2683 "pikchr.c"
yymsp[-4].minor.yy139 = yylhsminor.yy139;
break;
case 65: /* position ::= place PLUS LP expr COMMA expr RP */
#line 656 "pikchr.y"
{yylhsminor.yy139.x=yymsp[-6].minor.yy139.x+yymsp[-3].minor.yy73; yylhsminor.yy139.y=yymsp[-6].minor.yy139.y+yymsp[-1].minor.yy73;}
#line 2689 "pikchr.c"
yymsp[-6].minor.yy139 = yylhsminor.yy139;
break;
case 66: /* position ::= place MINUS LP expr COMMA expr RP */
#line 658 "pikchr.y"
{yylhsminor.yy139.x=yymsp[-6].minor.yy139.x-yymsp[-3].minor.yy73; yylhsminor.yy139.y=yymsp[-6].minor.yy139.y-yymsp[-1].minor.yy73;}
#line 2695 "pikchr.c"
yymsp[-6].minor.yy139 = yylhsminor.yy139;
break;
case 67: /* position ::= LP position COMMA position RP */
#line 659 "pikchr.y"
{yymsp[-4].minor.yy139.x=yymsp[-3].minor.yy139.x; yymsp[-4].minor.yy139.y=yymsp[-1].minor.yy139.y;}
#line 2701 "pikchr.c"
break;
case 68: /* position ::= LP position RP */
#line 660 "pikchr.y"
{yymsp[-2].minor.yy139=yymsp[-1].minor.yy139;}
#line 2706 "pikchr.c"
break;
case 69: /* position ::= expr between position AND position */
#line 662 "pikchr.y"
{yylhsminor.yy139 = pik_position_between(yymsp[-4].minor.yy73,yymsp[-2].minor.yy139,yymsp[0].minor.yy139);}
#line 2711 "pikchr.c"
yymsp[-4].minor.yy139 = yylhsminor.yy139;
break;
case 70: /* position ::= expr LT position COMMA position GT */
#line 664 "pikchr.y"
{yylhsminor.yy139 = pik_position_between(yymsp[-5].minor.yy73,yymsp[-3].minor.yy139,yymsp[-1].minor.yy139);}
#line 2717 "pikchr.c"
yymsp[-5].minor.yy139 = yylhsminor.yy139;
break;
case 71: /* position ::= expr ABOVE position */
#line 665 "pikchr.y"
{yylhsminor.yy139=yymsp[0].minor.yy139; yylhsminor.yy139.y += yymsp[-2].minor.yy73;}
#line 2723 "pikchr.c"
yymsp[-2].minor.yy139 = yylhsminor.yy139;
break;
case 72: /* position ::= expr BELOW position */
#line 666 "pikchr.y"
{yylhsminor.yy139=yymsp[0].minor.yy139; yylhsminor.yy139.y -= yymsp[-2].minor.yy73;}
#line 2729 "pikchr.c"
yymsp[-2].minor.yy139 = yylhsminor.yy139;
break;
case 73: /* position ::= expr LEFT OF position */
#line 667 "pikchr.y"
{yylhsminor.yy139=yymsp[0].minor.yy139; yylhsminor.yy139.x -= yymsp[-3].minor.yy73;}
#line 2735 "pikchr.c"
yymsp[-3].minor.yy139 = yylhsminor.yy139;
break;
case 74: /* position ::= expr RIGHT OF position */
#line 668 "pikchr.y"
{yylhsminor.yy139=yymsp[0].minor.yy139; yylhsminor.yy139.x += yymsp[-3].minor.yy73;}
#line 2741 "pikchr.c"
yymsp[-3].minor.yy139 = yylhsminor.yy139;
break;
case 75: /* position ::= expr ON HEADING EDGEPT OF position */
#line 670 "pikchr.y"
{yylhsminor.yy139 = pik_position_at_hdg(yymsp[-5].minor.yy73,&yymsp[-2].minor.yy0,yymsp[0].minor.yy139);}
#line 2747 "pikchr.c"
yymsp[-5].minor.yy139 = yylhsminor.yy139;
break;
case 76: /* position ::= expr HEADING EDGEPT OF position */
#line 672 "pikchr.y"
{yylhsminor.yy139 = pik_position_at_hdg(yymsp[-4].minor.yy73,&yymsp[-2].minor.yy0,yymsp[0].minor.yy139);}
#line 2753 "pikchr.c"
yymsp[-4].minor.yy139 = yylhsminor.yy139;
break;
case 77: /* position ::= expr EDGEPT OF position */
#line 674 "pikchr.y"
{yylhsminor.yy139 = pik_position_at_hdg(yymsp[-3].minor.yy73,&yymsp[-2].minor.yy0,yymsp[0].minor.yy139);}
#line 2759 "pikchr.c"
yymsp[-3].minor.yy139 = yylhsminor.yy139;
break;
case 78: /* position ::= expr ON HEADING expr FROM position */
#line 676 "pikchr.y"
{yylhsminor.yy139 = pik_position_at_angle(yymsp[-5].minor.yy73,yymsp[-2].minor.yy73,yymsp[0].minor.yy139);}
#line 2765 "pikchr.c"
yymsp[-5].minor.yy139 = yylhsminor.yy139;
break;
case 79: /* position ::= expr HEADING expr FROM position */
#line 678 "pikchr.y"
{yylhsminor.yy139 = pik_position_at_angle(yymsp[-4].minor.yy73,yymsp[-2].minor.yy73,yymsp[0].minor.yy139);}
#line 2771 "pikchr.c"
yymsp[-4].minor.yy139 = yylhsminor.yy139;
break;
case 80: /* place ::= edge OF object */
#line 690 "pikchr.y"
{yylhsminor.yy139 = pik_place_of_elem(p,yymsp[0].minor.yy254,&yymsp[-2].minor.yy0);}
#line 2777 "pikchr.c"
yymsp[-2].minor.yy139 = yylhsminor.yy139;
break;
case 81: /* place2 ::= object */
#line 691 "pikchr.y"
{yylhsminor.yy139 = pik_place_of_elem(p,yymsp[0].minor.yy254,0);}
#line 2783 "pikchr.c"
yymsp[0].minor.yy139 = yylhsminor.yy139;
break;
case 82: /* place2 ::= object DOT_E edge */
#line 692 "pikchr.y"
{yylhsminor.yy139 = pik_place_of_elem(p,yymsp[-2].minor.yy254,&yymsp[0].minor.yy0);}
#line 2789 "pikchr.c"
yymsp[-2].minor.yy139 = yylhsminor.yy139;
break;
case 83: /* place2 ::= NTH VERTEX OF object */
#line 693 "pikchr.y"
{yylhsminor.yy139 = pik_nth_vertex(p,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,yymsp[0].minor.yy254);}
#line 2795 "pikchr.c"
yymsp[-3].minor.yy139 = yylhsminor.yy139;
break;
case 84: /* object ::= nth */
#line 705 "pikchr.y"
{yylhsminor.yy254 = pik_find_nth(p,0,&yymsp[0].minor.yy0);}
#line 2801 "pikchr.c"
yymsp[0].minor.yy254 = yylhsminor.yy254;
break;
case 85: /* object ::= nth OF|IN object */
#line 706 "pikchr.y"
{yylhsminor.yy254 = pik_find_nth(p,yymsp[0].minor.yy254,&yymsp[-2].minor.yy0);}
#line 2807 "pikchr.c"
yymsp[-2].minor.yy254 = yylhsminor.yy254;
break;
case 86: /* objectname ::= PLACENAME */
#line 708 "pikchr.y"
{yylhsminor.yy254 = pik_find_byname(p,0,&yymsp[0].minor.yy0);}
#line 2813 "pikchr.c"
yymsp[0].minor.yy254 = yylhsminor.yy254;
break;
case 87: /* objectname ::= objectname DOT_U PLACENAME */
#line 710 "pikchr.y"
{yylhsminor.yy254 = pik_find_byname(p,yymsp[-2].minor.yy254,&yymsp[0].minor.yy0);}
#line 2819 "pikchr.c"
yymsp[-2].minor.yy254 = yylhsminor.yy254;
break;
case 88: /* nth ::= NTH CLASSNAME */
#line 712 "pikchr.y"
{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-1].minor.yy0); }
#line 2825 "pikchr.c"
yymsp[-1].minor.yy0 = yylhsminor.yy0;
break;
case 89: /* nth ::= NTH LAST CLASSNAME */
#line 713 "pikchr.y"
{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-2].minor.yy0); }
#line 2831 "pikchr.c"
yymsp[-2].minor.yy0 = yylhsminor.yy0;
break;
case 90: /* nth ::= LAST CLASSNAME */
#line 714 "pikchr.y"
{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.eCode = -1;}
#line 2837 "pikchr.c"
break;
case 91: /* nth ::= LAST */
#line 715 "pikchr.y"
{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -1;}
#line 2842 "pikchr.c"
yymsp[0].minor.yy0 = yylhsminor.yy0;
break;
case 92: /* nth ::= NTH LB RB */
#line 716 "pikchr.y"
{yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-2].minor.yy0);}
#line 2848 "pikchr.c"
yymsp[-2].minor.yy0 = yylhsminor.yy0;
break;
case 93: /* nth ::= NTH LAST LB RB */
#line 717 "pikchr.y"
{yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-3].minor.yy0);}
#line 2854 "pikchr.c"
yymsp[-3].minor.yy0 = yylhsminor.yy0;
break;
case 94: /* nth ::= LAST LB RB */
#line 718 "pikchr.y"
{yymsp[-2].minor.yy0=yymsp[-1].minor.yy0; yymsp[-2].minor.yy0.eCode = -1; }
#line 2860 "pikchr.c"
break;
case 95: /* expr ::= expr PLUS expr */
#line 720 "pikchr.y"
{yylhsminor.yy73=yymsp[-2].minor.yy73+yymsp[0].minor.yy73;}
#line 2865 "pikchr.c"
yymsp[-2].minor.yy73 = yylhsminor.yy73;
break;
case 96: /* expr ::= expr MINUS expr */
#line 721 "pikchr.y"
{yylhsminor.yy73=yymsp[-2].minor.yy73-yymsp[0].minor.yy73;}
#line 2871 "pikchr.c"
yymsp[-2].minor.yy73 = yylhsminor.yy73;
break;
case 97: /* expr ::= expr STAR expr */
#line 722 "pikchr.y"
{yylhsminor.yy73=yymsp[-2].minor.yy73*yymsp[0].minor.yy73;}
#line 2877 "pikchr.c"
yymsp[-2].minor.yy73 = yylhsminor.yy73;
break;
case 98: /* expr ::= expr SLASH expr */
#line 723 "pikchr.y"
{
if( yymsp[0].minor.yy73==0.0 ){ pik_error(p, &yymsp[-1].minor.yy0, "division by zero"); yylhsminor.yy73 = 0.0; }
else{ yylhsminor.yy73 = yymsp[-2].minor.yy73/yymsp[0].minor.yy73; }
}
#line 2886 "pikchr.c"
yymsp[-2].minor.yy73 = yylhsminor.yy73;
break;
case 99: /* expr ::= MINUS expr */
#line 727 "pikchr.y"
{yymsp[-1].minor.yy73=-yymsp[0].minor.yy73;}
#line 2892 "pikchr.c"
break;
case 100: /* expr ::= PLUS expr */
#line 728 "pikchr.y"
{yymsp[-1].minor.yy73=yymsp[0].minor.yy73;}
#line 2897 "pikchr.c"
break;
case 101: /* expr ::= LP expr RP */
#line 729 "pikchr.y"
{yymsp[-2].minor.yy73=yymsp[-1].minor.yy73;}
#line 2902 "pikchr.c"
break;
case 102: /* expr ::= NUMBER */
#line 730 "pikchr.y"
{yylhsminor.yy73=pik_atof(&yymsp[0].minor.yy0);}
#line 2907 "pikchr.c"
yymsp[0].minor.yy73 = yylhsminor.yy73;
break;
case 103: /* expr ::= ID */
#line 731 "pikchr.y"
{yylhsminor.yy73=pik_get_var(p,&yymsp[0].minor.yy0);}
#line 2913 "pikchr.c"
yymsp[0].minor.yy73 = yylhsminor.yy73;
break;
case 104: /* expr ::= FUNC1 LP expr RP */
#line 732 "pikchr.y"
{yylhsminor.yy73 = pik_func(p,&yymsp[-3].minor.yy0,yymsp[-1].minor.yy73,0.0);}
#line 2919 "pikchr.c"
yymsp[-3].minor.yy73 = yylhsminor.yy73;
break;
case 105: /* expr ::= FUNC2 LP expr COMMA expr RP */
#line 733 "pikchr.y"
{yylhsminor.yy73 = pik_func(p,&yymsp[-5].minor.yy0,yymsp[-3].minor.yy73,yymsp[-1].minor.yy73);}
#line 2925 "pikchr.c"
yymsp[-5].minor.yy73 = yylhsminor.yy73;
break;
case 106: /* expr ::= place2 DOT_XY X */
#line 734 "pikchr.y"
{yylhsminor.yy73 = yymsp[-2].minor.yy139.x;}
#line 2931 "pikchr.c"
yymsp[-2].minor.yy73 = yylhsminor.yy73;
break;
case 107: /* expr ::= place2 DOT_XY Y */
#line 735 "pikchr.y"
{yylhsminor.yy73 = yymsp[-2].minor.yy139.y;}
#line 2937 "pikchr.c"
yymsp[-2].minor.yy73 = yylhsminor.yy73;
break;
case 108: /* expr ::= object DOT_L numproperty */
case 109: /* expr ::= object DOT_L dashproperty */ yytestcase(yyruleno==109);
case 110: /* expr ::= object DOT_L colorproperty */ yytestcase(yyruleno==110);
#line 736 "pikchr.y"
{yylhsminor.yy73=pik_property_of(yymsp[-2].minor.yy254,&yymsp[0].minor.yy0);}
#line 2945 "pikchr.c"
yymsp[-2].minor.yy73 = yylhsminor.yy73;
break;
default:
/* (111) lvalue ::= ID */ yytestcase(yyruleno==111);
/* (112) lvalue ::= FILL */ yytestcase(yyruleno==112);
/* (113) lvalue ::= COLOR */ yytestcase(yyruleno==113);
/* (114) lvalue ::= THICKNESS */ yytestcase(yyruleno==114);
/* (115) rvalue ::= expr */ yytestcase(yyruleno==115);
/* (116) print ::= PRINT */ yytestcase(yyruleno==116);
/* (117) prlist ::= pritem (OPTIMIZED OUT) */ assert(yyruleno!=117);
/* (118) prlist ::= prlist prsep pritem */ yytestcase(yyruleno==118);
/* (119) direction ::= UP */ yytestcase(yyruleno==119);
/* (120) direction ::= DOWN */ yytestcase(yyruleno==120);
/* (121) direction ::= LEFT */ yytestcase(yyruleno==121);
/* (122) direction ::= RIGHT */ yytestcase(yyruleno==122);
/* (123) optrelexpr ::= relexpr (OPTIMIZED OUT) */ assert(yyruleno!=123);
/* (124) attribute_list ::= alist */ yytestcase(yyruleno==124);
/* (125) alist ::= */ yytestcase(yyruleno==125);
/* (126) alist ::= alist attribute */ yytestcase(yyruleno==126);
/* (127) attribute ::= boolproperty (OPTIMIZED OUT) */ assert(yyruleno!=127);
/* (128) attribute ::= WITH withclause */ yytestcase(yyruleno==128);
/* (129) go ::= GO */ yytestcase(yyruleno==129);
/* (130) go ::= */ yytestcase(yyruleno==130);
/* (131) even ::= UNTIL EVEN WITH */ yytestcase(yyruleno==131);
/* (132) even ::= EVEN WITH */ yytestcase(yyruleno==132);
/* (133) dashproperty ::= DOTTED */ yytestcase(yyruleno==133);
/* (134) dashproperty ::= DASHED */ yytestcase(yyruleno==134);
/* (135) colorproperty ::= FILL */ yytestcase(yyruleno==135);
/* (136) colorproperty ::= COLOR */ yytestcase(yyruleno==136);
/* (137) position ::= place */ yytestcase(yyruleno==137);
/* (138) between ::= WAY BETWEEN */ yytestcase(yyruleno==138);
/* (139) between ::= BETWEEN */ yytestcase(yyruleno==139);
/* (140) between ::= OF THE WAY BETWEEN */ yytestcase(yyruleno==140);
/* (141) place ::= place2 */ yytestcase(yyruleno==141);
/* (142) edge ::= CENTER */ yytestcase(yyruleno==142);
/* (143) edge ::= EDGEPT */ yytestcase(yyruleno==143);
/* (144) edge ::= TOP */ yytestcase(yyruleno==144);
/* (145) edge ::= BOTTOM */ yytestcase(yyruleno==145);
/* (146) edge ::= START */ yytestcase(yyruleno==146);
/* (147) edge ::= END */ yytestcase(yyruleno==147);
/* (148) edge ::= RIGHT */ yytestcase(yyruleno==148);
/* (149) edge ::= LEFT */ yytestcase(yyruleno==149);
/* (150) object ::= objectname */ yytestcase(yyruleno==150);
break;
/********** End reduce actions ************************************************/
};
assert( yyruleno<sizeof(yyRuleInfoLhs)/sizeof(yyRuleInfoLhs[0]) );
yygoto = yyRuleInfoLhs[yyruleno];
yysize = yyRuleInfoNRhs[yyruleno];
yyact = yy_find_reduce_action(yymsp[yysize].stateno,(YYCODETYPE)yygoto);
/* There are no SHIFTREDUCE actions on nonterminals because the table
** generator has simplified them to pure REDUCE actions. */
assert( !(yyact>YY_MAX_SHIFT && yyact<=YY_MAX_SHIFTREDUCE) );
/* It is not possible for a REDUCE to be followed by an error */
assert( yyact!=YY_ERROR_ACTION );
yymsp += yysize+1;
yypParser->yytos = yymsp;
yymsp->stateno = (YYACTIONTYPE)yyact;
yymsp->major = (YYCODETYPE)yygoto;
yyTraceShift(yypParser, yyact, "... then shift");
return yyact;
}
/*
** The following code executes when the parse fails
*/
#ifndef YYNOERRORRECOVERY
static void yy_parse_failed(
yyParser *yypParser /* The parser */
){
pik_parserARG_FETCH
pik_parserCTX_FETCH
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt);
}
#endif
while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
/* Here code is inserted which will be executed whenever the
** parser fails */
/************ Begin %parse_failure code ***************************************/
/************ End %parse_failure code *****************************************/
pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */
pik_parserCTX_STORE
}
#endif /* YYNOERRORRECOVERY */
/*
** The following code executes when a syntax error first occurs.
*/
static void yy_syntax_error(
yyParser *yypParser, /* The parser */
int yymajor, /* The major type of the error token */
pik_parserTOKENTYPE yyminor /* The minor type of the error token */
){
pik_parserARG_FETCH
pik_parserCTX_FETCH
#define TOKEN yyminor
/************ Begin %syntax_error code ****************************************/
#line 502 "pikchr.y"
if( TOKEN.z && TOKEN.z[0] ){
pik_error(p, &TOKEN, "syntax error");
}else{
pik_error(p, 0, "syntax error");
}
UNUSED_PARAMETER(yymajor);
#line 3056 "pikchr.c"
/************ End %syntax_error code ******************************************/
pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */
pik_parserCTX_STORE
}
/*
** The following is executed when the parser accepts
*/
static void yy_accept(
yyParser *yypParser /* The parser */
){
pik_parserARG_FETCH
pik_parserCTX_FETCH
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt);
}
#endif
#ifndef YYNOERRORRECOVERY
yypParser->yyerrcnt = -1;
#endif
assert( yypParser->yytos==yypParser->yystack );
/* Here code is inserted which will be executed whenever the
** parser accepts */
/*********** Begin %parse_accept code *****************************************/
/*********** End %parse_accept code *******************************************/
pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */
pik_parserCTX_STORE
}
/* The main parser program.
** The first argument is a pointer to a structure obtained from
** "pik_parserAlloc" which describes the current state of the parser.
** The second argument is the major token number. The third is
** the minor token. The fourth optional argument is whatever the
** user wants (and specified in the grammar) and is available for
** use by the action routines.
**
** Inputs:
** <ul>
** <li> A pointer to the parser (an opaque structure.)
** <li> The major token number.
** <li> The minor token number.
** <li> An option argument of a grammar-specified type.
** </ul>
**
** Outputs:
** None.
*/
void pik_parser(
void *yyp, /* The parser */
int yymajor, /* The major token code number */
pik_parserTOKENTYPE yyminor /* The value for the token */
pik_parserARG_PDECL /* Optional %extra_argument parameter */
){
YYMINORTYPE yyminorunion;
YYACTIONTYPE yyact; /* The parser action. */
#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
int yyendofinput; /* True if we are at the end of input */
#endif
#ifdef YYERRORSYMBOL
int yyerrorhit = 0; /* True if yymajor has invoked an error */
#endif
yyParser *yypParser = (yyParser*)yyp; /* The parser */
pik_parserCTX_FETCH
pik_parserARG_STORE
assert( yypParser->yytos!=0 );
#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
yyendofinput = (yymajor==0);
#endif
yyact = yypParser->yytos->stateno;
#ifndef NDEBUG
if( yyTraceFILE ){
if( yyact < YY_MIN_REDUCE ){
fprintf(yyTraceFILE,"%sInput '%s' in state %d\n",
yyTracePrompt,yyTokenName[yymajor],yyact);
}else{
fprintf(yyTraceFILE,"%sInput '%s' with pending reduce %d\n",
yyTracePrompt,yyTokenName[yymajor],yyact-YY_MIN_REDUCE);
}
}
#endif
do{
assert( yyact==yypParser->yytos->stateno );
yyact = yy_find_shift_action((YYCODETYPE)yymajor,yyact);
if( yyact >= YY_MIN_REDUCE ){
yyact = yy_reduce(yypParser,yyact-YY_MIN_REDUCE,yymajor,
yyminor pik_parserCTX_PARAM);
}else if( yyact <= YY_MAX_SHIFTREDUCE ){
yy_shift(yypParser,yyact,(YYCODETYPE)yymajor,yyminor);
#ifndef YYNOERRORRECOVERY
yypParser->yyerrcnt--;
#endif
break;
}else if( yyact==YY_ACCEPT_ACTION ){
yypParser->yytos--;
yy_accept(yypParser);
return;
}else{
assert( yyact == YY_ERROR_ACTION );
yyminorunion.yy0 = yyminor;
#ifdef YYERRORSYMBOL
int yymx;
#endif
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt);
}
#endif
#ifdef YYERRORSYMBOL
/* A syntax error has occurred.
** The response to an error depends upon whether or not the
** grammar defines an error token "ERROR".
**
** This is what we do if the grammar does define ERROR:
**
** * Call the %syntax_error function.
**
** * Begin popping the stack until we enter a state where
** it is legal to shift the error symbol, then shift
** the error symbol.
**
** * Set the error count to three.
**
** * Begin accepting and shifting new tokens. No new error
** processing will occur until three tokens have been
** shifted successfully.
**
*/
if( yypParser->yyerrcnt<0 ){
yy_syntax_error(yypParser,yymajor,yyminor);
}
yymx = yypParser->yytos->major;
if( yymx==YYERRORSYMBOL || yyerrorhit ){
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE,"%sDiscard input token %s\n",
yyTracePrompt,yyTokenName[yymajor]);
}
#endif
yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion);
yymajor = YYNOCODE;
}else{
while( yypParser->yytos >= yypParser->yystack
&& (yyact = yy_find_reduce_action(
yypParser->yytos->stateno,
YYERRORSYMBOL)) > YY_MAX_SHIFTREDUCE
){
yy_pop_parser_stack(yypParser);
}
if( yypParser->yytos < yypParser->yystack || yymajor==0 ){
yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
yy_parse_failed(yypParser);
#ifndef YYNOERRORRECOVERY
yypParser->yyerrcnt = -1;
#endif
yymajor = YYNOCODE;
}else if( yymx!=YYERRORSYMBOL ){
yy_shift(yypParser,yyact,YYERRORSYMBOL,yyminor);
}
}
yypParser->yyerrcnt = 3;
yyerrorhit = 1;
if( yymajor==YYNOCODE ) break;
yyact = yypParser->yytos->stateno;
#elif defined(YYNOERRORRECOVERY)
/* If the YYNOERRORRECOVERY macro is defined, then do not attempt to
** do any kind of error recovery. Instead, simply invoke the syntax
** error routine and continue going as if nothing had happened.
**
** Applications can set this macro (for example inside %include) if
** they intend to abandon the parse upon the first syntax error seen.
*/
yy_syntax_error(yypParser,yymajor, yyminor);
yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
break;
#else /* YYERRORSYMBOL is not defined */
/* This is what we do if the grammar does not define ERROR:
**
** * Report an error message, and throw away the input token.
**
** * If the input token is $, then fail the parse.
**
** As before, subsequent error messages are suppressed until
** three input tokens have been successfully shifted.
*/
if( yypParser->yyerrcnt<=0 ){
yy_syntax_error(yypParser,yymajor, yyminor);
}
yypParser->yyerrcnt = 3;
yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
if( yyendofinput ){
yy_parse_failed(yypParser);
#ifndef YYNOERRORRECOVERY
yypParser->yyerrcnt = -1;
#endif
}
break;
#endif
}
}while( yypParser->yytos>yypParser->yystack );
#ifndef NDEBUG
if( yyTraceFILE ){
yyStackEntry *i;
char cDiv = '[';
fprintf(yyTraceFILE,"%sReturn. Stack=",yyTracePrompt);
for(i=&yypParser->yystack[1]; i<=yypParser->yytos; i++){
fprintf(yyTraceFILE,"%c%s", cDiv, yyTokenName[i->major]);
cDiv = ' ';
}
fprintf(yyTraceFILE,"]\n");
}
#endif
return;
}
/*
** Return the fallback token corresponding to canonical token iToken, or
** 0 if iToken has no fallback.
*/
int pik_parserFallback(int iToken){
#ifdef YYFALLBACK
assert( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) );
return yyFallback[iToken];
#else
(void)iToken;
return 0;
#endif
}
#line 741 "pikchr.y"
/* Chart of the 140 official HTML color names with their
** corresponding RGB value.
**
** Two new names "None" and "Off" are added with a value
** of -1.
*/
static const struct {
const char *zName; /* Name of the color */
int val; /* RGB value */
} aColor[] = {
{ "AliceBlue", 0xf0f8ff },
{ "AntiqueWhite", 0xfaebd7 },
{ "Aqua", 0x00ffff },
{ "AquaMarine", 0x7fffd4 },
{ "Azure", 0xf0ffff },
{ "Beige", 0xf5f5dc },
{ "Bisque", 0xffe4c4 },
{ "Black", 0x000000 },
{ "BlanchedAlmond", 0xffebcd },
{ "Blue", 0x0000ff },
{ "BlueViolet", 0x8a2be2 },
{ "Brown", 0xa52a2a },
{ "BurlyWood", 0xdeb887 },
{ "CadetBlue", 0x5f9ea0 },
{ "Chartreuse", 0x7fff00 },
{ "Chocolate", 0xd2691e },
{ "Coral", 0xff7f50 },
{ "CornFlowerBlue", 0x6495ed },
{ "Cornsilk", 0xfff8dc },
{ "Crimson", 0xdc143c },
{ "Cyan", 0x00ffff },
{ "DarkBlue", 0x00008b },
{ "DarkCyan", 0x008b8b },
{ "DarkGoldenRod", 0xb8860b },
{ "DarkGray", 0xa9a9a9 },
{ "DarkGreen", 0x006400 },
{ "DarkKhaki", 0xbdb76b },
{ "DarkMagenta", 0x8b008b },
{ "DarkOliveGreen", 0x556b2f },
{ "DarkOrange", 0xff8c00 },
{ "DarkOrchid", 0x9932cc },
{ "DarkRed", 0x8b0000 },
{ "DarkSalmon", 0xe9967a },
{ "DarkSeaGreen", 0x8fbc8f },
{ "DarkSlateBlue", 0x483d8b },
{ "DarkSlateGray", 0x2f4f4f },
{ "DarkTurquoise", 0x00ced1 },
{ "DarkViolet", 0x9400d3 },
{ "DeepPink", 0xff1493 },
{ "DeepSkyBlue", 0x00bfff },
{ "DimGray", 0x696969 },
{ "DodgerBlue", 0x1e90ff },
{ "FireBrick", 0xb22222 },
{ "FloralWhite", 0xfffaf0 },
{ "ForestGreen", 0x228b22 },
{ "Fuchsia", 0xff00ff },
{ "Gainsboro", 0xdcdcdc },
{ "GhostWhite", 0xf8f8ff },
{ "Gold", 0xffd700 },
{ "GoldenRod", 0xdaa520 },
{ "Gray", 0x808080 },
{ "Green", 0x008000 },
{ "GreenYellow", 0xadff2f },
{ "HoneyDew", 0xf0fff0 },
{ "HotPink", 0xff69b4 },
{ "IndianRed", 0xcd5c5c },
{ "Indigo", 0x4b0082 },
{ "Ivory", 0xfffff0 },
{ "Khaki", 0xf0e68c },
{ "Lavender", 0xe6e6fa },
{ "LavenderBlush", 0xfff0f5 },
{ "LawnGreen", 0x7cfc00 },
{ "LemonChiffon", 0xfffacd },
{ "LightBlue", 0xadd8e6 },
{ "LightCoral", 0xf08080 },
{ "LightCyan", 0xe0ffff },
{ "LightGoldenrodYellow", 0xfafad2 },
{ "LightGray", 0xd3d3d3 },
{ "LightGreen", 0x90ee90 },
{ "LightPink", 0xffb6c1 },
{ "LightSalmon", 0xffa07a },
{ "LightSeaGreen", 0x20b2aa },
{ "LightSkyBlue", 0x87cefa },
{ "LightSlateGray", 0x778899 },
{ "LightSteelBlue", 0xb0c4de },
{ "LightYellow", 0xffffe0 },
{ "Lime", 0x00ff00 },
{ "LimeGreen", 0x32cd32 },
{ "Linen", 0xfaf0e6 },
{ "Magenta", 0xff00ff },
{ "Maroon", 0x800000 },
{ "MediumAquaMarine", 0x66cdaa },
{ "MediumBlue", 0x0000cd },
{ "MediumOrchid", 0xba55d3 },
{ "MediumPurple", 0x9370d8 },
{ "MediumSeaGreen", 0x3cb371 },
{ "MediumSlateBlue", 0x7b68ee },
{ "MediumSpringGreen", 0x00fa9a },
{ "MediumTurquoise", 0x48d1cc },
{ "MediumVioletRed", 0xc71585 },
{ "MidnightBlue", 0x191970 },
{ "MintCream", 0xf5fffa },
{ "MistyRose", 0xffe4e1 },
{ "Moccasin", 0xffe4b5 },
{ "NavajoWhite", 0xffdead },
{ "Navy", 0x000080 },
{ "None", -1 }, /* Non-standard addition */
{ "Off", -1 }, /* Non-standard addition */
{ "OldLace", 0xfdf5e6 },
{ "Olive", 0x808000 },
{ "OliveDrab", 0x6b8e23 },
{ "Orange", 0xffa500 },
{ "OrangeRed", 0xff4500 },
{ "Orchid", 0xda70d6 },
{ "PaleGoldenRod", 0xeee8aa },
{ "PaleGreen", 0x98fb98 },
{ "PaleTurquoise", 0xafeeee },
{ "PaleVioletRed", 0xdb7093 },
{ "PapayaWhip", 0xffefd5 },
{ "PeachPuff", 0xffdab9 },
{ "Peru", 0xcd853f },
{ "Pink", 0xffc0cb },
{ "Plum", 0xdda0dd },
{ "PowderBlue", 0xb0e0e6 },
{ "Purple", 0x800080 },
{ "Red", 0xff0000 },
{ "RosyBrown", 0xbc8f8f },
{ "RoyalBlue", 0x4169e1 },
{ "SaddleBrown", 0x8b4513 },
{ "Salmon", 0xfa8072 },
{ "SandyBrown", 0xf4a460 },
{ "SeaGreen", 0x2e8b57 },
{ "SeaShell", 0xfff5ee },
{ "Sienna", 0xa0522d },
{ "Silver", 0xc0c0c0 },
{ "SkyBlue", 0x87ceeb },
{ "SlateBlue", 0x6a5acd },
{ "SlateGray", 0x708090 },
{ "Snow", 0xfffafa },
{ "SpringGreen", 0x00ff7f },
{ "SteelBlue", 0x4682b4 },
{ "Tan", 0xd2b48c },
{ "Teal", 0x008080 },
{ "Thistle", 0xd8bfd8 },
{ "Tomato", 0xff6347 },
{ "Turquoise", 0x40e0d0 },
{ "Violet", 0xee82ee },
{ "Wheat", 0xf5deb3 },
{ "White", 0xffffff },
{ "WhiteSmoke", 0xf5f5f5 },
{ "Yellow", 0xffff00 },
{ "YellowGreen", 0x9acd32 },
};
/* Built-in variable names.
**
** This array is constant. When a script changes the value of one of
** these built-ins, a new PVar record is added at the head of
** the Pik.pVar list, which is searched first. Thus the new PVar entry
** will override this default value.
**
** Units are in inches, except for "color" and "fill" which are
** interpreted as 24-bit RGB values.
**
** Binary search used. Must be kept in sorted order.
*/
static const struct { const char *zName; PNum val; } aBuiltin[] = {
{ "arcrad", 0.25 },
{ "arrowhead", 2.0 },
{ "arrowht", 0.08 },
{ "arrowwid", 0.06 },
{ "boxht", 0.5 },
{ "boxrad", 0.0 },
{ "boxwid", 0.75 },
{ "charht", 0.14 },
{ "charwid", 0.08 },
{ "circlerad", 0.25 },
{ "color", 0.0 },
{ "cylht", 0.5 },
{ "cylrad", 0.075 },
{ "cylwid", 0.75 },
{ "dashwid", 0.05 },
{ "dotrad", 0.015 },
{ "ellipseht", 0.5 },
{ "ellipsewid", 0.75 },
{ "fileht", 0.75 },
{ "filerad", 0.15 },
{ "filewid", 0.5 },
{ "fill", -1.0 },
{ "lineht", 0.5 },
{ "linewid", 0.5 },
{ "movewid", 0.5 },
{ "ovalht", 0.5 },
{ "ovalwid", 1.0 },
{ "scale", 1.0 },
{ "textht", 0.5 },
{ "textwid", 0.75 },
{ "thickness", 0.015 },
};
/* Methods for the "arc" class */
static void arcInit(Pik *p, PElem *pElem){
pElem->w = pik_value(p, "arcrad",6,0);
pElem->h = pElem->w;
}
/* Hack: Arcs are here rendered as quadratic Bezier curves rather
** than true arcs. Multiple reasons: (1) the legacy-PIC parameters
** that control arcs are obscure and I could not figure out what they
** mean based on available documentation. (2) Arcs are rarely used,
** and so do not seem that important.
*/
static PPoint arcControlPoint(int cw, PPoint f, PPoint t, PNum rScale){
PPoint m;
PNum dx, dy;
m.x = 0.5*(f.x+t.x);
m.y = 0.5*(f.y+t.y);
dx = t.x - f.x;
dy = t.y - f.y;
if( cw ){
m.x -= 0.5*rScale*dy;
m.y += 0.5*rScale*dx;
}else{
m.x += 0.5*rScale*dy;
m.y -= 0.5*rScale*dx;
}
return m;
}
static void arcCheck(Pik *p, PElem *pElem){
PPoint m;
if( p->nTPath>2 ){
pik_error(p, &pElem->errTok, "arc geometry error");
return;
}
m = arcControlPoint(pElem->cw, p->aTPath[0], p->aTPath[1], 0.5);
pik_bbox_add_xy(&pElem->bbox, m.x, m.y);
}
static void arcRender(Pik *p, PElem *pElem){
PPoint f, m, t;
if( pElem->nPath<2 ) return;
if( pElem->sw<=0.0 ) return;
f = pElem->aPath[0];
t = pElem->aPath[1];
m = arcControlPoint(pElem->cw,f,t,1.0);
if( pElem->larrow ){
pik_draw_arrowhead(p,&m,&f,pElem);
}
if( pElem->rarrow ){
pik_draw_arrowhead(p,&m,&t,pElem);
}
pik_append_xy(p,"<path d=\"M", f.x, f.y);
pik_append_xy(p,"Q", m.x, m.y);
pik_append_xy(p," ", t.x, t.y);
pik_append(p,"\" ",2);
pik_append_style(p,pElem,0);
pik_append(p,"\" />\n", -1);
pik_append_txt(p, pElem, 0);
}
/* Methods for the "arrow" class */
static void arrowInit(Pik *p, PElem *pElem){
pElem->w = pik_value(p, "linewid",7,0);
pElem->h = pik_value(p, "lineht",6,0);
pElem->rad = pik_value(p, "linerad",7,0);
pElem->fill = -1.0;
pElem->rarrow = 1;
}
/* Methods for the "box" class */
static void boxInit(Pik *p, PElem *pElem){
pElem->w = pik_value(p, "boxwid",6,0);
pElem->h = pik_value(p, "boxht",5,0);
pElem->rad = pik_value(p, "boxrad",6,0);
}
/* Return offset from the center of the box to the compass point
** given by parameter cp */
static PPoint boxOffset(Pik *p, PElem *pElem, int cp){
PPoint pt;
PNum w2 = 0.5*pElem->w;
PNum h2 = 0.5*pElem->h;
PNum rad = pElem->rad;
PNum rx;
if( rad<=0.0 ){
rx = 0.0;
}else{
if( rad>w2 ) rad = w2;
if( rad>h2 ) rad = h2;
rx = 0.29289321881345252392*rad;
}
pt.x = pt.y = 0.0;
switch( cp ){
case CP_C: pt.x = 0.0; pt.y = 0.0; break;
case CP_N: pt.x = 0.0; pt.y = h2; break;
case CP_NE: pt.x = w2-rx; pt.y = h2-rx; break;
case CP_E: pt.x = w2; pt.y = 0.0; break;
case CP_SE: pt.x = w2-rx; pt.y = rx-h2; break;
case CP_S: pt.x = 0.0; pt.y = -h2; break;
case CP_SW: pt.x = rx-w2; pt.y = rx-h2; break;
case CP_W: pt.x = -w2; pt.y = 0.0; break;
case CP_NW: pt.x = rx-w2; pt.y = h2-rx; break;
}
UNUSED_PARAMETER(p);
return pt;
}
static PPoint boxChop(Pik *p, PElem *pElem, PPoint *pPt){
PNum dx, dy;
int cp = CP_C;
PPoint chop = pElem->ptAt;
if( pElem->w<=0.0 ) return chop;
if( pElem->h<=0.0 ) return chop;
dx = (pPt->x - pElem->ptAt.x)*pElem->h/pElem->w;
dy = (pPt->y - pElem->ptAt.y);
if( dx>0.0 ){
if( dy>=2.414*dx ){
cp = CP_N;
}else if( dy>=0.414*dx ){
cp = CP_NE;
}else if( dy>=-0.414*dx ){
cp = CP_E;
}else if( dy>-2.414*dx ){
cp = CP_SE;
}else{
cp = CP_S;
}
}else{
if( dy>=-2.414*dx ){
cp = CP_N;
}else if( dy>=-0.414*dx ){
cp = CP_NW;
}else if( dy>=0.414*dx ){
cp = CP_W;
}else if( dy>2.414*dx ){
cp = CP_SW;
}else{
cp = CP_S;
}
}
chop = pElem->type->xOffset(p,pElem,cp);
chop.x += pElem->ptAt.x;
chop.y += pElem->ptAt.y;
return chop;
}
static void boxFit(Pik *p, PElem *pElem, PNum w, PNum h){
if( w>0 ) pElem->w = w;
if( h>0 ) pElem->h = h;
UNUSED_PARAMETER(p);
}
static void boxRender(Pik *p, PElem *pElem){
PNum w2 = 0.5*pElem->w;
PNum h2 = 0.5*pElem->h;
PNum rad = pElem->rad;
PPoint pt = pElem->ptAt;
if( pElem->sw>0.0 ){
if( rad<=0.0 ){
pik_append_xy(p,"<path d=\"M", pt.x-w2,pt.y-h2);
pik_append_xy(p,"L", pt.x+w2,pt.y-h2);
pik_append_xy(p,"L", pt.x+w2,pt.y+h2);
pik_append_xy(p,"L", pt.x-w2,pt.y+h2);
pik_append(p,"Z\" ",-1);
}else{
/*
** ---- - y3
** / \
** / \ _ y2
** | |
** | | _ y1
** \ /
** \ /
** ---- _ y0
**
** ' ' ' '
** x0 x1 x2 x3
*/
PNum x0,x1,x2,x3,y0,y1,y2,y3;
if( rad>w2 ) rad = w2;
if( rad>h2 ) rad = h2;
x0 = pt.x - w2;
x1 = x0 + rad;
x3 = pt.x + w2;
x2 = x3 - rad;
y0 = pt.y - h2;
y1 = y0 + rad;
y3 = pt.y + h2;
y2 = y3 - rad;
pik_append_xy(p,"<path d=\"M", x1, y0);
if( x2>x1 ) pik_append_xy(p, "L", x2, y0);
pik_append_arc(p, rad, rad, x3, y1);
if( y2>y1 ) pik_append_xy(p, "L", x3, y2);
pik_append_arc(p, rad, rad, x2, y3);
if( x2>x1 ) pik_append_xy(p, "L", x1, y3);
pik_append_arc(p, rad, rad, x0, y2);
if( y2>y1 ) pik_append_xy(p, "L", x0, y1);
pik_append_arc(p, rad, rad, x1, y0);
pik_append(p,"Z\" ",-1);
}
pik_append_style(p,pElem,1);
pik_append(p,"\" />\n", -1);
}
pik_append_txt(p, pElem, 0);
}
/* Methods for the "circle" class */
static void circleInit(Pik *p, PElem *pElem){
pElem->w = pik_value(p, "circlerad",9,0)*2;
pElem->h = pElem->w;
pElem->rad = 0.5*pElem->w;
}
static void circleNumProp(Pik *p, PElem *pElem, PToken *pId){
/* For a circle, the width must equal the height and both must
** be twice the radius. Enforce those constraints. */
switch( pId->eType ){
case T_RADIUS:
pElem->w = pElem->h = 2.0*pElem->rad;
break;
case T_WIDTH:
pElem->h = pElem->w;
pElem->rad = 0.5*pElem->w;
break;
case T_HEIGHT:
pElem->w = pElem->h;
pElem->rad = 0.5*pElem->w;
break;
}
UNUSED_PARAMETER(p);
}
static PPoint circleChop(Pik *p, PElem *pElem, PPoint *pPt){
PPoint chop;
PNum dx = pPt->x - pElem->ptAt.x;
PNum dy = pPt->y - pElem->ptAt.y;
PNum dist = hypot(dx,dy);
if( dist<pElem->rad ) return pElem->ptAt;
chop.x = pElem->ptAt.x + dx*pElem->rad/dist;
chop.y = pElem->ptAt.y + dy*pElem->rad/dist;
UNUSED_PARAMETER(p);
return chop;
}
static void circleFit(Pik *p, PElem *pElem, PNum w, PNum h){
PNum mx = 0.0;
if( w>0 ) mx = w;
if( h>mx ) mx = h;
if( (w*w + h*h) > mx*mx ){
mx = hypot(w,h);
}
if( mx>0.0 ){
pElem->rad = 0.5*mx;
pElem->w = pElem->h = mx;
}
UNUSED_PARAMETER(p);
}
static void circleRender(Pik *p, PElem *pElem){
PNum r = pElem->rad;
PPoint pt = pElem->ptAt;
if( pElem->sw>0.0 ){
pik_append_x(p,"<circle cx=\"", pt.x, "\"");
pik_append_y(p," cy=\"", pt.y, "\"");
pik_append_dis(p," r=\"", r, "\" ");
pik_append_style(p,pElem,1);
pik_append(p,"\" />\n", -1);
}
pik_append_txt(p, pElem, 0);
}
/* Methods for the "cylinder" class */
static void cylinderInit(Pik *p, PElem *pElem){
pElem->w = pik_value(p, "cylwid",6,0);
pElem->h = pik_value(p, "cylht",5,0);
pElem->rad = pik_value(p, "cylrad",6,0); /* Minor radius of ellipses */
}
static void cylinderFit(Pik *p, PElem *pElem, PNum w, PNum h){
if( w>0 ) pElem->w = w;
if( h>0 ) pElem->h = h + 4*pElem->rad + pElem->sw;
UNUSED_PARAMETER(p);
}
static void cylinderRender(Pik *p, PElem *pElem){
PNum w2 = 0.5*pElem->w;
PNum h2 = 0.5*pElem->h;
PNum rad = pElem->rad;
PPoint pt = pElem->ptAt;
if( pElem->sw>0.0 ){
pik_append_xy(p,"<path d=\"M", pt.x-w2,pt.y+h2-rad);
pik_append_xy(p,"L", pt.x-w2,pt.y-h2+rad);
pik_append_arc(p,w2,rad,pt.x+w2,pt.y-h2+rad);
pik_append_xy(p,"L", pt.x+w2,pt.y+h2-rad);
pik_append_arc(p,w2,rad,pt.x-w2,pt.y+h2-rad);
pik_append_arc(p,w2,rad,pt.x+w2,pt.y+h2-rad);
pik_append(p,"\" ",-1);
pik_append_style(p,pElem,1);
pik_append(p,"\" />\n", -1);
}
pik_append_txt(p, pElem, 0);
}
static PPoint cylinderOffset(Pik *p, PElem *pElem, int cp){
PPoint pt;
PNum w2 = pElem->w*0.5;
PNum h1 = pElem->h*0.5;
PNum h2 = h1 - pElem->rad;
switch( cp ){
case CP_C: pt.x = 0.0; pt.y = 0.0; break;
case CP_N: pt.x = 0.0; pt.y = h1; break;
case CP_NE: pt.x = w2; pt.y = h2; break;
case CP_E: pt.x = w2; pt.y = 0.0; break;
case CP_SE: pt.x = w2; pt.y = -h2; break;
case CP_S: pt.x = 0.0; pt.y = -h1; break;
case CP_SW: pt.x = -w2; pt.y = -h2; break;
case CP_W: pt.x = -w2; pt.y = 0.0; break;
case CP_NW: pt.x = -w2; pt.y = h2; break;
}
UNUSED_PARAMETER(p);
return pt;
}
/* Methods for the "dot" class */
static void dotInit(Pik *p, PElem *pElem){
pElem->rad = pik_value(p, "dotrad",6,0);
pElem->h = pElem->w = pElem->rad*6;
pElem->fill = pElem->color;
}
static void dotNumProp(Pik *p, PElem *pElem, PToken *pId){
switch( pId->eType ){
case T_COLOR:
pElem->fill = pElem->color;
break;
case T_FILL:
pElem->color = pElem->fill;
break;
}
UNUSED_PARAMETER(p);
}
static void dotCheck(Pik *p, PElem *pElem){
pElem->w = pElem->h = 0;
pik_bbox_addellipse(&pElem->bbox, pElem->ptAt.x, pElem->ptAt.y,
pElem->rad, pElem->rad);
UNUSED_PARAMETER(p);
}
static PPoint dotOffset(Pik *p, PElem *pElem, int cp){
PPoint zero;
zero.x = zero.y = 0;
UNUSED_PARAMETER(p);
UNUSED_PARAMETER(pElem);
UNUSED_PARAMETER(cp);
return zero;
}
static void dotRender(Pik *p, PElem *pElem){
PNum r = pElem->rad;
PPoint pt = pElem->ptAt;
if( pElem->sw>0.0 ){
pik_append_x(p,"<circle cx=\"", pt.x, "\"");
pik_append_y(p," cy=\"", pt.y, "\"");
pik_append_dis(p," r=\"", r, "\"");
pik_append_style(p,pElem,1);
pik_append(p,"\" />\n", -1);
}
pik_append_txt(p, pElem, 0);
}
/* Methods for the "ellipse" class */
static void ellipseInit(Pik *p, PElem *pElem){
pElem->w = pik_value(p, "ellipsewid",10,0);
pElem->h = pik_value(p, "ellipseht",9,0);
}
static PPoint ellipseChop(Pik *p, PElem *pElem, PPoint *pPt){
PPoint chop;
PNum s, dq, dist;
PNum dx = pPt->x - pElem->ptAt.x;
PNum dy = pPt->y - pElem->ptAt.y;
if( pElem->w<=0.0 ) return pElem->ptAt;
if( pElem->h<=0.0 ) return pElem->ptAt;
s = pElem->h/pElem->w;
dq = dx*s;
dist = hypot(dq,dy);
if( dist<pElem->h ) return pElem->ptAt;
chop.x = pElem->ptAt.x + 0.5*dq*pElem->h/(dist*s);
chop.y = pElem->ptAt.y + 0.5*dy*pElem->h/dist;
UNUSED_PARAMETER(p);
return chop;
}
static PPoint ellipseOffset(Pik *p, PElem *pElem, int cp){
PPoint pt;
PNum w = pElem->w*0.5;
PNum w2 = w*0.70710678118654747608;
PNum h = pElem->h*0.5;
PNum h2 = h*0.70710678118654747608;
switch( cp ){
case CP_C: pt.x = 0.0; pt.y = 0.0; break;
case CP_N: pt.x = 0.0; pt.y = h; break;
case CP_NE: pt.x = w2; pt.y = h2; break;
case CP_E: pt.x = w; pt.y = 0.0; break;
case CP_SE: pt.x = w2; pt.y = -h2; break;
case CP_S: pt.x = 0.0; pt.y = -h; break;
case CP_SW: pt.x = -w2; pt.y = -h2; break;
case CP_W: pt.x = -w; pt.y = 0.0; break;
case CP_NW: pt.x = -w2; pt.y = h2; break;
}
UNUSED_PARAMETER(p);
return pt;
}
static void ellipseRender(Pik *p, PElem *pElem){
PNum w = pElem->w;
PNum h = pElem->h;
PPoint pt = pElem->ptAt;
if( pElem->sw>0.0 ){
pik_append_x(p,"<ellipse cx=\"", pt.x, "\"");
pik_append_y(p," cy=\"", pt.y, "\"");
pik_append_dis(p," rx=\"", w/2.0, "\"");
pik_append_dis(p," ry=\"", h/2.0, "\" ");
pik_append_style(p,pElem,1);
pik_append(p,"\" />\n", -1);
}
pik_append_txt(p, pElem, 0);
}
/* Methods for the "file" object */
static void fileInit(Pik *p, PElem *pElem){
pElem->w = pik_value(p, "filewid",7,0);
pElem->h = pik_value(p, "fileht",6,0);
pElem->rad = pik_value(p, "filerad",7,0);
}
/* Return offset from the center of the file to the compass point
** given by parameter cp */
static PPoint fileOffset(Pik *p, PElem *pElem, int cp){
PPoint pt;
PNum w2 = 0.5*pElem->w;
PNum h2 = 0.5*pElem->h;
PNum rx = pElem->rad;
PNum mn = w2<h2 ? w2 : h2;
if( rx>mn ) rx = mn;
if( rx<mn*0.25 ) rx = mn*0.25;
pt.x = pt.y = 0.0;
rx *= 0.5;
switch( cp ){
case CP_C: pt.x = 0.0; pt.y = 0.0; break;
case CP_N: pt.x = 0.0; pt.y = h2; break;
case CP_NE: pt.x = w2-rx; pt.y = h2-rx; break;
case CP_E: pt.x = w2; pt.y = 0.0; break;
case CP_SE: pt.x = w2; pt.y = -h2; break;
case CP_S: pt.x = 0.0; pt.y = -h2; break;
case CP_SW: pt.x = -w2; pt.y = -h2; break;
case CP_W: pt.x = -w2; pt.y = 0.0; break;
case CP_NW: pt.x = -w2; pt.y = h2; break;
}
UNUSED_PARAMETER(p);
return pt;
}
static void fileFit(Pik *p, PElem *pElem, PNum w, PNum h){
if( w>0 ) pElem->w = w;
if( h>0 ) pElem->h = h + 2*pElem->rad;
UNUSED_PARAMETER(p);
}
static void fileRender(Pik *p, PElem *pElem){
PNum w2 = 0.5*pElem->w;
PNum h2 = 0.5*pElem->h;
PNum rad = pElem->rad;
PPoint pt = pElem->ptAt;
PNum mn = w2<h2 ? w2 : h2;
if( rad>mn ) rad = mn;
if( rad<mn*0.25 ) rad = mn*0.25;
if( pElem->sw>0.0 ){
pik_append_xy(p,"<path d=\"M", pt.x-w2,pt.y-h2);
pik_append_xy(p,"L", pt.x+w2,pt.y-h2);
pik_append_xy(p,"L", pt.x+w2,pt.y+(h2-rad));
pik_append_xy(p,"L", pt.x+(w2-rad),pt.y+h2);
pik_append_xy(p,"L", pt.x-w2,pt.y+h2);
pik_append(p,"Z\" ",-1);
pik_append_style(p,pElem,1);
pik_append(p,"\" />\n",-1);
pik_append_xy(p,"<path d=\"M", pt.x+(w2-rad), pt.y+h2);
pik_append_xy(p,"L", pt.x+(w2-rad),pt.y+(h2-rad));
pik_append_xy(p,"L", pt.x+w2, pt.y+(h2-rad));
pik_append(p,"\" ",-1);
pik_append_style(p,pElem,0);
pik_append(p,"\" />\n",-1);
}
pik_append_txt(p, pElem, 0);
}
/* Methods for the "line" class */
static void lineInit(Pik *p, PElem *pElem){
pElem->w = pik_value(p, "linewid",7,0);
pElem->h = pik_value(p, "lineht",6,0);
pElem->rad = pik_value(p, "linerad",7,0);
pElem->fill = -1.0;
}
static PPoint lineOffset(Pik *p, PElem *pElem, int cp){
#if 0
/* In legacy PIC, the .center of an unclosed line is half way between
** its .start and .end. */
if( cp==CP_C && !pElem->bClose ){
PPoint out;
out.x = 0.5*(pElem->ptEnter.x + pElem->ptExit.x) - pElem->ptAt.x;
out.y = 0.5*(pElem->ptEnter.x + pElem->ptExit.y) - pElem->ptAt.y;
return out;
}
#endif
return boxOffset(p,pElem,cp);
}
static void lineRender(Pik *p, PElem *pElem){
int i;
if( pElem->sw>0.0 ){
const char *z = "<path d=\"M";
int n = pElem->nPath;
if( pElem->larrow ){
pik_draw_arrowhead(p,&pElem->aPath[1],&pElem->aPath[0],pElem);
}
if( pElem->rarrow ){
pik_draw_arrowhead(p,&pElem->aPath[n-2],&pElem->aPath[n-1],pElem);
}
for(i=0; i<pElem->nPath; i++){
pik_append_xy(p,z,pElem->aPath[i].x,pElem->aPath[i].y);
z = "L";
}
if( pElem->bClose ){
pik_append(p,"Z",1);
}else{
pElem->fill = -1.0;
}
pik_append(p,"\" ",-1);
pik_append_style(p,pElem,pElem->bClose);
pik_append(p,"\" />\n", -1);
}
pik_append_txt(p, pElem, 0);
}
/* Methods for the "move" class */
static void moveInit(Pik *p, PElem *pElem){
pElem->w = pik_value(p, "movewid",7,0);
pElem->h = pElem->w;
pElem->fill = -1.0;
pElem->color = -1.0;
pElem->sw = -1.0;
}
static void moveRender(Pik *p, PElem *pElem){
/* No-op */
UNUSED_PARAMETER(p);
UNUSED_PARAMETER(pElem);
}
/* Methods for the "oval" class */
static void ovalInit(Pik *p, PElem *pElem){
pElem->h = pik_value(p, "ovalht",6,0);
pElem->w = pik_value(p, "ovalwid",7,0);
pElem->rad = 0.5*(pElem->h<pElem->w?pElem->h:pElem->w);
}
static void ovalNumProp(Pik *p, PElem *pElem, PToken *pId){
UNUSED_PARAMETER(p);
UNUSED_PARAMETER(pId);
/* Always adjust the radius to be half of the smaller of
** the width and height. */
pElem->rad = 0.5*(pElem->h<pElem->w?pElem->h:pElem->w);
}
static void ovalFit(Pik *p, PElem *pElem, PNum w, PNum h){
UNUSED_PARAMETER(p);
if( w>0 ) pElem->w = w;
if( h>0 ) pElem->h = h;
if( pElem->w<pElem->h ) pElem->w = pElem->h;
}
/* Methods for the "spline" class */
static void splineInit(Pik *p, PElem *pElem){
pElem->w = pik_value(p, "linewid",7,0);
pElem->h = pik_value(p, "lineht",6,0);
pElem->rad = 1000;
pElem->fill = -1.0; /* Disable fill by default */
}
/* Return a point along the path from "f" to "t" that is r units
** prior to reaching "t", except if the path is less than 2*r total,
** return the midpoint.
*/
static PPoint radiusMidpoint(PPoint f, PPoint t, PNum r, int *pbMid){
PNum dx = t.x - f.x;
PNum dy = t.y - f.y;
PNum dist = hypot(dx,dy);
PPoint m;
if( dist<=0.0 ) return t;
dx /= dist;
dy /= dist;
if( r > 0.5*dist ){
r = 0.5*dist;
*pbMid = 1;
}else{
*pbMid = 0;
}
m.x = t.x - r*dx;
m.y = t.y - r*dy;
return m;
}
static void radiusPath(Pik *p, PElem *pElem, PNum r){
int i;
int n = pElem->nPath;
const PPoint *a = pElem->aPath;
PPoint m;
int isMid = 0;
pik_append_xy(p,"<path d=\"M", a[0].x, a[0].y);
m = radiusMidpoint(a[0], a[1], r, &isMid);
pik_append_xy(p," L ",m.x,m.y);
for(i=1; i<n-1; i++){
m = radiusMidpoint(a[i+1],a[i],r, &isMid);
pik_append_xy(p," Q ",a[i].x,a[i].y);
pik_append_xy(p," ",m.x,m.y);
if( !isMid ){
m = radiusMidpoint(a[i],a[i+1],r, &isMid);
pik_append_xy(p," L ",m.x,m.y);
}
}
pik_append_xy(p," L ",a[i].x,a[i].y);
pik_append(p,"\" ",-1);
pik_append_style(p,pElem,0);
pik_append(p,"\" />\n", -1);
}
static void splineRender(Pik *p, PElem *pElem){
if( pElem->sw>0.0 ){
int n = pElem->nPath;
PNum r = pElem->rad;
if( n<3 || r<=0.0 ){
lineRender(p,pElem);
return;
}
if( pElem->larrow ){
pik_draw_arrowhead(p,&pElem->aPath[1],&pElem->aPath[0],pElem);
}
if( pElem->rarrow ){
pik_draw_arrowhead(p,&pElem->aPath[n-2],&pElem->aPath[n-1],pElem);
}
radiusPath(p,pElem,pElem->rad);
}
pik_append_txt(p, pElem, 0);
}
/* Methods for the "text" class */
static void textInit(Pik *p, PElem *pElem){
pik_value(p, "textwid",7,0);
pik_value(p, "textht",6,0);
pElem->sw = 0.0;
}
static PPoint textOffset(Pik *p, PElem *pElem, int cp){
/* Automatically slim-down the width and height of text
** elements so that the bounding box tightly encloses the text,
** then get boxOffset() to do the offset computation.
*/
pik_size_to_fit(p, &pElem->errTok);
return boxOffset(p, pElem, cp);
}
/* Methods for the "sublist" class */
static void sublistInit(Pik *p, PElem *pElem){
PEList *pList = pElem->pSublist;
int i;
UNUSED_PARAMETER(p);
pik_bbox_init(&pElem->bbox);
for(i=0; i<pList->n; i++){
pik_bbox_addbox(&pElem->bbox, &pList->a[i]->bbox);
}
pElem->w = pElem->bbox.ne.x - pElem->bbox.sw.x;
pElem->h = pElem->bbox.ne.y - pElem->bbox.sw.y;
pElem->ptAt.x = 0.5*(pElem->bbox.ne.x + pElem->bbox.sw.x);
pElem->ptAt.y = 0.5*(pElem->bbox.ne.y + pElem->bbox.sw.y);
pElem->mCalc |= A_WIDTH|A_HEIGHT|A_RADIUS;
}
/*
** The following array holds all the different kinds of named
** elements. The special STRING and [] elements are separate.
*/
static const PClass aClass[] = {
{ /* name */ "arc",
/* isline */ 1,
/* eJust */ 0,
/* xInit */ arcInit,
/* xNumProp */ 0,
/* xCheck */ arcCheck,
/* xChop */ 0,
/* xOffset */ boxOffset,
/* xFit */ 0,
/* xRender */ arcRender
},
{ /* name */ "arrow",
/* isline */ 1,
/* eJust */ 0,
/* xInit */ arrowInit,
/* xNumProp */ 0,
/* xCheck */ 0,
/* xChop */ 0,
/* xOffset */ lineOffset,
/* xFit */ 0,
/* xRender */ splineRender
},
{ /* name */ "box",
/* isline */ 0,
/* eJust */ 1,
/* xInit */ boxInit,
/* xNumProp */ 0,
/* xCheck */ 0,
/* xChop */ boxChop,
/* xOffset */ boxOffset,
/* xFit */ boxFit,
/* xRender */ boxRender
},
{ /* name */ "circle",
/* isline */ 0,
/* eJust */ 0,
/* xInit */ circleInit,
/* xNumProp */ circleNumProp,
/* xCheck */ 0,
/* xChop */ circleChop,
/* xOffset */ ellipseOffset,
/* xFit */ circleFit,
/* xRender */ circleRender
},
{ /* name */ "cylinder",
/* isline */ 0,
/* eJust */ 1,
/* xInit */ cylinderInit,
/* xNumProp */ 0,
/* xCheck */ 0,
/* xChop */ boxChop,
/* xOffset */ cylinderOffset,
/* xFit */ cylinderFit,
/* xRender */ cylinderRender
},
{ /* name */ "dot",
/* isline */ 0,
/* eJust */ 0,
/* xInit */ dotInit,
/* xNumProp */ dotNumProp,
/* xCheck */ dotCheck,
/* xChop */ circleChop,
/* xOffset */ dotOffset,
/* xFit */ 0,
/* xRender */ dotRender
},
{ /* name */ "ellipse",
/* isline */ 0,
/* eJust */ 0,
/* xInit */ ellipseInit,
/* xNumProp */ 0,
/* xCheck */ 0,
/* xChop */ ellipseChop,
/* xOffset */ ellipseOffset,
/* xFit */ 0,
/* xRender */ ellipseRender
},
{ /* name */ "file",
/* isline */ 0,
/* eJust */ 1,
/* xInit */ fileInit,
/* xNumProp */ 0,
/* xCheck */ 0,
/* xChop */ boxChop,
/* xOffset */ fileOffset,
/* xFit */ fileFit,
/* xRender */ fileRender
},
{ /* name */ "line",
/* isline */ 1,
/* eJust */ 0,
/* xInit */ lineInit,
/* xNumProp */ 0,
/* xCheck */ 0,
/* xChop */ 0,
/* xOffset */ lineOffset,
/* xFit */ 0,
/* xRender */ splineRender
},
{ /* name */ "move",
/* isline */ 1,
/* eJust */ 0,
/* xInit */ moveInit,
/* xNumProp */ 0,
/* xCheck */ 0,
/* xChop */ 0,
/* xOffset */ boxOffset,
/* xFit */ 0,
/* xRender */ moveRender
},
{ /* name */ "oval",
/* isline */ 0,
/* eJust */ 1,
/* xInit */ ovalInit,
/* xNumProp */ ovalNumProp,
/* xCheck */ 0,
/* xChop */ boxChop,
/* xOffset */ boxOffset,
/* xFit */ ovalFit,
/* xRender */ boxRender
},
{ /* name */ "spline",
/* isline */ 1,
/* eJust */ 0,
/* xInit */ splineInit,
/* xNumProp */ 0,
/* xCheck */ 0,
/* xChop */ 0,
/* xOffset */ lineOffset,
/* xFit */ 0,
/* xRender */ splineRender
},
{ /* name */ "text",
/* isline */ 0,
/* eJust */ 0,
/* xInit */ textInit,
/* xNumProp */ 0,
/* xCheck */ 0,
/* xChop */ boxChop,
/* xOffset */ textOffset,
/* xFit */ boxFit,
/* xRender */ boxRender
},
};
static const PClass sublistClass =
{ /* name */ "[]",
/* isline */ 0,
/* eJust */ 0,
/* xInit */ sublistInit,
/* xNumProp */ 0,
/* xCheck */ 0,
/* xChop */ 0,
/* xOffset */ boxOffset,
/* xFit */ 0,
/* xRender */ 0
};
static const PClass noopClass =
{ /* name */ "noop",
/* isline */ 0,
/* eJust */ 0,
/* xInit */ 0,
/* xNumProp */ 0,
/* xCheck */ 0,
/* xChop */ 0,
/* xOffset */ boxOffset,
/* xFit */ 0,
/* xRender */ 0
};
/*
** Reduce the length of the line segment by amt (if possible) by
** modifying the location of *t.
*/
static void pik_chop(PPoint *f, PPoint *t, PNum amt){
PNum dx = t->x - f->x;
PNum dy = t->y - f->y;
PNum dist = hypot(dx,dy);
PNum r;
if( dist<=amt ){
*t = *f;
return;
}
r = 1.0 - amt/dist;
t->x = f->x + r*dx;
t->y = f->y + r*dy;
}
/*
** Draw an arrowhead on the end of the line segment from pFrom to pTo.
** Also, shorten the line segment (by changing the value of pTo) so that
** the shaft of the arrow does not extend into the arrowhead.
*/
static void pik_draw_arrowhead(Pik *p, PPoint *f, PPoint *t, PElem *pElem){
PNum dx = t->x - f->x;
PNum dy = t->y - f->y;
PNum dist = hypot(dx,dy);
PNum h = p->hArrow * pElem->sw;
PNum w = p->wArrow * pElem->sw;
PNum e1, ddx, ddy;
PNum bx, by;
if( pElem->color<0.0 ) return;
if( pElem->sw<=0.0 ) return;
if( dist<=0.0 ) return; /* Unable */
dx /= dist;
dy /= dist;
e1 = dist - h;
if( e1<0.0 ){
e1 = 0.0;
h = dist;
}
ddx = -w*dy;
ddy = w*dx;
bx = f->x + e1*dx;
by = f->y + e1*dy;
pik_append_xy(p,"<polygon points=\"", t->x, t->y);
pik_append_xy(p," ",bx-ddx, by-ddy);
pik_append_xy(p," ",bx+ddx, by+ddy);
pik_append_clr(p,"\" style=\"fill:",pElem->color,"\"/>\n");
pik_chop(f,t,h/2);
}
/*
** Compute the relative offset to an edge location from the reference for a
** an element.
*/
static PPoint pik_elem_offset(Pik *p, PElem *pElem, int cp){
return pElem->type->xOffset(p, pElem, cp);
}
/*
** Append raw text to zOut
*/
static void pik_append(Pik *p, const char *zText, int n){
if( n<0 ) n = (int)strlen(zText);
if( p->nOut+n>=p->nOutAlloc ){
int nNew = (p->nOut+n)*2 + 1;
char *z = realloc(p->zOut, nNew);
if( z==0 ){
pik_error(p, 0, 0);
return;
}
p->zOut = z;
p->nOutAlloc = n;
}
memcpy(p->zOut+p->nOut, zText, n);
p->nOut += n;
p->zOut[p->nOut] = 0;
}
/*
** Append text to zOut with HTML characters escaped.
**
** * The space character is changed into non-breaking space (U+0080)
** if mFlags has the 0x01 bit set. This is needed when outputting
** text to preserve leading and trailing whitespace. Turns out we
** cannot use 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;
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, "&", 5); break; }
case ' ': { pik_append(p, "\302\240;", 2); break; }
}
i++;
n -= i;
zText += i;
i = 0;
}
}
/* Append a PNum value
*/
static void pik_append_num(Pik *p, const char *z,PNum v){
char buf[100];
snprintf(buf, sizeof(buf)-1, "%.10g", (double)v);
buf[sizeof(buf)-1] = 0;
pik_append(p, z, -1);
pik_append(p, buf, -1);
}
/* Append a PPoint value (Used for debugging only)
*/
static void pik_append_point(Pik *p, const char *z, PPoint *pPt){
char buf[100];
snprintf(buf, sizeof(buf)-1, "%.10g,%.10g",
(double)pPt->x, (double)pPt->y);
buf[sizeof(buf)-1] = 0;
pik_append(p, z, -1);
pik_append(p, buf, -1);
}
/* Append a PNum value surrounded by text. Do coordinate transformations
** on the value.
*/
static void pik_append_x(Pik *p, const char *z1, PNum v, const char *z2){
char buf[200];
v -= p->bbox.sw.x;
snprintf(buf, sizeof(buf)-1, "%s%d%s", z1, (int)(p->rScale*v), z2);
buf[sizeof(buf)-1] = 0;
pik_append(p, buf, -1);
}
static void pik_append_y(Pik *p, const char *z1, PNum v, const char *z2){
char buf[200];
v = p->bbox.ne.y - v;
snprintf(buf, sizeof(buf)-1, "%s%d%s", z1, (int)(p->rScale*v), z2);
buf[sizeof(buf)-1] = 0;
pik_append(p, buf, -1);
}
static void pik_append_xy(Pik *p, const char *z1, PNum x, PNum y){
char buf[200];
x = x - p->bbox.sw.x;
y = p->bbox.ne.y - y;
snprintf(buf, sizeof(buf)-1, "%s%d,%d", z1,
(int)(p->rScale*x), (int)(p->rScale*y));
buf[sizeof(buf)-1] = 0;
pik_append(p, buf, -1);
}
static void pik_append_dis(Pik *p, const char *z1, PNum v, const char *z2){
char buf[200];
snprintf(buf, sizeof(buf)-1, "%s%g%s", z1, p->rScale*v, z2);
buf[sizeof(buf)-1] = 0;
pik_append(p, buf, -1);
}
static void pik_append_clr(Pik *p, const char *z1, PNum v, const char *z2){
char buf[200];
int x = (int)v;
int r = (x>>16) & 0xff;
int g = (x>>8) & 0xff;
int b = x & 0xff;
snprintf(buf, sizeof(buf)-1, "%srgb(%d,%d,%d)%s", z1, r, g, b, z2);
buf[sizeof(buf)-1] = 0;
pik_append(p, buf, -1);
}
/* Append an SVG path A record:
**
** A r1 r2 0 0 0 x y
*/
static void pik_append_arc(Pik *p, PNum r1, PNum r2, PNum x, PNum y){
char buf[200];
x = x - p->bbox.sw.x;
y = p->bbox.ne.y - y;
snprintf(buf, sizeof(buf)-1, "A%d %d 0 0 0 %d %d",
(int)(p->rScale*r1), (int)(p->rScale*r2),
(int)(p->rScale*x), (int)(p->rScale*y));
buf[sizeof(buf)-1] = 0;
pik_append(p, buf, -1);
}
/* Append a style="..." text. But, leave the quote unterminated, in case
** the caller wants to add some more.
*/
static void pik_append_style(Pik *p, PElem *pElem, int bFill){
pik_append(p, " style=\"", -1);
if( pElem->fill>=0 && bFill ){
pik_append_clr(p, "fill:", pElem->fill, ";");
}else{
pik_append(p,"fill:none;",-1);
}
if( pElem->sw>0.0 && pElem->color>=0.0 ){
PNum sw = pElem->sw;
pik_append_dis(p, "stroke-width:", sw, ";");
if( pElem->nPath>2 && pElem->rad<=pElem->sw ){
pik_append(p, "stroke-linejoin:round;", -1);
}
pik_append_clr(p, "stroke:",pElem->color,";");
if( pElem->dotted>0.0 ){
PNum v = pElem->dotted;
if( sw<2.1/p->rScale ) sw = 2.1/p->rScale;
pik_append_dis(p,"stroke-dasharray:",sw,"");
pik_append_dis(p,",",v,";");
}else if( pElem->dashed>0.0 ){
PNum v = pElem->dashed;
pik_append_dis(p,"stroke-dasharray:",v,"");
pik_append_dis(p,",",v,";");
}
}
}
/*
** Compute the vertical locations for all text items in the
** element pElem. In other words, set every pElem->aTxt[*].eCode
** value to contain exactly one of: TP_ABOVE2, TP_ABOVE, TP_CENTER,
** TP_BELOW, or TP_BELOW2 is set.
*/
static void pik_txt_vertical_layout(PElem *pElem){
int n, i;
PToken *aTxt;
n = pElem->nTxt;
if( n==0 ) return;
aTxt = pElem->aTxt;
if( n==1 ){
if( (aTxt[0].eCode & TP_VMASK)==0 ){
aTxt[0].eCode |= TP_CENTER;
}
}else{
int allSlots = 0;
int aFree[5];
int iSlot;
int j, mJust;
/* If there is more than one TP_ABOVE, change the first to TP_ABOVE2. */
for(j=mJust=0, i=n-1; i>=0; i--){
if( aTxt[i].eCode & TP_ABOVE ){
if( j==0 ){
j++;
mJust = aTxt[i].eCode & TP_JMASK;
}else if( j==1 && mJust!=0 && (aTxt[i].eCode & mJust)==0 ){
j++;
}else{
aTxt[i].eCode = (aTxt[i].eCode & ~TP_VMASK) | TP_ABOVE2;
break;
}
}
}
/* If there is more than one TP_BELOW, change the last to TP_BELOW2 */
for(j=mJust=0, i=0; i<n; i++){
if( aTxt[i].eCode & TP_BELOW ){
if( j==0 ){
j++;
mJust = aTxt[i].eCode & TP_JMASK;
}else if( j==1 && mJust!=0 && (aTxt[i].eCode & mJust)==0 ){
j++;
}else{
aTxt[i].eCode = (aTxt[i].eCode & ~TP_VMASK) | TP_BELOW2;
break;
}
}
}
/* Compute a mask of all slots used */
for(i=0; i<n; i++) allSlots |= aTxt[i].eCode & TP_VMASK;
/* Set of an array of available slots */
if( n==2
&& ((aTxt[0].eCode|aTxt[1].eCode)&TP_JMASK)==(TP_LJUST|TP_RJUST)
){
/* Special case of two texts that have opposite justification:
** Allow them both to float to center. */
iSlot = 2;
aFree[0] = aFree[1] = TP_CENTER;
}else{
/* Set up the arrow so that available slots are filled from top to
** bottom */
iSlot = 0;
if( n>=4 && (allSlots & TP_ABOVE2)==0 ) aFree[iSlot++] = TP_ABOVE2;
if( (allSlots & TP_ABOVE)==0 ) aFree[iSlot++] = TP_ABOVE;
if( (n&1)!=0 ) aFree[iSlot++] = TP_CENTER;
if( (allSlots & TP_BELOW)==0 ) aFree[iSlot++] = TP_BELOW;
if( n>=4 && (allSlots & TP_BELOW2)==0 ) aFree[iSlot++] = TP_BELOW2;
}
/* Set the VMASK for all unassigned texts */
for(i=iSlot=0; i<n; i++){
if( (aTxt[i].eCode & TP_VMASK)==0 ){
aTxt[i].eCode |= aFree[iSlot++];
}
}
}
}
/* Append multiple <text> SVG element for the text fields of the PElem.
** Parameters:
**
** p The Pik object into which we are rendering
**
** pElem Object containing the text to be rendered
**
** pBox If not NULL, do no rendering at all. Instead
** expand the box object so that it will include all
** of the text.
*/
static void pik_append_txt(Pik *p, PElem *pElem, PBox *pBox){
PNum dy; /* Half the height of a single line of text */
PNum dy2; /* Extra vertical space around the center */
PNum jw; /* Justification margin relative to center */
int n, i, nz;
PNum x, y, orig_y;
const char *z;
PToken *aTxt;
int hasCenter = 0;
if( p->nErr ) return;
if( pElem->nTxt==0 ) return;
aTxt = pElem->aTxt;
dy = 0.5*p->charHeight;
n = pElem->nTxt;
pik_txt_vertical_layout(pElem);
x = pElem->ptAt.x;
for(i=0; i<n; i++){
if( (pElem->aTxt[i].eCode & TP_CENTER)!=0 ) hasCenter = 1;
}
if( hasCenter ){
dy2 = dy;
}else if( pElem->type->isLine ){
dy2 = pElem->sw;
}else{
dy2 = 0.0;
}
if( pElem->type->eJust==1 ){
jw = 0.5*(pElem->w - 0.5*(p->charWidth + pElem->sw));
}else{
jw = 0.0;
}
for(i=0; i<n; i++){
PToken *t = &aTxt[i];
PNum xtraFontScale = 1.0;
orig_y = y = pElem->ptAt.y;
PNum nx = x;
if( t->eCode & TP_ABOVE2 ) y += dy2 + 3*dy;
if( t->eCode & TP_ABOVE ) y += dy2 + dy;
if( t->eCode & TP_BELOW ) y -= dy2 + dy;
if( t->eCode & TP_BELOW2 ) y -= dy2 + 3*dy;
if( t->eCode & TP_BIG ) xtraFontScale *= 1.25;
if( t->eCode & TP_SMALL ) xtraFontScale *= 0.8;
if( t->eCode & TP_XTRA ) xtraFontScale *= xtraFontScale;
if( t->eCode & TP_LJUST ) nx -= jw;
if( t->eCode & TP_RJUST ) nx += jw;
if( pBox!=0 ){
/* If pBox is not NULL, do not draw any <text>. Instead, just expand
** pBox to include the text */
PNum cw = pik_text_length(t)*p->charWidth*xtraFontScale;
PNum ch = p->charHeight*0.5*xtraFontScale;
if( t->eCode & TP_RJUST ){
pik_bbox_add_xy(pBox, nx, y-ch);
pik_bbox_add_xy(pBox, nx-cw, y+ch);
}else if( t->eCode & TP_LJUST ){
pik_bbox_add_xy(pBox, nx, y-ch);
pik_bbox_add_xy(pBox, nx+cw, y+ch);
}else{
pik_bbox_add_xy(pBox, nx+cw/2, y+ch);
pik_bbox_add_xy(pBox, nx-cw/2, y-ch);
}
continue;
}
pik_append_x(p, "<text x=\"", nx, "\"");
pik_append_y(p, " y=\"", y, "\"");
if( t->eCode & TP_RJUST ){
pik_append(p, " text-anchor=\"end\"", -1);
}else if( t->eCode & TP_LJUST ){
pik_append(p, " text-anchor=\"start\"", -1);
}else{
pik_append(p, " text-anchor=\"middle\"", -1);
}
if( t->eCode & TP_ITALIC ){
pik_append(p, " font-style=\"italic\"", -1);
}
if( t->eCode & TP_BOLD ){
pik_append(p, " font-weight=\"bold\"", -1);
}
if( pElem->color>=0.0 ){
pik_append_clr(p, " fill=\"", pElem->color, "\"");
}
xtraFontScale *= p->fontScale;
if( xtraFontScale<=0.99 || xtraFontScale>=1.01 ){
pik_append_num(p, " font-size=\"", xtraFontScale*100.0);
pik_append(p, "%\"", 2);
}
if( (t->eCode & TP_ALIGN)!=0 && pElem->nPath>=2 ){
int n = pElem->nPath;
PNum dx = pElem->aPath[n-1].x - pElem->aPath[0].x;
PNum dy = pElem->aPath[n-1].y - pElem->aPath[0].y;
PNum ang = atan2(dy,dx)*-180/M_PI;
pik_append_num(p, " transform=\"rotate(", ang);
pik_append_xy(p, " ", x, orig_y);
pik_append(p,")\"",2);
}
pik_append(p," dominant-baseline=\"central\">",-1);
z = t->z+1;
nz = t->n-2;
while( nz>0 ){
int j;
for(j=0; j<nz && z[j]!='\\'; j++){}
if( j ) pik_append_text(p, z, j, 1);
nz -= j+1;
z += j+1;
}
pik_append(p, "</text>\n", -1);
}
}
/*
** Generate an error message for the output. pErr is the token at which
** the error should point. zMsg is the text of the error message. If
** either pErr or zMsg is NULL, generate an out-of-memory error message.
**
** This routine is a no-op if there has already been an error reported.
*/
static void pik_error(Pik *p, PToken *pErr, const char *zMsg){
int i, j;
int iCol;
int nExtra;
char c;
if( p==0 ) return;
if( p->nErr ) return;
p->nErr++;
if( zMsg==0 ){
pik_append(p, "\n<div><p>Out of memory</p></div>\n", -1);
return;
}
if( pErr==0 ){
pik_append(p, "\n", 1);
pik_append_text(p, zMsg, -1, 0);
return;
}
i = (int)(pErr->z - p->zIn);
for(j=i; j>0 && p->zIn[j-1]!='\n'; j--){}
iCol = i - j;
for(nExtra=0; (c = p->zIn[i+nExtra])!=0 && c!='\n'; nExtra++){}
pik_append(p, "<div><pre>\n", -1);
pik_append_text(p, p->zIn, i+nExtra, 3);
pik_append(p, "\n", 1);
for(i=0; i<iCol; i++){ pik_append(p, " ", 1); }
for(i=0; i<(int)pErr->n; i++) pik_append(p, "^", 1);
pik_append(p, "\nERROR: ", -1);
pik_append_text(p, zMsg, -1, 0);
pik_append(p, "\n", 1);
pik_append(p, "\n</pre></div>\n", -1);
}
/*
** Process an "assert( e1 == e2 )" statement. Always return NULL.
*/
static PElem *pik_assert(Pik *p, PNum e1, PToken *pEq, PNum e2){
char zE1[100], zE2[100], zMsg[300];
/* Convert the numbers to strings using %g for comparison. This
** limits the precision of the comparison to account for rounding error. */
snprintf(zE1, sizeof(zE1), "%g", e1); zE1[sizeof(zE1)-1] = 0;
snprintf(zE2, sizeof(zE2), "%g", e2); zE1[sizeof(zE2)-1] = 0;
if( strcmp(zE1,zE2)!=0 ){
snprintf(zMsg, sizeof(zMsg), "%.50s != %.50s", zE1, zE2);
pik_error(p, pEq, zMsg);
}
return 0;
}
/*
** Process an "assert( place1 == place2 )" statement. Always return NULL.
*/
static PElem *pik_place_assert(Pik *p, PPoint *e1, PToken *pEq, PPoint *e2){
char zE1[100], zE2[100], zMsg[210];
/* Convert the numbers to strings using %g for comparison. This
** limits the precision of the comparison to account for rounding error. */
snprintf(zE1, sizeof(zE1), "(%g,%g)", e1->x, e1->y); zE1[sizeof(zE1)-1] = 0;
snprintf(zE2, sizeof(zE2), "(%g,%g)", e2->x, e2->y); zE1[sizeof(zE2)-1] = 0;
if( strcmp(zE1,zE2)!=0 ){
snprintf(zMsg, sizeof(zMsg), "%s != %s", zE1, zE2);
pik_error(p, pEq, zMsg);
}
return 0;
}
/* Free a complete list of elements */
static void pik_elist_free(Pik *p, PEList *pEList){
int i;
if( pEList==0 ) return;
for(i=0; i<pEList->n; i++){
pik_elem_free(p, pEList->a[i]);
}
free(pEList->a);
free(pEList);
return;
}
/* Free a single element, and its substructure */
static void pik_elem_free(Pik *p, PElem *pElem){
if( pElem==0 ) return;
free(pElem->zName);
pik_elist_free(p, pElem->pSublist);
free(pElem->aPath);
free(pElem);
}
/* Convert a numeric literal into a number. Return that number.
** There is no error handling because the tokenizer has already
** assured us that the numeric literal is valid.
**
** Allowed number forms:
**
** (1) Floating point literal
** (2) Same as (1) but followed by a unit: "cm", "mm", "in",
** "px", "pt", or "pc".
** (3) Hex integers: 0x000000
**
** This routine returns the result in inches. If a different unit
** is specified, the conversion happens automatically.
*/
PNum pik_atof(PToken *num){
char *endptr;
PNum ans;
if( num->n>=3 && num->z[0]=='0' && (num->z[1]=='x'||num->z[1]=='X') ){
return (PNum)strtol(num->z+2, 0, 16);
}
ans = strtod(num->z, &endptr);
if( (int)(endptr - num->z)==(int)num->n-2 ){
char c1 = endptr[0];
char c2 = endptr[1];
if( c1=='c' && c2=='m' ){
ans /= 2.54;
}else if( c1=='m' && c2=='m' ){
ans /= 25.4;
}else if( c1=='p' && c2=='x' ){
ans /= 96;
}else if( c1=='p' && c2=='t' ){
ans /= 72;
}else if( c1=='p' && c2=='c' ){
ans /= 6;
}
}
return ans;
}
/* Return true if a bounding box is empty.
*/
static int pik_bbox_isempty(PBox *p){
return p->sw.x>p->ne.x;
}
/* Initialize a bounding box to an empty container
*/
static void pik_bbox_init(PBox *p){
p->sw.x = 1.0;
p->sw.y = 1.0;
p->ne.x = 0.0;
p->ne.y = 0.0;
}
/* Enlarge the PBox of the first argument so that it fully
** covers the second PBox
*/
static void pik_bbox_addbox(PBox *pA, PBox *pB){
if( pik_bbox_isempty(pA) ){
*pA = *pB;
}
if( pik_bbox_isempty(pB) ) return;
if( pA->sw.x>pB->sw.x ) pA->sw.x = pB->sw.x;
if( pA->sw.y>pB->sw.y ) pA->sw.y = pB->sw.y;
if( pA->ne.x<pB->ne.x ) pA->ne.x = pB->ne.x;
if( pA->ne.y<pB->ne.y ) pA->ne.y = pB->ne.y;
}
/* Enlarge the PBox of the first argument, if necessary, so that
** it contains the point described by the 2nd and 3rd arguments.
*/
static void pik_bbox_add_xy(PBox *pA, PNum x, PNum y){
if( pik_bbox_isempty(pA) ){
pA->ne.x = x;
pA->ne.y = y;
pA->sw.x = x;
pA->sw.y = y;
return;
}
if( pA->sw.x>x ) pA->sw.x = x;
if( pA->sw.y>y ) pA->sw.y = y;
if( pA->ne.x<x ) pA->ne.x = x;
if( pA->ne.y<y ) pA->ne.y = y;
}
/* Enlarge the PBox so that it is able to contain an ellipse
** centered at x,y and with radiuses rx and ry.
*/
static void pik_bbox_addellipse(PBox *pA, PNum x, PNum y, PNum rx, PNum ry){
if( pik_bbox_isempty(pA) ){
pA->ne.x = x+rx;
pA->ne.y = y+ry;
pA->sw.x = x-rx;
pA->sw.y = y-ry;
return;
}
if( pA->sw.x>x-rx ) pA->sw.x = x-rx;
if( pA->sw.y>y-ry ) pA->sw.y = y-ry;
if( pA->ne.x<x+rx ) pA->ne.x = x+rx;
if( pA->ne.y<y+ry ) pA->ne.y = y+ry;
}
/* Append a new element onto the end of an element_list. The
** element_list is created if it does not already exist. Return
** the new element list.
*/
static PEList *pik_elist_append(Pik *p, PEList *pEList, PElem *pElem){
if( pElem==0 ) return pEList;
if( pEList==0 ){
pEList = malloc(sizeof(*pEList));
if( pEList==0 ){
pik_error(p, 0, 0);
pik_elem_free(p, pElem);
return 0;
}
memset(pEList, 0, sizeof(*pEList));
}
if( pEList->n>=pEList->nAlloc ){
int nNew = (pEList->n+5)*2;
PElem **pNew = realloc(pEList->a, sizeof(PElem*)*nNew);
if( pNew==0 ){
pik_error(p, 0, 0);
pik_elem_free(p, pElem);
return pEList;
}
pEList->nAlloc = nNew;
pEList->a = pNew;
}
pEList->a[pEList->n++] = pElem;
p->list = pEList;
return pEList;
}
/* Convert an element class name into a PClass pointer
*/
static const PClass *pik_find_class(PToken *pId){
int first = 0;
int last = count(aClass) - 1;
do{
int mid = (first+last)/2;
int c = strncmp(aClass[mid].zName, pId->z, pId->n);
if( c==0 ){
c = aClass[mid].zName[pId->n]!=0;
if( c==0 ) return &aClass[mid];
}
if( c<0 ){
first = mid + 1;
}else{
last = mid - 1;
}
}while( first<=last );
return 0;
}
/* Allocate and return a new PElem object.
**
** If pId!=0 then pId is an identifier that defines the element class.
** If pStr!=0 then it is a STRING literal that defines a text object.
** If pSublist!=0 then this is a [...] object. If all three parameters
** are NULL then this is a no-op object used to define a PLACENAME.
*/
static PElem *pik_elem_new(Pik *p, PToken *pId, PToken *pStr,PEList *pSublist){
PElem *pNew;
int miss = 0;
if( p->nErr ) return 0;
pNew = malloc( sizeof(*pNew) );
if( pNew==0 ){
pik_error(p,0,0);
pik_elist_free(p, pSublist);
return 0;
}
memset(pNew, 0, sizeof(*pNew));
p->cur = pNew;
p->nTPath = 1;
p->thenFlag = 0;
if( p->list==0 || p->list->n==0 ){
pNew->ptAt.x = pNew->ptAt.y = 0.0;
}else{
PElem *pPrior = p->list->a[p->list->n-1];
pNew->ptAt = pPrior->ptExit;
switch( p->eDir ){
default: pNew->eWith = CP_W; break;
case DIR_LEFT: pNew->eWith = CP_E; break;
case DIR_UP: pNew->eWith = CP_S; break;
case DIR_DOWN: pNew->eWith = CP_N; break;
}
}
p->aTPath[0] = pNew->ptAt;
pNew->with = pNew->ptAt;
pNew->outDir = pNew->inDir = p->eDir;
pNew->iLayer = (int)pik_value(p, "layer", 5, &miss);
if( miss ) pNew->iLayer = 1000;
if( pNew->iLayer<0 ) pNew->iLayer = 0;
if( pSublist ){
pNew->type = &sublistClass;
pNew->pSublist = pSublist;
sublistClass.xInit(p,pNew);
return pNew;
}
if( pStr ){
PToken n;
n.z = "text";
n.n = 4;
pNew->type = pik_find_class(&n);
assert( pNew->type!=0 );
pNew->errTok = *pStr;
pNew->type->xInit(p, pNew);
pik_add_txt(p, pStr, pStr->eCode);
return pNew;
}
if( pId ){
pNew->errTok = *pId;
const PClass *pClass = pik_find_class(pId);
if( pClass ){
pNew->type = pClass;
pNew->sw = pik_value(p, "thickness",9,0);
pNew->fill = pik_value(p, "fill",4,0);
pNew->color = pik_value(p, "color",5,0);
pClass->xInit(p, pNew);
return pNew;
}
pik_error(p, pId, "unknown element type");
pik_elem_free(p, pNew);
return 0;
}
pNew->type = &noopClass;
pNew->ptExit = pNew->ptEnter = pNew->ptAt;
return pNew;
}
/*
** Set the output direction and exit point for an element.
*/
static void pik_elem_set_exit(PElem *pElem, int eDir){
assert( ValidDir(eDir) );
pElem->outDir = eDir;
if( !pElem->type->isLine || pElem->bClose ){
pElem->ptExit = pElem->ptAt;
switch( pElem->outDir ){
default: pElem->ptExit.x += pElem->w*0.5; break;
case DIR_LEFT: pElem->ptExit.x -= pElem->w*0.5; break;
case DIR_UP: pElem->ptExit.y += pElem->h*0.5; break;
case DIR_DOWN: pElem->ptExit.y -= pElem->h*0.5; break;
}
}
}
/* Change the layout direction.
*/
static void pik_set_direction(Pik *p, int eDir){
assert( ValidDir(eDir) );
p->eDir = eDir;
/* It seems to make sense to reach back into the last object and
** change its exit point (its ".end") to correspond to the new
** direction. Things just seem to work better this way. However,
** legacy PIC does *not* do this.
**
** The difference can be seen in a script like this:
**
** arrow; circle; down; arrow
**
** You can make pikchr render the above exactly like PIC
** by deleting the following three lines. But I (drh) think
** it works better with those lines in place.
*/
if( p->list && p->list->n ){
pik_elem_set_exit(p->list->a[p->list->n-1], eDir);
}
}
/* Move all coordinates contained within an element (and within its
** substructure) by dx, dy
*/
static void pik_elem_move(PElem *pElem, PNum dx, PNum dy){
int i;
pElem->ptAt.x += dx;
pElem->ptAt.y += dy;
pElem->ptEnter.x += dx;
pElem->ptEnter.y += dy;
pElem->ptExit.x += dx;
pElem->ptExit.y += dy;
pElem->bbox.ne.x += dx;
pElem->bbox.ne.y += dy;
pElem->bbox.sw.x += dx;
pElem->bbox.sw.y += dy;
for(i=0; i<pElem->nPath; i++){
pElem->aPath[i].x += dx;
pElem->aPath[i].y += dy;
}
if( pElem->pSublist ){
pik_elist_move(pElem->pSublist, dx, dy);
}
}
static void pik_elist_move(PEList *pList, PNum dx, PNum dy){
int i;
for(i=0; i<pList->n; i++){
pik_elem_move(pList->a[i], dx, dy);
}
}
/*
** Check to see if it is ok to set the value of paraemeter mThis.
** Return 0 if it is ok. If it not ok, generate an appropriate
** error message and return non-zero.
**
** Flags are set in pElem so that the same element or conflicting
** elements may not be set again.
**
** To be ok, bit mThis must be clear and no more than one of
** the bits identified by mBlockers may be set.
*/
static int pik_param_ok(
Pik *p, /* For storing the error message (if any) */
PElem *pElem, /* The element under construction */
PToken *pId, /* Make the error point to this token */
int mThis /* Value we are trying to set */
){
if( pElem->mProp & mThis ){
pik_error(p, pId, "value is already set");
return 1;
}
if( pElem->mCalc & mThis ){
pik_error(p, pId, "value already fixed by prior constraints");
return 1;
}
pElem->mProp |= mThis;
return 0;
}
/*
** Set a numeric property like "width 7" or "radius 200%".
**
** The rAbs term is an absolute value to add in. rRel is
** a relative value by which to change the current value.
*/
void pik_set_numprop(Pik *p, PToken *pId, PRel *pVal){
PElem *pElem = p->cur;
switch( pId->eType ){
case T_HEIGHT:
if( pik_param_ok(p, pElem, pId, A_HEIGHT) ) return;
pElem->h = pElem->h*pVal->rRel + pVal->rAbs;
break;
case T_WIDTH:
if( pik_param_ok(p, pElem, pId, A_WIDTH) ) return;
pElem->w = pElem->w*pVal->rRel + pVal->rAbs;
break;
case T_RADIUS:
if( pik_param_ok(p, pElem, pId, A_RADIUS) ) return;
pElem->rad = pElem->rad*pVal->rRel + pVal->rAbs;
break;
case T_DIAMETER:
if( pik_param_ok(p, pElem, pId, A_RADIUS) ) return;
pElem->rad = pElem->rad*pVal->rRel + 0.5*pVal->rAbs; /* diam it 2x rad */
break;
case T_THICKNESS:
if( pik_param_ok(p, pElem, pId, A_THICKNESS) ) return;
pElem->sw = pElem->sw*pVal->rRel + pVal->rAbs;
break;
}
if( pElem->type->xNumProp ){
pElem->type->xNumProp(p, pElem, pId);
}
return;
}
/*
** Set a color property. The argument is an RGB value.
*/
void pik_set_clrprop(Pik *p, PToken *pId, PNum rClr){
PElem *pElem = p->cur;
switch( pId->eType ){
case T_FILL:
if( pik_param_ok(p, pElem, pId, A_FILL) ) return;
pElem->fill = rClr;
break;
case T_COLOR:
if( pik_param_ok(p, pElem, pId, A_COLOR) ) return;
pElem->color = rClr;
break;
}
if( pElem->type->xNumProp ){
pElem->type->xNumProp(p, pElem, pId);
}
return;
}
/*
** Set a "dashed" property like "dash 0.05"
**
** Use the value supplied by pVal if available. If pVal==0, use
** a default.
*/
void pik_set_dashed(Pik *p, PToken *pId, PNum *pVal){
PElem *pElem = p->cur;
PNum v;
switch( pId->eType ){
case T_DOTTED: {
v = pVal==0 ? pik_value(p,"dashwid",7,0) : *pVal;
pElem->dotted = v;
pElem->dashed = 0.0;
break;
}
case T_DASHED: {
v = pVal==0 ? pik_value(p,"dashwid",7,0) : *pVal;
pElem->dashed = v;
pElem->dotted = 0.0;
break;
}
}
}
/*
** If the current path information came from a "same" or "same as"
** reset it.
*/
static void pik_reset_samepath(Pik *p){
if( p->samePath ){
p->samePath = 0;
p->nTPath = 1;
}
}
/* Add a new term to the path for a line-oriented object by transferring
** the information in the ptTo field over onto the path and into ptFrom
** resetting the ptTo.
*/
static void pik_then(Pik *p, PToken *pToken, PElem *pElem){
int n;
if( !pElem->type->isLine ){
pik_error(p, pToken, "use with line-oriented objects only");
return;
}
n = p->nTPath - 1;
if( n<1 && (pElem->mProp & A_FROM)==0 ){
pik_error(p, pToken, "no prior path points");
return;
}
p->thenFlag = 1;
}
/* Advance to the next entry in p->aTPath. Return its index.
*/
static int pik_next_rpath(Pik *p, PToken *pErr){
int n = p->nTPath - 1;
if( n+1>=(int)count(p->aTPath) ){
pik_error(0, pErr, "too many path elements");
return n;
}
n++;
p->nTPath++;
p->aTPath[n] = p->aTPath[n-1];
p->mTPath = 0;
return n;
}
/* Add a direction term to an element. "up 0.5", or "left 3", or "down"
** or "down 50%".
*/
static void pik_add_direction(Pik *p, PToken *pDir, PRel *pVal){
PElem *pElem = p->cur;
int n;
int dir;
if( !pElem->type->isLine ){
if( pDir ){
pik_error(p, pDir, "use with line-oriented objects only");
}else{
PToken x = pik_next_semantic_token(&pElem->errTok);
pik_error(p, &x, "syntax error");
}
return;
}
pik_reset_samepath(p);
n = p->nTPath - 1;
if( p->thenFlag || p->mTPath==3 || n==0 ){
n = pik_next_rpath(p, pDir);
p->thenFlag = 0;
}
dir = pDir ? pDir->eCode : p->eDir;
switch( dir ){
case DIR_UP:
if( p->mTPath & 2 ) n = pik_next_rpath(p, pDir);
p->aTPath[n].y += pVal->rAbs + pElem->h*pVal->rRel;
p->mTPath |= 2;
break;
case DIR_DOWN:
if( p->mTPath & 2 ) n = pik_next_rpath(p, pDir);
p->aTPath[n].y -= pVal->rAbs + pElem->h*pVal->rRel;
p->mTPath |= 2;
break;
case DIR_RIGHT:
if( p->mTPath & 1 ) n = pik_next_rpath(p, pDir);
p->aTPath[n].x += pVal->rAbs + pElem->w*pVal->rRel;
p->mTPath |= 1;
break;
case DIR_LEFT:
if( p->mTPath & 1 ) n = pik_next_rpath(p, pDir);
p->aTPath[n].x -= pVal->rAbs + pElem->w*pVal->rRel;
p->mTPath |= 1;
break;
}
pElem->outDir = dir;
}
/* Process a movement attribute of one of these forms:
**
** pDist pHdgKW rHdg pEdgept
** GO distance HEADING angle
** GO distance compasspoint
*/
static void pik_move_hdg(
Pik *p, /* The Pikchr context */
PRel *pDist, /* Distance to move */
PToken *pHeading, /* "heading" keyword if present */
PNum rHdg, /* Angle argument to "heading" keyword */
PToken *pEdgept, /* EDGEPT keyword "ne", "sw", etc... */
PToken *pErr /* Token to use for error messages */
){
PElem *pElem = p->cur;
int n;
PNum rDist = pDist->rAbs + pik_value(p,"linewid",7,0)*pDist->rRel;
if( !pElem->type->isLine ){
pik_error(p, pErr, "use with line-oriented objects only");
return;
}
pik_reset_samepath(p);
do{
n = pik_next_rpath(p, pErr);
}while( n<1 );
if( pHeading ){
if( rHdg<0.0 || rHdg>360.0 ){
pik_error(p, pHeading, "headings should be between 0 and 360");
return;
}
}else if( pEdgept->eEdge==CP_C ){
pik_error(p, pEdgept, "syntax error");
return;
}else{
rHdg = pik_hdg_angle[pEdgept->eEdge];
}
if( rHdg<=45.0 ){
pElem->outDir = DIR_UP;
}else if( rHdg<=135.0 ){
pElem->outDir = DIR_RIGHT;
}else if( rHdg<=225.0 ){
pElem->outDir = DIR_DOWN;
}else if( rHdg<=315.0 ){
pElem->outDir = DIR_LEFT;
}else{
pElem->outDir = DIR_UP;
}
rHdg *= 0.017453292519943295769; /* degrees to radians */
p->aTPath[n].x += rDist*sin(rHdg);
p->aTPath[n].y += rDist*cos(rHdg);
p->mTPath = 2;
}
/* Process a movement attribute of the form "right until even with ..."
**
** pDir is the first keyword, "right" or "left" or "up" or "down".
** The movement is in that direction until its closest approach to
** the point specified by pPoint.
*/
static void pik_evenwith(Pik *p, PToken *pDir, PPoint *pPlace){
PElem *pElem = p->cur;
int n;
if( !pElem->type->isLine ){
pik_error(p, pDir, "use with line-oriented objects only");
return;
}
pik_reset_samepath(p);
n = p->nTPath - 1;
if( p->thenFlag || p->mTPath==3 || n==0 ){
n = pik_next_rpath(p, pDir);
p->thenFlag = 0;
}
switch( pDir->eCode ){
case DIR_DOWN:
case DIR_UP:
if( p->mTPath & 2 ) n = pik_next_rpath(p, pDir);
p->aTPath[n].y = pPlace->y;
p->mTPath |= 2;
break;
case DIR_RIGHT:
case DIR_LEFT:
if( p->mTPath & 1 ) n = pik_next_rpath(p, pDir);
p->aTPath[n].x = pPlace->x;
p->mTPath |= 1;
break;
}
pElem->outDir = pDir->eCode;
}
/* Set the "from" of an element
*/
static void pik_set_from(Pik *p, PElem *pElem, PToken *pTk, PPoint *pPt){
if( !pElem->type->isLine ){
pik_error(p, pTk, "use \"at\" to position this object");
return;
}
if( pElem->mProp & A_FROM ){
pik_error(p, pTk, "line start location already fixed");
return;
}
if( pElem->bClose ){
pik_error(p, pTk, "polygon is closed");
return;
}
if( p->nTPath>1 ){
PNum dx = pPt->x - p->aTPath[0].x;
PNum dy = pPt->y - p->aTPath[0].y;
int i;
for(i=1; i<p->nTPath; i++){
p->aTPath[i].x += dx;
p->aTPath[i].y += dy;
}
}
p->aTPath[0] = *pPt;
p->mTPath = 3;
pElem->mProp |= A_FROM;
}
/* Set the "to" of an element
*/
static void pik_add_to(Pik *p, PElem *pElem, PToken *pTk, PPoint *pPt){
int n = p->nTPath-1;
if( !pElem->type->isLine ){
pik_error(p, pTk, "use \"at\" to position this object");
return;
}
if( pElem->bClose ){
pik_error(p, pTk, "polygon is closed");
return;
}
if( n==0 || p->mTPath==3 || p->thenFlag ){
n = pik_next_rpath(p, pTk);
}
p->aTPath[n] = *pPt;
p->mTPath = 3;
}
static void pik_close_path(Pik *p, PToken *pErr){
PElem *pElem = p->cur;
if( p->nTPath<3 ){
pik_error(p, pErr,
"need at least 3 vertexes in order to close the polygon");
return;
}
if( pElem->bClose ){
pik_error(p, pErr, "polygon already closed");
return;
}
pElem->bClose = 1;
}
/* Lower the layer of the current element so that it is behind the
** given element.
*/
static void pik_behind(Pik *p, PElem *pOther){
PElem *pElem = p->cur;
if( pElem->iLayer>=pOther->iLayer ){
pElem->iLayer = pOther->iLayer - 1;
}
}
/* Set the "at" of an element
*/
static void pik_set_at(Pik *p, PToken *pEdge, PPoint *pAt, PToken *pErrTok){
PElem *pElem;
if( p->nErr ) return;
pElem = p->cur;
if( pElem->type->isLine ){
pik_error(p, pErrTok, "use \"from\" and \"to\" to position this object");
return;
}
if( pElem->mProp & A_AT ){
pik_error(p, pErrTok, "location fixed by prior \"at\"");
return;
}
pElem->mProp |= A_AT;
pElem->eWith = pEdge ? pEdge->eEdge : CP_C;
pElem->with = *pAt;
}
/*
** Try to add a text attribute to an element
*/
static void pik_add_txt(Pik *p, PToken *pTxt, int iPos){
PElem *pElem = p->cur;
PToken *pT;
if( pElem->nTxt >= count(pElem->aTxt) ){
pik_error(p, pTxt, "too many text terms");
return;
}
pT = &pElem->aTxt[pElem->nTxt++];
*pT = *pTxt;
pT->eCode = iPos;
}
/* Merge "text-position" flags
*/
static int pik_text_position(int iPrev, PToken *pFlag){
int iRes = iPrev;
switch( pFlag->eType ){
case T_LJUST: iRes = (iRes&~TP_JMASK) | TP_LJUST; break;
case T_RJUST: iRes = (iRes&~TP_JMASK) | TP_RJUST; break;
case T_ABOVE: iRes = (iRes&~TP_VMASK) | TP_ABOVE; break;
case T_CENTER: iRes = (iRes&~TP_VMASK) | TP_CENTER; break;
case T_BELOW: iRes = (iRes&~TP_VMASK) | TP_BELOW; break;
case T_ITALIC: iRes |= TP_ITALIC; break;
case T_BOLD: iRes |= TP_BOLD; break;
case T_ALIGNED: iRes |= TP_ALIGN; break;
case T_BIG: if( iRes & TP_BIG ) iRes |= TP_XTRA;
else iRes = (iRes &~TP_SZMASK)|TP_BIG; break;
case T_SMALL: if( iRes & TP_SMALL ) iRes |= TP_XTRA;
else iRes = (iRes &~TP_SZMASK)|TP_SMALL; break;
}
return iRes;
}
/* Return an estimate of the actual number of displayed characters
** in a character string.
**
** Omit "\" used to escape characters. And count entities like
** "<" as a single character. Multi-byte UTF8 characters count
** as a single character.
*/
static int pik_text_length(const PToken *pToken){
int n = pToken->n;
const char *z = pToken->z;
int cnt, j;
for(j=1, cnt=0; j<n-1; j++){
if( (z[j] & 0xc0)==0xc0 ) continue;
cnt++;
if( z[j]=='\\' && z[j+1]!='&' ){
j++;
}else if( z[j]=='&' ){
int k;
for(k=j+1; k<j+7 && z[k]!=0 && z[k]!=';'; k++){}
if( z[k]==';' ) j = k;
}
}
return cnt;
}
/* Adjust the width, height, and/or radius of the object so that
** it fits around the text that has been added so far.
**
** (1) Only text specified prior to this attribute is considered.
** (2) The text size is estimated based on the charht and charwid
** variable settings.
** (3) The fitted attributes can be changed again after this
** attribute, for example using "width 110%" if this auto-fit
** underestimates the text size.
** (4) Previously set attributes will not be altered. In other words,
** "width 1in fit" might cause the height to change, but the
** width is now set.
** (5) This only works for attributes that have an xFit method.
*/
static void pik_size_to_fit(Pik *p, PToken *pFit){
PElem *pElem;
int w = 0, h = 0;
int i;
if( p->nErr ) return;
pElem = p->cur;
if( pElem->nTxt==0 ){
pik_error(0, pFit, "no text to fit to");
return;
}
if( pElem->type->xFit==0 ) return;
if( (pElem->mProp & A_HEIGHT)==0 ){
int hasCenter = 0;
int hasSingleStack = 0;
int hasDoubleStack = 0;
pik_txt_vertical_layout(pElem);
for(i=0; i<pElem->nTxt; i++){
if( pElem->aTxt[i].eCode & TP_CENTER ){
hasCenter = 1;
}else if( pElem->aTxt[i].eCode & (TP_ABOVE2|TP_BELOW2) ){
hasDoubleStack = 1;
}else if( pElem->aTxt[i].eCode & (TP_ABOVE|TP_BELOW) ){
hasSingleStack = 1;
}
}
h = hasCenter + hasSingleStack*2 + hasDoubleStack*2;
}
if( (pElem->mProp & A_WIDTH)==0 ){
for(i=0; i<pElem->nTxt; i++){
int cnt = pik_text_length(&pElem->aTxt[i]);
if( pElem->type->eJust==0
&& (pElem->aTxt[i].eCode & TP_JMASK)!=0
){
cnt *= 2;
}
if( cnt>w ) w = cnt;
}
}
if( h>0 || w>0 ){
pik_compute_layout_settings(p);
pElem->type->xFit(p, pElem, w*p->charWidth, h*p->charHeight);
}
}
/* Set a local variable name to "val".
**
** The name might be a built-in variable or a color name. In either case,
** a new application-defined variable is set. Since app-defined variables
** are searched first, this will override any built-in variables.
*/
static void pik_set_var(Pik *p, PToken *pId, PNum val, PToken *pOp){
PVar *pVar = p->pVar;
while( pVar ){
if( pik_token_eq(pId,pVar->zName)==0 ) break;
pVar = pVar->pNext;
}
if( pVar==0 ){
char *z;
pVar = malloc( pId->n+1 + sizeof(*pVar) );
if( pVar==0 ){
pik_error(p, 0, 0);
return;
}
pVar->zName = z = (char*)&pVar[1];
memcpy(z, pId->z, pId->n);
z[pId->n] = 0;
pVar->pNext = p->pVar;
pVar->val = pik_value(p, pId->z, pId->n, 0);
p->pVar = pVar;
}
switch( pOp->eCode ){
case T_PLUS: pVar->val += val; break;
case T_STAR: pVar->val *= val; break;
case T_MINUS: pVar->val -= val; break;
case T_SLASH:
if( val==0.0 ){
pik_error(p, pOp, "division by zero");
}else{
pVar->val /= val;
}
break;
default: pVar->val = val; break;
}
p->bLayoutVars = 0; /* Clear the layout setting cache */
}
/*
** Search for the variable named z[0..n-1] in:
**
** * Application defined variables
** * Built-in variables
**
** Return the value of the variable if found. If not found
** return 0.0. Also if pMiss is not NULL, then set it to 1
** if not found.
**
** This routine is a subroutine to pik_get_var(). But it is also
** used by object implementations to look up (possibly overwritten)
** values for built-in variables like "boxwid".
*/
static PNum pik_value(Pik *p, const char *z, int n, int *pMiss){
PVar *pVar;
int first, last, mid, c;
for(pVar=p->pVar; pVar; pVar=pVar->pNext){
if( strncmp(pVar->zName,z,n)==0 && pVar->zName[n]==0 ){
return pVar->val;
}
}
first = 0;
last = count(aBuiltin)-1;
while( first<=last ){
mid = (first+last)/2;
c = strncmp(z,aBuiltin[mid].zName,n);
if( c==0 && aBuiltin[mid].zName[n] ) c = 1;
if( c==0 ) return aBuiltin[mid].val;
if( c>0 ){
first = mid+1;
}else{
last = mid-1;
}
}
if( pMiss ) *pMiss = 1;
return 0.0;
}
/*
** Look up a color-name. Unlike other names in this program, the
** color-names are not case sensitive. So "DarkBlue" and "darkblue"
** and "DARKBLUE" all find the same value (139).
**
** If not found, return -1.0. Also post an error if p!=NULL.
**
** Special color names "None" and "Off" return -1.0 without causing
** an error.
*/
static PNum pik_lookup_color(Pik *p, PToken *pId){
int first, last, mid, c = 0;
first = 0;
last = count(aColor)-1;
while( first<=last ){
const char *zClr;
int c1, c2;
unsigned int i;
mid = (first+last)/2;
zClr = aColor[mid].zName;
for(i=0; i<pId->n; i++){
c1 = zClr[i]&0x7f;
if( isupper(c1) ) c1 = tolower(c1);
c2 = pId->z[i]&0x7f;
if( isupper(c2) ) c2 = tolower(c2);
c = c2 - c1;
if( c ) break;
}
if( c==0 && aColor[mid].zName[pId->n] ) c = -1;
if( c==0 ) return (double)aColor[mid].val;
if( c>0 ){
first = mid+1;
}else{
last = mid-1;
}
}
if( p ) pik_error(p, pId, "not a known color name");
return -1.0;
}
/* Get the value of a variable.
**
** Search in order:
**
** * Application defined variables
** * Built-in variables
** * Color names
**
** If no such variable is found, throw an error.
*/
static PNum pik_get_var(Pik *p, PToken *pId){
int miss = 0;
PNum v = pik_value(p, pId->z, pId->n, &miss);
if( miss==0 ) return v;
v = pik_lookup_color(0, pId);
if( v>=0.0 ) return v;
pik_error(p,pId,"no such variable");
return 0.0;
}
/* Convert a T_NTH token (ex: "2nd", "5th"} into a numeric value and
** return that value. Throw an error if the value is too big.
*/
static short int pik_nth_value(Pik *p, PToken *pNth){
int i = atoi(pNth->z);
if( i>1000 ){
pik_error(p, pNth, "value too big - max '1000th'");
i = 1;
}
if( i==0 && pik_token_eq(pNth,"first")==0 ) i = 1;
return i;
}
/* Search for the NTH element.
**
** If pBasis is not NULL then it should be a [] element. Use the
** sublist of that [] element for the search. If pBasis is not a []
** element, then throw an error.
**
** The pNth token describes the N-th search. The pNth->eCode value
** is one more than the number of items to skip. It is negative
** to search backwards. If pNth->eType==T_ID, then it is the name
** of a class to search for. If pNth->eType==T_LB, then
** search for a [] object. If pNth->eType==T_LAST, then search for
** any type.
**
** Raise an error if the item is not found.
*/
static PElem *pik_find_nth(Pik *p, PElem *pBasis, PToken *pNth){
PEList *pList;
int i, n;
const PClass *pClass;
if( pBasis==0 ){
pList = p->list;
}else{
pList = pBasis->pSublist;
}
if( pList==0 ){
pik_error(p, pNth, "no such object");
return 0;
}
if( pNth->eType==T_LAST ){
pClass = 0;
}else if( pNth->eType==T_LB ){
pClass = &sublistClass;
}else{
pClass = pik_find_class(pNth);
if( pClass==0 ){
pik_error(0, pNth, "no such object type");
return 0;
}
}
n = pNth->eCode;
if( n<0 ){
for(i=pList->n-1; i>=0; i--){
PElem *pElem = pList->a[i];
if( pClass && pElem->type!=pClass ) continue;
n++;
if( n==0 ){ return pElem; }
}
}else{
for(i=0; i<pList->n; i++){
PElem *pElem = pList->a[i];
if( pClass && pElem->type!=pClass ) continue;
n--;
if( n==0 ){ return pElem; }
}
}
pik_error(p, pNth, "no such object");
return 0;
}
/* Search for an element by name.
**
** Search in pBasis->pSublist if pBasis is not NULL. If pBasis is NULL
** then search in p->list.
*/
static PElem *pik_find_byname(Pik *p, PElem *pBasis, PToken *pName){
PEList *pList;
int i, j;
if( pBasis==0 ){
pList = p->list;
}else{
pList = pBasis->pSublist;
}
if( pList==0 ){
pik_error(p, pName, "no such object");
return 0;
}
/* First look explicitly tagged objects */
for(i=pList->n-1; i>=0; i--){
PElem *pElem = pList->a[i];
if( pElem->zName && pik_token_eq(pName,pElem->zName)==0 ){
return pElem;
}
}
/* If not found, do a second pass looking for any object containing
** text which exactly matches pName */
for(i=pList->n-1; i>=0; i--){
PElem *pElem = pList->a[i];
for(j=0; j<pElem->nTxt; j++){
if( pElem->aTxt[j].n==pName->n+2
&& memcmp(pElem->aTxt[j].z+1,pName->z,pName->n)==0 ){
return pElem;
}
}
}
pik_error(p, pName, "no such object");
return 0;
}
/* Change most of the settings for the current object to be the
** same as the pOther object, or the most recent element of the same
** type if pOther is NULL.
*/
static void pik_same(Pik *p, PElem *pOther, PToken *pErrTok){
PElem *pElem = p->cur;
if( p->nErr ) return;
if( pOther==0 ){
int i;
for(i=(p->list ? p->list->n : 0)-1; i>=0; i--){
pOther = p->list->a[i];
if( pOther->type==pElem->type ) break;
}
if( i<0 ){
pik_error(p, pErrTok, "no prior objects of the same type");
return;
}
}
if( pOther->nPath && pElem->type->isLine ){
PNum dx, dy;
int i;
dx = p->aTPath[0].x - pOther->aPath[0].x;
dy = p->aTPath[0].y - pOther->aPath[0].y;
for(i=1; i<pOther->nPath; i++){
p->aTPath[i].x = pOther->aPath[i].x + dx;
p->aTPath[i].y = pOther->aPath[i].y + dy;
}
p->nTPath = pOther->nPath;
p->mTPath = 3;
p->samePath = 1;
}
if( !pElem->type->isLine ){
pElem->w = pOther->w;
pElem->h = pOther->h;
}
pElem->rad = pOther->rad;
pElem->sw = pOther->sw;
pElem->dashed = pOther->dashed;
pElem->dotted = pOther->dotted;
pElem->fill = pOther->fill;
pElem->color = pOther->color;
pElem->cw = pOther->cw;
pElem->larrow = pOther->larrow;
pElem->rarrow = pOther->rarrow;
pElem->bClose = pOther->bClose;
pElem->bChop = pOther->bChop;
pElem->inDir = pOther->inDir;
pElem->outDir = pOther->outDir;
}
/* Return a "Place" associated with element pElem. If pEdge is NULL
** return the center of the object. Otherwise, return the corner
** described by pEdge.
*/
static PPoint pik_place_of_elem(Pik *p, PElem *pElem, PToken *pEdge){
PPoint pt;
const PClass *pClass;
pt.x = 0.0;
pt.y = 0.0;
if( pElem==0 ) return pt;
if( pEdge==0 ){
return pElem->ptAt;
}
pClass = pElem->type;
if( pEdge->eType==T_EDGEPT || (pEdge->eEdge>0 && pEdge->eEdge<CP_END) ){
pt = pClass->xOffset(p, pElem, pEdge->eEdge);
pt.x += pElem->ptAt.x;
pt.y += pElem->ptAt.y;
return pt;
}
if( pEdge->eType==T_START ){
return pElem->ptEnter;
}else{
return pElem->ptExit;
}
}
/* Do a linear interpolation of two positions.
*/
static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2){
PPoint out;
if( x<0.0 ) x = 0.0;
if( x>1.0 ) x = 1.0;
out.x = p2.x*x + p1.x*(1.0 - x);
out.y = p2.y*x + p1.y*(1.0 - x);
return out;
}
/* Compute the position that is dist away from pt at an heading angle of r
**
** The angle is a compass heading in degrees. North is 0 (or 360).
** East is 90. South is 180. West is 270. And so forth.
*/
static PPoint pik_position_at_angle(PNum dist, PNum r, PPoint pt){
r *= 0.017453292519943295769; /* degrees to radians */
pt.x += dist*sin(r);
pt.y += dist*cos(r);
return pt;
}
/* Compute the position that is dist away at a compass point
*/
static PPoint pik_position_at_hdg(PNum dist, PToken *pD, PPoint pt){
return pik_position_at_angle(dist, pik_hdg_angle[pD->eEdge], pt);
}
/* Return the coordinates for the n-th vertex of a line.
*/
static PPoint pik_nth_vertex(Pik *p, PToken *pNth, PToken *pErr, PElem *pObj){
static const PPoint zero;
int n;
if( p->nErr || pObj==0 ) return p->aTPath[0];
if( !pObj->type->isLine ){
pik_error(p, pErr, "object is not a line");
return zero;
}
n = atoi(pNth->z);
if( n<1 || n>pObj->nPath ){
pik_error(p, pNth, "no such vertex");
return zero;
}
return pObj->aPath[n-1];
}
/* Return the value of a property of an object.
*/
static PNum pik_property_of(PElem *pElem, PToken *pProp){
PNum v = 0.0;
switch( pProp->eType ){
case T_HEIGHT: v = pElem->h; break;
case T_WIDTH: v = pElem->w; break;
case T_RADIUS: v = pElem->rad; break;
case T_DIAMETER: v = pElem->rad*2.0; break;
case T_THICKNESS: v = pElem->sw; break;
case T_DASHED: v = pElem->dashed; break;
case T_DOTTED: v = pElem->dotted; break;
case T_FILL: v = pElem->fill; break;
case T_COLOR: v = pElem->color; break;
case T_X: v = pElem->ptAt.x; break;
case T_Y: v = pElem->ptAt.y; break;
case T_TOP: v = pElem->bbox.ne.y; break;
case T_BOTTOM: v = pElem->bbox.sw.y; break;
case T_LEFT: v = pElem->bbox.sw.x; break;
case T_RIGHT: v = pElem->bbox.ne.x; break;
}
return v;
}
/* Compute one of the built-in functions
*/
static PNum pik_func(Pik *p, PToken *pFunc, PNum x, PNum y){
PNum v = 0.0;
switch( pFunc->eCode ){
case FN_ABS: v = v<0.0 ? -v : v; break;
case FN_COS: v = cos(x); break;
case FN_INT: v = rint(x); break;
case FN_SIN: v = sin(x); break;
case FN_SQRT:
if( x<0.0 ){
pik_error(p, pFunc, "sqrt of negative value");
v = 0.0;
}else{
v = sqrt(x);
}
break;
case FN_MAX: v = x>y ? x : y; break;
case FN_MIN: v = x<y ? x : y; break;
default: v = 0.0;
}
return v;
}
/* Attach a name to an element
*/
static void pik_elem_setname(Pik *p, PElem *pElem, PToken *pName){
if( pElem==0 ) return;
if( pName==0 ) return;
free(pElem->zName);
pElem->zName = malloc(pName->n+1);
if( pElem->zName==0 ){
pik_error(p,0,0);
}else{
memcpy(pElem->zName,pName->z,pName->n);
pElem->zName[pName->n] = 0;
}
return;
}
/*
** Search for object located at *pCenter that has an xChop method.
** Return a pointer to the object, or NULL if not found.
*/
static PElem *pik_find_chopper(PEList *pList, PPoint *pCenter){
int i;
if( pList==0 ) return 0;
for(i=pList->n-1; i>=0; i--){
PElem *pElem = pList->a[i];
if( pElem->type->xChop!=0
&& pElem->ptAt.x==pCenter->x
&& pElem->ptAt.y==pCenter->y
){
return pElem;
}else if( pElem->pSublist ){
pElem = pik_find_chopper(pElem->pSublist,pCenter);
if( pElem ) return pElem;
}
}
return 0;
}
/*
** There is a line traveling from pFrom to pTo.
**
** If point pTo is the exact enter of a choppable object,
** then adjust pTo by the appropriate amount in the direction
** of pFrom.
*/
static void pik_autochop(Pik *p, PPoint *pFrom, PPoint *pTo){
PElem *pElem = pik_find_chopper(p->list, pTo);
if( pElem ){
*pTo = pElem->type->xChop(p, pElem, pFrom);
}
}
/* This routine runs after all attributes have been received
** on an element.
*/
static void pik_after_adding_attributes(Pik *p, PElem *pElem){
int i;
PPoint ofst;
PNum dx, dy;
if( p->nErr ) return;
/* Position block elements */
if( pElem->type->isLine==0 ){
ofst = pik_elem_offset(p, pElem, pElem->eWith);
dx = (pElem->with.x - ofst.x) - pElem->ptAt.x;
dy = (pElem->with.y - ofst.y) - pElem->ptAt.y;
if( dx!=0 || dy!=0 ){
pik_elem_move(pElem, dx, dy);
}
}
/* For a line object with no movement specified, a single movement
** of the default length in the current direction
*/
if( pElem->type->isLine && p->nTPath<2 ){
pik_next_rpath(p, 0);
assert( p->nTPath==2 );
switch( pElem->inDir ){
default: p->aTPath[1].x += pElem->w; break;
case DIR_DOWN: p->aTPath[1].y -= pElem->h; break;
case DIR_LEFT: p->aTPath[1].x -= pElem->w; break;
case DIR_UP: p->aTPath[1].y += pElem->h; break;
}
if( pElem->type->xInit==arcInit ){
p->eDir = pElem->outDir = (pElem->inDir + (pElem->cw ? 1 : 3))%4;
switch( pElem->outDir ){
default: p->aTPath[1].x += pElem->w; break;
case DIR_DOWN: p->aTPath[1].y -= pElem->h; break;
case DIR_LEFT: p->aTPath[1].x -= pElem->w; break;
case DIR_UP: p->aTPath[1].y += pElem->h; break;
}
}
}
/* Initialize the bounding box prior to running xCheck */
pik_bbox_init(&pElem->bbox);
/* Run object-specific code */
if( pElem->type->xCheck!=0 ){
pElem->type->xCheck(p,pElem);
if( p->nErr ) return;
}
/* Compute final bounding box, entry and exit points, center
** point (ptAt) and path for the element
*/
if( pElem->type->isLine ){
pElem->aPath = malloc( sizeof(PPoint)*p->nTPath );
if( pElem->aPath==0 ){
pik_error(p, 0, 0);
return;
}else{
pElem->nPath = p->nTPath;
for(i=0; i<p->nTPath; i++){
pElem->aPath[i] = p->aTPath[i];
}
}
/* "chop" processing:
** If the line goes to the center of an object with an
** xChop method, then use the xChop method to trim the line.
*/
if( pElem->bChop && pElem->nPath>=2 ){
int n = pElem->nPath;
pik_autochop(p, &pElem->aPath[n-2], &pElem->aPath[n-1]);
pik_autochop(p, &pElem->aPath[1], &pElem->aPath[0]);
}
pElem->ptEnter = pElem->aPath[0];
pElem->ptExit = pElem->aPath[pElem->nPath-1];
/* Compute the center of the line based on the bounding box over
** the vertexes */
for(i=0; i<pElem->nPath; i++){
pik_bbox_add_xy(&pElem->bbox, pElem->aPath[i].x, pElem->aPath[i].y);
}
pElem->ptAt.x = (pElem->bbox.ne.x + pElem->bbox.sw.x)/2.0;
pElem->ptAt.y = (pElem->bbox.ne.y + pElem->bbox.sw.y)/2.0;
/* Reset the width and height of the object to be the width and height
** of the bounding box over vertexes */
pElem->w = pElem->bbox.ne.x - pElem->bbox.sw.x;
pElem->h = pElem->bbox.ne.y - pElem->bbox.sw.y;
/* If this is a polygon (if it has the "close" attribute), then
** adjust the exit point */
if( pElem->bClose ){
/* "closed" lines work like block objects */
pik_elem_set_exit(pElem, pElem->inDir);
}else{
/* For an open line, the "center" is half way between
** the .start and the .end */
}
}else{
PNum w2 = pElem->w/2.0;
PNum h2 = pElem->h/2.0;
pElem->ptEnter = pElem->ptAt;
pElem->ptExit = pElem->ptAt;
switch( pElem->inDir ){
default: pElem->ptEnter.x -= w2; break;
case DIR_LEFT: pElem->ptEnter.x += w2; break;
case DIR_UP: pElem->ptEnter.y -= h2; break;
case DIR_DOWN: pElem->ptEnter.y += h2; break;
}
switch( pElem->outDir ){
default: pElem->ptExit.x += w2; break;
case DIR_LEFT: pElem->ptExit.x -= w2; break;
case DIR_UP: pElem->ptExit.y += h2; break;
case DIR_DOWN: pElem->ptExit.y -= h2; break;
}
pik_bbox_add_xy(&pElem->bbox, pElem->ptAt.x - w2, pElem->ptAt.y - h2);
pik_bbox_add_xy(&pElem->bbox, pElem->ptAt.x + w2, pElem->ptAt.y + h2);
}
p->eDir = pElem->outDir;
}
/* Show basic information about each element as a comment in the
** generated HTML. Used for testing and debugging. Activated
** by the (undocumented) "debug = 1;"
** command.
*/
static void pik_elem_render(Pik *p, PElem *pElem){
char *zDir;
if( pElem==0 ) return;
pik_append(p,"<!-- ", -1);
if( pElem->zName ){
pik_append_text(p, pElem->zName, -1, 0);
pik_append(p, ": ", 2);
}
pik_append_text(p, pElem->type->zName, -1, 0);
if( pElem->nTxt ){
pik_append(p, " \"", 2);
pik_append_text(p, pElem->aTxt[0].z+1, pElem->aTxt[0].n-2, 1);
pik_append(p, "\"", 1);
}
pik_append_num(p, " w=", pElem->w);
pik_append_num(p, " h=", pElem->h);
pik_append_point(p, " center=", &pElem->ptAt);
pik_append_point(p, " enter=", &pElem->ptEnter);
switch( pElem->outDir ){
default: zDir = " right"; break;
case DIR_LEFT: zDir = " left"; break;
case DIR_UP: zDir = " up"; break;
case DIR_DOWN: zDir = " down"; break;
}
pik_append_point(p, " exit=", &pElem->ptExit);
pik_append(p, zDir, -1);
pik_append(p, " -->\n", -1);
}
/* Render a list of elements
*/
void pik_elist_render(Pik *p, PEList *pEList){
int i;
int iNextLayer = 0;
int iThisLayer;
int bMoreToDo;
int mDebug = (int)pik_value(p, "debug", 5, 0);
do{
bMoreToDo = 0;
iThisLayer = iNextLayer;
iNextLayer = 0x7fffffff;
for(i=0; i<pEList->n; i++){
PElem *pElem = pEList->a[i];
if( pElem->iLayer>iThisLayer ){
if( pElem->iLayer<iNextLayer ) iNextLayer = pElem->iLayer;
bMoreToDo = 1;
continue; /* Defer until another round */
}else if( pElem->iLayer<iThisLayer ){
continue;
}
void (*xRender)(Pik*,PElem*);
if( mDebug & 1 ) pik_elem_render(p, pElem);
xRender = pElem->type->xRender;
if( xRender ){
xRender(p, pElem);
}
if( pElem->pSublist ){
pik_elist_render(p, pElem->pSublist);
}
}
}while( bMoreToDo );
}
/* Add all elements of the list pEList to the bounding box
*/
static void pik_bbox_add_elist(Pik *p, PEList *pEList, PNum wArrow){
int i;
for(i=0; i<pEList->n; i++){
PElem *pElem = pEList->a[i];
if( pElem->sw>0.0 ) pik_bbox_addbox(&p->bbox, &pElem->bbox);
pik_append_txt(p, pElem, &p->bbox);
if( pElem->pSublist ) pik_bbox_add_elist(p, pElem->pSublist, wArrow);
/* Expand the bounding box to account for arrowheads on lines */
if( pElem->type->isLine && pElem->nPath>0 ){
if( pElem->larrow ){
pik_bbox_addellipse(&p->bbox, pElem->aPath[0].x, pElem->aPath[0].y,
wArrow, wArrow);
}
if( pElem->rarrow ){
int j = pElem->nPath-1;
pik_bbox_addellipse(&p->bbox, pElem->aPath[j].x, pElem->aPath[j].y,
wArrow, wArrow);
}
}
}
}
/* Recompute key layout parameters from variables. */
static void pik_compute_layout_settings(Pik *p){
PNum thickness; /* Line thickness */
PNum wArrow; /* Width of arrowheads */
/* Set up rendering parameters */
if( p->bLayoutVars ) return;
thickness = pik_value(p,"thickness",9,0);
if( thickness<=0.01 ) thickness = 0.01;
wArrow = 0.5*pik_value(p,"arrowwid",8,0);
p->wArrow = wArrow/thickness;
p->hArrow = pik_value(p,"arrowht",7,0)/thickness;
p->rScale = 144.0*pik_value(p,"scale",5,0);
if( p->rScale<5.0 ) p->rScale = 5.0;
p->fontScale = pik_value(p,"fontscale",9,0);
if( p->fontScale<=0.0 ) p->fontScale = 1.0;
p->fontScale *= p->rScale/144.0;
p->charWidth = pik_value(p,"charwid",7,0)*p->fontScale;
p->charHeight = pik_value(p,"charht",6,0)*p->fontScale;
p->bLayoutVars = 1;
}
/* Render a list of elements. Write the SVG into p->zOut.
** Delete the input element_list before returnning.
*/
static void pik_render(Pik *p, PEList *pEList){
if( pEList==0 ) return;
if( p->nErr==0 ){
PNum thickness; /* Stroke width */
PNum margin; /* Extra bounding box margin */
PNum leftmargin; /* Extra bounding box area on the left */
PNum w, h; /* Drawing width and height */
PNum wArrow;
/* Set up rendering parameters */
pik_compute_layout_settings(p);
thickness = pik_value(p,"thickness",9,0);
if( thickness<=0.01 ) thickness = 0.01;
margin = pik_value(p,"margin",6,0);
margin += thickness;
leftmargin = pik_value(p,"leftmargin",10,0);
wArrow = p->wArrow*thickness;
/* Compute a bounding box over all objects so that we can know
** how big to declare the SVG canvas */
pik_bbox_init(&p->bbox);
pik_bbox_add_elist(p, pEList, wArrow);
/* Expand the bounding box slightly to account for line thickness
** and the optional "margin = EXPR" setting. */
p->bbox.ne.x += margin;
p->bbox.ne.y += margin;
p->bbox.sw.x -= margin + leftmargin;
p->bbox.sw.y -= margin;
/* Output the SVG */
pik_append(p, "<svg",4);
if( p->zClass ){
pik_append(p, " class=\"", -1);
pik_append(p, p->zClass, -1);
pik_append(p, "\"", 1);
}
w = p->bbox.ne.x - p->bbox.sw.x;
h = p->bbox.ne.y - p->bbox.sw.y;
p->wSVG = (int)(p->rScale*w);
p->hSVG = (int)(p->rScale*h);
pik_append_dis(p, " viewBox=\"0 0 ",w,"");
pik_append_dis(p, " ",h,"\">\n");
if( (p->mFlags & PIKCHR_INCLUDE_SOURCE)!=0 ){
/* emit original pikchr source code as metadata */
/* FIXME: emit this only if a certain p->mFlags is set. */
pik_append(p, "<metadata>\n", 11);
pik_append(p, "<pikchr:pikchr xmlns:pikchr="
"\"https://pikchr.org/home/doc/trunk/doc/grammar.md\">\n",
-1);
pik_append(p, "<pikchr:src><![CDATA[", 21);
pik_append(p, p->zIn, (int)p->nIn);
pik_append(p, "]]></pikchr:src>\n", 17);
pik_append(p, "</pikchr:pikchr>\n", 17);
pik_append(p, "</metadata>\n", 12);
}
pik_elist_render(p, pEList);
pik_append(p,"</svg>\n", -1);
}else{
p->wSVG = -1;
p->hSVG = -1;
}
pik_elist_free(p, pEList);
}
/*
** An array of this structure defines a list of keywords.
*/
typedef struct PikWord {
char *zWord; /* Text of the keyword */
unsigned char nChar; /* Length of keyword text in bytes */
unsigned char eType; /* Token code */
unsigned char eCode; /* Extra code for the token */
unsigned char eEdge; /* CP_* code for corner/edge keywords */
} PikWord;
/*
** Keywords
*/
static const PikWord pik_keywords[] = {
{ "above", 5, T_ABOVE, 0, 0 },
{ "abs", 3, T_FUNC1, FN_ABS, 0 },
{ "aligned", 7, T_ALIGNED, 0, 0 },
{ "and", 3, T_AND, 0, 0 },
{ "as", 2, T_AS, 0, 0 },
{ "assert", 6, T_ASSERT, 0, 0 },
{ "at", 2, T_AT, 0, 0 },
{ "behind", 6, T_BEHIND, 0, 0 },
{ "below", 5, T_BELOW, 0, 0 },
{ "between", 7, T_BETWEEN, 0, 0 },
{ "big", 3, T_BIG, 0, 0 },
{ "bold", 4, T_BOLD, 0, 0 },
{ "bot", 3, T_EDGEPT, 0, CP_S },
{ "bottom", 6, T_BOTTOM, 0, CP_S },
{ "c", 1, T_EDGEPT, 0, CP_C },
{ "ccw", 3, T_CCW, 0, 0 },
{ "center", 6, T_CENTER, 0, CP_C },
{ "chop", 4, T_CHOP, 0, 0 },
{ "close", 5, T_CLOSE, 0, 0 },
{ "color", 5, T_COLOR, 0, 0 },
{ "cos", 3, T_FUNC1, FN_COS, 0 },
{ "cw", 2, T_CW, 0, 0 },
{ "dashed", 6, T_DASHED, 0, 0 },
{ "diameter", 8, T_DIAMETER, 0, 0 },
{ "dotted", 6, T_DOTTED, 0, 0 },
{ "down", 4, T_DOWN, DIR_DOWN, 0 },
{ "e", 1, T_EDGEPT, 0, CP_E },
{ "east", 4, T_EDGEPT, 0, CP_E },
{ "end", 3, T_END, 0, CP_END },
{ "even", 4, T_EVEN, 0, 0 },
{ "fill", 4, T_FILL, 0, 0 },
{ "first", 5, T_NTH, 0, 0 },
{ "fit", 3, T_FIT, 0, 0 },
{ "from", 4, T_FROM, 0, 0 },
{ "go", 2, T_GO, 0, 0 },
{ "heading", 7, T_HEADING, 0, 0 },
{ "height", 6, T_HEIGHT, 0, 0 },
{ "ht", 2, T_HEIGHT, 0, 0 },
{ "in", 2, T_IN, 0, 0 },
{ "int", 3, T_FUNC1, FN_INT, 0 },
{ "invis", 5, T_INVIS, 0, 0 },
{ "invisible", 9, T_INVIS, 0, 0 },
{ "italic", 6, T_ITALIC, 0, 0 },
{ "last", 4, T_LAST, 0, 0 },
{ "left", 4, T_LEFT, DIR_LEFT, CP_W },
{ "ljust", 5, T_LJUST, 0, 0 },
{ "max", 3, T_FUNC2, FN_MAX, 0 },
{ "min", 3, T_FUNC2, FN_MIN, 0 },
{ "n", 1, T_EDGEPT, 0, CP_N },
{ "ne", 2, T_EDGEPT, 0, CP_NE },
{ "north", 5, T_EDGEPT, 0, CP_N },
{ "nw", 2, T_EDGEPT, 0, CP_NW },
{ "of", 2, T_OF, 0, 0 },
{ "previous", 8, T_LAST, 0, 0, },
{ "print", 5, T_PRINT, 0, 0 },
{ "rad", 3, T_RADIUS, 0, 0 },
{ "radius", 6, T_RADIUS, 0, 0 },
{ "right", 5, T_RIGHT, DIR_RIGHT, CP_E },
{ "rjust", 5, T_RJUST, 0, 0 },
{ "s", 1, T_EDGEPT, 0, CP_S },
{ "same", 4, T_SAME, 0, 0 },
{ "se", 2, T_EDGEPT, 0, CP_SE },
{ "sin", 3, T_FUNC1, FN_SIN, 0 },
{ "small", 5, T_SMALL, 0, 0 },
{ "south", 5, T_EDGEPT, 0, CP_S },
{ "sqrt", 4, T_FUNC1, FN_SQRT, 0 },
{ "start", 5, T_START, 0, CP_START },
{ "sw", 2, T_EDGEPT, 0, CP_SW },
{ "t", 1, T_TOP, 0, CP_N },
{ "the", 3, T_THE, 0, 0 },
{ "then", 4, T_THEN, 0, 0 },
{ "thick", 5, T_THICK, 0, 0 },
{ "thickness", 9, T_THICKNESS, 0, 0 },
{ "thin", 4, T_THIN, 0, 0 },
{ "to", 2, T_TO, 0, 0 },
{ "top", 3, T_TOP, 0, CP_N },
{ "until", 5, T_UNTIL, 0, 0 },
{ "up", 2, T_UP, DIR_UP, 0 },
{ "vertex", 6, T_VERTEX, 0, 0 },
{ "w", 1, T_EDGEPT, 0, CP_W },
{ "way", 3, T_WAY, 0, 0 },
{ "west", 4, T_EDGEPT, 0, CP_W },
{ "wid", 3, T_WIDTH, 0, 0 },
{ "width", 5, T_WIDTH, 0, 0 },
{ "with", 4, T_WITH, 0, 0 },
{ "x", 1, T_X, 0, 0 },
{ "y", 1, T_Y, 0, 0 },
};
/*
** Search a PikWordlist for the given keyword. Return a pointer to the
** element found. Or return 0 if not found.
*/
static const PikWord *pik_find_word(
const char *zIn, /* Word to search for */
int n, /* Length of zIn */
const PikWord *aList, /* List to search */
int nList /* Number of entries in aList */
){
int first = 0;
int last = nList-1;
while( first<=last ){
int mid = (first + last)/2;
int sz = aList[mid].nChar;
int c = strncmp(zIn, aList[mid].zWord, sz<n ? sz : n);
if( c==0 ){
c = n - sz;
if( c==0 ) return &aList[mid];
}
if( c<0 ){
last = mid-1;
}else{
first = mid+1;
}
}
return 0;
}
/*
** Set a symbolic debugger breakpoint on this routine to receive a
** breakpoint when the "#breakpoint" token is parsed.
*/
static void pik_breakpoint(const unsigned char *z){
/* Prevent C compilers from optimizing out this routine. */
if( z[2]=='X' ) exit(1);
}
/*
** Return the length of next token. The token starts on
** the pToken->z character. Fill in other fields of the
** pToken object as appropriate.
*/
static int pik_token_length(PToken *pToken){
const unsigned char *z = (const unsigned char*)pToken->z;
int i;
unsigned char c, c2;
switch( z[0] ){
case '\\': {
pToken->eType = T_WHITESPACE;
for(i=1; z[i]=='\r' || z[i]==' ' || z[i]=='\t'; i++){}
if( z[i]=='\n' ) return i+1;
pToken->eType = T_ERROR;
return 1;
}
case ';':
case '\n': {
pToken->eType = T_EOL;
return 1;
}
case '"': {
for(i=1; (c = z[i])!=0; i++){
if( c=='\\' ){
if( z[i+1]==0 ) break;
i++;
continue;
}
if( c=='"' ){
pToken->eType = T_STRING;
return i+1;
}
}
pToken->eType = T_ERROR;
return i;
}
case ' ':
case '\t':
case '\f':
case '\r': {
for(i=1; (c = z[i])==' ' || c=='\t' || c=='\r' || c=='\t'; i++){}
pToken->eType = T_WHITESPACE;
return i;
}
case '#': {
for(i=1; (c = z[i])!=0 && c!='\n'; i++){}
pToken->eType = T_WHITESPACE;
/* If the comment is "#breakpoint" then invoke the pik_breakpoint()
** routine. The pik_breakpoint() routie is a no-op that serves as
** a convenient place to set a gdb breakpoint when debugging. */
if( strncmp((const char*)z,"#breakpoint",11)==0 ) pik_breakpoint(z);
return i;
}
case '/': {
if( z[1]=='*' ){
for(i=2; z[i]!=0 && (z[i]!='*' || z[i+1]!='/'); i++){}
if( z[i]=='*' ){
pToken->eType = T_WHITESPACE;
return i+2;
}else{
pToken->eType = T_ERROR;
return i;
}
}else if( z[1]=='/' ){
for(i=2; z[i]!=0 && z[i]!='\n'; i++){}
pToken->eType = T_WHITESPACE;
return i;
}else if( z[1]=='=' ){
pToken->eType = T_ASSIGN;
pToken->eCode = T_SLASH;
return 2;
}else{
pToken->eType = T_SLASH;
return 1;
}
}
case '+': {
if( z[1]=='=' ){
pToken->eType = T_ASSIGN;
pToken->eCode = T_PLUS;
return 2;
}
pToken->eType = T_PLUS;
return 1;
}
case '*': {
if( z[1]=='=' ){
pToken->eType = T_ASSIGN;
pToken->eCode = T_STAR;
return 2;
}
pToken->eType = T_STAR;
return 1;
}
case '%': { pToken->eType = T_PERCENT; return 1; }
case '(': { pToken->eType = T_LP; return 1; }
case ')': { pToken->eType = T_RP; return 1; }
case '[': { pToken->eType = T_LB; return 1; }
case ']': { pToken->eType = T_RB; return 1; }
case ',': { pToken->eType = T_COMMA; return 1; }
case ':': { pToken->eType = T_COLON; return 1; }
case '>': { pToken->eType = T_GT; return 1; }
case '=': {
if( z[1]=='=' ){
pToken->eType = T_EQ;
return 2;
}
pToken->eType = T_ASSIGN;
pToken->eCode = T_ASSIGN;
return 1;
}
case '-': {
if( z[1]=='>' ){
pToken->eType = T_RARROW;
return 2;
}else if( z[1]=='=' ){
pToken->eType = T_ASSIGN;
pToken->eCode = T_MINUS;
return 2;
}else{
pToken->eType = T_MINUS;
return 1;
}
}
case '<': {
if( z[1]=='-' ){
if( z[2]=='>' ){
pToken->eType = T_LRARROW;
return 3;
}else{
pToken->eType = T_LARROW;
return 2;
}
}else{
pToken->eType = T_LT;
return 1;
}
}
default: {
c = z[0];
if( c=='.' ){
unsigned char c1 = z[1];
if( islower(c1) ){
const PikWord *pFound;
for(i=2; (c = z[i])>='a' && c<='z'; i++){}
pFound = pik_find_word((const char*)z+1, i-1,
pik_keywords, count(pik_keywords));
if( pFound && (pFound->eEdge>0 ||
pFound->eType==T_EDGEPT ||
pFound->eType==T_START ||
pFound->eType==T_END )
){
/* Dot followed by something that is a 2-D place value */
pToken->eType = T_DOT_E;
}else if( pFound && (pFound->eType==T_X || pFound->eType==T_Y) ){
/* Dot followed by "x" or "y" */
pToken->eType = T_DOT_XY;
}else{
/* Any other "dot" */
pToken->eType = T_DOT_L;
}
return 1;
}else if( isdigit(c1) ){
i = 0;
/* no-op. Fall through to number handling */
}else if( isupper(c1) ){
for(i=2; (c = z[i])!=0 && (isalnum(c) || c=='_'); i++){}
pToken->eType = T_DOT_U;
return 1;
}else{
pToken->eType = T_ERROR;
return 1;
}
}
if( (c>='0' && c<='9') || c=='.' ){
int nDigit;
int isInt = 1;
if( c!='.' ){
nDigit = 1;
for(i=1; (c = z[i])>='0' && c<='9'; i++){ nDigit++; }
if( i==1 && (c=='x' || c=='X') ){
for(i=2; (c = z[i])!=0 && isxdigit(c); i++){}
pToken->eType = T_NUMBER;
return i;
}
}else{
isInt = 0;
nDigit = 0;
}
if( c=='.' ){
isInt = 0;
for(i++; (c = z[i])>='0' && c<='9'; i++){ nDigit++; }
}
if( nDigit==0 ){
pToken->eType = T_ERROR;
return i;
}
if( c=='e' || c=='E' ){
i++;
c2 = z[i];
if( c2=='+' || c2=='-' ){
i++;
c2 = z[i];
}
if( c2<'0' || c>'9' ){
/* This is not an exp */
i -= 2;
}else{
i++;
isInt = 0;
while( (c = z[i])>='0' && c<='9' ){ i++; }
}
}
c2 = c ? z[i+1] : 0;
if( isInt ){
if( (c=='t' && c2=='h')
|| (c=='r' && c2=='d')
|| (c=='n' && c2=='d')
|| (c=='s' && c2=='t')
){
pToken->eType = T_NTH;
return i+2;
}
}
if( (c=='i' && c2=='n')
|| (c=='c' && c2=='m')
|| (c=='m' && c2=='m')
|| (c=='p' && c2=='t')
|| (c=='p' && c2=='x')
|| (c=='p' && c2=='c')
){
i += 2;
}
pToken->eType = T_NUMBER;
return i;
}else if( islower(c) || c=='_' || c=='$' || c=='@' ){
const PikWord *pFound;
for(i=1; (c = z[i])!=0 && (isalnum(c) || c=='_'); i++){}
pFound = pik_find_word((const char*)z, i,
pik_keywords, count(pik_keywords));
if( pFound ){
pToken->eType = pFound->eType;
pToken->eCode = pFound->eCode;
pToken->eEdge = pFound->eEdge;
return i;
}
pToken->n = i;
if( pik_find_class(pToken)!=0 ){
pToken->eType = T_CLASSNAME;
}else{
pToken->eType = T_ID;
}
return i;
}else if( c>='A' && c<='Z' ){
for(i=1; (c = z[i])!=0 && (isalnum(c) || c=='_'); i++){}
pToken->eType = T_PLACENAME;
return i;
}else{
pToken->eType = T_ERROR;
return 1;
}
}
}
}
/*
** Return a pointer to the next non-whitespace token after pThis.
** This is used to help form error messages.
*/
static PToken pik_next_semantic_token(PToken *pThis){
PToken x;
int sz;
int i = pThis->n;
memset(&x, 0, sizeof(x));
x.z = pThis->z;
while(1){
x.z = pThis->z + i;
sz = pik_token_length(&x);
if( x.eType!=T_WHITESPACE ){
x.n = sz;
return x;
}
i += sz;
}
}
/*
** Parse the PIKCHR script contained in zText[]. Return a rendering. Or
** if an error is encountered, return the error text. The error message
** is HTML formatted. So regardless of what happens, the return text
** is safe to be insertd into an HTML output stream.
**
** If pnWidth and pnHeight are not NULL, then this routine writes the
** width and height of the <SVG> object into the integers that they
** point to. A value of -1 is written if an error is seen.
**
** If zClass is not NULL, then it is a class name to be included in
** the <SVG> markup.
**
** The returned string is contained in memory obtained from malloc()
** and should be released by the caller.
*/
char *pikchr(
const char *zText, /* Input PIKCHR source text. zero-terminated */
const char *zClass, /* Add class="%s" to <svg> markup */
unsigned int mFlags, /* Flags used to influence rendering behavior */
int *pnWidth, /* Write width of <svg> here, if not NULL */
int *pnHeight /* Write height here, if not NULL */
){
int i;
int sz;
PToken token;
Pik s;
yyParser sParse;
memset(&s, 0, sizeof(s));
s.zIn = zText;
s.nIn = (unsigned int)strlen(zText);
s.eDir = DIR_RIGHT;
s.zClass = zClass;
s.mFlags = mFlags;
pik_parserInit(&sParse, &s);
#if 0
pik_parserTrace(stdout, "parser: ");
#endif
for(i=0; zText[i] && s.nErr==0; i+=sz){
token.eCode = 0;
token.eEdge = 0;
token.z = zText + i;
sz = pik_token_length(&token);
if( token.eType==T_WHITESPACE ){
/* no-op */
}else if( sz>1000 ){
token.n = 1;
pik_error(&s, &token, "token is too long - max length 1000 bytes");
break;
}else if( token.eType==T_ERROR ){
token.n = (unsigned short)(sz & 0xffff);
pik_error(&s, &token, "unrecognized token");
break;
}else{
#if 0
printf("******** Token %s (%d): \"%.*s\" **************\n",
yyTokenName[token.eType], token.eType,
isspace(token.z[0]) ? 0 : token.n, token.z);
#endif
token.n = (unsigned short)(sz & 0xffff);
pik_parser(&sParse, token.eType, token);
}
}
if( s.nErr==0 ){
memset(&token,0,sizeof(token));
token.z = zText;
pik_parser(&sParse, 0, token);
}
pik_parserFinalize(&sParse);
while( s.pVar ){
PVar *pNext = s.pVar->pNext;
free(s.pVar);
s.pVar = pNext;
}
if( pnWidth ) *pnWidth = s.nErr ? -1 : s.wSVG;
if( pnHeight ) *pnHeight = s.nErr ? -1 : s.hSVG;
if( s.zOut ){
s.zOut[s.nOut] = 0;
s.zOut = realloc(s.zOut, s.nOut+1);
}
return s.zOut;
}
#if defined(PIKCHR_FUZZ)
#include <stdint.h>
int LLVMFuzzerTestOneInput(const uint8_t *aData, size_t nByte){
int w,h;
char *zIn, *zOut;
zIn = malloc( nByte + 1 );
if( zIn==0 ) return 0;
memcpy(zIn, aData, nByte);
zIn[nByte] = 0;
zOut = pikchr(zIn, "pikchr", 0, &w, &h);
free(zIn);
free(zOut);
return 0;
}
#endif /* PIKCHR_FUZZ */
#if defined(PIKCHR_SHELL)
/* Texting interface
**
** Generate HTML on standard output that displays both the original
** input text and the rendered SVG for all files named on the command
** line.
*/
int main(int argc, char **argv){
int i;
int bNoEcho = 0; /* Do not show the text of the script */
int mPikchrFlags = 0; /* Flags passed into pikchr() */
printf(
"<!DOCTYPE html>\n"
"<html lang=\"en-US\">\n"
"<head>\n<title>PIKCHR Test</title>\n"
"<meta charset=\"utf-8\">\n"
"</head>\n"
"<body>\n"
);
for(i=1; i<argc; i++){
FILE *in;
size_t sz;
char *zIn;
char *zOut;
char *z, c;
int j;
int w, h;
if( argv[i][0]=='-' ){
char *z = argv[i];
z++;
if( z[0]=='-' ) z++;
if( strcmp(z,"no-echo")==0 ){
bNoEcho = 1;
}else
if( strcmp(z,"include-source")==0 ){
mPikchrFlags |= PIKCHR_INCLUDE_SOURCE;
}else
{
fprintf(stderr,"unknown option: \"%s\"\n", argv[i]);
exit(1);
}
continue;
}
printf("<h1>File %s</h1>\n", argv[i]);
in = fopen(argv[i], "rb");
if( in==0 ){
fprintf(stderr, "cannot open \"%s\" for reading\n", argv[i]);
continue;
}
fseek(in, 0, SEEK_END);
sz = ftell(in);
rewind(in);
zIn = malloc( sz+1 );
if( zIn==0 ){
fprintf(stderr, "cannot allocate space for file \"%s\"\n", argv[i]);
fclose(in);
continue;
}
sz = fread(zIn, 1, sz, in);
fclose(in);
zIn[sz] = 0;
if( !bNoEcho ){
printf("<p>Source text:</p>\n<blockquote><pre>\n");
z = zIn;
while( z[0]!=0 ){
for(j=0; (c = z[j])!=0 && c!='<' && c!='>' && c!='&'; j++){}
if( j ) printf("%.*s", j, z);
z += j+1;
j = -1;
if( c=='<' ){
printf("<");
}else if( c=='>' ){
printf(">");
}else if( c=='&' ){
printf("&");
}else if( c==0 ){
break;
}
}
printf("</pre></blockquote>\n");
}
zOut = pikchr(zIn, "pikchr", mPikchrFlags, &w, &h);
free(zIn);
if( zOut ){
if( w<0 ){
printf("<p>ERROR:</p>\n");
}else if( bNoEcho==0 ){
printf("<p>Output size: %d by %d</p>\n", w, h);
}
printf("<div style='border:3px solid lightgray;max-width:%dpx'>\n"
"%s</div>\n",
w, zOut);
free(zOut);
}
}
printf("</body></html>\n");
return 0;
}
#endif /* PIKCHR_SHELL */
#line 7094 "pikchr.c"