1 /* This file is automatically generated by Lemon from input grammar
2 ** source file "pikchr.y". */
3 /*
4 ** Zero-Clause BSD license:
5 **
6 ** Copyright (C) 2020-09-01 by D. Richard Hipp <drh@sqlite.org>
7 **
8 ** Permission to use, copy, modify, and/or distribute this software for
9 ** any purpose with or without fee is hereby granted.
10 **
11 ****************************************************************************
12 **
13 ** This software translates a PIC-inspired diagram language into SVG.
14 **
15 ** PIKCHR (pronounced like "picture") is *mostly* backwards compatible
16 ** with legacy PIC, though some features of legacy PIC are removed
17 ** (for example, the "sh" command is removed for security) and
18 ** many enhancements are added.
19 **
20 ** PIKCHR is designed for use in an internet facing web environment.
21 ** In particular, PIKCHR is designed to safely generate benign SVG from
22 ** source text that provided by a hostile agent.
23 **
24 ** This code was originally written by D. Richard Hipp using documentation
25 ** from prior PIC implementations but without reference to prior code.
26 ** All of the code in this project is original.
27 **
28 ** This file implements a C-language subroutine that accepts a string
29 ** of PIKCHR language text and generates a second string of SVG output that
30 ** renders the drawing defined by the input.  Space to hold the returned
31 ** string is obtained from malloc() and should be freed by the caller.
32 ** NULL might be returned if there is a memory allocation error.
33 **
34 ** If there are errors in the PIKCHR input, the output will consist of an
35 ** error message and the original PIKCHR input text (inside of <pre>...</pre>).
36 **
37 ** The subroutine implemented by this file is intended to be stand-alone.
38 ** It uses no external routines other than routines commonly found in
39 ** the standard C library.
40 **
41 ****************************************************************************
42 ** COMPILING:
43 **
44 ** The original source text is a mixture of C99 and "Lemon"
45 ** (See https://sqlite.org/src/file/doc/lemon.html).  Lemon is an LALR(1)
46 ** parser generator program, similar to Yacc.  The grammar of the
47 ** input language is specified in Lemon.  C-code is attached.  Lemon
48 ** runs to generate a single output file ("pikchr.c") which is then
49 ** compiled to generate the Pikchr library.  This header comment is
50 ** preserved in the Lemon output, so you might be reading this in either
51 ** the generated "pikchr.c" file that is output by Lemon, or in the
52 ** "pikchr.y" source file that is input into Lemon.  If you make changes,
53 ** you should change the input source file "pikchr.y", not the
54 ** Lemon-generated output file.
55 **
56 ** Basic compilation steps:
57 **
58 **      lemon pikchr.y
59 **      cc pikchr.c -o pikchr.o
60 **
61 ** Add -DPIKCHR_SHELL to add a main() routine that reads input files
62 ** and sends them through Pikchr, for testing.  Add -DPIKCHR_FUZZ for
63 ** -fsanitizer=fuzzer testing.
64 **
65 ****************************************************************************
66 ** IMPLEMENTATION NOTES (for people who want to understand the internal
67 ** operation of this software, perhaps to extend the code or to fix bugs):
68 **
69 ** Each call to pikchr() uses a single instance of the Pik structure to
70 ** track its internal state.  The Pik structure lives for the duration
71 ** of the pikchr() call.
72 **
73 ** The input is a sequence of objects or "statements".  Each statement is
74 ** parsed into a PObj object.  These are stored on an extensible array
75 ** called PList.  All parameters to each PObj are computed as the
76 ** object is parsed.  (Hence, the parameters to a PObj may only refer
77 ** to prior statements.) Once the PObj is completely assembled, it is
78 ** added to the end of a PList and never changes thereafter - except,
79 ** PObj objects that are part of a "[...]" block might have their
80 ** absolute position shifted when the outer [...] block is positioned.
81 ** But apart from this repositioning, PObj objects are unchanged once
82 ** they are added to the list. The order of statements on a PList does
83 ** not change.
84 **
85 ** After all input has been parsed, the top-level PList is walked to
86 ** generate output.  Sub-lists resulting from [...] blocks are scanned
87 ** as they are encountered.  All input must be collected and parsed ahead
88 ** of output generation because the size and position of statements must be
89 ** known in order to compute a bounding box on the output.
90 **
91 ** Each PObj is on a "layer".  (The common case is that all PObj's are
92 ** on a single layer, but multiple layers are possible.)  A separate pass
93 ** is made through the list for each layer.
94 **
95 ** After all output is generated, the Pik object and all the PList
96 ** and PObj objects are deallocated and the generated output string is
97 ** returned.  Upon any error, the Pik.nErr flag is set, processing quickly
98 ** stops, and the stack unwinds.  No attempt is made to continue reading
99 ** input after an error.
100 **
101 ** Most statements begin with a class name like "box" or "arrow" or "move".
102 ** There is a class named "text" which is used for statements that begin
103 ** with a string literal.  You can also specify the "text" class.
104 ** A Sublist ("[...]") is a single object that contains a pointer to
105 ** its substatements, all gathered onto a separate PList object.
106 **
107 ** Variables go into PVar objects that form a linked list.
108 **
109 ** Each PObj has zero or one names.  Input constructs that attempt
110 ** to assign a new name from an older name, for example:
111 **
112 **      Abc:  Abc + (0.5cm, 0)
113 **
114 ** Statements like these generate a new "noop" object at the specified
115 ** place and with the given name. As place-names are searched by scanning
116 ** the list in reverse order, this has the effect of overriding the "Abc"
117 ** name when referenced by subsequent objects.
118 */
119 #include <stdio.h>
120 #include <stdlib.h>
121 #include <string.h>
122 #include <ctype.h>
123 #include <math.h>
124 #include <assert.h>
125 #define count(X) (sizeof(X)/sizeof(X[0]))
126 #ifndef M_PI
127 # define M_PI 3.1415926535897932385
128 #endif
129 
130 /* Tag intentionally unused parameters with this macro to prevent
131 ** compiler warnings with -Wextra */
132 #define UNUSED_PARAMETER(X)  (void)(X)
133 
134 typedef struct Pik Pik;          /* Complete parsing context */
135 typedef struct PToken PToken;    /* A single token */
136 typedef struct PObj PObj;        /* A single diagram object */
137 typedef struct PList PList;      /* A list of diagram objects */
138 typedef struct PClass PClass;    /* Description of statements types */
139 typedef double PNum;             /* Numeric value */
140 typedef struct PRel PRel;        /* Absolute or percentage value */
141 typedef struct PPoint PPoint;    /* A position in 2-D space */
142 typedef struct PVar PVar;        /* script-defined variable */
143 typedef struct PBox PBox;        /* A bounding box */
144 typedef struct PMacro PMacro;    /* A "define" macro */
145 
146 /* Compass points */
147 #define CP_N      1
148 #define CP_NE     2
149 #define CP_E      3
150 #define CP_SE     4
151 #define CP_S      5
152 #define CP_SW     6
153 #define CP_W      7
154 #define CP_NW     8
155 #define CP_C      9   /* .center or .c */
156 #define CP_END   10   /* .end */
157 #define CP_START 11   /* .start */
158 
159 /* Heading angles corresponding to compass points */
160 static const PNum pik_hdg_angle[] = {
161 /* none  */   0.0,
162   /* N  */    0.0,
163   /* NE */   45.0,
164   /* E  */   90.0,
165   /* SE */  135.0,
166   /* S  */  180.0,
167   /* SW */  225.0,
168   /* W  */  270.0,
169   /* NW */  315.0,
170   /* C  */    0.0,
171 };
172 
173 /* Built-in functions */
174 #define FN_ABS    0
175 #define FN_COS    1
176 #define FN_INT    2
177 #define FN_MAX    3
178 #define FN_MIN    4
179 #define FN_SIN    5
180 #define FN_SQRT   6
181 
182 /* Text position and style flags.  Stored in PToken.eCode so limited
183 ** to 15 bits. */
184 #define TP_LJUST   0x0001  /* left justify......          */
185 #define TP_RJUST   0x0002  /*            ...Right justify */
186 #define TP_JMASK   0x0003  /* Mask for justification bits */
187 #define TP_ABOVE2  0x0004  /* Position text way above PObj.ptAt */
188 #define TP_ABOVE   0x0008  /* Position text above PObj.ptAt */
189 #define TP_CENTER  0x0010  /* On the line */
190 #define TP_BELOW   0x0020  /* Position text below PObj.ptAt */
191 #define TP_BELOW2  0x0040  /* Position text way below PObj.ptAt */
192 #define TP_VMASK   0x007c  /* Mask for text positioning flags */
193 #define TP_BIG     0x0100  /* Larger font */
194 #define TP_SMALL   0x0200  /* Smaller font */
195 #define TP_XTRA    0x0400  /* Amplify TP_BIG or TP_SMALL */
196 #define TP_SZMASK  0x0700  /* Font size mask */
197 #define TP_ITALIC  0x1000  /* Italic font */
198 #define TP_BOLD    0x2000  /* Bold font */
199 #define TP_FMASK   0x3000  /* Mask for font style */
200 #define TP_ALIGN   0x4000  /* Rotate to align with the line */
201 
202 /* An object to hold a position in 2-D space */
203 struct PPoint {
204   PNum x, y;             /* X and Y coordinates */
205 };
206 static const PPoint cZeroPoint = {0.0,0.0};
207 
208 /* A bounding box */
209 struct PBox {
210   PPoint sw, ne;         /* Lower-left and top-right corners */
211 };
212 
213 /* An Absolute or a relative distance.  The absolute distance
214 ** is stored in rAbs and the relative distance is stored in rRel.
215 ** Usually, one or the other will be 0.0.  When using a PRel to
216 ** update an existing value, the computation is usually something
217 ** like this:
218 **
219 **          value = PRel.rAbs + value*PRel.rRel
220 **
221 */
222 struct PRel {
223   PNum rAbs;            /* Absolute value */
224   PNum rRel;            /* Value relative to current value */
225 };
226 
227 /* A variable created by the ID = EXPR construct of the PIKCHR script
228 **
229 ** PIKCHR (and PIC) scripts do not use many varaibles, so it is reasonable
230 ** to store them all on a linked list.
231 */
232 struct PVar {
233   const char *zName;       /* Name of the variable */
234   PNum val;                /* Value of the variable */
235   PVar *pNext;             /* Next variable in a list of them all */
236 };
237 
238 /* A single token in the parser input stream
239 */
240 struct PToken {
241   const char *z;             /* Pointer to the token text */
242   unsigned int n;            /* Length of the token in bytes */
243   short int eCode;           /* Auxiliary code */
244   unsigned char eType;       /* The numeric parser code */
245   unsigned char eEdge;       /* Corner value for corner keywords */
246 };
247 
248 /* Return negative, zero, or positive if pToken is less than, equal to
249 ** or greater than the zero-terminated string z[]
250 */
pik_token_eq(PToken * pToken,const char * z)251 static int pik_token_eq(PToken *pToken, const char *z){
252   int c = strncmp(pToken->z,z,pToken->n);
253   if( c==0 && z[pToken->n]!=0 ) c = -1;
254   return c;
255 }
256 
257 /* Extra token types not generated by LEMON but needed by the
258 ** tokenizer
259 */
260 #define T_PARAMETER  253     /* $1, $2, ..., $9 */
261 #define T_WHITESPACE 254     /* Whitespace of comments */
262 #define T_ERROR      255     /* Any text that is not a valid token */
263 
264 /* Directions of movement */
265 #define DIR_RIGHT     0
266 #define DIR_DOWN      1
267 #define DIR_LEFT      2
268 #define DIR_UP        3
269 #define ValidDir(X)     ((X)>=0 && (X)<=3)
270 #define IsUpDown(X)     (((X)&1)==1)
271 #define IsLeftRight(X)  (((X)&1)==0)
272 
273 /* Bitmask for the various attributes for PObj.  These bits are
274 ** collected in PObj.mProp and PObj.mCalc to check for constraint
275 ** errors. */
276 #define A_WIDTH         0x0001
277 #define A_HEIGHT        0x0002
278 #define A_RADIUS        0x0004
279 #define A_THICKNESS     0x0008
280 #define A_DASHED        0x0010 /* Includes "dotted" */
281 #define A_FILL          0x0020
282 #define A_COLOR         0x0040
283 #define A_ARROW         0x0080
284 #define A_FROM          0x0100
285 #define A_CW            0x0200
286 #define A_AT            0x0400
287 #define A_TO            0x0800 /* one or more movement attributes */
288 #define A_FIT           0x1000
289 
290 
291 /* A single graphics object */
292 struct PObj {
293   const PClass *type;      /* Object type or class */
294   PToken errTok;           /* Reference token for error messages */
295   PPoint ptAt;             /* Reference point for the object */
296   PPoint ptEnter, ptExit;  /* Entry and exit points */
297   PList *pSublist;         /* Substructure for [...] objects */
298   char *zName;             /* Name assigned to this statement */
299   PNum w;                  /* "width" property */
300   PNum h;                  /* "height" property */
301   PNum rad;                /* "radius" property */
302   PNum sw;                 /* "thickness" property. (Mnemonic: "stroke width")*/
303   PNum dotted;             /* "dotted" property.   <=0.0 for off */
304   PNum dashed;             /* "dashed" property.   <=0.0 for off */
305   PNum fill;               /* "fill" property.  Negative for off */
306   PNum color;              /* "color" property */
307   PPoint with;             /* Position constraint from WITH clause */
308   char eWith;              /* Type of heading point on WITH clause */
309   char cw;                 /* True for clockwise arc */
310   char larrow;             /* Arrow at beginning (<- or <->) */
311   char rarrow;             /* Arrow at end  (-> or <->) */
312   char bClose;             /* True if "close" is seen */
313   char bChop;              /* True if "chop" is seen */
314   unsigned char nTxt;      /* Number of text values */
315   unsigned mProp;          /* Masks of properties set so far */
316   unsigned mCalc;          /* Values computed from other constraints */
317   PToken aTxt[5];          /* Text with .eCode holding TP flags */
318   int iLayer;              /* Rendering order */
319   int inDir, outDir;       /* Entry and exit directions */
320   int nPath;               /* Number of path points */
321   PPoint *aPath;           /* Array of path points */
322   PBox bbox;               /* Bounding box */
323 };
324 
325 /* A list of graphics objects */
326 struct PList {
327   int n;          /* Number of statements in the list */
328   int nAlloc;     /* Allocated slots in a[] */
329   PObj **a;       /* Pointers to individual objects */
330 };
331 
332 /* A macro definition */
333 struct PMacro {
334   PMacro *pNext;       /* Next in the list */
335   PToken macroName;    /* Name of the macro */
336   PToken macroBody;    /* Body of the macro */
337   int inUse;           /* Do not allow recursion */
338 };
339 
340 /* Each call to the pikchr() subroutine uses an instance of the following
341 ** object to pass around context to all of its subroutines.
342 */
343 struct Pik {
344   unsigned nErr;           /* Number of errors seen */
345   PToken sIn;              /* Input Pikchr-language text */
346   char *zOut;              /* Result accumulates here */
347   unsigned int nOut;       /* Bytes written to zOut[] so far */
348   unsigned int nOutAlloc;  /* Space allocated to zOut[] */
349   unsigned char eDir;      /* Current direction */
350   unsigned int mFlags;     /* Flags passed to pikchr() */
351   PObj *cur;               /* Object under construction */
352   PList *list;             /* Object list under construction */
353   PMacro *pMacros;         /* List of all defined macros */
354   PVar *pVar;              /* Application-defined variables */
355   PBox bbox;               /* Bounding box around all statements */
356                            /* Cache of layout values.  <=0.0 for unknown... */
357   PNum rScale;                 /* Multiply to convert inches to pixels */
358   PNum fontScale;              /* Scale fonts by this percent */
359   PNum charWidth;              /* Character width */
360   PNum charHeight;             /* Character height */
361   PNum wArrow;                 /* Width of arrowhead at the fat end */
362   PNum hArrow;                 /* Ht of arrowhead - dist from tip to fat end */
363   char bLayoutVars;            /* True if cache is valid */
364   char thenFlag;           /* True if "then" seen */
365   char samePath;           /* aTPath copied by "same" */
366   const char *zClass;      /* Class name for the <svg> */
367   int wSVG, hSVG;          /* Width and height of the <svg> */
368   int fgcolor;             /* foreground color value, or -1 for none */
369   int bgcolor;             /* background color value, or -1 for none */
370   /* Paths for lines are constructed here first, then transferred into
371   ** the PObj object at the end: */
372   int nTPath;              /* Number of entries on aTPath[] */
373   int mTPath;              /* For last entry, 1: x set,  2: y set */
374   PPoint aTPath[1000];     /* Path under construction */
375   /* Error contexts */
376   unsigned int nCtx;       /* Number of error contexts */
377   PToken aCtx[10];         /* Nested error contexts */
378 };
379 
380 /* Include PIKCHR_PLAINTEXT_ERRORS among the bits of mFlags on the 3rd
381 ** argument to pikchr() in order to cause error message text to come out
382 ** as text/plain instead of as text/html
383 */
384 #define PIKCHR_PLAINTEXT_ERRORS 0x0001
385 
386 /* Include PIKCHR_DARK_MODE among the mFlag bits to invert colors.
387 */
388 #define PIKCHR_DARK_MODE        0x0002
389 
390 /*
391 ** The behavior of an object class is defined by an instance of
392 ** this structure. This is the "virtual method" table.
393 */
394 struct PClass {
395   const char *zName;                     /* Name of class */
396   char isLine;                           /* True if a line class */
397   char eJust;                            /* Use box-style text justification */
398   void (*xInit)(Pik*,PObj*);              /* Initializer */
399   void (*xNumProp)(Pik*,PObj*,PToken*);   /* Value change notification */
400   void (*xCheck)(Pik*,PObj*);             /* Checks to do after parsing */
401   PPoint (*xChop)(Pik*,PObj*,PPoint*);    /* Chopper */
402   PPoint (*xOffset)(Pik*,PObj*,int);      /* Offset from .c to edge point */
403   void (*xFit)(Pik*,PObj*,PNum w,PNum h); /* Size to fit text */
404   void (*xRender)(Pik*,PObj*);            /* Render */
405 };
406 
407 
408 /* Forward declarations */
409 static void pik_append(Pik*, const char*,int);
410 static void pik_append_text(Pik*,const char*,int,int);
411 static void pik_append_num(Pik*,const char*,PNum);
412 static void pik_append_point(Pik*,const char*,PPoint*);
413 static void pik_append_x(Pik*,const char*,PNum,const char*);
414 static void pik_append_y(Pik*,const char*,PNum,const char*);
415 static void pik_append_xy(Pik*,const char*,PNum,PNum);
416 static void pik_append_dis(Pik*,const char*,PNum,const char*);
417 static void pik_append_arc(Pik*,PNum,PNum,PNum,PNum);
418 static void pik_append_clr(Pik*,const char*,PNum,const char*,int);
419 static void pik_append_style(Pik*,PObj*,int);
420 static void pik_append_txt(Pik*,PObj*, PBox*);
421 static void pik_draw_arrowhead(Pik*,PPoint*pFrom,PPoint*pTo,PObj*);
422 static void pik_chop(PPoint*pFrom,PPoint*pTo,PNum);
423 static void pik_error(Pik*,PToken*,const char*);
424 static void pik_elist_free(Pik*,PList*);
425 static void pik_elem_free(Pik*,PObj*);
426 static void pik_render(Pik*,PList*);
427 static PList *pik_elist_append(Pik*,PList*,PObj*);
428 static PObj *pik_elem_new(Pik*,PToken*,PToken*,PList*);
429 static void pik_set_direction(Pik*,int);
430 static void pik_elem_setname(Pik*,PObj*,PToken*);
431 static void pik_set_var(Pik*,PToken*,PNum,PToken*);
432 static PNum pik_value(Pik*,const char*,int,int*);
433 static PNum pik_lookup_color(Pik*,PToken*);
434 static PNum pik_get_var(Pik*,PToken*);
435 static PNum pik_atof(PToken*);
436 static void pik_after_adding_attributes(Pik*,PObj*);
437 static void pik_elem_move(PObj*,PNum dx, PNum dy);
438 static void pik_elist_move(PList*,PNum dx, PNum dy);
439 static void pik_set_numprop(Pik*,PToken*,PRel*);
440 static void pik_set_clrprop(Pik*,PToken*,PNum);
441 static void pik_set_dashed(Pik*,PToken*,PNum*);
442 static void pik_then(Pik*,PToken*,PObj*);
443 static void pik_add_direction(Pik*,PToken*,PRel*);
444 static void pik_move_hdg(Pik*,PRel*,PToken*,PNum,PToken*,PToken*);
445 static void pik_evenwith(Pik*,PToken*,PPoint*);
446 static void pik_set_from(Pik*,PObj*,PToken*,PPoint*);
447 static void pik_add_to(Pik*,PObj*,PToken*,PPoint*);
448 static void pik_close_path(Pik*,PToken*);
449 static void pik_set_at(Pik*,PToken*,PPoint*,PToken*);
450 static short int pik_nth_value(Pik*,PToken*);
451 static PObj *pik_find_nth(Pik*,PObj*,PToken*);
452 static PObj *pik_find_byname(Pik*,PObj*,PToken*);
453 static PPoint pik_place_of_elem(Pik*,PObj*,PToken*);
454 static int pik_bbox_isempty(PBox*);
455 static void pik_bbox_init(PBox*);
456 static void pik_bbox_addbox(PBox*,PBox*);
457 static void pik_bbox_add_xy(PBox*,PNum,PNum);
458 static void pik_bbox_addellipse(PBox*,PNum x,PNum y,PNum rx,PNum ry);
459 static void pik_add_txt(Pik*,PToken*,int);
460 static int pik_text_length(const PToken *pToken);
461 static void pik_size_to_fit(Pik*,PToken*,int);
462 static int pik_text_position(int,PToken*);
463 static PNum pik_property_of(PObj*,PToken*);
464 static PNum pik_func(Pik*,PToken*,PNum,PNum);
465 static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2);
466 static PPoint pik_position_at_angle(PNum dist, PNum r, PPoint pt);
467 static PPoint pik_position_at_hdg(PNum dist, PToken *pD, PPoint pt);
468 static void pik_same(Pik *p, PObj*, PToken*);
469 static PPoint pik_nth_vertex(Pik *p, PToken *pNth, PToken *pErr, PObj *pObj);
470 static PToken pik_next_semantic_token(PToken *pThis);
471 static void pik_compute_layout_settings(Pik*);
472 static void pik_behind(Pik*,PObj*);
473 static PObj *pik_assert(Pik*,PNum,PToken*,PNum);
474 static PObj *pik_position_assert(Pik*,PPoint*,PToken*,PPoint*);
475 static PNum pik_dist(PPoint*,PPoint*);
476 static void pik_add_macro(Pik*,PToken *pId,PToken *pCode);
477 
478 
479 #line 505 "pikchr.c"
480 /**************** End of %include directives **********************************/
481 /* These constants specify the various numeric values for terminal symbols.
482 ***************** Begin token definitions *************************************/
483 #ifndef T_ID
484 #define T_ID                              1
485 #define T_EDGEPT                          2
486 #define T_OF                              3
487 #define T_PLUS                            4
488 #define T_MINUS                           5
489 #define T_STAR                            6
490 #define T_SLASH                           7
491 #define T_PERCENT                         8
492 #define T_UMINUS                          9
493 #define T_EOL                            10
494 #define T_ASSIGN                         11
495 #define T_PLACENAME                      12
496 #define T_COLON                          13
497 #define T_ASSERT                         14
498 #define T_LP                             15
499 #define T_EQ                             16
500 #define T_RP                             17
501 #define T_DEFINE                         18
502 #define T_CODEBLOCK                      19
503 #define T_FILL                           20
504 #define T_COLOR                          21
505 #define T_THICKNESS                      22
506 #define T_PRINT                          23
507 #define T_STRING                         24
508 #define T_COMMA                          25
509 #define T_CLASSNAME                      26
510 #define T_LB                             27
511 #define T_RB                             28
512 #define T_UP                             29
513 #define T_DOWN                           30
514 #define T_LEFT                           31
515 #define T_RIGHT                          32
516 #define T_CLOSE                          33
517 #define T_CHOP                           34
518 #define T_FROM                           35
519 #define T_TO                             36
520 #define T_THEN                           37
521 #define T_HEADING                        38
522 #define T_GO                             39
523 #define T_AT                             40
524 #define T_WITH                           41
525 #define T_SAME                           42
526 #define T_AS                             43
527 #define T_FIT                            44
528 #define T_BEHIND                         45
529 #define T_UNTIL                          46
530 #define T_EVEN                           47
531 #define T_DOT_E                          48
532 #define T_HEIGHT                         49
533 #define T_WIDTH                          50
534 #define T_RADIUS                         51
535 #define T_DIAMETER                       52
536 #define T_DOTTED                         53
537 #define T_DASHED                         54
538 #define T_CW                             55
539 #define T_CCW                            56
540 #define T_LARROW                         57
541 #define T_RARROW                         58
542 #define T_LRARROW                        59
543 #define T_INVIS                          60
544 #define T_THICK                          61
545 #define T_THIN                           62
546 #define T_SOLID                          63
547 #define T_CENTER                         64
548 #define T_LJUST                          65
549 #define T_RJUST                          66
550 #define T_ABOVE                          67
551 #define T_BELOW                          68
552 #define T_ITALIC                         69
553 #define T_BOLD                           70
554 #define T_ALIGNED                        71
555 #define T_BIG                            72
556 #define T_SMALL                          73
557 #define T_AND                            74
558 #define T_LT                             75
559 #define T_GT                             76
560 #define T_ON                             77
561 #define T_WAY                            78
562 #define T_BETWEEN                        79
563 #define T_THE                            80
564 #define T_NTH                            81
565 #define T_VERTEX                         82
566 #define T_TOP                            83
567 #define T_BOTTOM                         84
568 #define T_START                          85
569 #define T_END                            86
570 #define T_IN                             87
571 #define T_THIS                           88
572 #define T_DOT_U                          89
573 #define T_LAST                           90
574 #define T_NUMBER                         91
575 #define T_FUNC1                          92
576 #define T_FUNC2                          93
577 #define T_DIST                           94
578 #define T_DOT_XY                         95
579 #define T_X                              96
580 #define T_Y                              97
581 #define T_DOT_L                          98
582 #endif
583 /**************** End token definitions ***************************************/
584 
585 /* The next sections is a series of control #defines.
586 ** various aspects of the generated parser.
587 **    YYCODETYPE         is the data type used to store the integer codes
588 **                       that represent terminal and non-terminal symbols.
589 **                       "unsigned char" is used if there are fewer than
590 **                       256 symbols.  Larger types otherwise.
591 **    YYNOCODE           is a number of type YYCODETYPE that is not used for
592 **                       any terminal or nonterminal symbol.
593 **    YYFALLBACK         If defined, this indicates that one or more tokens
594 **                       (also known as: "terminal symbols") have fall-back
595 **                       values which should be used if the original symbol
596 **                       would not parse.  This permits keywords to sometimes
597 **                       be used as identifiers, for example.
598 **    YYACTIONTYPE       is the data type used for "action codes" - numbers
599 **                       that indicate what to do in response to the next
600 **                       token.
601 **    pik_parserTOKENTYPE     is the data type used for minor type for terminal
602 **                       symbols.  Background: A "minor type" is a semantic
603 **                       value associated with a terminal or non-terminal
604 **                       symbols.  For example, for an "ID" terminal symbol,
605 **                       the minor type might be the name of the identifier.
606 **                       Each non-terminal can have a different minor type.
607 **                       Terminal symbols all have the same minor type, though.
608 **                       This macros defines the minor type for terminal
609 **                       symbols.
610 **    YYMINORTYPE        is the data type used for all minor types.
611 **                       This is typically a union of many types, one of
612 **                       which is pik_parserTOKENTYPE.  The entry in the union
613 **                       for terminal symbols is called "yy0".
614 **    YYSTACKDEPTH       is the maximum depth of the parser's stack.  If
615 **                       zero the stack is dynamically sized using realloc()
616 **    pik_parserARG_SDECL     A static variable declaration for the %extra_argument
617 **    pik_parserARG_PDECL     A parameter declaration for the %extra_argument
618 **    pik_parserARG_PARAM     Code to pass %extra_argument as a subroutine parameter
619 **    pik_parserARG_STORE     Code to store %extra_argument into yypParser
620 **    pik_parserARG_FETCH     Code to extract %extra_argument from yypParser
621 **    pik_parserCTX_*         As pik_parserARG_ except for %extra_context
622 **    YYERRORSYMBOL      is the code number of the error symbol.  If not
623 **                       defined, then do no error processing.
624 **    YYNSTATE           the combined number of states.
625 **    YYNRULE            the number of rules in the grammar
626 **    YYNTOKEN           Number of terminal symbols
627 **    YY_MAX_SHIFT       Maximum value for shift actions
628 **    YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions
629 **    YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions
630 **    YY_ERROR_ACTION    The yy_action[] code for syntax error
631 **    YY_ACCEPT_ACTION   The yy_action[] code for accept
632 **    YY_NO_ACTION       The yy_action[] code for no-op
633 **    YY_MIN_REDUCE      Minimum value for reduce actions
634 **    YY_MAX_REDUCE      Maximum value for reduce actions
635 */
636 #ifndef INTERFACE
637 # define INTERFACE 1
638 #endif
639 /************* Begin control #defines *****************************************/
640 #define YYCODETYPE unsigned char
641 #define YYNOCODE 135
642 #define YYACTIONTYPE unsigned short int
643 #define pik_parserTOKENTYPE PToken
644 typedef union {
645   int yyinit;
646   pik_parserTOKENTYPE yy0;
647   PRel yy10;
648   PObj* yy36;
649   PPoint yy79;
650   PNum yy153;
651   short int yy164;
652   PList* yy227;
653 } YYMINORTYPE;
654 #ifndef YYSTACKDEPTH
655 #define YYSTACKDEPTH 100
656 #endif
657 #define pik_parserARG_SDECL
658 #define pik_parserARG_PDECL
659 #define pik_parserARG_PARAM
660 #define pik_parserARG_FETCH
661 #define pik_parserARG_STORE
662 #define pik_parserCTX_SDECL Pik *p;
663 #define pik_parserCTX_PDECL ,Pik *p
664 #define pik_parserCTX_PARAM ,p
665 #define pik_parserCTX_FETCH Pik *p=yypParser->p;
666 #define pik_parserCTX_STORE yypParser->p=p;
667 #define YYFALLBACK 1
668 #define YYNSTATE             164
669 #define YYNRULE              156
670 #define YYNRULE_WITH_ACTION  116
671 #define YYNTOKEN             99
672 #define YY_MAX_SHIFT         163
673 #define YY_MIN_SHIFTREDUCE   287
674 #define YY_MAX_SHIFTREDUCE   442
675 #define YY_ERROR_ACTION      443
676 #define YY_ACCEPT_ACTION     444
677 #define YY_NO_ACTION         445
678 #define YY_MIN_REDUCE        446
679 #define YY_MAX_REDUCE        601
680 /************* End control #defines *******************************************/
681 #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])))
682 
683 /* Define the yytestcase() macro to be a no-op if is not already defined
684 ** otherwise.
685 **
686 ** Applications can choose to define yytestcase() in the %include section
687 ** to a macro that can assist in verifying code coverage.  For production
688 ** code the yytestcase() macro should be turned off.  But it is useful
689 ** for testing.
690 */
691 #ifndef yytestcase
692 # define yytestcase(X)
693 #endif
694 
695 
696 /* Next are the tables used to determine what action to take based on the
697 ** current state and lookahead token.  These tables are used to implement
698 ** functions that take a state number and lookahead value and return an
699 ** action integer.
700 **
701 ** Suppose the action integer is N.  Then the action is determined as
702 ** follows
703 **
704 **   0 <= N <= YY_MAX_SHIFT             Shift N.  That is, push the lookahead
705 **                                      token onto the stack and goto state N.
706 **
707 **   N between YY_MIN_SHIFTREDUCE       Shift to an arbitrary state then
708 **     and YY_MAX_SHIFTREDUCE           reduce by rule N-YY_MIN_SHIFTREDUCE.
709 **
710 **   N == YY_ERROR_ACTION               A syntax error has occurred.
711 **
712 **   N == YY_ACCEPT_ACTION              The parser accepts its input.
713 **
714 **   N == YY_NO_ACTION                  No such action.  Denotes unused
715 **                                      slots in the yy_action[] table.
716 **
717 **   N between YY_MIN_REDUCE            Reduce by rule N-YY_MIN_REDUCE
718 **     and YY_MAX_REDUCE
719 **
720 ** The action table is constructed as a single large table named yy_action[].
721 ** Given state S and lookahead X, the action is computed as either:
722 **
723 **    (A)   N = yy_action[ yy_shift_ofst[S] + X ]
724 **    (B)   N = yy_default[S]
725 **
726 ** The (A) formula is preferred.  The B formula is used instead if
727 ** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X.
728 **
729 ** The formulas above are for computing the action when the lookahead is
730 ** a terminal symbol.  If the lookahead is a non-terminal (as occurs after
731 ** a reduce action) then the yy_reduce_ofst[] array is used in place of
732 ** the yy_shift_ofst[] array.
733 **
734 ** The following are the tables generated in this section:
735 **
736 **  yy_action[]        A single table containing all actions.
737 **  yy_lookahead[]     A table containing the lookahead for each entry in
738 **                     yy_action.  Used to detect hash collisions.
739 **  yy_shift_ofst[]    For each state, the offset into yy_action for
740 **                     shifting terminals.
741 **  yy_reduce_ofst[]   For each state, the offset into yy_action for
742 **                     shifting non-terminals after a reduce.
743 **  yy_default[]       Default action for each state.
744 **
745 *********** Begin parsing tables **********************************************/
746 #define YY_ACTTAB_COUNT (1303)
747 static const YYACTIONTYPE yy_action[] = {
748  /*     0 */   575,  495,  161,  119,   25,  452,   29,   74,  129,  148,
749  /*    10 */   575,  492,  161,  119,  453,  113,  120,  161,  119,  530,
750  /*    20 */   427,  428,  339,  559,   81,   30,  560,  561,  575,   64,
751  /*    30 */    63,   62,   61,  322,  323,    9,    8,   33,  149,   32,
752  /*    40 */     7,   71,  127,   38,  335,   66,   48,   37,   28,  339,
753  /*    50 */   339,  339,  339,  425,  426,  340,  341,  342,  343,  344,
754  /*    60 */   345,  346,  347,  348,  474,  528,  161,  119,  577,   77,
755  /*    70 */   577,   73,  376,  148,  474,  533,  161,  119,  112,  113,
756  /*    80 */   120,  161,  119,  128,  427,  428,  339,  357,   81,  531,
757  /*    90 */   161,  119,  474,   36,  330,   13,  306,  322,  323,    9,
758  /*   100 */     8,   33,  149,   32,    7,   71,  127,  328,  335,   66,
759  /*   110 */   579,  310,   31,  339,  339,  339,  339,  425,  426,  340,
760  /*   120 */   341,  342,  343,  344,  345,  346,  347,  348,  394,  435,
761  /*   130 */    46,   59,   60,   64,   63,   62,   61,   54,   51,  376,
762  /*   140 */    69,  108,    2,   47,  403,   83,  297,  435,  375,   84,
763  /*   150 */   117,   80,   35,  308,   79,  133,  122,  126,  441,  440,
764  /*   160 */   299,  123,    3,  404,  405,  406,  408,   80,  298,  308,
765  /*   170 */    79,    4,  411,  412,  413,  414,  441,  440,  350,  350,
766  /*   180 */   350,  350,  350,  350,  350,  350,  350,  350,   62,   61,
767  /*   190 */    67,  434,    1,   75,  378,  158,   74,   76,  148,  411,
768  /*   200 */   412,  413,  414,  124,  113,  120,  161,  119,  106,  434,
769  /*   210 */   436,  437,  438,  439,    5,  375,    6,  117,  393,  155,
770  /*   220 */   154,  153,  394,  435,   69,   59,   60,  149,  436,  437,
771  /*   230 */   438,  439,  535,  376,  398,  399,    2,  424,  427,  428,
772  /*   240 */   339,  156,  156,  156,  423,  394,  435,   65,   59,   60,
773  /*   250 */   162,  131,  441,  440,  397,   72,  376,  148,  118,    2,
774  /*   260 */   380,  157,  125,  113,  120,  161,  119,  339,  339,  339,
775  /*   270 */   339,  425,  426,  535,   11,  441,  440,  394,  356,  535,
776  /*   280 */    59,   60,  535,  379,  159,  434,  149,   12,  102,  446,
777  /*   290 */   432,   42,  138,   14,  435,  139,  301,  302,  303,   36,
778  /*   300 */   305,  430,  106,   16,  436,  437,  438,  439,  434,  375,
779  /*   310 */    18,  117,  393,  155,  154,  153,   44,  142,  140,   64,
780  /*   320 */    63,   62,   61,  441,  440,  106,   19,  436,  437,  438,
781  /*   330 */   439,   45,  375,   20,  117,  393,  155,  154,  153,   68,
782  /*   340 */    55,  114,   64,   63,   62,   61,  147,  146,  394,  473,
783  /*   350 */   359,   59,   60,   43,   23,  391,  434,  106,   26,  376,
784  /*   360 */    57,   58,   42,   49,  375,  392,  117,  393,  155,  154,
785  /*   370 */   153,   64,   63,   62,   61,  436,  437,  438,  439,  384,
786  /*   380 */   382,  383,   22,   21,  377,  473,  160,   70,   39,  445,
787  /*   390 */    24,  445,  145,  141,  431,  142,  140,   64,   63,   62,
788  /*   400 */    61,  394,   15,  445,   59,   60,   64,   63,   62,   61,
789  /*   410 */   391,  445,  376,  445,  445,   42,  445,  445,   55,  391,
790  /*   420 */   156,  156,  156,  445,  147,  146,  445,   52,  106,  445,
791  /*   430 */   445,   43,  445,  445,  445,  375,  445,  117,  393,  155,
792  /*   440 */   154,  153,  445,  394,  143,  445,   59,   60,   64,   63,
793  /*   450 */    62,   61,  313,  445,  376,  378,  158,   42,  445,  445,
794  /*   460 */    22,   21,  121,  447,  454,   29,  445,  445,   24,  450,
795  /*   470 */   145,  141,  431,  142,  140,   64,   63,   62,   61,  445,
796  /*   480 */   163,  106,  445,  445,  444,   27,  445,  445,  375,  445,
797  /*   490 */   117,  393,  155,  154,  153,  445,   55,   74,  445,  148,
798  /*   500 */   445,  445,  147,  146,  497,  113,  120,  161,  119,   43,
799  /*   510 */   445,  394,  445,  445,   59,   60,  445,  445,  445,  118,
800  /*   520 */   445,  445,  376,  106,  445,   42,  445,  445,  149,  445,
801  /*   530 */   375,  445,  117,  393,  155,  154,  153,  445,   22,   21,
802  /*   540 */   394,  144,  445,   59,   60,  445,   24,  445,  145,  141,
803  /*   550 */   431,  376,  445,  445,   42,  445,  132,  130,  394,  445,
804  /*   560 */   445,   59,   60,  109,  447,  454,   29,  445,  445,  376,
805  /*   570 */   450,  445,   42,  445,  394,  445,  445,   59,   60,  445,
806  /*   580 */   445,  163,  445,  445,  445,  102,   27,  445,   42,  445,
807  /*   590 */   445,  106,  445,   64,   63,   62,   61,  445,  375,  445,
808  /*   600 */   117,  393,  155,  154,  153,  394,  355,  445,   59,   60,
809  /*   610 */   445,  445,  445,  445,  445,   74,  376,  148,  445,   40,
810  /*   620 */   106,  445,  496,  113,  120,  161,  119,  375,  445,  117,
811  /*   630 */   393,  155,  154,  153,  445,  448,  454,   29,  106,  445,
812  /*   640 */   445,  450,  445,  445,  445,  375,  149,  117,  393,  155,
813  /*   650 */   154,  153,  163,  445,  106,  445,  445,   27,  445,  445,
814  /*   660 */   445,  375,  445,  117,  393,  155,  154,  153,  394,  445,
815  /*   670 */   445,   59,   60,   64,   63,   62,   61,  445,  445,  376,
816  /*   680 */   445,  445,   41,  445,  445,  106,  354,   64,   63,   62,
817  /*   690 */    61,  445,  375,  445,  117,  393,  155,  154,  153,  445,
818  /*   700 */   445,  445,   74,  445,  148,  445,   88,  445,  445,  490,
819  /*   710 */   113,  120,  161,  119,  445,  120,  161,  119,   17,   74,
820  /*   720 */   445,  148,  110,  110,  445,  445,  484,  113,  120,  161,
821  /*   730 */   119,  445,  445,  149,   74,  445,  148,  152,  445,  445,
822  /*   740 */   445,  483,  113,  120,  161,  119,  445,  445,  106,  445,
823  /*   750 */   149,  445,  445,  107,  445,  375,  445,  117,  393,  155,
824  /*   760 */   154,  153,  120,  161,  119,  149,  478,   74,  445,  148,
825  /*   770 */   445,   88,  445,  445,  480,  113,  120,  161,  119,  445,
826  /*   780 */   120,  161,  119,   74,  152,  148,   10,  479,  479,  445,
827  /*   790 */   134,  113,  120,  161,  119,  445,  445,  445,  149,   74,
828  /*   800 */   445,  148,  152,  445,  445,  445,  517,  113,  120,  161,
829  /*   810 */   119,  445,  445,   74,  149,  148,  445,  445,  445,  445,
830  /*   820 */   137,  113,  120,  161,  119,   74,  445,  148,  445,  445,
831  /*   830 */   149,  445,  525,  113,  120,  161,  119,  445,   74,  445,
832  /*   840 */   148,  445,  445,  445,  149,  527,  113,  120,  161,  119,
833  /*   850 */   445,  445,   74,  445,  148,  445,  149,  445,  445,  524,
834  /*   860 */   113,  120,  161,  119,   74,  445,  148,  445,  445,  149,
835  /*   870 */   445,  526,  113,  120,  161,  119,  445,  445,   74,  445,
836  /*   880 */   148,  445,   88,  149,  445,  523,  113,  120,  161,  119,
837  /*   890 */   445,  120,  161,  119,   74,  149,  148,   85,  111,  111,
838  /*   900 */   445,  522,  113,  120,  161,  119,  120,  161,  119,  149,
839  /*   910 */    74,  445,  148,  152,  445,  445,  445,  521,  113,  120,
840  /*   920 */   161,  119,  445,  445,   74,  149,  148,  445,  152,  445,
841  /*   930 */   445,  520,  113,  120,  161,  119,   74,  445,  148,  445,
842  /*   940 */   445,  149,  445,  519,  113,  120,  161,  119,  445,   74,
843  /*   950 */   445,  148,  445,  445,  445,  149,  150,  113,  120,  161,
844  /*   960 */   119,  445,  445,   74,  445,  148,  445,  149,  445,  445,
845  /*   970 */   151,  113,  120,  161,  119,   74,  445,  148,  445,  445,
846  /*   980 */   149,  445,  136,  113,  120,  161,  119,  445,  445,   74,
847  /*   990 */   445,  148,  107,  445,  149,  445,  135,  113,  120,  161,
848  /*  1000 */   119,  120,  161,  119,  445,  463,  149,  445,   88,  445,
849  /*  1010 */   445,  445,   78,   78,  445,  445,  107,  120,  161,  119,
850  /*  1020 */   149,  445,  445,  152,   82,  120,  161,  119,  445,  463,
851  /*  1030 */   445,  466,   86,   34,  445,   88,  445,  569,  445,  152,
852  /*  1040 */   445,  120,  161,  119,  120,  161,  119,  152,  107,  445,
853  /*  1050 */   445,  475,   64,   63,   62,   61,  445,  120,  161,  119,
854  /*  1060 */    98,  451,  445,  152,   89,  396,  152,   90,  445,  120,
855  /*  1070 */   161,  119,  445,  120,  161,  119,  120,  161,  119,  152,
856  /*  1080 */   445,   64,   63,   62,   61,  445,  445,  445,  445,  445,
857  /*  1090 */    87,  152,  445,   99,  395,  152,  100,  445,  152,  120,
858  /*  1100 */   161,  119,  120,  161,  119,  120,  161,  119,  445,  101,
859  /*  1110 */    64,   63,   62,   61,  445,  445,  445,  445,  120,  161,
860  /*  1120 */   119,  152,   91,  391,  152,  445,  445,  152,  103,  445,
861  /*  1130 */   445,  120,  161,  119,  445,   92,  445,  120,  161,  119,
862  /*  1140 */   152,   93,  445,  445,  120,  161,  119,  104,  445,  445,
863  /*  1150 */   120,  161,  119,  152,  445,  445,  120,  161,  119,  152,
864  /*  1160 */   445,  445,  445,  445,   94,  445,  152,  445,  445,  445,
865  /*  1170 */   105,  445,  152,  120,  161,  119,  445,   95,  152,  120,
866  /*  1180 */   161,  119,   96,  445,  445,  445,  120,  161,  119,  445,
867  /*  1190 */   445,  120,  161,  119,   97,  152,  445,  445,  445,  445,
868  /*  1200 */   549,  152,  445,  120,  161,  119,  548,  445,  152,  120,
869  /*  1210 */   161,  119,  445,  152,  445,  120,  161,  119,  445,  445,
870  /*  1220 */   445,  445,  445,  547,  445,  152,  445,  445,  445,  445,
871  /*  1230 */   445,  152,  120,  161,  119,  546,  445,  152,  445,  115,
872  /*  1240 */   445,  445,  116,  445,  120,  161,  119,  445,  120,  161,
873  /*  1250 */   119,  120,  161,  119,  152,   64,   63,   62,   61,   64,
874  /*  1260 */    63,   62,   61,  445,  445,  445,  152,  445,  445,  445,
875  /*  1270 */   152,  445,  445,  152,  445,  445,   50,  445,  445,  445,
876  /*  1280 */    53,   64,   63,   62,   61,  445,  445,  445,  445,  445,
877  /*  1290 */   445,  445,  445,  445,  445,  445,  445,  445,  445,  445,
878  /*  1300 */   445,  445,   56,
879 };
880 static const YYCODETYPE yy_lookahead[] = {
881  /*     0 */     0,  112,  113,  114,  133,  101,  102,  103,  105,  105,
882  /*    10 */    10,  112,  113,  114,  110,  111,  112,  113,  114,  105,
883  /*    20 */    20,   21,   22,  104,   24,  125,  107,  108,   28,    4,
884  /*    30 */     5,    6,    7,   33,   34,   35,   36,   37,  134,   39,
885  /*    40 */    40,   41,   42,  104,   44,   45,  107,  108,  106,   49,
886  /*    50 */    50,   51,   52,   53,   54,   55,   56,   57,   58,   59,
887  /*    60 */    60,   61,   62,   63,    0,  112,  113,  114,  129,  130,
888  /*    70 */   131,  103,   12,  105,   10,  112,  113,  114,  110,  111,
889  /*    80 */   112,  113,  114,  105,   20,   21,   22,   17,   24,  112,
890  /*    90 */   113,  114,   28,   10,    2,   25,   25,   33,   34,   35,
891  /*   100 */    36,   37,  134,   39,   40,   41,   42,    2,   44,   45,
892  /*   110 */   132,   28,  127,   49,   50,   51,   52,   53,   54,   55,
893  /*   120 */    56,   57,   58,   59,   60,   61,   62,   63,    1,    2,
894  /*   130 */    38,    4,    5,    4,    5,    6,    7,    4,    5,   12,
895  /*   140 */     3,   81,   15,   38,    1,  115,   17,    2,   88,  115,
896  /*   150 */    90,   24,  128,   26,   27,   12,    1,   14,   31,   32,
897  /*   160 */    19,   18,   16,   20,   21,   22,   23,   24,   17,   26,
898  /*   170 */    27,   15,   29,   30,   31,   32,   31,   32,   64,   65,
899  /*   180 */    66,   67,   68,   69,   70,   71,   72,   73,    6,    7,
900  /*   190 */    43,   64,   13,   48,   26,   27,  103,   48,  105,   29,
901  /*   200 */    30,   31,   32,  110,  111,  112,  113,  114,   81,   64,
902  /*   210 */    83,   84,   85,   86,   40,   88,   40,   90,   91,   92,
903  /*   220 */    93,   94,    1,    2,   87,    4,    5,  134,   83,   84,
904  /*   230 */    85,   86,   48,   12,   96,   97,   15,   41,   20,   21,
905  /*   240 */    22,   20,   21,   22,   41,    1,    2,   98,    4,    5,
906  /*   250 */    82,   47,   31,   32,   17,  103,   12,  105,   90,   15,
907  /*   260 */    26,   27,  110,  111,  112,  113,  114,   49,   50,   51,
908  /*   270 */    52,   53,   54,   89,   25,   31,   32,    1,   17,   95,
909  /*   280 */     4,    5,   98,   26,   27,   64,  134,   74,   12,    0,
910  /*   290 */    79,   15,   78,    3,    2,   80,   20,   21,   22,   10,
911  /*   300 */    24,   79,   81,    3,   83,   84,   85,   86,   64,   88,
912  /*   310 */     3,   90,   91,   92,   93,   94,   38,    2,    3,    4,
913  /*   320 */     5,    6,    7,   31,   32,   81,    3,   83,   84,   85,
914  /*   330 */    86,   16,   88,    3,   90,   91,   92,   93,   94,    3,
915  /*   340 */    25,   95,    4,    5,    6,    7,   31,   32,    1,    2,
916  /*   350 */    76,    4,    5,   38,   25,   17,   64,   81,   15,   12,
917  /*   360 */    15,   15,   15,   25,   88,   17,   90,   91,   92,   93,
918  /*   370 */    94,    4,    5,    6,    7,   83,   84,   85,   86,   28,
919  /*   380 */    28,   28,   67,   68,   12,   38,   89,    3,   11,  135,
920  /*   390 */    75,  135,   77,   78,   79,    2,    3,    4,    5,    6,
921  /*   400 */     7,    1,   35,  135,    4,    5,    4,    5,    6,    7,
922  /*   410 */    17,  135,   12,  135,  135,   15,  135,  135,   25,   17,
923  /*   420 */    20,   21,   22,  135,   31,   32,  135,   25,   81,  135,
924  /*   430 */   135,   38,  135,  135,  135,   88,  135,   90,   91,   92,
925  /*   440 */    93,   94,  135,    1,    2,  135,    4,    5,    4,    5,
926  /*   450 */     6,    7,    8,  135,   12,   26,   27,   15,  135,  135,
927  /*   460 */    67,   68,   99,  100,  101,  102,  135,  135,   75,  106,
928  /*   470 */    77,   78,   79,    2,    3,    4,    5,    6,    7,  135,
929  /*   480 */   117,   81,  135,  135,  121,  122,  135,  135,   88,  135,
930  /*   490 */    90,   91,   92,   93,   94,  135,   25,  103,  135,  105,
931  /*   500 */   135,  135,   31,   32,  110,  111,  112,  113,  114,   38,
932  /*   510 */   135,    1,  135,  135,    4,    5,  135,  135,  135,   90,
933  /*   520 */   135,  135,   12,   81,  135,   15,  135,  135,  134,  135,
934  /*   530 */    88,  135,   90,   91,   92,   93,   94,  135,   67,   68,
935  /*   540 */     1,    2,  135,    4,    5,  135,   75,  135,   77,   78,
936  /*   550 */    79,   12,  135,  135,   15,  135,   46,   47,    1,  135,
937  /*   560 */   135,    4,    5,   99,  100,  101,  102,  135,  135,   12,
938  /*   570 */   106,  135,   15,  135,    1,  135,  135,    4,    5,  135,
939  /*   580 */   135,  117,  135,  135,  135,   12,  122,  135,   15,  135,
940  /*   590 */   135,   81,  135,    4,    5,    6,    7,  135,   88,  135,
941  /*   600 */    90,   91,   92,   93,   94,    1,   17,  135,    4,    5,
942  /*   610 */   135,  135,  135,  135,  135,  103,   12,  105,  135,   15,
943  /*   620 */    81,  135,  110,  111,  112,  113,  114,   88,  135,   90,
944  /*   630 */    91,   92,   93,   94,  135,  100,  101,  102,   81,  135,
945  /*   640 */   135,  106,  135,  135,  135,   88,  134,   90,   91,   92,
946  /*   650 */    93,   94,  117,  135,   81,  135,  135,  122,  135,  135,
947  /*   660 */   135,   88,  135,   90,   91,   92,   93,   94,    1,  135,
948  /*   670 */   135,    4,    5,    4,    5,    6,    7,  135,  135,   12,
949  /*   680 */   135,  135,   15,  135,  135,   81,   17,    4,    5,    6,
950  /*   690 */     7,  135,   88,  135,   90,   91,   92,   93,   94,  135,
951  /*   700 */   135,  135,  103,  135,  105,  135,  103,  135,  135,  110,
952  /*   710 */   111,  112,  113,  114,  135,  112,  113,  114,   35,  103,
953  /*   720 */   135,  105,  119,  120,  135,  135,  110,  111,  112,  113,
954  /*   730 */   114,  135,  135,  134,  103,  135,  105,  134,  135,  135,
955  /*   740 */   135,  110,  111,  112,  113,  114,  135,  135,   81,  135,
956  /*   750 */   134,  135,  135,  103,  135,   88,  135,   90,   91,   92,
957  /*   760 */    93,   94,  112,  113,  114,  134,  116,  103,  135,  105,
958  /*   770 */   135,  103,  135,  135,  110,  111,  112,  113,  114,  135,
959  /*   780 */   112,  113,  114,  103,  134,  105,  118,  119,  120,  135,
960  /*   790 */   110,  111,  112,  113,  114,  135,  135,  135,  134,  103,
961  /*   800 */   135,  105,  134,  135,  135,  135,  110,  111,  112,  113,
962  /*   810 */   114,  135,  135,  103,  134,  105,  135,  135,  135,  135,
963  /*   820 */   110,  111,  112,  113,  114,  103,  135,  105,  135,  135,
964  /*   830 */   134,  135,  110,  111,  112,  113,  114,  135,  103,  135,
965  /*   840 */   105,  135,  135,  135,  134,  110,  111,  112,  113,  114,
966  /*   850 */   135,  135,  103,  135,  105,  135,  134,  135,  135,  110,
967  /*   860 */   111,  112,  113,  114,  103,  135,  105,  135,  135,  134,
968  /*   870 */   135,  110,  111,  112,  113,  114,  135,  135,  103,  135,
969  /*   880 */   105,  135,  103,  134,  135,  110,  111,  112,  113,  114,
970  /*   890 */   135,  112,  113,  114,  103,  134,  105,  103,  119,  120,
971  /*   900 */   135,  110,  111,  112,  113,  114,  112,  113,  114,  134,
972  /*   910 */   103,  135,  105,  134,  135,  135,  135,  110,  111,  112,
973  /*   920 */   113,  114,  135,  135,  103,  134,  105,  135,  134,  135,
974  /*   930 */   135,  110,  111,  112,  113,  114,  103,  135,  105,  135,
975  /*   940 */   135,  134,  135,  110,  111,  112,  113,  114,  135,  103,
976  /*   950 */   135,  105,  135,  135,  135,  134,  110,  111,  112,  113,
977  /*   960 */   114,  135,  135,  103,  135,  105,  135,  134,  135,  135,
978  /*   970 */   110,  111,  112,  113,  114,  103,  135,  105,  135,  135,
979  /*   980 */   134,  135,  110,  111,  112,  113,  114,  135,  135,  103,
980  /*   990 */   135,  105,  103,  135,  134,  135,  110,  111,  112,  113,
981  /*  1000 */   114,  112,  113,  114,  135,  116,  134,  135,  103,  135,
982  /*  1010 */   135,  135,  123,  124,  135,  135,  103,  112,  113,  114,
983  /*  1020 */   134,  135,  135,  134,  119,  112,  113,  114,  135,  116,
984  /*  1030 */   135,  126,  103,  128,  135,  103,  135,  124,  135,  134,
985  /*  1040 */   135,  112,  113,  114,  112,  113,  114,  134,  103,  135,
986  /*  1050 */   135,  119,    4,    5,    6,    7,  135,  112,  113,  114,
987  /*  1060 */   103,  116,  135,  134,  103,   17,  134,  103,  135,  112,
988  /*  1070 */   113,  114,  135,  112,  113,  114,  112,  113,  114,  134,
989  /*  1080 */   135,    4,    5,    6,    7,  135,  135,  135,  135,  135,
990  /*  1090 */   103,  134,  135,  103,   17,  134,  103,  135,  134,  112,
991  /*  1100 */   113,  114,  112,  113,  114,  112,  113,  114,  135,  103,
992  /*  1110 */     4,    5,    6,    7,  135,  135,  135,  135,  112,  113,
993  /*  1120 */   114,  134,  103,   17,  134,  135,  135,  134,  103,  135,
994  /*  1130 */   135,  112,  113,  114,  135,  103,  135,  112,  113,  114,
995  /*  1140 */   134,  103,  135,  135,  112,  113,  114,  103,  135,  135,
996  /*  1150 */   112,  113,  114,  134,  135,  135,  112,  113,  114,  134,
997  /*  1160 */   135,  135,  135,  135,  103,  135,  134,  135,  135,  135,
998  /*  1170 */   103,  135,  134,  112,  113,  114,  135,  103,  134,  112,
999  /*  1180 */   113,  114,  103,  135,  135,  135,  112,  113,  114,  135,
1000  /*  1190 */   135,  112,  113,  114,  103,  134,  135,  135,  135,  135,
1001  /*  1200 */   103,  134,  135,  112,  113,  114,  103,  135,  134,  112,
1002  /*  1210 */   113,  114,  135,  134,  135,  112,  113,  114,  135,  135,
1003  /*  1220 */   135,  135,  135,  103,  135,  134,  135,  135,  135,  135,
1004  /*  1230 */   135,  134,  112,  113,  114,  103,  135,  134,  135,  103,
1005  /*  1240 */   135,  135,  103,  135,  112,  113,  114,  135,  112,  113,
1006  /*  1250 */   114,  112,  113,  114,  134,    4,    5,    6,    7,    4,
1007  /*  1260 */     5,    6,    7,  135,  135,  135,  134,  135,  135,  135,
1008  /*  1270 */   134,  135,  135,  134,  135,  135,   25,  135,  135,  135,
1009  /*  1280 */    25,    4,    5,    6,    7,  135,  135,  135,  135,  135,
1010  /*  1290 */   135,  135,  135,  135,  135,  135,  135,  135,  135,  135,
1011  /*  1300 */   135,  135,   25,  135,  135,  135,  135,  135,  135,  135,
1012  /*  1310 */   135,  135,  135,  135,  135,  135,  135,  135,  135,  135,
1013  /*  1320 */   135,  135,  135,  135,  135,  135,  135,  135,  135,  135,
1014  /*  1330 */   135,  135,  135,  135,  135,  135,  135,  135,  135,  135,
1015  /*  1340 */   135,  135,  135,  135,  135,  135,  135,  135,  135,  135,
1016  /*  1350 */   135,  135,  135,  135,  135,  135,  135,  135,  135,  135,
1017  /*  1360 */   135,  135,  135,  135,  135,  135,  135,  135,  135,  135,
1018  /*  1370 */   135,  135,  135,  135,  135,  135,  135,  135,  135,  135,
1019  /*  1380 */   135,   99,   99,   99,   99,   99,   99,   99,   99,   99,
1020  /*  1390 */    99,   99,   99,   99,   99,   99,   99,   99,   99,   99,
1021  /*  1400 */    99,   99,
1022 };
1023 #define YY_SHIFT_COUNT    (163)
1024 #define YY_SHIFT_MIN      (0)
1025 #define YY_SHIFT_MAX      (1277)
1026 static const unsigned short int yy_shift_ofst[] = {
1027  /*     0 */   143,  127,  221,  244,  244,  244,  244,  244,  244,  244,
1028  /*    10 */   244,  244,  244,  244,  244,  244,  244,  244,  244,  244,
1029  /*    20 */   244,  244,  244,  244,  244,  244,  244,  276,  510,  557,
1030  /*    30 */   276,  143,  347,  347,    0,   64,  143,  573,  557,  573,
1031  /*    40 */   400,  400,  400,  442,  539,  557,  557,  557,  557,  557,
1032  /*    50 */   557,  604,  557,  557,  667,  557,  557,  557,  557,  557,
1033  /*    60 */   557,  557,  557,  557,  557,  218,   60,   60,   60,   60,
1034  /*    70 */    60,  145,  315,  393,  471,  292,  292,  170,   71, 1303,
1035  /*    80 */  1303, 1303, 1303,  114,  114,  338,  402,  129,  444,  367,
1036  /*    90 */   683,  589, 1251,  669, 1255, 1048, 1277, 1077, 1106,   25,
1037  /*   100 */    25,   25,  184,   25,   25,   25,  168,   25,  429,   83,
1038  /*   110 */    92,  105,   70,  133,  138,  182,  182,  234,  257,  137,
1039  /*   120 */   149,  289,  141,  155,  151,  146,  156,  147,  174,  176,
1040  /*   130 */   196,  203,  204,  179,  237,  249,  213,  261,  211,  214,
1041  /*   140 */   215,  222,  290,  300,  307,  278,  323,  330,  336,  246,
1042  /*   150 */   274,  329,  246,  343,  345,  346,  348,  351,  352,  353,
1043  /*   160 */   372,  297,  384,  377,
1044 };
1045 #define YY_REDUCE_COUNT (82)
1046 #define YY_REDUCE_MIN   (-129)
1047 #define YY_REDUCE_MAX   (1139)
1048 static const short yy_reduce_ofst[] = {
1049  /*     0 */   363,  -96,  -32,   93,  152,  394,  512,  599,  616,  631,
1050  /*    10 */   664,  680,  696,  710,  722,  735,  749,  761,  775,  791,
1051  /*    20 */   807,  821,  833,  846,  860,  872,  886,  889,  668,  905,
1052  /*    30 */   913,  464,  603,  779,  -61,  -61,  535,  650,  932,  945,
1053  /*    40 */   794,  929,  957,  961,  964,  987,  990,  993, 1006, 1019,
1054  /*    50 */  1025, 1032, 1038, 1044, 1061, 1067, 1074, 1079, 1091, 1097,
1055  /*    60 */  1103, 1120, 1132, 1136, 1139,  -81, -111, -101,  -47,  -37,
1056  /*    70 */   -23,  -22, -129, -129, -129,  -97,  -86,  -58, -100,  -15,
1057  /*    80 */    30,   34,   24,
1058 };
1059 static const YYACTIONTYPE yy_default[] = {
1060  /*     0 */   449,  443,  443,  443,  443,  443,  443,  443,  443,  443,
1061  /*    10 */   443,  443,  443,  443,  443,  443,  443,  443,  443,  443,
1062  /*    20 */   443,  443,  443,  443,  443,  443,  443,  443,  473,  576,
1063  /*    30 */   443,  449,  580,  485,  581,  581,  449,  443,  443,  443,
1064  /*    40 */   443,  443,  443,  443,  443,  443,  443,  443,  477,  443,
1065  /*    50 */   443,  443,  443,  443,  443,  443,  443,  443,  443,  443,
1066  /*    60 */   443,  443,  443,  443,  443,  443,  443,  443,  443,  443,
1067  /*    70 */   443,  443,  443,  443,  443,  443,  443,  443,  455,  470,
1068  /*    80 */   508,  508,  576,  468,  493,  443,  443,  443,  471,  443,
1069  /*    90 */   443,  443,  443,  443,  443,  443,  443,  443,  443,  488,
1070  /*   100 */   486,  476,  459,  512,  511,  510,  443,  566,  443,  443,
1071  /*   110 */   443,  443,  443,  588,  443,  545,  544,  540,  443,  532,
1072  /*   120 */   529,  443,  443,  443,  443,  443,  443,  491,  443,  443,
1073  /*   130 */   443,  443,  443,  443,  443,  443,  443,  443,  443,  443,
1074  /*   140 */   443,  443,  443,  443,  443,  443,  443,  443,  443,  592,
1075  /*   150 */   443,  443,  443,  443,  443,  443,  443,  443,  443,  443,
1076  /*   160 */   443,  601,  443,  443,
1077 };
1078 /********** End of lemon-generated parsing tables *****************************/
1079 
1080 /* The next table maps tokens (terminal symbols) into fallback tokens.
1081 ** If a construct like the following:
1082 **
1083 **      %fallback ID X Y Z.
1084 **
1085 ** appears in the grammar, then ID becomes a fallback token for X, Y,
1086 ** and Z.  Whenever one of the tokens X, Y, or Z is input to the parser
1087 ** but it does not parse, the type of the token is changed to ID and
1088 ** the parse is retried before an error is thrown.
1089 **
1090 ** This feature can be used, for example, to cause some keywords in a language
1091 ** to revert to identifiers if they keyword does not apply in the context where
1092 ** it appears.
1093 */
1094 #ifdef YYFALLBACK
1095 static const YYCODETYPE yyFallback[] = {
1096     0,  /*          $ => nothing */
1097     0,  /*         ID => nothing */
1098     1,  /*     EDGEPT => ID */
1099     0,  /*         OF => nothing */
1100     0,  /*       PLUS => nothing */
1101     0,  /*      MINUS => nothing */
1102     0,  /*       STAR => nothing */
1103     0,  /*      SLASH => nothing */
1104     0,  /*    PERCENT => nothing */
1105     0,  /*     UMINUS => nothing */
1106     0,  /*        EOL => nothing */
1107     0,  /*     ASSIGN => nothing */
1108     0,  /*  PLACENAME => nothing */
1109     0,  /*      COLON => nothing */
1110     0,  /*     ASSERT => nothing */
1111     0,  /*         LP => nothing */
1112     0,  /*         EQ => nothing */
1113     0,  /*         RP => nothing */
1114     0,  /*     DEFINE => nothing */
1115     0,  /*  CODEBLOCK => nothing */
1116     0,  /*       FILL => nothing */
1117     0,  /*      COLOR => nothing */
1118     0,  /*  THICKNESS => nothing */
1119     0,  /*      PRINT => nothing */
1120     0,  /*     STRING => nothing */
1121     0,  /*      COMMA => nothing */
1122     0,  /*  CLASSNAME => nothing */
1123     0,  /*         LB => nothing */
1124     0,  /*         RB => nothing */
1125     0,  /*         UP => nothing */
1126     0,  /*       DOWN => nothing */
1127     0,  /*       LEFT => nothing */
1128     0,  /*      RIGHT => nothing */
1129     0,  /*      CLOSE => nothing */
1130     0,  /*       CHOP => nothing */
1131     0,  /*       FROM => nothing */
1132     0,  /*         TO => nothing */
1133     0,  /*       THEN => nothing */
1134     0,  /*    HEADING => nothing */
1135     0,  /*         GO => nothing */
1136     0,  /*         AT => nothing */
1137     0,  /*       WITH => nothing */
1138     0,  /*       SAME => nothing */
1139     0,  /*         AS => nothing */
1140     0,  /*        FIT => nothing */
1141     0,  /*     BEHIND => nothing */
1142     0,  /*      UNTIL => nothing */
1143     0,  /*       EVEN => nothing */
1144     0,  /*      DOT_E => nothing */
1145     0,  /*     HEIGHT => nothing */
1146     0,  /*      WIDTH => nothing */
1147     0,  /*     RADIUS => nothing */
1148     0,  /*   DIAMETER => nothing */
1149     0,  /*     DOTTED => nothing */
1150     0,  /*     DASHED => nothing */
1151     0,  /*         CW => nothing */
1152     0,  /*        CCW => nothing */
1153     0,  /*     LARROW => nothing */
1154     0,  /*     RARROW => nothing */
1155     0,  /*    LRARROW => nothing */
1156     0,  /*      INVIS => nothing */
1157     0,  /*      THICK => nothing */
1158     0,  /*       THIN => nothing */
1159     0,  /*      SOLID => nothing */
1160     0,  /*     CENTER => nothing */
1161     0,  /*      LJUST => nothing */
1162     0,  /*      RJUST => nothing */
1163     0,  /*      ABOVE => nothing */
1164     0,  /*      BELOW => nothing */
1165     0,  /*     ITALIC => nothing */
1166     0,  /*       BOLD => nothing */
1167     0,  /*    ALIGNED => nothing */
1168     0,  /*        BIG => nothing */
1169     0,  /*      SMALL => nothing */
1170     0,  /*        AND => nothing */
1171     0,  /*         LT => nothing */
1172     0,  /*         GT => nothing */
1173     0,  /*         ON => nothing */
1174     0,  /*        WAY => nothing */
1175     0,  /*    BETWEEN => nothing */
1176     0,  /*        THE => nothing */
1177     0,  /*        NTH => nothing */
1178     0,  /*     VERTEX => nothing */
1179     0,  /*        TOP => nothing */
1180     0,  /*     BOTTOM => nothing */
1181     0,  /*      START => nothing */
1182     0,  /*        END => nothing */
1183     0,  /*         IN => nothing */
1184     0,  /*       THIS => nothing */
1185     0,  /*      DOT_U => nothing */
1186     0,  /*       LAST => nothing */
1187     0,  /*     NUMBER => nothing */
1188     0,  /*      FUNC1 => nothing */
1189     0,  /*      FUNC2 => nothing */
1190     0,  /*       DIST => nothing */
1191     0,  /*     DOT_XY => nothing */
1192     0,  /*          X => nothing */
1193     0,  /*          Y => nothing */
1194     0,  /*      DOT_L => nothing */
1195 };
1196 #endif /* YYFALLBACK */
1197 
1198 /* The following structure represents a single element of the
1199 ** parser's stack.  Information stored includes:
1200 **
1201 **   +  The state number for the parser at this level of the stack.
1202 **
1203 **   +  The value of the token stored at this level of the stack.
1204 **      (In other words, the "major" token.)
1205 **
1206 **   +  The semantic value stored at this level of the stack.  This is
1207 **      the information used by the action routines in the grammar.
1208 **      It is sometimes called the "minor" token.
1209 **
1210 ** After the "shift" half of a SHIFTREDUCE action, the stateno field
1211 ** actually contains the reduce action for the second half of the
1212 ** SHIFTREDUCE.
1213 */
1214 struct yyStackEntry {
1215   YYACTIONTYPE stateno;  /* The state-number, or reduce action in SHIFTREDUCE */
1216   YYCODETYPE major;      /* The major token value.  This is the code
1217                          ** number for the token at this stack level */
1218   YYMINORTYPE minor;     /* The user-supplied minor token value.  This
1219                          ** is the value of the token  */
1220 };
1221 typedef struct yyStackEntry yyStackEntry;
1222 
1223 /* The state of the parser is completely contained in an instance of
1224 ** the following structure */
1225 struct yyParser {
1226   yyStackEntry *yytos;          /* Pointer to top element of the stack */
1227 #ifdef YYTRACKMAXSTACKDEPTH
1228   int yyhwm;                    /* High-water mark of the stack */
1229 #endif
1230 #ifndef YYNOERRORRECOVERY
1231   int yyerrcnt;                 /* Shifts left before out of the error */
1232 #endif
1233   pik_parserARG_SDECL                /* A place to hold %extra_argument */
1234   pik_parserCTX_SDECL                /* A place to hold %extra_context */
1235 #if YYSTACKDEPTH<=0
1236   int yystksz;                  /* Current side of the stack */
1237   yyStackEntry *yystack;        /* The parser's stack */
1238   yyStackEntry yystk0;          /* First stack entry */
1239 #else
1240   yyStackEntry yystack[YYSTACKDEPTH];  /* The parser's stack */
1241   yyStackEntry *yystackEnd;            /* Last entry in the stack */
1242 #endif
1243 };
1244 typedef struct yyParser yyParser;
1245 
1246 #ifndef NDEBUG
1247 #include <stdio.h>
1248 #include <assert.h>
1249 static FILE *yyTraceFILE = 0;
1250 static char *yyTracePrompt = 0;
1251 #endif /* NDEBUG */
1252 
1253 #ifndef NDEBUG
1254 /*
1255 ** Turn parser tracing on by giving a stream to which to write the trace
1256 ** and a prompt to preface each trace message.  Tracing is turned off
1257 ** by making either argument NULL
1258 **
1259 ** Inputs:
1260 ** <ul>
1261 ** <li> A FILE* to which trace output should be written.
1262 **      If NULL, then tracing is turned off.
1263 ** <li> A prefix string written at the beginning of every
1264 **      line of trace output.  If NULL, then tracing is
1265 **      turned off.
1266 ** </ul>
1267 **
1268 ** Outputs:
1269 ** None.
1270 */
pik_parserTrace(FILE * TraceFILE,char * zTracePrompt)1271 void pik_parserTrace(FILE *TraceFILE, char *zTracePrompt){
1272   yyTraceFILE = TraceFILE;
1273   yyTracePrompt = zTracePrompt;
1274   if( yyTraceFILE==0 ) yyTracePrompt = 0;
1275   else if( yyTracePrompt==0 ) yyTraceFILE = 0;
1276 }
1277 #endif /* NDEBUG */
1278 
1279 #if defined(YYCOVERAGE) || !defined(NDEBUG)
1280 /* For tracing shifts, the names of all terminals and nonterminals
1281 ** are required.  The following table supplies these names */
1282 static const char *const yyTokenName[] = {
1283   /*    0 */ "$",
1284   /*    1 */ "ID",
1285   /*    2 */ "EDGEPT",
1286   /*    3 */ "OF",
1287   /*    4 */ "PLUS",
1288   /*    5 */ "MINUS",
1289   /*    6 */ "STAR",
1290   /*    7 */ "SLASH",
1291   /*    8 */ "PERCENT",
1292   /*    9 */ "UMINUS",
1293   /*   10 */ "EOL",
1294   /*   11 */ "ASSIGN",
1295   /*   12 */ "PLACENAME",
1296   /*   13 */ "COLON",
1297   /*   14 */ "ASSERT",
1298   /*   15 */ "LP",
1299   /*   16 */ "EQ",
1300   /*   17 */ "RP",
1301   /*   18 */ "DEFINE",
1302   /*   19 */ "CODEBLOCK",
1303   /*   20 */ "FILL",
1304   /*   21 */ "COLOR",
1305   /*   22 */ "THICKNESS",
1306   /*   23 */ "PRINT",
1307   /*   24 */ "STRING",
1308   /*   25 */ "COMMA",
1309   /*   26 */ "CLASSNAME",
1310   /*   27 */ "LB",
1311   /*   28 */ "RB",
1312   /*   29 */ "UP",
1313   /*   30 */ "DOWN",
1314   /*   31 */ "LEFT",
1315   /*   32 */ "RIGHT",
1316   /*   33 */ "CLOSE",
1317   /*   34 */ "CHOP",
1318   /*   35 */ "FROM",
1319   /*   36 */ "TO",
1320   /*   37 */ "THEN",
1321   /*   38 */ "HEADING",
1322   /*   39 */ "GO",
1323   /*   40 */ "AT",
1324   /*   41 */ "WITH",
1325   /*   42 */ "SAME",
1326   /*   43 */ "AS",
1327   /*   44 */ "FIT",
1328   /*   45 */ "BEHIND",
1329   /*   46 */ "UNTIL",
1330   /*   47 */ "EVEN",
1331   /*   48 */ "DOT_E",
1332   /*   49 */ "HEIGHT",
1333   /*   50 */ "WIDTH",
1334   /*   51 */ "RADIUS",
1335   /*   52 */ "DIAMETER",
1336   /*   53 */ "DOTTED",
1337   /*   54 */ "DASHED",
1338   /*   55 */ "CW",
1339   /*   56 */ "CCW",
1340   /*   57 */ "LARROW",
1341   /*   58 */ "RARROW",
1342   /*   59 */ "LRARROW",
1343   /*   60 */ "INVIS",
1344   /*   61 */ "THICK",
1345   /*   62 */ "THIN",
1346   /*   63 */ "SOLID",
1347   /*   64 */ "CENTER",
1348   /*   65 */ "LJUST",
1349   /*   66 */ "RJUST",
1350   /*   67 */ "ABOVE",
1351   /*   68 */ "BELOW",
1352   /*   69 */ "ITALIC",
1353   /*   70 */ "BOLD",
1354   /*   71 */ "ALIGNED",
1355   /*   72 */ "BIG",
1356   /*   73 */ "SMALL",
1357   /*   74 */ "AND",
1358   /*   75 */ "LT",
1359   /*   76 */ "GT",
1360   /*   77 */ "ON",
1361   /*   78 */ "WAY",
1362   /*   79 */ "BETWEEN",
1363   /*   80 */ "THE",
1364   /*   81 */ "NTH",
1365   /*   82 */ "VERTEX",
1366   /*   83 */ "TOP",
1367   /*   84 */ "BOTTOM",
1368   /*   85 */ "START",
1369   /*   86 */ "END",
1370   /*   87 */ "IN",
1371   /*   88 */ "THIS",
1372   /*   89 */ "DOT_U",
1373   /*   90 */ "LAST",
1374   /*   91 */ "NUMBER",
1375   /*   92 */ "FUNC1",
1376   /*   93 */ "FUNC2",
1377   /*   94 */ "DIST",
1378   /*   95 */ "DOT_XY",
1379   /*   96 */ "X",
1380   /*   97 */ "Y",
1381   /*   98 */ "DOT_L",
1382   /*   99 */ "statement_list",
1383   /*  100 */ "statement",
1384   /*  101 */ "unnamed_statement",
1385   /*  102 */ "basetype",
1386   /*  103 */ "expr",
1387   /*  104 */ "numproperty",
1388   /*  105 */ "edge",
1389   /*  106 */ "direction",
1390   /*  107 */ "dashproperty",
1391   /*  108 */ "colorproperty",
1392   /*  109 */ "locproperty",
1393   /*  110 */ "position",
1394   /*  111 */ "place",
1395   /*  112 */ "object",
1396   /*  113 */ "objectname",
1397   /*  114 */ "nth",
1398   /*  115 */ "textposition",
1399   /*  116 */ "rvalue",
1400   /*  117 */ "lvalue",
1401   /*  118 */ "even",
1402   /*  119 */ "relexpr",
1403   /*  120 */ "optrelexpr",
1404   /*  121 */ "document",
1405   /*  122 */ "print",
1406   /*  123 */ "prlist",
1407   /*  124 */ "pritem",
1408   /*  125 */ "prsep",
1409   /*  126 */ "attribute_list",
1410   /*  127 */ "savelist",
1411   /*  128 */ "alist",
1412   /*  129 */ "attribute",
1413   /*  130 */ "go",
1414   /*  131 */ "boolproperty",
1415   /*  132 */ "withclause",
1416   /*  133 */ "between",
1417   /*  134 */ "place2",
1418 };
1419 #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */
1420 
1421 #ifndef NDEBUG
1422 /* For tracing reduce actions, the names of all rules are required.
1423 */
1424 static const char *const yyRuleName[] = {
1425  /*   0 */ "document ::= statement_list",
1426  /*   1 */ "statement_list ::= statement",
1427  /*   2 */ "statement_list ::= statement_list EOL statement",
1428  /*   3 */ "statement ::=",
1429  /*   4 */ "statement ::= direction",
1430  /*   5 */ "statement ::= lvalue ASSIGN rvalue",
1431  /*   6 */ "statement ::= PLACENAME COLON unnamed_statement",
1432  /*   7 */ "statement ::= PLACENAME COLON position",
1433  /*   8 */ "statement ::= unnamed_statement",
1434  /*   9 */ "statement ::= print prlist",
1435  /*  10 */ "statement ::= ASSERT LP expr EQ expr RP",
1436  /*  11 */ "statement ::= ASSERT LP position EQ position RP",
1437  /*  12 */ "statement ::= DEFINE ID CODEBLOCK",
1438  /*  13 */ "rvalue ::= PLACENAME",
1439  /*  14 */ "pritem ::= FILL",
1440  /*  15 */ "pritem ::= COLOR",
1441  /*  16 */ "pritem ::= THICKNESS",
1442  /*  17 */ "pritem ::= rvalue",
1443  /*  18 */ "pritem ::= STRING",
1444  /*  19 */ "prsep ::= COMMA",
1445  /*  20 */ "unnamed_statement ::= basetype attribute_list",
1446  /*  21 */ "basetype ::= CLASSNAME",
1447  /*  22 */ "basetype ::= STRING textposition",
1448  /*  23 */ "basetype ::= LB savelist statement_list RB",
1449  /*  24 */ "savelist ::=",
1450  /*  25 */ "relexpr ::= expr",
1451  /*  26 */ "relexpr ::= expr PERCENT",
1452  /*  27 */ "optrelexpr ::=",
1453  /*  28 */ "attribute_list ::= relexpr alist",
1454  /*  29 */ "attribute ::= numproperty relexpr",
1455  /*  30 */ "attribute ::= dashproperty expr",
1456  /*  31 */ "attribute ::= dashproperty",
1457  /*  32 */ "attribute ::= colorproperty rvalue",
1458  /*  33 */ "attribute ::= go direction optrelexpr",
1459  /*  34 */ "attribute ::= go direction even position",
1460  /*  35 */ "attribute ::= CLOSE",
1461  /*  36 */ "attribute ::= CHOP",
1462  /*  37 */ "attribute ::= FROM position",
1463  /*  38 */ "attribute ::= TO position",
1464  /*  39 */ "attribute ::= THEN",
1465  /*  40 */ "attribute ::= THEN optrelexpr HEADING expr",
1466  /*  41 */ "attribute ::= THEN optrelexpr EDGEPT",
1467  /*  42 */ "attribute ::= GO optrelexpr HEADING expr",
1468  /*  43 */ "attribute ::= GO optrelexpr EDGEPT",
1469  /*  44 */ "attribute ::= AT position",
1470  /*  45 */ "attribute ::= SAME",
1471  /*  46 */ "attribute ::= SAME AS object",
1472  /*  47 */ "attribute ::= STRING textposition",
1473  /*  48 */ "attribute ::= FIT",
1474  /*  49 */ "attribute ::= BEHIND object",
1475  /*  50 */ "withclause ::= DOT_E edge AT position",
1476  /*  51 */ "withclause ::= edge AT position",
1477  /*  52 */ "numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS",
1478  /*  53 */ "boolproperty ::= CW",
1479  /*  54 */ "boolproperty ::= CCW",
1480  /*  55 */ "boolproperty ::= LARROW",
1481  /*  56 */ "boolproperty ::= RARROW",
1482  /*  57 */ "boolproperty ::= LRARROW",
1483  /*  58 */ "boolproperty ::= INVIS",
1484  /*  59 */ "boolproperty ::= THICK",
1485  /*  60 */ "boolproperty ::= THIN",
1486  /*  61 */ "boolproperty ::= SOLID",
1487  /*  62 */ "textposition ::=",
1488  /*  63 */ "textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|ALIGNED|BIG|SMALL",
1489  /*  64 */ "position ::= expr COMMA expr",
1490  /*  65 */ "position ::= place PLUS expr COMMA expr",
1491  /*  66 */ "position ::= place MINUS expr COMMA expr",
1492  /*  67 */ "position ::= place PLUS LP expr COMMA expr RP",
1493  /*  68 */ "position ::= place MINUS LP expr COMMA expr RP",
1494  /*  69 */ "position ::= LP position COMMA position RP",
1495  /*  70 */ "position ::= LP position RP",
1496  /*  71 */ "position ::= expr between position AND position",
1497  /*  72 */ "position ::= expr LT position COMMA position GT",
1498  /*  73 */ "position ::= expr ABOVE position",
1499  /*  74 */ "position ::= expr BELOW position",
1500  /*  75 */ "position ::= expr LEFT OF position",
1501  /*  76 */ "position ::= expr RIGHT OF position",
1502  /*  77 */ "position ::= expr ON HEADING EDGEPT OF position",
1503  /*  78 */ "position ::= expr HEADING EDGEPT OF position",
1504  /*  79 */ "position ::= expr EDGEPT OF position",
1505  /*  80 */ "position ::= expr ON HEADING expr FROM position",
1506  /*  81 */ "position ::= expr HEADING expr FROM position",
1507  /*  82 */ "place ::= edge OF object",
1508  /*  83 */ "place2 ::= object",
1509  /*  84 */ "place2 ::= object DOT_E edge",
1510  /*  85 */ "place2 ::= NTH VERTEX OF object",
1511  /*  86 */ "object ::= nth",
1512  /*  87 */ "object ::= nth OF|IN object",
1513  /*  88 */ "objectname ::= THIS",
1514  /*  89 */ "objectname ::= PLACENAME",
1515  /*  90 */ "objectname ::= objectname DOT_U PLACENAME",
1516  /*  91 */ "nth ::= NTH CLASSNAME",
1517  /*  92 */ "nth ::= NTH LAST CLASSNAME",
1518  /*  93 */ "nth ::= LAST CLASSNAME",
1519  /*  94 */ "nth ::= LAST",
1520  /*  95 */ "nth ::= NTH LB RB",
1521  /*  96 */ "nth ::= NTH LAST LB RB",
1522  /*  97 */ "nth ::= LAST LB RB",
1523  /*  98 */ "expr ::= expr PLUS expr",
1524  /*  99 */ "expr ::= expr MINUS expr",
1525  /* 100 */ "expr ::= expr STAR expr",
1526  /* 101 */ "expr ::= expr SLASH expr",
1527  /* 102 */ "expr ::= MINUS expr",
1528  /* 103 */ "expr ::= PLUS expr",
1529  /* 104 */ "expr ::= LP expr RP",
1530  /* 105 */ "expr ::= LP FILL|COLOR|THICKNESS RP",
1531  /* 106 */ "expr ::= NUMBER",
1532  /* 107 */ "expr ::= ID",
1533  /* 108 */ "expr ::= FUNC1 LP expr RP",
1534  /* 109 */ "expr ::= FUNC2 LP expr COMMA expr RP",
1535  /* 110 */ "expr ::= DIST LP position COMMA position RP",
1536  /* 111 */ "expr ::= place2 DOT_XY X",
1537  /* 112 */ "expr ::= place2 DOT_XY Y",
1538  /* 113 */ "expr ::= object DOT_L numproperty",
1539  /* 114 */ "expr ::= object DOT_L dashproperty",
1540  /* 115 */ "expr ::= object DOT_L colorproperty",
1541  /* 116 */ "lvalue ::= ID",
1542  /* 117 */ "lvalue ::= FILL",
1543  /* 118 */ "lvalue ::= COLOR",
1544  /* 119 */ "lvalue ::= THICKNESS",
1545  /* 120 */ "rvalue ::= expr",
1546  /* 121 */ "print ::= PRINT",
1547  /* 122 */ "prlist ::= pritem",
1548  /* 123 */ "prlist ::= prlist prsep pritem",
1549  /* 124 */ "direction ::= UP",
1550  /* 125 */ "direction ::= DOWN",
1551  /* 126 */ "direction ::= LEFT",
1552  /* 127 */ "direction ::= RIGHT",
1553  /* 128 */ "optrelexpr ::= relexpr",
1554  /* 129 */ "attribute_list ::= alist",
1555  /* 130 */ "alist ::=",
1556  /* 131 */ "alist ::= alist attribute",
1557  /* 132 */ "attribute ::= boolproperty",
1558  /* 133 */ "attribute ::= WITH withclause",
1559  /* 134 */ "go ::= GO",
1560  /* 135 */ "go ::=",
1561  /* 136 */ "even ::= UNTIL EVEN WITH",
1562  /* 137 */ "even ::= EVEN WITH",
1563  /* 138 */ "dashproperty ::= DOTTED",
1564  /* 139 */ "dashproperty ::= DASHED",
1565  /* 140 */ "colorproperty ::= FILL",
1566  /* 141 */ "colorproperty ::= COLOR",
1567  /* 142 */ "position ::= place",
1568  /* 143 */ "between ::= WAY BETWEEN",
1569  /* 144 */ "between ::= BETWEEN",
1570  /* 145 */ "between ::= OF THE WAY BETWEEN",
1571  /* 146 */ "place ::= place2",
1572  /* 147 */ "edge ::= CENTER",
1573  /* 148 */ "edge ::= EDGEPT",
1574  /* 149 */ "edge ::= TOP",
1575  /* 150 */ "edge ::= BOTTOM",
1576  /* 151 */ "edge ::= START",
1577  /* 152 */ "edge ::= END",
1578  /* 153 */ "edge ::= RIGHT",
1579  /* 154 */ "edge ::= LEFT",
1580  /* 155 */ "object ::= objectname",
1581 };
1582 #endif /* NDEBUG */
1583 
1584 
1585 #if YYSTACKDEPTH<=0
1586 /*
1587 ** Try to increase the size of the parser stack.  Return the number
1588 ** of errors.  Return 0 on success.
1589 */
yyGrowStack(yyParser * p)1590 static int yyGrowStack(yyParser *p){
1591   int newSize;
1592   int idx;
1593   yyStackEntry *pNew;
1594 
1595   newSize = p->yystksz*2 + 100;
1596   idx = p->yytos ? (int)(p->yytos - p->yystack) : 0;
1597   if( p->yystack==&p->yystk0 ){
1598     pNew = malloc(newSize*sizeof(pNew[0]));
1599     if( pNew ) pNew[0] = p->yystk0;
1600   }else{
1601     pNew = realloc(p->yystack, newSize*sizeof(pNew[0]));
1602   }
1603   if( pNew ){
1604     p->yystack = pNew;
1605     p->yytos = &p->yystack[idx];
1606 #ifndef NDEBUG
1607     if( yyTraceFILE ){
1608       fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n",
1609               yyTracePrompt, p->yystksz, newSize);
1610     }
1611 #endif
1612     p->yystksz = newSize;
1613   }
1614   return pNew==0;
1615 }
1616 #endif
1617 
1618 /* Datatype of the argument to the memory allocated passed as the
1619 ** second argument to pik_parserAlloc() below.  This can be changed by
1620 ** putting an appropriate #define in the %include section of the input
1621 ** grammar.
1622 */
1623 #ifndef YYMALLOCARGTYPE
1624 # define YYMALLOCARGTYPE size_t
1625 #endif
1626 
1627 /* Initialize a new parser that has already been allocated.
1628 */
pik_parserInit(void * yypRawParser pik_parserCTX_PDECL)1629 void pik_parserInit(void *yypRawParser pik_parserCTX_PDECL){
1630   yyParser *yypParser = (yyParser*)yypRawParser;
1631   pik_parserCTX_STORE
1632 #ifdef YYTRACKMAXSTACKDEPTH
1633   yypParser->yyhwm = 0;
1634 #endif
1635 #if YYSTACKDEPTH<=0
1636   yypParser->yytos = NULL;
1637   yypParser->yystack = NULL;
1638   yypParser->yystksz = 0;
1639   if( yyGrowStack(yypParser) ){
1640     yypParser->yystack = &yypParser->yystk0;
1641     yypParser->yystksz = 1;
1642   }
1643 #endif
1644 #ifndef YYNOERRORRECOVERY
1645   yypParser->yyerrcnt = -1;
1646 #endif
1647   yypParser->yytos = yypParser->yystack;
1648   yypParser->yystack[0].stateno = 0;
1649   yypParser->yystack[0].major = 0;
1650 #if YYSTACKDEPTH>0
1651   yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1];
1652 #endif
1653 }
1654 
1655 #ifndef pik_parser_ENGINEALWAYSONSTACK
1656 /*
1657 ** This function allocates a new parser.
1658 ** The only argument is a pointer to a function which works like
1659 ** malloc.
1660 **
1661 ** Inputs:
1662 ** A pointer to the function used to allocate memory.
1663 **
1664 ** Outputs:
1665 ** A pointer to a parser.  This pointer is used in subsequent calls
1666 ** to pik_parser and pik_parserFree.
1667 */
pik_parserAlloc(void * (* mallocProc)(YYMALLOCARGTYPE)pik_parserCTX_PDECL)1668 void *pik_parserAlloc(void *(*mallocProc)(YYMALLOCARGTYPE) pik_parserCTX_PDECL){
1669   yyParser *yypParser;
1670   yypParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) );
1671   if( yypParser ){
1672     pik_parserCTX_STORE
1673     pik_parserInit(yypParser pik_parserCTX_PARAM);
1674   }
1675   return (void*)yypParser;
1676 }
1677 #endif /* pik_parser_ENGINEALWAYSONSTACK */
1678 
1679 
1680 /* The following function deletes the "minor type" or semantic value
1681 ** associated with a symbol.  The symbol can be either a terminal
1682 ** or nonterminal. "yymajor" is the symbol code, and "yypminor" is
1683 ** a pointer to the value to be deleted.  The code used to do the
1684 ** deletions is derived from the %destructor and/or %token_destructor
1685 ** directives of the input grammar.
1686 */
yy_destructor(yyParser * yypParser,YYCODETYPE yymajor,YYMINORTYPE * yypminor)1687 static void yy_destructor(
1688   yyParser *yypParser,    /* The parser */
1689   YYCODETYPE yymajor,     /* Type code for object to destroy */
1690   YYMINORTYPE *yypminor   /* The object to be destroyed */
1691 ){
1692   pik_parserARG_FETCH
1693   pik_parserCTX_FETCH
1694   switch( yymajor ){
1695     /* Here is inserted the actions which take place when a
1696     ** terminal or non-terminal is destroyed.  This can happen
1697     ** when the symbol is popped from the stack during a
1698     ** reduce or during error processing or when a parser is
1699     ** being destroyed before it is finished parsing.
1700     **
1701     ** Note: during a reduce, the only symbols destroyed are those
1702     ** which appear on the RHS of the rule, but which are *not* used
1703     ** inside the C code.
1704     */
1705 /********* Begin destructor definitions ***************************************/
1706     case 99: /* statement_list */
1707 {
1708 #line 494 "pikchr.y"
1709 pik_elist_free(p,(yypminor->yy227));
1710 #line 1735 "pikchr.c"
1711 }
1712       break;
1713     case 100: /* statement */
1714     case 101: /* unnamed_statement */
1715     case 102: /* basetype */
1716 {
1717 #line 496 "pikchr.y"
1718 pik_elem_free(p,(yypminor->yy36));
1719 #line 1744 "pikchr.c"
1720 }
1721       break;
1722 /********* End destructor definitions *****************************************/
1723     default:  break;   /* If no destructor action specified: do nothing */
1724   }
1725 }
1726 
1727 /*
1728 ** Pop the parser's stack once.
1729 **
1730 ** If there is a destructor routine associated with the token which
1731 ** is popped from the stack, then call it.
1732 */
yy_pop_parser_stack(yyParser * pParser)1733 static void yy_pop_parser_stack(yyParser *pParser){
1734   yyStackEntry *yytos;
1735   assert( pParser->yytos!=0 );
1736   assert( pParser->yytos > pParser->yystack );
1737   yytos = pParser->yytos--;
1738 #ifndef NDEBUG
1739   if( yyTraceFILE ){
1740     fprintf(yyTraceFILE,"%sPopping %s\n",
1741       yyTracePrompt,
1742       yyTokenName[yytos->major]);
1743   }
1744 #endif
1745   yy_destructor(pParser, yytos->major, &yytos->minor);
1746 }
1747 
1748 /*
1749 ** Clear all secondary memory allocations from the parser
1750 */
pik_parserFinalize(void * p)1751 void pik_parserFinalize(void *p){
1752   yyParser *pParser = (yyParser*)p;
1753   while( pParser->yytos>pParser->yystack ) yy_pop_parser_stack(pParser);
1754 #if YYSTACKDEPTH<=0
1755   if( pParser->yystack!=&pParser->yystk0 ) free(pParser->yystack);
1756 #endif
1757 }
1758 
1759 #ifndef pik_parser_ENGINEALWAYSONSTACK
1760 /*
1761 ** Deallocate and destroy a parser.  Destructors are called for
1762 ** all stack elements before shutting the parser down.
1763 **
1764 ** If the YYPARSEFREENEVERNULL macro exists (for example because it
1765 ** is defined in a %include section of the input grammar) then it is
1766 ** assumed that the input pointer is never NULL.
1767 */
pik_parserFree(void * p,void (* freeProc)(void *))1768 void pik_parserFree(
1769   void *p,                    /* The parser to be deleted */
1770   void (*freeProc)(void*)     /* Function used to reclaim memory */
1771 ){
1772 #ifndef YYPARSEFREENEVERNULL
1773   if( p==0 ) return;
1774 #endif
1775   pik_parserFinalize(p);
1776   (*freeProc)(p);
1777 }
1778 #endif /* pik_parser_ENGINEALWAYSONSTACK */
1779 
1780 /*
1781 ** Return the peak depth of the stack for a parser.
1782 */
1783 #ifdef YYTRACKMAXSTACKDEPTH
pik_parserStackPeak(void * p)1784 int pik_parserStackPeak(void *p){
1785   yyParser *pParser = (yyParser*)p;
1786   return pParser->yyhwm;
1787 }
1788 #endif
1789 
1790 /* This array of booleans keeps track of the parser statement
1791 ** coverage.  The element yycoverage[X][Y] is set when the parser
1792 ** is in state X and has a lookahead token Y.  In a well-tested
1793 ** systems, every element of this matrix should end up being set.
1794 */
1795 #if defined(YYCOVERAGE)
1796 static unsigned char yycoverage[YYNSTATE][YYNTOKEN];
1797 #endif
1798 
1799 /*
1800 ** Write into out a description of every state/lookahead combination that
1801 **
1802 **   (1)  has not been used by the parser, and
1803 **   (2)  is not a syntax error.
1804 **
1805 ** Return the number of missed state/lookahead combinations.
1806 */
1807 #if defined(YYCOVERAGE)
pik_parserCoverage(FILE * out)1808 int pik_parserCoverage(FILE *out){
1809   int stateno, iLookAhead, i;
1810   int nMissed = 0;
1811   for(stateno=0; stateno<YYNSTATE; stateno++){
1812     i = yy_shift_ofst[stateno];
1813     for(iLookAhead=0; iLookAhead<YYNTOKEN; iLookAhead++){
1814       if( yy_lookahead[i+iLookAhead]!=iLookAhead ) continue;
1815       if( yycoverage[stateno][iLookAhead]==0 ) nMissed++;
1816       if( out ){
1817         fprintf(out,"State %d lookahead %s %s\n", stateno,
1818                 yyTokenName[iLookAhead],
1819                 yycoverage[stateno][iLookAhead] ? "ok" : "missed");
1820       }
1821     }
1822   }
1823   return nMissed;
1824 }
1825 #endif
1826 
1827 /*
1828 ** Find the appropriate action for a parser given the terminal
1829 ** look-ahead token iLookAhead.
1830 */
yy_find_shift_action(YYCODETYPE iLookAhead,YYACTIONTYPE stateno)1831 static YYACTIONTYPE yy_find_shift_action(
1832   YYCODETYPE iLookAhead,    /* The look-ahead token */
1833   YYACTIONTYPE stateno      /* Current state number */
1834 ){
1835   int i;
1836 
1837   if( stateno>YY_MAX_SHIFT ) return stateno;
1838   assert( stateno <= YY_SHIFT_COUNT );
1839 #if defined(YYCOVERAGE)
1840   yycoverage[stateno][iLookAhead] = 1;
1841 #endif
1842   do{
1843     i = yy_shift_ofst[stateno];
1844     assert( i>=0 );
1845     assert( i<=YY_ACTTAB_COUNT );
1846     assert( i+YYNTOKEN<=(int)YY_NLOOKAHEAD );
1847     assert( iLookAhead!=YYNOCODE );
1848     assert( iLookAhead < YYNTOKEN );
1849     i += iLookAhead;
1850     assert( i<(int)YY_NLOOKAHEAD );
1851     if( yy_lookahead[i]!=iLookAhead ){
1852 #ifdef YYFALLBACK
1853       YYCODETYPE iFallback;            /* Fallback token */
1854       assert( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0]) );
1855       iFallback = yyFallback[iLookAhead];
1856       if( iFallback!=0 ){
1857 #ifndef NDEBUG
1858         if( yyTraceFILE ){
1859           fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
1860              yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
1861         }
1862 #endif
1863         assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */
1864         iLookAhead = iFallback;
1865         continue;
1866       }
1867 #endif
1868 #ifdef YYWILDCARD
1869       {
1870         int j = i - iLookAhead + YYWILDCARD;
1871         assert( j<(int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])) );
1872         if( yy_lookahead[j]==YYWILDCARD && iLookAhead>0 ){
1873 #ifndef NDEBUG
1874           if( yyTraceFILE ){
1875             fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
1876                yyTracePrompt, yyTokenName[iLookAhead],
1877                yyTokenName[YYWILDCARD]);
1878           }
1879 #endif /* NDEBUG */
1880           return yy_action[j];
1881         }
1882       }
1883 #endif /* YYWILDCARD */
1884       return yy_default[stateno];
1885     }else{
1886       assert( i>=0 && i<(int)(sizeof(yy_action)/sizeof(yy_action[0])) );
1887       return yy_action[i];
1888     }
1889   }while(1);
1890 }
1891 
1892 /*
1893 ** Find the appropriate action for a parser given the non-terminal
1894 ** look-ahead token iLookAhead.
1895 */
yy_find_reduce_action(YYACTIONTYPE stateno,YYCODETYPE iLookAhead)1896 static YYACTIONTYPE yy_find_reduce_action(
1897   YYACTIONTYPE stateno,     /* Current state number */
1898   YYCODETYPE iLookAhead     /* The look-ahead token */
1899 ){
1900   int i;
1901 #ifdef YYERRORSYMBOL
1902   if( stateno>YY_REDUCE_COUNT ){
1903     return yy_default[stateno];
1904   }
1905 #else
1906   assert( stateno<=YY_REDUCE_COUNT );
1907 #endif
1908   i = yy_reduce_ofst[stateno];
1909   assert( iLookAhead!=YYNOCODE );
1910   i += iLookAhead;
1911 #ifdef YYERRORSYMBOL
1912   if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
1913     return yy_default[stateno];
1914   }
1915 #else
1916   assert( i>=0 && i<YY_ACTTAB_COUNT );
1917   assert( yy_lookahead[i]==iLookAhead );
1918 #endif
1919   return yy_action[i];
1920 }
1921 
1922 /*
1923 ** The following routine is called if the stack overflows.
1924 */
yyStackOverflow(yyParser * yypParser)1925 static void yyStackOverflow(yyParser *yypParser){
1926    pik_parserARG_FETCH
1927    pik_parserCTX_FETCH
1928 #ifndef NDEBUG
1929    if( yyTraceFILE ){
1930      fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
1931    }
1932 #endif
1933    while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
1934    /* Here code is inserted which will execute if the parser
1935    ** stack every overflows */
1936 /******** Begin %stack_overflow code ******************************************/
1937 #line 528 "pikchr.y"
1938 
1939   pik_error(p, 0, "parser stack overflow");
1940 #line 1965 "pikchr.c"
1941 /******** End %stack_overflow code ********************************************/
1942    pik_parserARG_STORE /* Suppress warning about unused %extra_argument var */
1943    pik_parserCTX_STORE
1944 }
1945 
1946 /*
1947 ** Print tracing information for a SHIFT action
1948 */
1949 #ifndef NDEBUG
yyTraceShift(yyParser * yypParser,int yyNewState,const char * zTag)1950 static void yyTraceShift(yyParser *yypParser, int yyNewState, const char *zTag){
1951   if( yyTraceFILE ){
1952     if( yyNewState<YYNSTATE ){
1953       fprintf(yyTraceFILE,"%s%s '%s', go to state %d\n",
1954          yyTracePrompt, zTag, yyTokenName[yypParser->yytos->major],
1955          yyNewState);
1956     }else{
1957       fprintf(yyTraceFILE,"%s%s '%s', pending reduce %d\n",
1958          yyTracePrompt, zTag, yyTokenName[yypParser->yytos->major],
1959          yyNewState - YY_MIN_REDUCE);
1960     }
1961   }
1962 }
1963 #else
1964 # define yyTraceShift(X,Y,Z)
1965 #endif
1966 
1967 /*
1968 ** Perform a shift action.
1969 */
yy_shift(yyParser * yypParser,YYACTIONTYPE yyNewState,YYCODETYPE yyMajor,pik_parserTOKENTYPE yyMinor)1970 static void yy_shift(
1971   yyParser *yypParser,          /* The parser to be shifted */
1972   YYACTIONTYPE yyNewState,      /* The new state to shift in */
1973   YYCODETYPE yyMajor,           /* The major token to shift in */
1974   pik_parserTOKENTYPE yyMinor        /* The minor token to shift in */
1975 ){
1976   yyStackEntry *yytos;
1977   yypParser->yytos++;
1978 #ifdef YYTRACKMAXSTACKDEPTH
1979   if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
1980     yypParser->yyhwm++;
1981     assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) );
1982   }
1983 #endif
1984 #if YYSTACKDEPTH>0
1985   if( yypParser->yytos>yypParser->yystackEnd ){
1986     yypParser->yytos--;
1987     yyStackOverflow(yypParser);
1988     return;
1989   }
1990 #else
1991   if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz] ){
1992     if( yyGrowStack(yypParser) ){
1993       yypParser->yytos--;
1994       yyStackOverflow(yypParser);
1995       return;
1996     }
1997   }
1998 #endif
1999   if( yyNewState > YY_MAX_SHIFT ){
2000     yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
2001   }
2002   yytos = yypParser->yytos;
2003   yytos->stateno = yyNewState;
2004   yytos->major = yyMajor;
2005   yytos->minor.yy0 = yyMinor;
2006   yyTraceShift(yypParser, yyNewState, "Shift");
2007 }
2008 
2009 /* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side
2010 ** of that rule */
2011 static const YYCODETYPE yyRuleInfoLhs[] = {
2012    121,  /* (0) document ::= statement_list */
2013     99,  /* (1) statement_list ::= statement */
2014     99,  /* (2) statement_list ::= statement_list EOL statement */
2015    100,  /* (3) statement ::= */
2016    100,  /* (4) statement ::= direction */
2017    100,  /* (5) statement ::= lvalue ASSIGN rvalue */
2018    100,  /* (6) statement ::= PLACENAME COLON unnamed_statement */
2019    100,  /* (7) statement ::= PLACENAME COLON position */
2020    100,  /* (8) statement ::= unnamed_statement */
2021    100,  /* (9) statement ::= print prlist */
2022    100,  /* (10) statement ::= ASSERT LP expr EQ expr RP */
2023    100,  /* (11) statement ::= ASSERT LP position EQ position RP */
2024    100,  /* (12) statement ::= DEFINE ID CODEBLOCK */
2025    116,  /* (13) rvalue ::= PLACENAME */
2026    124,  /* (14) pritem ::= FILL */
2027    124,  /* (15) pritem ::= COLOR */
2028    124,  /* (16) pritem ::= THICKNESS */
2029    124,  /* (17) pritem ::= rvalue */
2030    124,  /* (18) pritem ::= STRING */
2031    125,  /* (19) prsep ::= COMMA */
2032    101,  /* (20) unnamed_statement ::= basetype attribute_list */
2033    102,  /* (21) basetype ::= CLASSNAME */
2034    102,  /* (22) basetype ::= STRING textposition */
2035    102,  /* (23) basetype ::= LB savelist statement_list RB */
2036    127,  /* (24) savelist ::= */
2037    119,  /* (25) relexpr ::= expr */
2038    119,  /* (26) relexpr ::= expr PERCENT */
2039    120,  /* (27) optrelexpr ::= */
2040    126,  /* (28) attribute_list ::= relexpr alist */
2041    129,  /* (29) attribute ::= numproperty relexpr */
2042    129,  /* (30) attribute ::= dashproperty expr */
2043    129,  /* (31) attribute ::= dashproperty */
2044    129,  /* (32) attribute ::= colorproperty rvalue */
2045    129,  /* (33) attribute ::= go direction optrelexpr */
2046    129,  /* (34) attribute ::= go direction even position */
2047    129,  /* (35) attribute ::= CLOSE */
2048    129,  /* (36) attribute ::= CHOP */
2049    129,  /* (37) attribute ::= FROM position */
2050    129,  /* (38) attribute ::= TO position */
2051    129,  /* (39) attribute ::= THEN */
2052    129,  /* (40) attribute ::= THEN optrelexpr HEADING expr */
2053    129,  /* (41) attribute ::= THEN optrelexpr EDGEPT */
2054    129,  /* (42) attribute ::= GO optrelexpr HEADING expr */
2055    129,  /* (43) attribute ::= GO optrelexpr EDGEPT */
2056    129,  /* (44) attribute ::= AT position */
2057    129,  /* (45) attribute ::= SAME */
2058    129,  /* (46) attribute ::= SAME AS object */
2059    129,  /* (47) attribute ::= STRING textposition */
2060    129,  /* (48) attribute ::= FIT */
2061    129,  /* (49) attribute ::= BEHIND object */
2062    132,  /* (50) withclause ::= DOT_E edge AT position */
2063    132,  /* (51) withclause ::= edge AT position */
2064    104,  /* (52) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */
2065    131,  /* (53) boolproperty ::= CW */
2066    131,  /* (54) boolproperty ::= CCW */
2067    131,  /* (55) boolproperty ::= LARROW */
2068    131,  /* (56) boolproperty ::= RARROW */
2069    131,  /* (57) boolproperty ::= LRARROW */
2070    131,  /* (58) boolproperty ::= INVIS */
2071    131,  /* (59) boolproperty ::= THICK */
2072    131,  /* (60) boolproperty ::= THIN */
2073    131,  /* (61) boolproperty ::= SOLID */
2074    115,  /* (62) textposition ::= */
2075    115,  /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|ALIGNED|BIG|SMALL */
2076    110,  /* (64) position ::= expr COMMA expr */
2077    110,  /* (65) position ::= place PLUS expr COMMA expr */
2078    110,  /* (66) position ::= place MINUS expr COMMA expr */
2079    110,  /* (67) position ::= place PLUS LP expr COMMA expr RP */
2080    110,  /* (68) position ::= place MINUS LP expr COMMA expr RP */
2081    110,  /* (69) position ::= LP position COMMA position RP */
2082    110,  /* (70) position ::= LP position RP */
2083    110,  /* (71) position ::= expr between position AND position */
2084    110,  /* (72) position ::= expr LT position COMMA position GT */
2085    110,  /* (73) position ::= expr ABOVE position */
2086    110,  /* (74) position ::= expr BELOW position */
2087    110,  /* (75) position ::= expr LEFT OF position */
2088    110,  /* (76) position ::= expr RIGHT OF position */
2089    110,  /* (77) position ::= expr ON HEADING EDGEPT OF position */
2090    110,  /* (78) position ::= expr HEADING EDGEPT OF position */
2091    110,  /* (79) position ::= expr EDGEPT OF position */
2092    110,  /* (80) position ::= expr ON HEADING expr FROM position */
2093    110,  /* (81) position ::= expr HEADING expr FROM position */
2094    111,  /* (82) place ::= edge OF object */
2095    134,  /* (83) place2 ::= object */
2096    134,  /* (84) place2 ::= object DOT_E edge */
2097    134,  /* (85) place2 ::= NTH VERTEX OF object */
2098    112,  /* (86) object ::= nth */
2099    112,  /* (87) object ::= nth OF|IN object */
2100    113,  /* (88) objectname ::= THIS */
2101    113,  /* (89) objectname ::= PLACENAME */
2102    113,  /* (90) objectname ::= objectname DOT_U PLACENAME */
2103    114,  /* (91) nth ::= NTH CLASSNAME */
2104    114,  /* (92) nth ::= NTH LAST CLASSNAME */
2105    114,  /* (93) nth ::= LAST CLASSNAME */
2106    114,  /* (94) nth ::= LAST */
2107    114,  /* (95) nth ::= NTH LB RB */
2108    114,  /* (96) nth ::= NTH LAST LB RB */
2109    114,  /* (97) nth ::= LAST LB RB */
2110    103,  /* (98) expr ::= expr PLUS expr */
2111    103,  /* (99) expr ::= expr MINUS expr */
2112    103,  /* (100) expr ::= expr STAR expr */
2113    103,  /* (101) expr ::= expr SLASH expr */
2114    103,  /* (102) expr ::= MINUS expr */
2115    103,  /* (103) expr ::= PLUS expr */
2116    103,  /* (104) expr ::= LP expr RP */
2117    103,  /* (105) expr ::= LP FILL|COLOR|THICKNESS RP */
2118    103,  /* (106) expr ::= NUMBER */
2119    103,  /* (107) expr ::= ID */
2120    103,  /* (108) expr ::= FUNC1 LP expr RP */
2121    103,  /* (109) expr ::= FUNC2 LP expr COMMA expr RP */
2122    103,  /* (110) expr ::= DIST LP position COMMA position RP */
2123    103,  /* (111) expr ::= place2 DOT_XY X */
2124    103,  /* (112) expr ::= place2 DOT_XY Y */
2125    103,  /* (113) expr ::= object DOT_L numproperty */
2126    103,  /* (114) expr ::= object DOT_L dashproperty */
2127    103,  /* (115) expr ::= object DOT_L colorproperty */
2128    117,  /* (116) lvalue ::= ID */
2129    117,  /* (117) lvalue ::= FILL */
2130    117,  /* (118) lvalue ::= COLOR */
2131    117,  /* (119) lvalue ::= THICKNESS */
2132    116,  /* (120) rvalue ::= expr */
2133    122,  /* (121) print ::= PRINT */
2134    123,  /* (122) prlist ::= pritem */
2135    123,  /* (123) prlist ::= prlist prsep pritem */
2136    106,  /* (124) direction ::= UP */
2137    106,  /* (125) direction ::= DOWN */
2138    106,  /* (126) direction ::= LEFT */
2139    106,  /* (127) direction ::= RIGHT */
2140    120,  /* (128) optrelexpr ::= relexpr */
2141    126,  /* (129) attribute_list ::= alist */
2142    128,  /* (130) alist ::= */
2143    128,  /* (131) alist ::= alist attribute */
2144    129,  /* (132) attribute ::= boolproperty */
2145    129,  /* (133) attribute ::= WITH withclause */
2146    130,  /* (134) go ::= GO */
2147    130,  /* (135) go ::= */
2148    118,  /* (136) even ::= UNTIL EVEN WITH */
2149    118,  /* (137) even ::= EVEN WITH */
2150    107,  /* (138) dashproperty ::= DOTTED */
2151    107,  /* (139) dashproperty ::= DASHED */
2152    108,  /* (140) colorproperty ::= FILL */
2153    108,  /* (141) colorproperty ::= COLOR */
2154    110,  /* (142) position ::= place */
2155    133,  /* (143) between ::= WAY BETWEEN */
2156    133,  /* (144) between ::= BETWEEN */
2157    133,  /* (145) between ::= OF THE WAY BETWEEN */
2158    111,  /* (146) place ::= place2 */
2159    105,  /* (147) edge ::= CENTER */
2160    105,  /* (148) edge ::= EDGEPT */
2161    105,  /* (149) edge ::= TOP */
2162    105,  /* (150) edge ::= BOTTOM */
2163    105,  /* (151) edge ::= START */
2164    105,  /* (152) edge ::= END */
2165    105,  /* (153) edge ::= RIGHT */
2166    105,  /* (154) edge ::= LEFT */
2167    112,  /* (155) object ::= objectname */
2168 };
2169 
2170 /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number
2171 ** of symbols on the right-hand side of that rule. */
2172 static const signed char yyRuleInfoNRhs[] = {
2173    -1,  /* (0) document ::= statement_list */
2174    -1,  /* (1) statement_list ::= statement */
2175    -3,  /* (2) statement_list ::= statement_list EOL statement */
2176     0,  /* (3) statement ::= */
2177    -1,  /* (4) statement ::= direction */
2178    -3,  /* (5) statement ::= lvalue ASSIGN rvalue */
2179    -3,  /* (6) statement ::= PLACENAME COLON unnamed_statement */
2180    -3,  /* (7) statement ::= PLACENAME COLON position */
2181    -1,  /* (8) statement ::= unnamed_statement */
2182    -2,  /* (9) statement ::= print prlist */
2183    -6,  /* (10) statement ::= ASSERT LP expr EQ expr RP */
2184    -6,  /* (11) statement ::= ASSERT LP position EQ position RP */
2185    -3,  /* (12) statement ::= DEFINE ID CODEBLOCK */
2186    -1,  /* (13) rvalue ::= PLACENAME */
2187    -1,  /* (14) pritem ::= FILL */
2188    -1,  /* (15) pritem ::= COLOR */
2189    -1,  /* (16) pritem ::= THICKNESS */
2190    -1,  /* (17) pritem ::= rvalue */
2191    -1,  /* (18) pritem ::= STRING */
2192    -1,  /* (19) prsep ::= COMMA */
2193    -2,  /* (20) unnamed_statement ::= basetype attribute_list */
2194    -1,  /* (21) basetype ::= CLASSNAME */
2195    -2,  /* (22) basetype ::= STRING textposition */
2196    -4,  /* (23) basetype ::= LB savelist statement_list RB */
2197     0,  /* (24) savelist ::= */
2198    -1,  /* (25) relexpr ::= expr */
2199    -2,  /* (26) relexpr ::= expr PERCENT */
2200     0,  /* (27) optrelexpr ::= */
2201    -2,  /* (28) attribute_list ::= relexpr alist */
2202    -2,  /* (29) attribute ::= numproperty relexpr */
2203    -2,  /* (30) attribute ::= dashproperty expr */
2204    -1,  /* (31) attribute ::= dashproperty */
2205    -2,  /* (32) attribute ::= colorproperty rvalue */
2206    -3,  /* (33) attribute ::= go direction optrelexpr */
2207    -4,  /* (34) attribute ::= go direction even position */
2208    -1,  /* (35) attribute ::= CLOSE */
2209    -1,  /* (36) attribute ::= CHOP */
2210    -2,  /* (37) attribute ::= FROM position */
2211    -2,  /* (38) attribute ::= TO position */
2212    -1,  /* (39) attribute ::= THEN */
2213    -4,  /* (40) attribute ::= THEN optrelexpr HEADING expr */
2214    -3,  /* (41) attribute ::= THEN optrelexpr EDGEPT */
2215    -4,  /* (42) attribute ::= GO optrelexpr HEADING expr */
2216    -3,  /* (43) attribute ::= GO optrelexpr EDGEPT */
2217    -2,  /* (44) attribute ::= AT position */
2218    -1,  /* (45) attribute ::= SAME */
2219    -3,  /* (46) attribute ::= SAME AS object */
2220    -2,  /* (47) attribute ::= STRING textposition */
2221    -1,  /* (48) attribute ::= FIT */
2222    -2,  /* (49) attribute ::= BEHIND object */
2223    -4,  /* (50) withclause ::= DOT_E edge AT position */
2224    -3,  /* (51) withclause ::= edge AT position */
2225    -1,  /* (52) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */
2226    -1,  /* (53) boolproperty ::= CW */
2227    -1,  /* (54) boolproperty ::= CCW */
2228    -1,  /* (55) boolproperty ::= LARROW */
2229    -1,  /* (56) boolproperty ::= RARROW */
2230    -1,  /* (57) boolproperty ::= LRARROW */
2231    -1,  /* (58) boolproperty ::= INVIS */
2232    -1,  /* (59) boolproperty ::= THICK */
2233    -1,  /* (60) boolproperty ::= THIN */
2234    -1,  /* (61) boolproperty ::= SOLID */
2235     0,  /* (62) textposition ::= */
2236    -2,  /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|ALIGNED|BIG|SMALL */
2237    -3,  /* (64) position ::= expr COMMA expr */
2238    -5,  /* (65) position ::= place PLUS expr COMMA expr */
2239    -5,  /* (66) position ::= place MINUS expr COMMA expr */
2240    -7,  /* (67) position ::= place PLUS LP expr COMMA expr RP */
2241    -7,  /* (68) position ::= place MINUS LP expr COMMA expr RP */
2242    -5,  /* (69) position ::= LP position COMMA position RP */
2243    -3,  /* (70) position ::= LP position RP */
2244    -5,  /* (71) position ::= expr between position AND position */
2245    -6,  /* (72) position ::= expr LT position COMMA position GT */
2246    -3,  /* (73) position ::= expr ABOVE position */
2247    -3,  /* (74) position ::= expr BELOW position */
2248    -4,  /* (75) position ::= expr LEFT OF position */
2249    -4,  /* (76) position ::= expr RIGHT OF position */
2250    -6,  /* (77) position ::= expr ON HEADING EDGEPT OF position */
2251    -5,  /* (78) position ::= expr HEADING EDGEPT OF position */
2252    -4,  /* (79) position ::= expr EDGEPT OF position */
2253    -6,  /* (80) position ::= expr ON HEADING expr FROM position */
2254    -5,  /* (81) position ::= expr HEADING expr FROM position */
2255    -3,  /* (82) place ::= edge OF object */
2256    -1,  /* (83) place2 ::= object */
2257    -3,  /* (84) place2 ::= object DOT_E edge */
2258    -4,  /* (85) place2 ::= NTH VERTEX OF object */
2259    -1,  /* (86) object ::= nth */
2260    -3,  /* (87) object ::= nth OF|IN object */
2261    -1,  /* (88) objectname ::= THIS */
2262    -1,  /* (89) objectname ::= PLACENAME */
2263    -3,  /* (90) objectname ::= objectname DOT_U PLACENAME */
2264    -2,  /* (91) nth ::= NTH CLASSNAME */
2265    -3,  /* (92) nth ::= NTH LAST CLASSNAME */
2266    -2,  /* (93) nth ::= LAST CLASSNAME */
2267    -1,  /* (94) nth ::= LAST */
2268    -3,  /* (95) nth ::= NTH LB RB */
2269    -4,  /* (96) nth ::= NTH LAST LB RB */
2270    -3,  /* (97) nth ::= LAST LB RB */
2271    -3,  /* (98) expr ::= expr PLUS expr */
2272    -3,  /* (99) expr ::= expr MINUS expr */
2273    -3,  /* (100) expr ::= expr STAR expr */
2274    -3,  /* (101) expr ::= expr SLASH expr */
2275    -2,  /* (102) expr ::= MINUS expr */
2276    -2,  /* (103) expr ::= PLUS expr */
2277    -3,  /* (104) expr ::= LP expr RP */
2278    -3,  /* (105) expr ::= LP FILL|COLOR|THICKNESS RP */
2279    -1,  /* (106) expr ::= NUMBER */
2280    -1,  /* (107) expr ::= ID */
2281    -4,  /* (108) expr ::= FUNC1 LP expr RP */
2282    -6,  /* (109) expr ::= FUNC2 LP expr COMMA expr RP */
2283    -6,  /* (110) expr ::= DIST LP position COMMA position RP */
2284    -3,  /* (111) expr ::= place2 DOT_XY X */
2285    -3,  /* (112) expr ::= place2 DOT_XY Y */
2286    -3,  /* (113) expr ::= object DOT_L numproperty */
2287    -3,  /* (114) expr ::= object DOT_L dashproperty */
2288    -3,  /* (115) expr ::= object DOT_L colorproperty */
2289    -1,  /* (116) lvalue ::= ID */
2290    -1,  /* (117) lvalue ::= FILL */
2291    -1,  /* (118) lvalue ::= COLOR */
2292    -1,  /* (119) lvalue ::= THICKNESS */
2293    -1,  /* (120) rvalue ::= expr */
2294    -1,  /* (121) print ::= PRINT */
2295    -1,  /* (122) prlist ::= pritem */
2296    -3,  /* (123) prlist ::= prlist prsep pritem */
2297    -1,  /* (124) direction ::= UP */
2298    -1,  /* (125) direction ::= DOWN */
2299    -1,  /* (126) direction ::= LEFT */
2300    -1,  /* (127) direction ::= RIGHT */
2301    -1,  /* (128) optrelexpr ::= relexpr */
2302    -1,  /* (129) attribute_list ::= alist */
2303     0,  /* (130) alist ::= */
2304    -2,  /* (131) alist ::= alist attribute */
2305    -1,  /* (132) attribute ::= boolproperty */
2306    -2,  /* (133) attribute ::= WITH withclause */
2307    -1,  /* (134) go ::= GO */
2308     0,  /* (135) go ::= */
2309    -3,  /* (136) even ::= UNTIL EVEN WITH */
2310    -2,  /* (137) even ::= EVEN WITH */
2311    -1,  /* (138) dashproperty ::= DOTTED */
2312    -1,  /* (139) dashproperty ::= DASHED */
2313    -1,  /* (140) colorproperty ::= FILL */
2314    -1,  /* (141) colorproperty ::= COLOR */
2315    -1,  /* (142) position ::= place */
2316    -2,  /* (143) between ::= WAY BETWEEN */
2317    -1,  /* (144) between ::= BETWEEN */
2318    -4,  /* (145) between ::= OF THE WAY BETWEEN */
2319    -1,  /* (146) place ::= place2 */
2320    -1,  /* (147) edge ::= CENTER */
2321    -1,  /* (148) edge ::= EDGEPT */
2322    -1,  /* (149) edge ::= TOP */
2323    -1,  /* (150) edge ::= BOTTOM */
2324    -1,  /* (151) edge ::= START */
2325    -1,  /* (152) edge ::= END */
2326    -1,  /* (153) edge ::= RIGHT */
2327    -1,  /* (154) edge ::= LEFT */
2328    -1,  /* (155) object ::= objectname */
2329 };
2330 
2331 static void yy_accept(yyParser*);  /* Forward Declaration */
2332 
2333 /*
2334 ** Perform a reduce action and the shift that must immediately
2335 ** follow the reduce.
2336 **
2337 ** The yyLookahead and yyLookaheadToken parameters provide reduce actions
2338 ** access to the lookahead token (if any).  The yyLookahead will be YYNOCODE
2339 ** if the lookahead token has already been consumed.  As this procedure is
2340 ** only called from one place, optimizing compilers will in-line it, which
2341 ** means that the extra parameters have no performance impact.
2342 */
yy_reduce(yyParser * yypParser,unsigned int yyruleno,int yyLookahead,pik_parserTOKENTYPE yyLookaheadToken pik_parserCTX_PDECL)2343 static YYACTIONTYPE yy_reduce(
2344   yyParser *yypParser,         /* The parser */
2345   unsigned int yyruleno,       /* Number of the rule by which to reduce */
2346   int yyLookahead,             /* Lookahead token, or YYNOCODE if none */
2347   pik_parserTOKENTYPE yyLookaheadToken  /* Value of the lookahead token */
2348   pik_parserCTX_PDECL                   /* %extra_context */
2349 ){
2350   int yygoto;                     /* The next state */
2351   YYACTIONTYPE yyact;             /* The next action */
2352   yyStackEntry *yymsp;            /* The top of the parser's stack */
2353   int yysize;                     /* Amount to pop the stack */
2354   pik_parserARG_FETCH
2355   (void)yyLookahead;
2356   (void)yyLookaheadToken;
2357   yymsp = yypParser->yytos;
2358   assert( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) );
2359 #ifndef NDEBUG
2360   if( yyTraceFILE ){
2361     yysize = yyRuleInfoNRhs[yyruleno];
2362     if( yysize ){
2363       fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n",
2364         yyTracePrompt,
2365         yyruleno, yyRuleName[yyruleno],
2366         yyruleno<YYNRULE_WITH_ACTION ? "" : " without external action",
2367         yymsp[yysize].stateno);
2368     }else{
2369       fprintf(yyTraceFILE, "%sReduce %d [%s]%s.\n",
2370         yyTracePrompt, yyruleno, yyRuleName[yyruleno],
2371         yyruleno<YYNRULE_WITH_ACTION ? "" : " without external action");
2372     }
2373   }
2374 #endif /* NDEBUG */
2375 
2376   /* Check that the stack is large enough to grow by a single entry
2377   ** if the RHS of the rule is empty.  This ensures that there is room
2378   ** enough on the stack to push the LHS value */
2379   if( yyRuleInfoNRhs[yyruleno]==0 ){
2380 #ifdef YYTRACKMAXSTACKDEPTH
2381     if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
2382       yypParser->yyhwm++;
2383       assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack));
2384     }
2385 #endif
2386 #if YYSTACKDEPTH>0
2387     if( yypParser->yytos>=yypParser->yystackEnd ){
2388       yyStackOverflow(yypParser);
2389       /* The call to yyStackOverflow() above pops the stack until it is
2390       ** empty, causing the main parser loop to exit.  So the return value
2391       ** is never used and does not matter. */
2392       return 0;
2393     }
2394 #else
2395     if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){
2396       if( yyGrowStack(yypParser) ){
2397         yyStackOverflow(yypParser);
2398         /* The call to yyStackOverflow() above pops the stack until it is
2399         ** empty, causing the main parser loop to exit.  So the return value
2400         ** is never used and does not matter. */
2401         return 0;
2402       }
2403       yymsp = yypParser->yytos;
2404     }
2405 #endif
2406   }
2407 
2408   switch( yyruleno ){
2409   /* Beginning here are the reduction cases.  A typical example
2410   ** follows:
2411   **   case 0:
2412   **  #line <lineno> <grammarfile>
2413   **     { ... }           // User supplied code
2414   **  #line <lineno> <thisfile>
2415   **     break;
2416   */
2417 /********** Begin reduce actions **********************************************/
2418         YYMINORTYPE yylhsminor;
2419       case 0: /* document ::= statement_list */
2420 #line 532 "pikchr.y"
2421 {pik_render(p,yymsp[0].minor.yy227);}
2422 #line 2447 "pikchr.c"
2423         break;
2424       case 1: /* statement_list ::= statement */
2425 #line 535 "pikchr.y"
2426 { yylhsminor.yy227 = pik_elist_append(p,0,yymsp[0].minor.yy36); }
2427 #line 2452 "pikchr.c"
2428   yymsp[0].minor.yy227 = yylhsminor.yy227;
2429         break;
2430       case 2: /* statement_list ::= statement_list EOL statement */
2431 #line 537 "pikchr.y"
2432 { yylhsminor.yy227 = pik_elist_append(p,yymsp[-2].minor.yy227,yymsp[0].minor.yy36); }
2433 #line 2458 "pikchr.c"
2434   yymsp[-2].minor.yy227 = yylhsminor.yy227;
2435         break;
2436       case 3: /* statement ::= */
2437 #line 540 "pikchr.y"
2438 { yymsp[1].minor.yy36 = 0; }
2439 #line 2464 "pikchr.c"
2440         break;
2441       case 4: /* statement ::= direction */
2442 #line 541 "pikchr.y"
2443 { pik_set_direction(p,yymsp[0].minor.yy0.eCode);  yylhsminor.yy36=0; }
2444 #line 2469 "pikchr.c"
2445   yymsp[0].minor.yy36 = yylhsminor.yy36;
2446         break;
2447       case 5: /* statement ::= lvalue ASSIGN rvalue */
2448 #line 542 "pikchr.y"
2449 {pik_set_var(p,&yymsp[-2].minor.yy0,yymsp[0].minor.yy153,&yymsp[-1].minor.yy0); yylhsminor.yy36=0;}
2450 #line 2475 "pikchr.c"
2451   yymsp[-2].minor.yy36 = yylhsminor.yy36;
2452         break;
2453       case 6: /* statement ::= PLACENAME COLON unnamed_statement */
2454 #line 544 "pikchr.y"
2455 { yylhsminor.yy36 = yymsp[0].minor.yy36;  pik_elem_setname(p,yymsp[0].minor.yy36,&yymsp[-2].minor.yy0); }
2456 #line 2481 "pikchr.c"
2457   yymsp[-2].minor.yy36 = yylhsminor.yy36;
2458         break;
2459       case 7: /* statement ::= PLACENAME COLON position */
2460 #line 546 "pikchr.y"
2461 { yylhsminor.yy36 = pik_elem_new(p,0,0,0);
2462                  if(yylhsminor.yy36){ yylhsminor.yy36->ptAt = yymsp[0].minor.yy79; pik_elem_setname(p,yylhsminor.yy36,&yymsp[-2].minor.yy0); }}
2463 #line 2488 "pikchr.c"
2464   yymsp[-2].minor.yy36 = yylhsminor.yy36;
2465         break;
2466       case 8: /* statement ::= unnamed_statement */
2467 #line 548 "pikchr.y"
2468 {yylhsminor.yy36 = yymsp[0].minor.yy36;}
2469 #line 2494 "pikchr.c"
2470   yymsp[0].minor.yy36 = yylhsminor.yy36;
2471         break;
2472       case 9: /* statement ::= print prlist */
2473 #line 549 "pikchr.y"
2474 {pik_append(p,"<br>\n",5); yymsp[-1].minor.yy36=0;}
2475 #line 2500 "pikchr.c"
2476         break;
2477       case 10: /* statement ::= ASSERT LP expr EQ expr RP */
2478 #line 554 "pikchr.y"
2479 {yymsp[-5].minor.yy36=pik_assert(p,yymsp[-3].minor.yy153,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy153);}
2480 #line 2505 "pikchr.c"
2481         break;
2482       case 11: /* statement ::= ASSERT LP position EQ position RP */
2483 #line 556 "pikchr.y"
2484 {yymsp[-5].minor.yy36=pik_position_assert(p,&yymsp[-3].minor.yy79,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy79);}
2485 #line 2510 "pikchr.c"
2486         break;
2487       case 12: /* statement ::= DEFINE ID CODEBLOCK */
2488 #line 557 "pikchr.y"
2489 {yymsp[-2].minor.yy36=0; pik_add_macro(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);}
2490 #line 2515 "pikchr.c"
2491         break;
2492       case 13: /* rvalue ::= PLACENAME */
2493 #line 568 "pikchr.y"
2494 {yylhsminor.yy153 = pik_lookup_color(p,&yymsp[0].minor.yy0);}
2495 #line 2520 "pikchr.c"
2496   yymsp[0].minor.yy153 = yylhsminor.yy153;
2497         break;
2498       case 14: /* pritem ::= FILL */
2499       case 15: /* pritem ::= COLOR */ yytestcase(yyruleno==15);
2500       case 16: /* pritem ::= THICKNESS */ yytestcase(yyruleno==16);
2501 #line 573 "pikchr.y"
2502 {pik_append_num(p,"",pik_value(p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.n,0));}
2503 #line 2528 "pikchr.c"
2504         break;
2505       case 17: /* pritem ::= rvalue */
2506 #line 576 "pikchr.y"
2507 {pik_append_num(p,"",yymsp[0].minor.yy153);}
2508 #line 2533 "pikchr.c"
2509         break;
2510       case 18: /* pritem ::= STRING */
2511 #line 577 "pikchr.y"
2512 {pik_append_text(p,yymsp[0].minor.yy0.z+1,yymsp[0].minor.yy0.n-2,0);}
2513 #line 2538 "pikchr.c"
2514         break;
2515       case 19: /* prsep ::= COMMA */
2516 #line 578 "pikchr.y"
2517 {pik_append(p, " ", 1);}
2518 #line 2543 "pikchr.c"
2519         break;
2520       case 20: /* unnamed_statement ::= basetype attribute_list */
2521 #line 581 "pikchr.y"
2522 {yylhsminor.yy36 = yymsp[-1].minor.yy36; pik_after_adding_attributes(p,yylhsminor.yy36);}
2523 #line 2548 "pikchr.c"
2524   yymsp[-1].minor.yy36 = yylhsminor.yy36;
2525         break;
2526       case 21: /* basetype ::= CLASSNAME */
2527 #line 583 "pikchr.y"
2528 {yylhsminor.yy36 = pik_elem_new(p,&yymsp[0].minor.yy0,0,0); }
2529 #line 2554 "pikchr.c"
2530   yymsp[0].minor.yy36 = yylhsminor.yy36;
2531         break;
2532       case 22: /* basetype ::= STRING textposition */
2533 #line 585 "pikchr.y"
2534 {yymsp[-1].minor.yy0.eCode = yymsp[0].minor.yy164; yylhsminor.yy36 = pik_elem_new(p,0,&yymsp[-1].minor.yy0,0); }
2535 #line 2560 "pikchr.c"
2536   yymsp[-1].minor.yy36 = yylhsminor.yy36;
2537         break;
2538       case 23: /* basetype ::= LB savelist statement_list RB */
2539 #line 587 "pikchr.y"
2540 { p->list = yymsp[-2].minor.yy227; yymsp[-3].minor.yy36 = pik_elem_new(p,0,0,yymsp[-1].minor.yy227); if(yymsp[-3].minor.yy36) yymsp[-3].minor.yy36->errTok = yymsp[0].minor.yy0; }
2541 #line 2566 "pikchr.c"
2542         break;
2543       case 24: /* savelist ::= */
2544 #line 592 "pikchr.y"
2545 {yymsp[1].minor.yy227 = p->list; p->list = 0;}
2546 #line 2571 "pikchr.c"
2547         break;
2548       case 25: /* relexpr ::= expr */
2549 #line 599 "pikchr.y"
2550 {yylhsminor.yy10.rAbs = yymsp[0].minor.yy153; yylhsminor.yy10.rRel = 0;}
2551 #line 2576 "pikchr.c"
2552   yymsp[0].minor.yy10 = yylhsminor.yy10;
2553         break;
2554       case 26: /* relexpr ::= expr PERCENT */
2555 #line 600 "pikchr.y"
2556 {yylhsminor.yy10.rAbs = 0; yylhsminor.yy10.rRel = yymsp[-1].minor.yy153/100;}
2557 #line 2582 "pikchr.c"
2558   yymsp[-1].minor.yy10 = yylhsminor.yy10;
2559         break;
2560       case 27: /* optrelexpr ::= */
2561 #line 602 "pikchr.y"
2562 {yymsp[1].minor.yy10.rAbs = 0; yymsp[1].minor.yy10.rRel = 1.0;}
2563 #line 2588 "pikchr.c"
2564         break;
2565       case 28: /* attribute_list ::= relexpr alist */
2566 #line 604 "pikchr.y"
2567 {pik_add_direction(p,0,&yymsp[-1].minor.yy10);}
2568 #line 2593 "pikchr.c"
2569         break;
2570       case 29: /* attribute ::= numproperty relexpr */
2571 #line 608 "pikchr.y"
2572 { pik_set_numprop(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy10); }
2573 #line 2598 "pikchr.c"
2574         break;
2575       case 30: /* attribute ::= dashproperty expr */
2576 #line 609 "pikchr.y"
2577 { pik_set_dashed(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy153); }
2578 #line 2603 "pikchr.c"
2579         break;
2580       case 31: /* attribute ::= dashproperty */
2581 #line 610 "pikchr.y"
2582 { pik_set_dashed(p,&yymsp[0].minor.yy0,0);  }
2583 #line 2608 "pikchr.c"
2584         break;
2585       case 32: /* attribute ::= colorproperty rvalue */
2586 #line 611 "pikchr.y"
2587 { pik_set_clrprop(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy153); }
2588 #line 2613 "pikchr.c"
2589         break;
2590       case 33: /* attribute ::= go direction optrelexpr */
2591 #line 612 "pikchr.y"
2592 { pik_add_direction(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy10);}
2593 #line 2618 "pikchr.c"
2594         break;
2595       case 34: /* attribute ::= go direction even position */
2596 #line 613 "pikchr.y"
2597 {pik_evenwith(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy79);}
2598 #line 2623 "pikchr.c"
2599         break;
2600       case 35: /* attribute ::= CLOSE */
2601 #line 614 "pikchr.y"
2602 { pik_close_path(p,&yymsp[0].minor.yy0); }
2603 #line 2628 "pikchr.c"
2604         break;
2605       case 36: /* attribute ::= CHOP */
2606 #line 615 "pikchr.y"
2607 { p->cur->bChop = 1; }
2608 #line 2633 "pikchr.c"
2609         break;
2610       case 37: /* attribute ::= FROM position */
2611 #line 616 "pikchr.y"
2612 { pik_set_from(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy79); }
2613 #line 2638 "pikchr.c"
2614         break;
2615       case 38: /* attribute ::= TO position */
2616 #line 617 "pikchr.y"
2617 { pik_add_to(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy79); }
2618 #line 2643 "pikchr.c"
2619         break;
2620       case 39: /* attribute ::= THEN */
2621 #line 618 "pikchr.y"
2622 { pik_then(p, &yymsp[0].minor.yy0, p->cur); }
2623 #line 2648 "pikchr.c"
2624         break;
2625       case 40: /* attribute ::= THEN optrelexpr HEADING expr */
2626       case 42: /* attribute ::= GO optrelexpr HEADING expr */ yytestcase(yyruleno==42);
2627 #line 620 "pikchr.y"
2628 {pik_move_hdg(p,&yymsp[-2].minor.yy10,&yymsp[-1].minor.yy0,yymsp[0].minor.yy153,0,&yymsp[-3].minor.yy0);}
2629 #line 2654 "pikchr.c"
2630         break;
2631       case 41: /* attribute ::= THEN optrelexpr EDGEPT */
2632       case 43: /* attribute ::= GO optrelexpr EDGEPT */ yytestcase(yyruleno==43);
2633 #line 621 "pikchr.y"
2634 {pik_move_hdg(p,&yymsp[-1].minor.yy10,0,0,&yymsp[0].minor.yy0,&yymsp[-2].minor.yy0);}
2635 #line 2660 "pikchr.c"
2636         break;
2637       case 44: /* attribute ::= AT position */
2638 #line 626 "pikchr.y"
2639 { pik_set_at(p,0,&yymsp[0].minor.yy79,&yymsp[-1].minor.yy0); }
2640 #line 2665 "pikchr.c"
2641         break;
2642       case 45: /* attribute ::= SAME */
2643 #line 628 "pikchr.y"
2644 {pik_same(p,0,&yymsp[0].minor.yy0);}
2645 #line 2670 "pikchr.c"
2646         break;
2647       case 46: /* attribute ::= SAME AS object */
2648 #line 629 "pikchr.y"
2649 {pik_same(p,yymsp[0].minor.yy36,&yymsp[-2].minor.yy0);}
2650 #line 2675 "pikchr.c"
2651         break;
2652       case 47: /* attribute ::= STRING textposition */
2653 #line 630 "pikchr.y"
2654 {pik_add_txt(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy164);}
2655 #line 2680 "pikchr.c"
2656         break;
2657       case 48: /* attribute ::= FIT */
2658 #line 631 "pikchr.y"
2659 {pik_size_to_fit(p,&yymsp[0].minor.yy0,3); }
2660 #line 2685 "pikchr.c"
2661         break;
2662       case 49: /* attribute ::= BEHIND object */
2663 #line 632 "pikchr.y"
2664 {pik_behind(p,yymsp[0].minor.yy36);}
2665 #line 2690 "pikchr.c"
2666         break;
2667       case 50: /* withclause ::= DOT_E edge AT position */
2668       case 51: /* withclause ::= edge AT position */ yytestcase(yyruleno==51);
2669 #line 640 "pikchr.y"
2670 { pik_set_at(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy79,&yymsp[-1].minor.yy0); }
2671 #line 2696 "pikchr.c"
2672         break;
2673       case 52: /* numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */
2674 #line 644 "pikchr.y"
2675 {yylhsminor.yy0 = yymsp[0].minor.yy0;}
2676 #line 2701 "pikchr.c"
2677   yymsp[0].minor.yy0 = yylhsminor.yy0;
2678         break;
2679       case 53: /* boolproperty ::= CW */
2680 #line 655 "pikchr.y"
2681 {p->cur->cw = 1;}
2682 #line 2707 "pikchr.c"
2683         break;
2684       case 54: /* boolproperty ::= CCW */
2685 #line 656 "pikchr.y"
2686 {p->cur->cw = 0;}
2687 #line 2712 "pikchr.c"
2688         break;
2689       case 55: /* boolproperty ::= LARROW */
2690 #line 657 "pikchr.y"
2691 {p->cur->larrow=1; p->cur->rarrow=0; }
2692 #line 2717 "pikchr.c"
2693         break;
2694       case 56: /* boolproperty ::= RARROW */
2695 #line 658 "pikchr.y"
2696 {p->cur->larrow=0; p->cur->rarrow=1; }
2697 #line 2722 "pikchr.c"
2698         break;
2699       case 57: /* boolproperty ::= LRARROW */
2700 #line 659 "pikchr.y"
2701 {p->cur->larrow=1; p->cur->rarrow=1; }
2702 #line 2727 "pikchr.c"
2703         break;
2704       case 58: /* boolproperty ::= INVIS */
2705 #line 660 "pikchr.y"
2706 {p->cur->sw = 0.0;}
2707 #line 2732 "pikchr.c"
2708         break;
2709       case 59: /* boolproperty ::= THICK */
2710 #line 661 "pikchr.y"
2711 {p->cur->sw *= 1.5;}
2712 #line 2737 "pikchr.c"
2713         break;
2714       case 60: /* boolproperty ::= THIN */
2715 #line 662 "pikchr.y"
2716 {p->cur->sw *= 0.67;}
2717 #line 2742 "pikchr.c"
2718         break;
2719       case 61: /* boolproperty ::= SOLID */
2720 #line 663 "pikchr.y"
2721 {p->cur->sw = pik_value(p,"thickness",9,0);
2722                                p->cur->dotted = p->cur->dashed = 0.0;}
2723 #line 2748 "pikchr.c"
2724         break;
2725       case 62: /* textposition ::= */
2726 #line 666 "pikchr.y"
2727 {yymsp[1].minor.yy164 = 0;}
2728 #line 2753 "pikchr.c"
2729         break;
2730       case 63: /* textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|ALIGNED|BIG|SMALL */
2731 #line 669 "pikchr.y"
2732 {yylhsminor.yy164 = (short int)pik_text_position(yymsp[-1].minor.yy164,&yymsp[0].minor.yy0);}
2733 #line 2758 "pikchr.c"
2734   yymsp[-1].minor.yy164 = yylhsminor.yy164;
2735         break;
2736       case 64: /* position ::= expr COMMA expr */
2737 #line 672 "pikchr.y"
2738 {yylhsminor.yy79.x=yymsp[-2].minor.yy153; yylhsminor.yy79.y=yymsp[0].minor.yy153;}
2739 #line 2764 "pikchr.c"
2740   yymsp[-2].minor.yy79 = yylhsminor.yy79;
2741         break;
2742       case 65: /* position ::= place PLUS expr COMMA expr */
2743 #line 674 "pikchr.y"
2744 {yylhsminor.yy79.x=yymsp[-4].minor.yy79.x+yymsp[-2].minor.yy153; yylhsminor.yy79.y=yymsp[-4].minor.yy79.y+yymsp[0].minor.yy153;}
2745 #line 2770 "pikchr.c"
2746   yymsp[-4].minor.yy79 = yylhsminor.yy79;
2747         break;
2748       case 66: /* position ::= place MINUS expr COMMA expr */
2749 #line 675 "pikchr.y"
2750 {yylhsminor.yy79.x=yymsp[-4].minor.yy79.x-yymsp[-2].minor.yy153; yylhsminor.yy79.y=yymsp[-4].minor.yy79.y-yymsp[0].minor.yy153;}
2751 #line 2776 "pikchr.c"
2752   yymsp[-4].minor.yy79 = yylhsminor.yy79;
2753         break;
2754       case 67: /* position ::= place PLUS LP expr COMMA expr RP */
2755 #line 677 "pikchr.y"
2756 {yylhsminor.yy79.x=yymsp[-6].minor.yy79.x+yymsp[-3].minor.yy153; yylhsminor.yy79.y=yymsp[-6].minor.yy79.y+yymsp[-1].minor.yy153;}
2757 #line 2782 "pikchr.c"
2758   yymsp[-6].minor.yy79 = yylhsminor.yy79;
2759         break;
2760       case 68: /* position ::= place MINUS LP expr COMMA expr RP */
2761 #line 679 "pikchr.y"
2762 {yylhsminor.yy79.x=yymsp[-6].minor.yy79.x-yymsp[-3].minor.yy153; yylhsminor.yy79.y=yymsp[-6].minor.yy79.y-yymsp[-1].minor.yy153;}
2763 #line 2788 "pikchr.c"
2764   yymsp[-6].minor.yy79 = yylhsminor.yy79;
2765         break;
2766       case 69: /* position ::= LP position COMMA position RP */
2767 #line 680 "pikchr.y"
2768 {yymsp[-4].minor.yy79.x=yymsp[-3].minor.yy79.x; yymsp[-4].minor.yy79.y=yymsp[-1].minor.yy79.y;}
2769 #line 2794 "pikchr.c"
2770         break;
2771       case 70: /* position ::= LP position RP */
2772 #line 681 "pikchr.y"
2773 {yymsp[-2].minor.yy79=yymsp[-1].minor.yy79;}
2774 #line 2799 "pikchr.c"
2775         break;
2776       case 71: /* position ::= expr between position AND position */
2777 #line 683 "pikchr.y"
2778 {yylhsminor.yy79 = pik_position_between(yymsp[-4].minor.yy153,yymsp[-2].minor.yy79,yymsp[0].minor.yy79);}
2779 #line 2804 "pikchr.c"
2780   yymsp[-4].minor.yy79 = yylhsminor.yy79;
2781         break;
2782       case 72: /* position ::= expr LT position COMMA position GT */
2783 #line 685 "pikchr.y"
2784 {yylhsminor.yy79 = pik_position_between(yymsp[-5].minor.yy153,yymsp[-3].minor.yy79,yymsp[-1].minor.yy79);}
2785 #line 2810 "pikchr.c"
2786   yymsp[-5].minor.yy79 = yylhsminor.yy79;
2787         break;
2788       case 73: /* position ::= expr ABOVE position */
2789 #line 686 "pikchr.y"
2790 {yylhsminor.yy79=yymsp[0].minor.yy79; yylhsminor.yy79.y += yymsp[-2].minor.yy153;}
2791 #line 2816 "pikchr.c"
2792   yymsp[-2].minor.yy79 = yylhsminor.yy79;
2793         break;
2794       case 74: /* position ::= expr BELOW position */
2795 #line 687 "pikchr.y"
2796 {yylhsminor.yy79=yymsp[0].minor.yy79; yylhsminor.yy79.y -= yymsp[-2].minor.yy153;}
2797 #line 2822 "pikchr.c"
2798   yymsp[-2].minor.yy79 = yylhsminor.yy79;
2799         break;
2800       case 75: /* position ::= expr LEFT OF position */
2801 #line 688 "pikchr.y"
2802 {yylhsminor.yy79=yymsp[0].minor.yy79; yylhsminor.yy79.x -= yymsp[-3].minor.yy153;}
2803 #line 2828 "pikchr.c"
2804   yymsp[-3].minor.yy79 = yylhsminor.yy79;
2805         break;
2806       case 76: /* position ::= expr RIGHT OF position */
2807 #line 689 "pikchr.y"
2808 {yylhsminor.yy79=yymsp[0].minor.yy79; yylhsminor.yy79.x += yymsp[-3].minor.yy153;}
2809 #line 2834 "pikchr.c"
2810   yymsp[-3].minor.yy79 = yylhsminor.yy79;
2811         break;
2812       case 77: /* position ::= expr ON HEADING EDGEPT OF position */
2813 #line 691 "pikchr.y"
2814 {yylhsminor.yy79 = pik_position_at_hdg(yymsp[-5].minor.yy153,&yymsp[-2].minor.yy0,yymsp[0].minor.yy79);}
2815 #line 2840 "pikchr.c"
2816   yymsp[-5].minor.yy79 = yylhsminor.yy79;
2817         break;
2818       case 78: /* position ::= expr HEADING EDGEPT OF position */
2819 #line 693 "pikchr.y"
2820 {yylhsminor.yy79 = pik_position_at_hdg(yymsp[-4].minor.yy153,&yymsp[-2].minor.yy0,yymsp[0].minor.yy79);}
2821 #line 2846 "pikchr.c"
2822   yymsp[-4].minor.yy79 = yylhsminor.yy79;
2823         break;
2824       case 79: /* position ::= expr EDGEPT OF position */
2825 #line 695 "pikchr.y"
2826 {yylhsminor.yy79 = pik_position_at_hdg(yymsp[-3].minor.yy153,&yymsp[-2].minor.yy0,yymsp[0].minor.yy79);}
2827 #line 2852 "pikchr.c"
2828   yymsp[-3].minor.yy79 = yylhsminor.yy79;
2829         break;
2830       case 80: /* position ::= expr ON HEADING expr FROM position */
2831 #line 697 "pikchr.y"
2832 {yylhsminor.yy79 = pik_position_at_angle(yymsp[-5].minor.yy153,yymsp[-2].minor.yy153,yymsp[0].minor.yy79);}
2833 #line 2858 "pikchr.c"
2834   yymsp[-5].minor.yy79 = yylhsminor.yy79;
2835         break;
2836       case 81: /* position ::= expr HEADING expr FROM position */
2837 #line 699 "pikchr.y"
2838 {yylhsminor.yy79 = pik_position_at_angle(yymsp[-4].minor.yy153,yymsp[-2].minor.yy153,yymsp[0].minor.yy79);}
2839 #line 2864 "pikchr.c"
2840   yymsp[-4].minor.yy79 = yylhsminor.yy79;
2841         break;
2842       case 82: /* place ::= edge OF object */
2843 #line 711 "pikchr.y"
2844 {yylhsminor.yy79 = pik_place_of_elem(p,yymsp[0].minor.yy36,&yymsp[-2].minor.yy0);}
2845 #line 2870 "pikchr.c"
2846   yymsp[-2].minor.yy79 = yylhsminor.yy79;
2847         break;
2848       case 83: /* place2 ::= object */
2849 #line 712 "pikchr.y"
2850 {yylhsminor.yy79 = pik_place_of_elem(p,yymsp[0].minor.yy36,0);}
2851 #line 2876 "pikchr.c"
2852   yymsp[0].minor.yy79 = yylhsminor.yy79;
2853         break;
2854       case 84: /* place2 ::= object DOT_E edge */
2855 #line 713 "pikchr.y"
2856 {yylhsminor.yy79 = pik_place_of_elem(p,yymsp[-2].minor.yy36,&yymsp[0].minor.yy0);}
2857 #line 2882 "pikchr.c"
2858   yymsp[-2].minor.yy79 = yylhsminor.yy79;
2859         break;
2860       case 85: /* place2 ::= NTH VERTEX OF object */
2861 #line 714 "pikchr.y"
2862 {yylhsminor.yy79 = pik_nth_vertex(p,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,yymsp[0].minor.yy36);}
2863 #line 2888 "pikchr.c"
2864   yymsp[-3].minor.yy79 = yylhsminor.yy79;
2865         break;
2866       case 86: /* object ::= nth */
2867 #line 726 "pikchr.y"
2868 {yylhsminor.yy36 = pik_find_nth(p,0,&yymsp[0].minor.yy0);}
2869 #line 2894 "pikchr.c"
2870   yymsp[0].minor.yy36 = yylhsminor.yy36;
2871         break;
2872       case 87: /* object ::= nth OF|IN object */
2873 #line 727 "pikchr.y"
2874 {yylhsminor.yy36 = pik_find_nth(p,yymsp[0].minor.yy36,&yymsp[-2].minor.yy0);}
2875 #line 2900 "pikchr.c"
2876   yymsp[-2].minor.yy36 = yylhsminor.yy36;
2877         break;
2878       case 88: /* objectname ::= THIS */
2879 #line 729 "pikchr.y"
2880 {yymsp[0].minor.yy36 = p->cur;}
2881 #line 2906 "pikchr.c"
2882         break;
2883       case 89: /* objectname ::= PLACENAME */
2884 #line 730 "pikchr.y"
2885 {yylhsminor.yy36 = pik_find_byname(p,0,&yymsp[0].minor.yy0);}
2886 #line 2911 "pikchr.c"
2887   yymsp[0].minor.yy36 = yylhsminor.yy36;
2888         break;
2889       case 90: /* objectname ::= objectname DOT_U PLACENAME */
2890 #line 732 "pikchr.y"
2891 {yylhsminor.yy36 = pik_find_byname(p,yymsp[-2].minor.yy36,&yymsp[0].minor.yy0);}
2892 #line 2917 "pikchr.c"
2893   yymsp[-2].minor.yy36 = yylhsminor.yy36;
2894         break;
2895       case 91: /* nth ::= NTH CLASSNAME */
2896 #line 734 "pikchr.y"
2897 {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-1].minor.yy0); }
2898 #line 2923 "pikchr.c"
2899   yymsp[-1].minor.yy0 = yylhsminor.yy0;
2900         break;
2901       case 92: /* nth ::= NTH LAST CLASSNAME */
2902 #line 735 "pikchr.y"
2903 {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-2].minor.yy0); }
2904 #line 2929 "pikchr.c"
2905   yymsp[-2].minor.yy0 = yylhsminor.yy0;
2906         break;
2907       case 93: /* nth ::= LAST CLASSNAME */
2908 #line 736 "pikchr.y"
2909 {yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.eCode = -1;}
2910 #line 2935 "pikchr.c"
2911         break;
2912       case 94: /* nth ::= LAST */
2913 #line 737 "pikchr.y"
2914 {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -1;}
2915 #line 2940 "pikchr.c"
2916   yymsp[0].minor.yy0 = yylhsminor.yy0;
2917         break;
2918       case 95: /* nth ::= NTH LB RB */
2919 #line 738 "pikchr.y"
2920 {yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-2].minor.yy0);}
2921 #line 2946 "pikchr.c"
2922   yymsp[-2].minor.yy0 = yylhsminor.yy0;
2923         break;
2924       case 96: /* nth ::= NTH LAST LB RB */
2925 #line 739 "pikchr.y"
2926 {yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-3].minor.yy0);}
2927 #line 2952 "pikchr.c"
2928   yymsp[-3].minor.yy0 = yylhsminor.yy0;
2929         break;
2930       case 97: /* nth ::= LAST LB RB */
2931 #line 740 "pikchr.y"
2932 {yymsp[-2].minor.yy0=yymsp[-1].minor.yy0; yymsp[-2].minor.yy0.eCode = -1; }
2933 #line 2958 "pikchr.c"
2934         break;
2935       case 98: /* expr ::= expr PLUS expr */
2936 #line 742 "pikchr.y"
2937 {yylhsminor.yy153=yymsp[-2].minor.yy153+yymsp[0].minor.yy153;}
2938 #line 2963 "pikchr.c"
2939   yymsp[-2].minor.yy153 = yylhsminor.yy153;
2940         break;
2941       case 99: /* expr ::= expr MINUS expr */
2942 #line 743 "pikchr.y"
2943 {yylhsminor.yy153=yymsp[-2].minor.yy153-yymsp[0].minor.yy153;}
2944 #line 2969 "pikchr.c"
2945   yymsp[-2].minor.yy153 = yylhsminor.yy153;
2946         break;
2947       case 100: /* expr ::= expr STAR expr */
2948 #line 744 "pikchr.y"
2949 {yylhsminor.yy153=yymsp[-2].minor.yy153*yymsp[0].minor.yy153;}
2950 #line 2975 "pikchr.c"
2951   yymsp[-2].minor.yy153 = yylhsminor.yy153;
2952         break;
2953       case 101: /* expr ::= expr SLASH expr */
2954 #line 745 "pikchr.y"
2955 {
2956   if( yymsp[0].minor.yy153==0.0 ){ pik_error(p, &yymsp[-1].minor.yy0, "division by zero"); yylhsminor.yy153 = 0.0; }
2957   else{ yylhsminor.yy153 = yymsp[-2].minor.yy153/yymsp[0].minor.yy153; }
2958 }
2959 #line 2984 "pikchr.c"
2960   yymsp[-2].minor.yy153 = yylhsminor.yy153;
2961         break;
2962       case 102: /* expr ::= MINUS expr */
2963 #line 749 "pikchr.y"
2964 {yymsp[-1].minor.yy153=-yymsp[0].minor.yy153;}
2965 #line 2990 "pikchr.c"
2966         break;
2967       case 103: /* expr ::= PLUS expr */
2968 #line 750 "pikchr.y"
2969 {yymsp[-1].minor.yy153=yymsp[0].minor.yy153;}
2970 #line 2995 "pikchr.c"
2971         break;
2972       case 104: /* expr ::= LP expr RP */
2973 #line 751 "pikchr.y"
2974 {yymsp[-2].minor.yy153=yymsp[-1].minor.yy153;}
2975 #line 3000 "pikchr.c"
2976         break;
2977       case 105: /* expr ::= LP FILL|COLOR|THICKNESS RP */
2978 #line 752 "pikchr.y"
2979 {yymsp[-2].minor.yy153=pik_get_var(p,&yymsp[-1].minor.yy0);}
2980 #line 3005 "pikchr.c"
2981         break;
2982       case 106: /* expr ::= NUMBER */
2983 #line 753 "pikchr.y"
2984 {yylhsminor.yy153=pik_atof(&yymsp[0].minor.yy0);}
2985 #line 3010 "pikchr.c"
2986   yymsp[0].minor.yy153 = yylhsminor.yy153;
2987         break;
2988       case 107: /* expr ::= ID */
2989 #line 754 "pikchr.y"
2990 {yylhsminor.yy153=pik_get_var(p,&yymsp[0].minor.yy0);}
2991 #line 3016 "pikchr.c"
2992   yymsp[0].minor.yy153 = yylhsminor.yy153;
2993         break;
2994       case 108: /* expr ::= FUNC1 LP expr RP */
2995 #line 755 "pikchr.y"
2996 {yylhsminor.yy153 = pik_func(p,&yymsp[-3].minor.yy0,yymsp[-1].minor.yy153,0.0);}
2997 #line 3022 "pikchr.c"
2998   yymsp[-3].minor.yy153 = yylhsminor.yy153;
2999         break;
3000       case 109: /* expr ::= FUNC2 LP expr COMMA expr RP */
3001 #line 756 "pikchr.y"
3002 {yylhsminor.yy153 = pik_func(p,&yymsp[-5].minor.yy0,yymsp[-3].minor.yy153,yymsp[-1].minor.yy153);}
3003 #line 3028 "pikchr.c"
3004   yymsp[-5].minor.yy153 = yylhsminor.yy153;
3005         break;
3006       case 110: /* expr ::= DIST LP position COMMA position RP */
3007 #line 757 "pikchr.y"
3008 {yymsp[-5].minor.yy153 = pik_dist(&yymsp[-3].minor.yy79,&yymsp[-1].minor.yy79);}
3009 #line 3034 "pikchr.c"
3010         break;
3011       case 111: /* expr ::= place2 DOT_XY X */
3012 #line 758 "pikchr.y"
3013 {yylhsminor.yy153 = yymsp[-2].minor.yy79.x;}
3014 #line 3039 "pikchr.c"
3015   yymsp[-2].minor.yy153 = yylhsminor.yy153;
3016         break;
3017       case 112: /* expr ::= place2 DOT_XY Y */
3018 #line 759 "pikchr.y"
3019 {yylhsminor.yy153 = yymsp[-2].minor.yy79.y;}
3020 #line 3045 "pikchr.c"
3021   yymsp[-2].minor.yy153 = yylhsminor.yy153;
3022         break;
3023       case 113: /* expr ::= object DOT_L numproperty */
3024       case 114: /* expr ::= object DOT_L dashproperty */ yytestcase(yyruleno==114);
3025       case 115: /* expr ::= object DOT_L colorproperty */ yytestcase(yyruleno==115);
3026 #line 760 "pikchr.y"
3027 {yylhsminor.yy153=pik_property_of(yymsp[-2].minor.yy36,&yymsp[0].minor.yy0);}
3028 #line 3053 "pikchr.c"
3029   yymsp[-2].minor.yy153 = yylhsminor.yy153;
3030         break;
3031       default:
3032       /* (116) lvalue ::= ID */ yytestcase(yyruleno==116);
3033       /* (117) lvalue ::= FILL */ yytestcase(yyruleno==117);
3034       /* (118) lvalue ::= COLOR */ yytestcase(yyruleno==118);
3035       /* (119) lvalue ::= THICKNESS */ yytestcase(yyruleno==119);
3036       /* (120) rvalue ::= expr */ yytestcase(yyruleno==120);
3037       /* (121) print ::= PRINT */ yytestcase(yyruleno==121);
3038       /* (122) prlist ::= pritem (OPTIMIZED OUT) */ assert(yyruleno!=122);
3039       /* (123) prlist ::= prlist prsep pritem */ yytestcase(yyruleno==123);
3040       /* (124) direction ::= UP */ yytestcase(yyruleno==124);
3041       /* (125) direction ::= DOWN */ yytestcase(yyruleno==125);
3042       /* (126) direction ::= LEFT */ yytestcase(yyruleno==126);
3043       /* (127) direction ::= RIGHT */ yytestcase(yyruleno==127);
3044       /* (128) optrelexpr ::= relexpr (OPTIMIZED OUT) */ assert(yyruleno!=128);
3045       /* (129) attribute_list ::= alist */ yytestcase(yyruleno==129);
3046       /* (130) alist ::= */ yytestcase(yyruleno==130);
3047       /* (131) alist ::= alist attribute */ yytestcase(yyruleno==131);
3048       /* (132) attribute ::= boolproperty (OPTIMIZED OUT) */ assert(yyruleno!=132);
3049       /* (133) attribute ::= WITH withclause */ yytestcase(yyruleno==133);
3050       /* (134) go ::= GO */ yytestcase(yyruleno==134);
3051       /* (135) go ::= */ yytestcase(yyruleno==135);
3052       /* (136) even ::= UNTIL EVEN WITH */ yytestcase(yyruleno==136);
3053       /* (137) even ::= EVEN WITH */ yytestcase(yyruleno==137);
3054       /* (138) dashproperty ::= DOTTED */ yytestcase(yyruleno==138);
3055       /* (139) dashproperty ::= DASHED */ yytestcase(yyruleno==139);
3056       /* (140) colorproperty ::= FILL */ yytestcase(yyruleno==140);
3057       /* (141) colorproperty ::= COLOR */ yytestcase(yyruleno==141);
3058       /* (142) position ::= place */ yytestcase(yyruleno==142);
3059       /* (143) between ::= WAY BETWEEN */ yytestcase(yyruleno==143);
3060       /* (144) between ::= BETWEEN */ yytestcase(yyruleno==144);
3061       /* (145) between ::= OF THE WAY BETWEEN */ yytestcase(yyruleno==145);
3062       /* (146) place ::= place2 */ yytestcase(yyruleno==146);
3063       /* (147) edge ::= CENTER */ yytestcase(yyruleno==147);
3064       /* (148) edge ::= EDGEPT */ yytestcase(yyruleno==148);
3065       /* (149) edge ::= TOP */ yytestcase(yyruleno==149);
3066       /* (150) edge ::= BOTTOM */ yytestcase(yyruleno==150);
3067       /* (151) edge ::= START */ yytestcase(yyruleno==151);
3068       /* (152) edge ::= END */ yytestcase(yyruleno==152);
3069       /* (153) edge ::= RIGHT */ yytestcase(yyruleno==153);
3070       /* (154) edge ::= LEFT */ yytestcase(yyruleno==154);
3071       /* (155) object ::= objectname */ yytestcase(yyruleno==155);
3072         break;
3073 /********** End reduce actions ************************************************/
3074   };
3075   assert( yyruleno<sizeof(yyRuleInfoLhs)/sizeof(yyRuleInfoLhs[0]) );
3076   yygoto = yyRuleInfoLhs[yyruleno];
3077   yysize = yyRuleInfoNRhs[yyruleno];
3078   yyact = yy_find_reduce_action(yymsp[yysize].stateno,(YYCODETYPE)yygoto);
3079 
3080   /* There are no SHIFTREDUCE actions on nonterminals because the table
3081   ** generator has simplified them to pure REDUCE actions. */
3082   assert( !(yyact>YY_MAX_SHIFT && yyact<=YY_MAX_SHIFTREDUCE) );
3083 
3084   /* It is not possible for a REDUCE to be followed by an error */
3085   assert( yyact!=YY_ERROR_ACTION );
3086 
3087   yymsp += yysize+1;
3088   yypParser->yytos = yymsp;
3089   yymsp->stateno = (YYACTIONTYPE)yyact;
3090   yymsp->major = (YYCODETYPE)yygoto;
3091   yyTraceShift(yypParser, yyact, "... then shift");
3092   return yyact;
3093 }
3094 
3095 /*
3096 ** The following code executes when the parse fails
3097 */
3098 #ifndef YYNOERRORRECOVERY
yy_parse_failed(yyParser * yypParser)3099 static void yy_parse_failed(
3100   yyParser *yypParser           /* The parser */
3101 ){
3102   pik_parserARG_FETCH
3103   pik_parserCTX_FETCH
3104 #ifndef NDEBUG
3105   if( yyTraceFILE ){
3106     fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt);
3107   }
3108 #endif
3109   while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
3110   /* Here code is inserted which will be executed whenever the
3111   ** parser fails */
3112 /************ Begin %parse_failure code ***************************************/
3113 /************ End %parse_failure code *****************************************/
3114   pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */
3115   pik_parserCTX_STORE
3116 }
3117 #endif /* YYNOERRORRECOVERY */
3118 
3119 /*
3120 ** The following code executes when a syntax error first occurs.
3121 */
yy_syntax_error(yyParser * yypParser,int yymajor,pik_parserTOKENTYPE yyminor)3122 static void yy_syntax_error(
3123   yyParser *yypParser,           /* The parser */
3124   int yymajor,                   /* The major type of the error token */
3125   pik_parserTOKENTYPE yyminor         /* The minor type of the error token */
3126 ){
3127   pik_parserARG_FETCH
3128   pik_parserCTX_FETCH
3129 #define TOKEN yyminor
3130 /************ Begin %syntax_error code ****************************************/
3131 #line 520 "pikchr.y"
3132 
3133   if( TOKEN.z && TOKEN.z[0] ){
3134     pik_error(p, &TOKEN, "syntax error");
3135   }else{
3136     pik_error(p, 0, "syntax error");
3137   }
3138   UNUSED_PARAMETER(yymajor);
3139 #line 3164 "pikchr.c"
3140 /************ End %syntax_error code ******************************************/
3141   pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */
3142   pik_parserCTX_STORE
3143 }
3144 
3145 /*
3146 ** The following is executed when the parser accepts
3147 */
yy_accept(yyParser * yypParser)3148 static void yy_accept(
3149   yyParser *yypParser           /* The parser */
3150 ){
3151   pik_parserARG_FETCH
3152   pik_parserCTX_FETCH
3153 #ifndef NDEBUG
3154   if( yyTraceFILE ){
3155     fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt);
3156   }
3157 #endif
3158 #ifndef YYNOERRORRECOVERY
3159   yypParser->yyerrcnt = -1;
3160 #endif
3161   assert( yypParser->yytos==yypParser->yystack );
3162   /* Here code is inserted which will be executed whenever the
3163   ** parser accepts */
3164 /*********** Begin %parse_accept code *****************************************/
3165 /*********** End %parse_accept code *******************************************/
3166   pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */
3167   pik_parserCTX_STORE
3168 }
3169 
3170 /* The main parser program.
3171 ** The first argument is a pointer to a structure obtained from
3172 ** "pik_parserAlloc" which describes the current state of the parser.
3173 ** The second argument is the major token number.  The third is
3174 ** the minor token.  The fourth optional argument is whatever the
3175 ** user wants (and specified in the grammar) and is available for
3176 ** use by the action routines.
3177 **
3178 ** Inputs:
3179 ** <ul>
3180 ** <li> A pointer to the parser (an opaque structure.)
3181 ** <li> The major token number.
3182 ** <li> The minor token number.
3183 ** <li> An option argument of a grammar-specified type.
3184 ** </ul>
3185 **
3186 ** Outputs:
3187 ** None.
3188 */
pik_parser(void * yyp,int yymajor,pik_parserTOKENTYPE yyminor pik_parserARG_PDECL)3189 void pik_parser(
3190   void *yyp,                   /* The parser */
3191   int yymajor,                 /* The major token code number */
3192   pik_parserTOKENTYPE yyminor       /* The value for the token */
3193   pik_parserARG_PDECL               /* Optional %extra_argument parameter */
3194 ){
3195   YYMINORTYPE yyminorunion;
3196   YYACTIONTYPE yyact;   /* The parser action. */
3197 #if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
3198   int yyendofinput;     /* True if we are at the end of input */
3199 #endif
3200 #ifdef YYERRORSYMBOL
3201   int yyerrorhit = 0;   /* True if yymajor has invoked an error */
3202 #endif
3203   yyParser *yypParser = (yyParser*)yyp;  /* The parser */
3204   pik_parserCTX_FETCH
3205   pik_parserARG_STORE
3206 
3207   assert( yypParser->yytos!=0 );
3208 #if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
3209   yyendofinput = (yymajor==0);
3210 #endif
3211 
3212   yyact = yypParser->yytos->stateno;
3213 #ifndef NDEBUG
3214   if( yyTraceFILE ){
3215     if( yyact < YY_MIN_REDUCE ){
3216       fprintf(yyTraceFILE,"%sInput '%s' in state %d\n",
3217               yyTracePrompt,yyTokenName[yymajor],yyact);
3218     }else{
3219       fprintf(yyTraceFILE,"%sInput '%s' with pending reduce %d\n",
3220               yyTracePrompt,yyTokenName[yymajor],yyact-YY_MIN_REDUCE);
3221     }
3222   }
3223 #endif
3224 
3225   do{
3226     assert( yyact==yypParser->yytos->stateno );
3227     yyact = yy_find_shift_action((YYCODETYPE)yymajor,yyact);
3228     if( yyact >= YY_MIN_REDUCE ){
3229       yyact = yy_reduce(yypParser,yyact-YY_MIN_REDUCE,yymajor,
3230                         yyminor pik_parserCTX_PARAM);
3231     }else if( yyact <= YY_MAX_SHIFTREDUCE ){
3232       yy_shift(yypParser,yyact,(YYCODETYPE)yymajor,yyminor);
3233 #ifndef YYNOERRORRECOVERY
3234       yypParser->yyerrcnt--;
3235 #endif
3236       break;
3237     }else if( yyact==YY_ACCEPT_ACTION ){
3238       yypParser->yytos--;
3239       yy_accept(yypParser);
3240       return;
3241     }else{
3242       assert( yyact == YY_ERROR_ACTION );
3243       yyminorunion.yy0 = yyminor;
3244 #ifdef YYERRORSYMBOL
3245       int yymx;
3246 #endif
3247 #ifndef NDEBUG
3248       if( yyTraceFILE ){
3249         fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt);
3250       }
3251 #endif
3252 #ifdef YYERRORSYMBOL
3253       /* A syntax error has occurred.
3254       ** The response to an error depends upon whether or not the
3255       ** grammar defines an error token "ERROR".
3256       **
3257       ** This is what we do if the grammar does define ERROR:
3258       **
3259       **  * Call the %syntax_error function.
3260       **
3261       **  * Begin popping the stack until we enter a state where
3262       **    it is legal to shift the error symbol, then shift
3263       **    the error symbol.
3264       **
3265       **  * Set the error count to three.
3266       **
3267       **  * Begin accepting and shifting new tokens.  No new error
3268       **    processing will occur until three tokens have been
3269       **    shifted successfully.
3270       **
3271       */
3272       if( yypParser->yyerrcnt<0 ){
3273         yy_syntax_error(yypParser,yymajor,yyminor);
3274       }
3275       yymx = yypParser->yytos->major;
3276       if( yymx==YYERRORSYMBOL || yyerrorhit ){
3277 #ifndef NDEBUG
3278         if( yyTraceFILE ){
3279           fprintf(yyTraceFILE,"%sDiscard input token %s\n",
3280              yyTracePrompt,yyTokenName[yymajor]);
3281         }
3282 #endif
3283         yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion);
3284         yymajor = YYNOCODE;
3285       }else{
3286         while( yypParser->yytos >= yypParser->yystack
3287             && (yyact = yy_find_reduce_action(
3288                         yypParser->yytos->stateno,
3289                         YYERRORSYMBOL)) > YY_MAX_SHIFTREDUCE
3290         ){
3291           yy_pop_parser_stack(yypParser);
3292         }
3293         if( yypParser->yytos < yypParser->yystack || yymajor==0 ){
3294           yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
3295           yy_parse_failed(yypParser);
3296 #ifndef YYNOERRORRECOVERY
3297           yypParser->yyerrcnt = -1;
3298 #endif
3299           yymajor = YYNOCODE;
3300         }else if( yymx!=YYERRORSYMBOL ){
3301           yy_shift(yypParser,yyact,YYERRORSYMBOL,yyminor);
3302         }
3303       }
3304       yypParser->yyerrcnt = 3;
3305       yyerrorhit = 1;
3306       if( yymajor==YYNOCODE ) break;
3307       yyact = yypParser->yytos->stateno;
3308 #elif defined(YYNOERRORRECOVERY)
3309       /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to
3310       ** do any kind of error recovery.  Instead, simply invoke the syntax
3311       ** error routine and continue going as if nothing had happened.
3312       **
3313       ** Applications can set this macro (for example inside %include) if
3314       ** they intend to abandon the parse upon the first syntax error seen.
3315       */
3316       yy_syntax_error(yypParser,yymajor, yyminor);
3317       yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
3318       break;
3319 #else  /* YYERRORSYMBOL is not defined */
3320       /* This is what we do if the grammar does not define ERROR:
3321       **
3322       **  * Report an error message, and throw away the input token.
3323       **
3324       **  * If the input token is $, then fail the parse.
3325       **
3326       ** As before, subsequent error messages are suppressed until
3327       ** three input tokens have been successfully shifted.
3328       */
3329       if( yypParser->yyerrcnt<=0 ){
3330         yy_syntax_error(yypParser,yymajor, yyminor);
3331       }
3332       yypParser->yyerrcnt = 3;
3333       yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
3334       if( yyendofinput ){
3335         yy_parse_failed(yypParser);
3336 #ifndef YYNOERRORRECOVERY
3337         yypParser->yyerrcnt = -1;
3338 #endif
3339       }
3340       break;
3341 #endif
3342     }
3343   }while( yypParser->yytos>yypParser->yystack );
3344 #ifndef NDEBUG
3345   if( yyTraceFILE ){
3346     yyStackEntry *i;
3347     char cDiv = '[';
3348     fprintf(yyTraceFILE,"%sReturn. Stack=",yyTracePrompt);
3349     for(i=&yypParser->yystack[1]; i<=yypParser->yytos; i++){
3350       fprintf(yyTraceFILE,"%c%s", cDiv, yyTokenName[i->major]);
3351       cDiv = ' ';
3352     }
3353     fprintf(yyTraceFILE,"]\n");
3354   }
3355 #endif
3356   return;
3357 }
3358 
3359 /*
3360 ** Return the fallback token corresponding to canonical token iToken, or
3361 ** 0 if iToken has no fallback.
3362 */
pik_parserFallback(int iToken)3363 int pik_parserFallback(int iToken){
3364 #ifdef YYFALLBACK
3365   assert( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) );
3366   return yyFallback[iToken];
3367 #else
3368   (void)iToken;
3369   return 0;
3370 #endif
3371 }
3372 #line 765 "pikchr.y"
3373 
3374 
3375 
3376 /* Chart of the 148 official CSS color names with their
3377 ** corresponding RGB values thru Color Module Level 4:
3378 ** https://developer.mozilla.org/en-US/docs/Web/CSS/color_value
3379 **
3380 ** Two new names "None" and "Off" are added with a value
3381 ** of -1.
3382 */
3383 static const struct {
3384   const char *zName;  /* Name of the color */
3385   int val;            /* RGB value */
3386 } aColor[] = {
3387   { "AliceBlue",                   0xf0f8ff },
3388   { "AntiqueWhite",                0xfaebd7 },
3389   { "Aqua",                        0x00ffff },
3390   { "Aquamarine",                  0x7fffd4 },
3391   { "Azure",                       0xf0ffff },
3392   { "Beige",                       0xf5f5dc },
3393   { "Bisque",                      0xffe4c4 },
3394   { "Black",                       0x000000 },
3395   { "BlanchedAlmond",              0xffebcd },
3396   { "Blue",                        0x0000ff },
3397   { "BlueViolet",                  0x8a2be2 },
3398   { "Brown",                       0xa52a2a },
3399   { "BurlyWood",                   0xdeb887 },
3400   { "CadetBlue",                   0x5f9ea0 },
3401   { "Chartreuse",                  0x7fff00 },
3402   { "Chocolate",                   0xd2691e },
3403   { "Coral",                       0xff7f50 },
3404   { "CornflowerBlue",              0x6495ed },
3405   { "Cornsilk",                    0xfff8dc },
3406   { "Crimson",                     0xdc143c },
3407   { "Cyan",                        0x00ffff },
3408   { "DarkBlue",                    0x00008b },
3409   { "DarkCyan",                    0x008b8b },
3410   { "DarkGoldenrod",               0xb8860b },
3411   { "DarkGray",                    0xa9a9a9 },
3412   { "DarkGreen",                   0x006400 },
3413   { "DarkGrey",                    0xa9a9a9 },
3414   { "DarkKhaki",                   0xbdb76b },
3415   { "DarkMagenta",                 0x8b008b },
3416   { "DarkOliveGreen",              0x556b2f },
3417   { "DarkOrange",                  0xff8c00 },
3418   { "DarkOrchid",                  0x9932cc },
3419   { "DarkRed",                     0x8b0000 },
3420   { "DarkSalmon",                  0xe9967a },
3421   { "DarkSeaGreen",                0x8fbc8f },
3422   { "DarkSlateBlue",               0x483d8b },
3423   { "DarkSlateGray",               0x2f4f4f },
3424   { "DarkSlateGrey",               0x2f4f4f },
3425   { "DarkTurquoise",               0x00ced1 },
3426   { "DarkViolet",                  0x9400d3 },
3427   { "DeepPink",                    0xff1493 },
3428   { "DeepSkyBlue",                 0x00bfff },
3429   { "DimGray",                     0x696969 },
3430   { "DimGrey",                     0x696969 },
3431   { "DodgerBlue",                  0x1e90ff },
3432   { "Firebrick",                   0xb22222 },
3433   { "FloralWhite",                 0xfffaf0 },
3434   { "ForestGreen",                 0x228b22 },
3435   { "Fuchsia",                     0xff00ff },
3436   { "Gainsboro",                   0xdcdcdc },
3437   { "GhostWhite",                  0xf8f8ff },
3438   { "Gold",                        0xffd700 },
3439   { "Goldenrod",                   0xdaa520 },
3440   { "Gray",                        0x808080 },
3441   { "Green",                       0x008000 },
3442   { "GreenYellow",                 0xadff2f },
3443   { "Grey",                        0x808080 },
3444   { "Honeydew",                    0xf0fff0 },
3445   { "HotPink",                     0xff69b4 },
3446   { "IndianRed",                   0xcd5c5c },
3447   { "Indigo",                      0x4b0082 },
3448   { "Ivory",                       0xfffff0 },
3449   { "Khaki",                       0xf0e68c },
3450   { "Lavender",                    0xe6e6fa },
3451   { "LavenderBlush",               0xfff0f5 },
3452   { "LawnGreen",                   0x7cfc00 },
3453   { "LemonChiffon",                0xfffacd },
3454   { "LightBlue",                   0xadd8e6 },
3455   { "LightCoral",                  0xf08080 },
3456   { "LightCyan",                   0xe0ffff },
3457   { "LightGoldenrodYellow",        0xfafad2 },
3458   { "LightGray",                   0xd3d3d3 },
3459   { "LightGreen",                  0x90ee90 },
3460   { "LightGrey",                   0xd3d3d3 },
3461   { "LightPink",                   0xffb6c1 },
3462   { "LightSalmon",                 0xffa07a },
3463   { "LightSeaGreen",               0x20b2aa },
3464   { "LightSkyBlue",                0x87cefa },
3465   { "LightSlateGray",              0x778899 },
3466   { "LightSlateGrey",              0x778899 },
3467   { "LightSteelBlue",              0xb0c4de },
3468   { "LightYellow",                 0xffffe0 },
3469   { "Lime",                        0x00ff00 },
3470   { "LimeGreen",                   0x32cd32 },
3471   { "Linen",                       0xfaf0e6 },
3472   { "Magenta",                     0xff00ff },
3473   { "Maroon",                      0x800000 },
3474   { "MediumAquamarine",            0x66cdaa },
3475   { "MediumBlue",                  0x0000cd },
3476   { "MediumOrchid",                0xba55d3 },
3477   { "MediumPurple",                0x9370db },
3478   { "MediumSeaGreen",              0x3cb371 },
3479   { "MediumSlateBlue",             0x7b68ee },
3480   { "MediumSpringGreen",           0x00fa9a },
3481   { "MediumTurquoise",             0x48d1cc },
3482   { "MediumVioletRed",             0xc71585 },
3483   { "MidnightBlue",                0x191970 },
3484   { "MintCream",                   0xf5fffa },
3485   { "MistyRose",                   0xffe4e1 },
3486   { "Moccasin",                    0xffe4b5 },
3487   { "NavajoWhite",                 0xffdead },
3488   { "Navy",                        0x000080 },
3489   { "None",                              -1 },  /* Non-standard addition */
3490   { "Off",                               -1 },  /* Non-standard addition */
3491   { "OldLace",                     0xfdf5e6 },
3492   { "Olive",                       0x808000 },
3493   { "OliveDrab",                   0x6b8e23 },
3494   { "Orange",                      0xffa500 },
3495   { "OrangeRed",                   0xff4500 },
3496   { "Orchid",                      0xda70d6 },
3497   { "PaleGoldenrod",               0xeee8aa },
3498   { "PaleGreen",                   0x98fb98 },
3499   { "PaleTurquoise",               0xafeeee },
3500   { "PaleVioletRed",               0xdb7093 },
3501   { "PapayaWhip",                  0xffefd5 },
3502   { "PeachPuff",                   0xffdab9 },
3503   { "Peru",                        0xcd853f },
3504   { "Pink",                        0xffc0cb },
3505   { "Plum",                        0xdda0dd },
3506   { "PowderBlue",                  0xb0e0e6 },
3507   { "Purple",                      0x800080 },
3508   { "RebeccaPurple",               0x663399 },
3509   { "Red",                         0xff0000 },
3510   { "RosyBrown",                   0xbc8f8f },
3511   { "RoyalBlue",                   0x4169e1 },
3512   { "SaddleBrown",                 0x8b4513 },
3513   { "Salmon",                      0xfa8072 },
3514   { "SandyBrown",                  0xf4a460 },
3515   { "SeaGreen",                    0x2e8b57 },
3516   { "Seashell",                    0xfff5ee },
3517   { "Sienna",                      0xa0522d },
3518   { "Silver",                      0xc0c0c0 },
3519   { "SkyBlue",                     0x87ceeb },
3520   { "SlateBlue",                   0x6a5acd },
3521   { "SlateGray",                   0x708090 },
3522   { "SlateGrey",                   0x708090 },
3523   { "Snow",                        0xfffafa },
3524   { "SpringGreen",                 0x00ff7f },
3525   { "SteelBlue",                   0x4682b4 },
3526   { "Tan",                         0xd2b48c },
3527   { "Teal",                        0x008080 },
3528   { "Thistle",                     0xd8bfd8 },
3529   { "Tomato",                      0xff6347 },
3530   { "Turquoise",                   0x40e0d0 },
3531   { "Violet",                      0xee82ee },
3532   { "Wheat",                       0xf5deb3 },
3533   { "White",                       0xffffff },
3534   { "WhiteSmoke",                  0xf5f5f5 },
3535   { "Yellow",                      0xffff00 },
3536   { "YellowGreen",                 0x9acd32 },
3537 };
3538 
3539 /* Built-in variable names.
3540 **
3541 ** This array is constant.  When a script changes the value of one of
3542 ** these built-ins, a new PVar record is added at the head of
3543 ** the Pik.pVar list, which is searched first.  Thus the new PVar entry
3544 ** will override this default value.
3545 **
3546 ** Units are in inches, except for "color" and "fill" which are
3547 ** interpreted as 24-bit RGB values.
3548 **
3549 ** Binary search used.  Must be kept in sorted order.
3550 */
3551 static const struct { const char *zName; PNum val; } aBuiltin[] = {
3552   { "arcrad",      0.25  },
3553   { "arrowhead",   2.0   },
3554   { "arrowht",     0.08  },
3555   { "arrowwid",    0.06  },
3556   { "boxht",       0.5   },
3557   { "boxrad",      0.0   },
3558   { "boxwid",      0.75  },
3559   { "charht",      0.14  },
3560   { "charwid",     0.08  },
3561   { "circlerad",   0.25  },
3562   { "color",       0.0   },
3563   { "cylht",       0.5   },
3564   { "cylrad",      0.075 },
3565   { "cylwid",      0.75  },
3566   { "dashwid",     0.05  },
3567   { "dotrad",      0.015 },
3568   { "ellipseht",   0.5   },
3569   { "ellipsewid",  0.75  },
3570   { "fileht",      0.75  },
3571   { "filerad",     0.15  },
3572   { "filewid",     0.5   },
3573   { "fill",        -1.0  },
3574   { "lineht",      0.5   },
3575   { "linewid",     0.5   },
3576   { "movewid",     0.5   },
3577   { "ovalht",      0.5   },
3578   { "ovalwid",     1.0   },
3579   { "scale",       1.0   },
3580   { "textht",      0.5   },
3581   { "textwid",     0.75  },
3582   { "thickness",   0.015 },
3583 };
3584 
3585 
3586 /* Methods for the "arc" class */
arcInit(Pik * p,PObj * pObj)3587 static void arcInit(Pik *p, PObj *pObj){
3588   pObj->w = pik_value(p, "arcrad",6,0);
3589   pObj->h = pObj->w;
3590 }
3591 /* Hack: Arcs are here rendered as quadratic Bezier curves rather
3592 ** than true arcs.  Multiple reasons: (1) the legacy-PIC parameters
3593 ** that control arcs are obscure and I could not figure out what they
3594 ** mean based on available documentation.  (2) Arcs are rarely used,
3595 ** and so do not seem that important.
3596 */
arcControlPoint(int cw,PPoint f,PPoint t,PNum rScale)3597 static PPoint arcControlPoint(int cw, PPoint f, PPoint t, PNum rScale){
3598   PPoint m;
3599   PNum dx, dy;
3600   m.x = 0.5*(f.x+t.x);
3601   m.y = 0.5*(f.y+t.y);
3602   dx = t.x - f.x;
3603   dy = t.y - f.y;
3604   if( cw ){
3605     m.x -= 0.5*rScale*dy;
3606     m.y += 0.5*rScale*dx;
3607   }else{
3608     m.x += 0.5*rScale*dy;
3609     m.y -= 0.5*rScale*dx;
3610   }
3611   return m;
3612 }
arcCheck(Pik * p,PObj * pObj)3613 static void arcCheck(Pik *p, PObj *pObj){
3614   PPoint m;
3615   if( p->nTPath>2 ){
3616     pik_error(p, &pObj->errTok, "arc geometry error");
3617     return;
3618   }
3619   m = arcControlPoint(pObj->cw, p->aTPath[0], p->aTPath[1], 0.5);
3620   pik_bbox_add_xy(&pObj->bbox, m.x, m.y);
3621 }
arcRender(Pik * p,PObj * pObj)3622 static void arcRender(Pik *p, PObj *pObj){
3623   PPoint f, m, t;
3624   if( pObj->nPath<2 ) return;
3625   if( pObj->sw<=0.0 ) return;
3626   f = pObj->aPath[0];
3627   t = pObj->aPath[1];
3628   m = arcControlPoint(pObj->cw,f,t,1.0);
3629   if( pObj->larrow ){
3630     pik_draw_arrowhead(p,&m,&f,pObj);
3631   }
3632   if( pObj->rarrow ){
3633     pik_draw_arrowhead(p,&m,&t,pObj);
3634   }
3635   pik_append_xy(p,"<path d=\"M", f.x, f.y);
3636   pik_append_xy(p,"Q", m.x, m.y);
3637   pik_append_xy(p," ", t.x, t.y);
3638   pik_append(p,"\" ",2);
3639   pik_append_style(p,pObj,0);
3640   pik_append(p,"\" />\n", -1);
3641 
3642   pik_append_txt(p, pObj, 0);
3643 }
3644 
3645 
3646 /* Methods for the "arrow" class */
arrowInit(Pik * p,PObj * pObj)3647 static void arrowInit(Pik *p, PObj *pObj){
3648   pObj->w = pik_value(p, "linewid",7,0);
3649   pObj->h = pik_value(p, "lineht",6,0);
3650   pObj->rad = pik_value(p, "linerad",7,0);
3651   pObj->rarrow = 1;
3652 }
3653 
3654 /* Methods for the "box" class */
boxInit(Pik * p,PObj * pObj)3655 static void boxInit(Pik *p, PObj *pObj){
3656   pObj->w = pik_value(p, "boxwid",6,0);
3657   pObj->h = pik_value(p, "boxht",5,0);
3658   pObj->rad = pik_value(p, "boxrad",6,0);
3659 }
3660 /* Return offset from the center of the box to the compass point
3661 ** given by parameter cp */
boxOffset(Pik * p,PObj * pObj,int cp)3662 static PPoint boxOffset(Pik *p, PObj *pObj, int cp){
3663   PPoint pt = cZeroPoint;
3664   PNum w2 = 0.5*pObj->w;
3665   PNum h2 = 0.5*pObj->h;
3666   PNum rad = pObj->rad;
3667   PNum rx;
3668   if( rad<=0.0 ){
3669     rx = 0.0;
3670   }else{
3671     if( rad>w2 ) rad = w2;
3672     if( rad>h2 ) rad = h2;
3673     rx = 0.29289321881345252392*rad;
3674   }
3675   switch( cp ){
3676     case CP_C:                                   break;
3677     case CP_N:   pt.x = 0.0;      pt.y = h2;     break;
3678     case CP_NE:  pt.x = w2-rx;    pt.y = h2-rx;  break;
3679     case CP_E:   pt.x = w2;       pt.y = 0.0;    break;
3680     case CP_SE:  pt.x = w2-rx;    pt.y = rx-h2;  break;
3681     case CP_S:   pt.x = 0.0;      pt.y = -h2;    break;
3682     case CP_SW:  pt.x = rx-w2;    pt.y = rx-h2;  break;
3683     case CP_W:   pt.x = -w2;      pt.y = 0.0;    break;
3684     case CP_NW:  pt.x = rx-w2;    pt.y = h2-rx;  break;
3685     default:     assert(0);
3686   }
3687   UNUSED_PARAMETER(p);
3688   return pt;
3689 }
boxChop(Pik * p,PObj * pObj,PPoint * pPt)3690 static PPoint boxChop(Pik *p, PObj *pObj, PPoint *pPt){
3691   PNum dx, dy;
3692   int cp = CP_C;
3693   PPoint chop = pObj->ptAt;
3694   if( pObj->w<=0.0 ) return chop;
3695   if( pObj->h<=0.0 ) return chop;
3696   dx = (pPt->x - pObj->ptAt.x)*pObj->h/pObj->w;
3697   dy = (pPt->y - pObj->ptAt.y);
3698   if( dx>0.0 ){
3699     if( dy>=2.414*dx ){
3700       cp = CP_N;
3701     }else if( dy>=0.414*dx ){
3702       cp = CP_NE;
3703     }else if( dy>=-0.414*dx ){
3704       cp = CP_E;
3705     }else if( dy>-2.414*dx ){
3706       cp = CP_SE;
3707     }else{
3708       cp = CP_S;
3709     }
3710   }else{
3711     if( dy>=-2.414*dx ){
3712       cp = CP_N;
3713     }else if( dy>=-0.414*dx ){
3714       cp = CP_NW;
3715     }else if( dy>=0.414*dx ){
3716       cp = CP_W;
3717     }else if( dy>2.414*dx ){
3718       cp = CP_SW;
3719     }else{
3720       cp = CP_S;
3721     }
3722   }
3723   chop = pObj->type->xOffset(p,pObj,cp);
3724   chop.x += pObj->ptAt.x;
3725   chop.y += pObj->ptAt.y;
3726   return chop;
3727 }
boxFit(Pik * p,PObj * pObj,PNum w,PNum h)3728 static void boxFit(Pik *p, PObj *pObj, PNum w, PNum h){
3729   if( w>0 ) pObj->w = w;
3730   if( h>0 ) pObj->h = h;
3731   UNUSED_PARAMETER(p);
3732 }
boxRender(Pik * p,PObj * pObj)3733 static void boxRender(Pik *p, PObj *pObj){
3734   PNum w2 = 0.5*pObj->w;
3735   PNum h2 = 0.5*pObj->h;
3736   PNum rad = pObj->rad;
3737   PPoint pt = pObj->ptAt;
3738   if( pObj->sw>0.0 ){
3739     if( rad<=0.0 ){
3740       pik_append_xy(p,"<path d=\"M", pt.x-w2,pt.y-h2);
3741       pik_append_xy(p,"L", pt.x+w2,pt.y-h2);
3742       pik_append_xy(p,"L", pt.x+w2,pt.y+h2);
3743       pik_append_xy(p,"L", pt.x-w2,pt.y+h2);
3744       pik_append(p,"Z\" ",-1);
3745     }else{
3746       /*
3747       **         ----       - y3
3748       **        /    \
3749       **       /      \     _ y2
3750       **      |        |
3751       **      |        |    _ y1
3752       **       \      /
3753       **        \    /
3754       **         ----       _ y0
3755       **
3756       **      '  '  '  '
3757       **     x0 x1 x2 x3
3758       */
3759       PNum x0,x1,x2,x3,y0,y1,y2,y3;
3760       if( rad>w2 ) rad = w2;
3761       if( rad>h2 ) rad = h2;
3762       x0 = pt.x - w2;
3763       x1 = x0 + rad;
3764       x3 = pt.x + w2;
3765       x2 = x3 - rad;
3766       y0 = pt.y - h2;
3767       y1 = y0 + rad;
3768       y3 = pt.y + h2;
3769       y2 = y3 - rad;
3770       pik_append_xy(p,"<path d=\"M", x1, y0);
3771       if( x2>x1 ) pik_append_xy(p, "L", x2, y0);
3772       pik_append_arc(p, rad, rad, x3, y1);
3773       if( y2>y1 ) pik_append_xy(p, "L", x3, y2);
3774       pik_append_arc(p, rad, rad, x2, y3);
3775       if( x2>x1 ) pik_append_xy(p, "L", x1, y3);
3776       pik_append_arc(p, rad, rad, x0, y2);
3777       if( y2>y1 ) pik_append_xy(p, "L", x0, y1);
3778       pik_append_arc(p, rad, rad, x1, y0);
3779       pik_append(p,"Z\" ",-1);
3780     }
3781     pik_append_style(p,pObj,3);
3782     pik_append(p,"\" />\n", -1);
3783   }
3784   pik_append_txt(p, pObj, 0);
3785 }
3786 
3787 /* Methods for the "circle" class */
circleInit(Pik * p,PObj * pObj)3788 static void circleInit(Pik *p, PObj *pObj){
3789   pObj->w = pik_value(p, "circlerad",9,0)*2;
3790   pObj->h = pObj->w;
3791   pObj->rad = 0.5*pObj->w;
3792 }
circleNumProp(Pik * p,PObj * pObj,PToken * pId)3793 static void circleNumProp(Pik *p, PObj *pObj, PToken *pId){
3794   /* For a circle, the width must equal the height and both must
3795   ** be twice the radius.  Enforce those constraints. */
3796   switch( pId->eType ){
3797     case T_RADIUS:
3798       pObj->w = pObj->h = 2.0*pObj->rad;
3799       break;
3800     case T_WIDTH:
3801       pObj->h = pObj->w;
3802       pObj->rad = 0.5*pObj->w;
3803       break;
3804     case T_HEIGHT:
3805       pObj->w = pObj->h;
3806       pObj->rad = 0.5*pObj->w;
3807       break;
3808   }
3809   UNUSED_PARAMETER(p);
3810 }
circleChop(Pik * p,PObj * pObj,PPoint * pPt)3811 static PPoint circleChop(Pik *p, PObj *pObj, PPoint *pPt){
3812   PPoint chop;
3813   PNum dx = pPt->x - pObj->ptAt.x;
3814   PNum dy = pPt->y - pObj->ptAt.y;
3815   PNum dist = hypot(dx,dy);
3816   if( dist<pObj->rad ) return pObj->ptAt;
3817   chop.x = pObj->ptAt.x + dx*pObj->rad/dist;
3818   chop.y = pObj->ptAt.y + dy*pObj->rad/dist;
3819   UNUSED_PARAMETER(p);
3820   return chop;
3821 }
circleFit(Pik * p,PObj * pObj,PNum w,PNum h)3822 static void circleFit(Pik *p, PObj *pObj, PNum w, PNum h){
3823   PNum mx = 0.0;
3824   if( w>0 ) mx = w;
3825   if( h>mx ) mx = h;
3826   if( w*h>0 && (w*w + h*h) > mx*mx ){
3827     mx = hypot(w,h);
3828   }
3829   if( mx>0.0 ){
3830     pObj->rad = 0.5*mx;
3831     pObj->w = pObj->h = mx;
3832   }
3833   UNUSED_PARAMETER(p);
3834 }
3835 
circleRender(Pik * p,PObj * pObj)3836 static void circleRender(Pik *p, PObj *pObj){
3837   PNum r = pObj->rad;
3838   PPoint pt = pObj->ptAt;
3839   if( pObj->sw>0.0 ){
3840     pik_append_x(p,"<circle cx=\"", pt.x, "\"");
3841     pik_append_y(p," cy=\"", pt.y, "\"");
3842     pik_append_dis(p," r=\"", r, "\" ");
3843     pik_append_style(p,pObj,3);
3844     pik_append(p,"\" />\n", -1);
3845   }
3846   pik_append_txt(p, pObj, 0);
3847 }
3848 
3849 /* Methods for the "cylinder" class */
cylinderInit(Pik * p,PObj * pObj)3850 static void cylinderInit(Pik *p, PObj *pObj){
3851   pObj->w = pik_value(p, "cylwid",6,0);
3852   pObj->h = pik_value(p, "cylht",5,0);
3853   pObj->rad = pik_value(p, "cylrad",6,0); /* Minor radius of ellipses */
3854 }
cylinderFit(Pik * p,PObj * pObj,PNum w,PNum h)3855 static void cylinderFit(Pik *p, PObj *pObj, PNum w, PNum h){
3856   if( w>0 ) pObj->w = w;
3857   if( h>0 ) pObj->h = h + 0.25*pObj->rad + pObj->sw;
3858   UNUSED_PARAMETER(p);
3859 }
cylinderRender(Pik * p,PObj * pObj)3860 static void cylinderRender(Pik *p, PObj *pObj){
3861   PNum w2 = 0.5*pObj->w;
3862   PNum h2 = 0.5*pObj->h;
3863   PNum rad = pObj->rad;
3864   PPoint pt = pObj->ptAt;
3865   if( pObj->sw>0.0 ){
3866     if( rad>h2 ){
3867       rad = h2;
3868     }else if( rad<0 ){
3869       rad = 0;
3870     }
3871     pik_append_xy(p,"<path d=\"M", pt.x-w2,pt.y+h2-rad);
3872     pik_append_xy(p,"L", pt.x-w2,pt.y-h2+rad);
3873     pik_append_arc(p,w2,rad,pt.x+w2,pt.y-h2+rad);
3874     pik_append_xy(p,"L", pt.x+w2,pt.y+h2-rad);
3875     pik_append_arc(p,w2,rad,pt.x-w2,pt.y+h2-rad);
3876     pik_append_arc(p,w2,rad,pt.x+w2,pt.y+h2-rad);
3877     pik_append(p,"\" ",-1);
3878     pik_append_style(p,pObj,3);
3879     pik_append(p,"\" />\n", -1);
3880   }
3881   pik_append_txt(p, pObj, 0);
3882 }
cylinderOffset(Pik * p,PObj * pObj,int cp)3883 static PPoint cylinderOffset(Pik *p, PObj *pObj, int cp){
3884   PPoint pt = cZeroPoint;
3885   PNum w2 = pObj->w*0.5;
3886   PNum h1 = pObj->h*0.5;
3887   PNum h2 = h1 - pObj->rad;
3888   switch( cp ){
3889     case CP_C:                                break;
3890     case CP_N:   pt.x = 0.0;   pt.y = h1;     break;
3891     case CP_NE:  pt.x = w2;    pt.y = h2;     break;
3892     case CP_E:   pt.x = w2;    pt.y = 0.0;    break;
3893     case CP_SE:  pt.x = w2;    pt.y = -h2;    break;
3894     case CP_S:   pt.x = 0.0;   pt.y = -h1;    break;
3895     case CP_SW:  pt.x = -w2;   pt.y = -h2;    break;
3896     case CP_W:   pt.x = -w2;   pt.y = 0.0;    break;
3897     case CP_NW:  pt.x = -w2;   pt.y = h2;     break;
3898     default:     assert(0);
3899   }
3900   UNUSED_PARAMETER(p);
3901   return pt;
3902 }
3903 
3904 /* Methods for the "dot" class */
dotInit(Pik * p,PObj * pObj)3905 static void dotInit(Pik *p, PObj *pObj){
3906   pObj->rad = pik_value(p, "dotrad",6,0);
3907   pObj->h = pObj->w = pObj->rad*6;
3908   pObj->fill = pObj->color;
3909 }
dotNumProp(Pik * p,PObj * pObj,PToken * pId)3910 static void dotNumProp(Pik *p, PObj *pObj, PToken *pId){
3911   switch( pId->eType ){
3912     case T_COLOR:
3913       pObj->fill = pObj->color;
3914       break;
3915     case T_FILL:
3916       pObj->color = pObj->fill;
3917       break;
3918   }
3919   UNUSED_PARAMETER(p);
3920 }
dotCheck(Pik * p,PObj * pObj)3921 static void dotCheck(Pik *p, PObj *pObj){
3922   pObj->w = pObj->h = 0;
3923   pik_bbox_addellipse(&pObj->bbox, pObj->ptAt.x, pObj->ptAt.y,
3924                        pObj->rad, pObj->rad);
3925   UNUSED_PARAMETER(p);
3926 }
dotOffset(Pik * p,PObj * pObj,int cp)3927 static PPoint dotOffset(Pik *p, PObj *pObj, int cp){
3928   UNUSED_PARAMETER(p);
3929   UNUSED_PARAMETER(pObj);
3930   UNUSED_PARAMETER(cp);
3931   return cZeroPoint;
3932 }
dotRender(Pik * p,PObj * pObj)3933 static void dotRender(Pik *p, PObj *pObj){
3934   PNum r = pObj->rad;
3935   PPoint pt = pObj->ptAt;
3936   if( pObj->sw>0.0 ){
3937     pik_append_x(p,"<circle cx=\"", pt.x, "\"");
3938     pik_append_y(p," cy=\"", pt.y, "\"");
3939     pik_append_dis(p," r=\"", r, "\"");
3940     pik_append_style(p,pObj,2);
3941     pik_append(p,"\" />\n", -1);
3942   }
3943   pik_append_txt(p, pObj, 0);
3944 }
3945 
3946 
3947 
3948 /* Methods for the "ellipse" class */
ellipseInit(Pik * p,PObj * pObj)3949 static void ellipseInit(Pik *p, PObj *pObj){
3950   pObj->w = pik_value(p, "ellipsewid",10,0);
3951   pObj->h = pik_value(p, "ellipseht",9,0);
3952 }
ellipseChop(Pik * p,PObj * pObj,PPoint * pPt)3953 static PPoint ellipseChop(Pik *p, PObj *pObj, PPoint *pPt){
3954   PPoint chop;
3955   PNum s, dq, dist;
3956   PNum dx = pPt->x - pObj->ptAt.x;
3957   PNum dy = pPt->y - pObj->ptAt.y;
3958   if( pObj->w<=0.0 ) return pObj->ptAt;
3959   if( pObj->h<=0.0 ) return pObj->ptAt;
3960   s = pObj->h/pObj->w;
3961   dq = dx*s;
3962   dist = hypot(dq,dy);
3963   if( dist<pObj->h ) return pObj->ptAt;
3964   chop.x = pObj->ptAt.x + 0.5*dq*pObj->h/(dist*s);
3965   chop.y = pObj->ptAt.y + 0.5*dy*pObj->h/dist;
3966   UNUSED_PARAMETER(p);
3967   return chop;
3968 }
ellipseOffset(Pik * p,PObj * pObj,int cp)3969 static PPoint ellipseOffset(Pik *p, PObj *pObj, int cp){
3970   PPoint pt = cZeroPoint;
3971   PNum w = pObj->w*0.5;
3972   PNum w2 = w*0.70710678118654747608;
3973   PNum h = pObj->h*0.5;
3974   PNum h2 = h*0.70710678118654747608;
3975   switch( cp ){
3976     case CP_C:                                break;
3977     case CP_N:   pt.x = 0.0;   pt.y = h;      break;
3978     case CP_NE:  pt.x = w2;    pt.y = h2;     break;
3979     case CP_E:   pt.x = w;     pt.y = 0.0;    break;
3980     case CP_SE:  pt.x = w2;    pt.y = -h2;    break;
3981     case CP_S:   pt.x = 0.0;   pt.y = -h;     break;
3982     case CP_SW:  pt.x = -w2;   pt.y = -h2;    break;
3983     case CP_W:   pt.x = -w;    pt.y = 0.0;    break;
3984     case CP_NW:  pt.x = -w2;   pt.y = h2;     break;
3985     default:     assert(0);
3986   }
3987   UNUSED_PARAMETER(p);
3988   return pt;
3989 }
ellipseRender(Pik * p,PObj * pObj)3990 static void ellipseRender(Pik *p, PObj *pObj){
3991   PNum w = pObj->w;
3992   PNum h = pObj->h;
3993   PPoint pt = pObj->ptAt;
3994   if( pObj->sw>0.0 ){
3995     pik_append_x(p,"<ellipse cx=\"", pt.x, "\"");
3996     pik_append_y(p," cy=\"", pt.y, "\"");
3997     pik_append_dis(p," rx=\"", w/2.0, "\"");
3998     pik_append_dis(p," ry=\"", h/2.0, "\" ");
3999     pik_append_style(p,pObj,3);
4000     pik_append(p,"\" />\n", -1);
4001   }
4002   pik_append_txt(p, pObj, 0);
4003 }
4004 
4005 /* Methods for the "file" object */
fileInit(Pik * p,PObj * pObj)4006 static void fileInit(Pik *p, PObj *pObj){
4007   pObj->w = pik_value(p, "filewid",7,0);
4008   pObj->h = pik_value(p, "fileht",6,0);
4009   pObj->rad = pik_value(p, "filerad",7,0);
4010 }
4011 /* Return offset from the center of the file to the compass point
4012 ** given by parameter cp */
fileOffset(Pik * p,PObj * pObj,int cp)4013 static PPoint fileOffset(Pik *p, PObj *pObj, int cp){
4014   PPoint pt = cZeroPoint;
4015   PNum w2 = 0.5*pObj->w;
4016   PNum h2 = 0.5*pObj->h;
4017   PNum rx = pObj->rad;
4018   PNum mn = w2<h2 ? w2 : h2;
4019   if( rx>mn ) rx = mn;
4020   if( rx<mn*0.25 ) rx = mn*0.25;
4021   pt.x = pt.y = 0.0;
4022   rx *= 0.5;
4023   switch( cp ){
4024     case CP_C:                                   break;
4025     case CP_N:   pt.x = 0.0;      pt.y = h2;     break;
4026     case CP_NE:  pt.x = w2-rx;    pt.y = h2-rx;  break;
4027     case CP_E:   pt.x = w2;       pt.y = 0.0;    break;
4028     case CP_SE:  pt.x = w2;       pt.y = -h2;    break;
4029     case CP_S:   pt.x = 0.0;      pt.y = -h2;    break;
4030     case CP_SW:  pt.x = -w2;      pt.y = -h2;    break;
4031     case CP_W:   pt.x = -w2;      pt.y = 0.0;    break;
4032     case CP_NW:  pt.x = -w2;      pt.y = h2;     break;
4033     default:     assert(0);
4034   }
4035   UNUSED_PARAMETER(p);
4036   return pt;
4037 }
fileFit(Pik * p,PObj * pObj,PNum w,PNum h)4038 static void fileFit(Pik *p, PObj *pObj, PNum w, PNum h){
4039   if( w>0 ) pObj->w = w;
4040   if( h>0 ) pObj->h = h + 2*pObj->rad;
4041   UNUSED_PARAMETER(p);
4042 }
fileRender(Pik * p,PObj * pObj)4043 static void fileRender(Pik *p, PObj *pObj){
4044   PNum w2 = 0.5*pObj->w;
4045   PNum h2 = 0.5*pObj->h;
4046   PNum rad = pObj->rad;
4047   PPoint pt = pObj->ptAt;
4048   PNum mn = w2<h2 ? w2 : h2;
4049   if( rad>mn ) rad = mn;
4050   if( rad<mn*0.25 ) rad = mn*0.25;
4051   if( pObj->sw>0.0 ){
4052     pik_append_xy(p,"<path d=\"M", pt.x-w2,pt.y-h2);
4053     pik_append_xy(p,"L", pt.x+w2,pt.y-h2);
4054     pik_append_xy(p,"L", pt.x+w2,pt.y+(h2-rad));
4055     pik_append_xy(p,"L", pt.x+(w2-rad),pt.y+h2);
4056     pik_append_xy(p,"L", pt.x-w2,pt.y+h2);
4057     pik_append(p,"Z\" ",-1);
4058     pik_append_style(p,pObj,1);
4059     pik_append(p,"\" />\n",-1);
4060     pik_append_xy(p,"<path d=\"M", pt.x+(w2-rad), pt.y+h2);
4061     pik_append_xy(p,"L", pt.x+(w2-rad),pt.y+(h2-rad));
4062     pik_append_xy(p,"L", pt.x+w2, pt.y+(h2-rad));
4063     pik_append(p,"\" ",-1);
4064     pik_append_style(p,pObj,0);
4065     pik_append(p,"\" />\n",-1);
4066   }
4067   pik_append_txt(p, pObj, 0);
4068 }
4069 
4070 
4071 /* Methods for the "line" class */
lineInit(Pik * p,PObj * pObj)4072 static void lineInit(Pik *p, PObj *pObj){
4073   pObj->w = pik_value(p, "linewid",7,0);
4074   pObj->h = pik_value(p, "lineht",6,0);
4075   pObj->rad = pik_value(p, "linerad",7,0);
4076 }
lineOffset(Pik * p,PObj * pObj,int cp)4077 static PPoint lineOffset(Pik *p, PObj *pObj, int cp){
4078 #if 0
4079   /* In legacy PIC, the .center of an unclosed line is half way between
4080   ** its .start and .end. */
4081   if( cp==CP_C && !pObj->bClose ){
4082     PPoint out;
4083     out.x = 0.5*(pObj->ptEnter.x + pObj->ptExit.x) - pObj->ptAt.x;
4084     out.y = 0.5*(pObj->ptEnter.x + pObj->ptExit.y) - pObj->ptAt.y;
4085     return out;
4086   }
4087 #endif
4088   return boxOffset(p,pObj,cp);
4089 }
lineRender(Pik * p,PObj * pObj)4090 static void lineRender(Pik *p, PObj *pObj){
4091   int i;
4092   if( pObj->sw>0.0 ){
4093     const char *z = "<path d=\"M";
4094     int n = pObj->nPath;
4095     if( pObj->larrow ){
4096       pik_draw_arrowhead(p,&pObj->aPath[1],&pObj->aPath[0],pObj);
4097     }
4098     if( pObj->rarrow ){
4099       pik_draw_arrowhead(p,&pObj->aPath[n-2],&pObj->aPath[n-1],pObj);
4100     }
4101     for(i=0; i<pObj->nPath; i++){
4102       pik_append_xy(p,z,pObj->aPath[i].x,pObj->aPath[i].y);
4103       z = "L";
4104     }
4105     if( pObj->bClose ){
4106       pik_append(p,"Z",1);
4107     }else{
4108       pObj->fill = -1.0;
4109     }
4110     pik_append(p,"\" ",-1);
4111     pik_append_style(p,pObj,pObj->bClose?3:0);
4112     pik_append(p,"\" />\n", -1);
4113   }
4114   pik_append_txt(p, pObj, 0);
4115 }
4116 
4117 /* Methods for the "move" class */
moveInit(Pik * p,PObj * pObj)4118 static void moveInit(Pik *p, PObj *pObj){
4119   pObj->w = pik_value(p, "movewid",7,0);
4120   pObj->h = pObj->w;
4121   pObj->fill = -1.0;
4122   pObj->color = -1.0;
4123   pObj->sw = -1.0;
4124 }
moveRender(Pik * p,PObj * pObj)4125 static void moveRender(Pik *p, PObj *pObj){
4126   /* No-op */
4127   UNUSED_PARAMETER(p);
4128   UNUSED_PARAMETER(pObj);
4129 }
4130 
4131 /* Methods for the "oval" class */
ovalInit(Pik * p,PObj * pObj)4132 static void ovalInit(Pik *p, PObj *pObj){
4133   pObj->h = pik_value(p, "ovalht",6,0);
4134   pObj->w = pik_value(p, "ovalwid",7,0);
4135   pObj->rad = 0.5*(pObj->h<pObj->w?pObj->h:pObj->w);
4136 }
ovalNumProp(Pik * p,PObj * pObj,PToken * pId)4137 static void ovalNumProp(Pik *p, PObj *pObj, PToken *pId){
4138   UNUSED_PARAMETER(p);
4139   UNUSED_PARAMETER(pId);
4140   /* Always adjust the radius to be half of the smaller of
4141   ** the width and height. */
4142   pObj->rad = 0.5*(pObj->h<pObj->w?pObj->h:pObj->w);
4143 }
ovalFit(Pik * p,PObj * pObj,PNum w,PNum h)4144 static void ovalFit(Pik *p, PObj *pObj, PNum w, PNum h){
4145   UNUSED_PARAMETER(p);
4146   if( w>0 ) pObj->w = w;
4147   if( h>0 ) pObj->h = h;
4148   if( pObj->w<pObj->h ) pObj->w = pObj->h;
4149   pObj->rad = 0.5*(pObj->h<pObj->w?pObj->h:pObj->w);
4150 }
4151 
4152 
4153 
4154 /* Methods for the "spline" class */
splineInit(Pik * p,PObj * pObj)4155 static void splineInit(Pik *p, PObj *pObj){
4156   pObj->w = pik_value(p, "linewid",7,0);
4157   pObj->h = pik_value(p, "lineht",6,0);
4158   pObj->rad = 1000;
4159 }
4160 /* Return a point along the path from "f" to "t" that is r units
4161 ** prior to reaching "t", except if the path is less than 2*r total,
4162 ** return the midpoint.
4163 */
radiusMidpoint(PPoint f,PPoint t,PNum r,int * pbMid)4164 static PPoint radiusMidpoint(PPoint f, PPoint t, PNum r, int *pbMid){
4165   PNum dx = t.x - f.x;
4166   PNum dy = t.y - f.y;
4167   PNum dist = hypot(dx,dy);
4168   PPoint m;
4169   if( dist<=0.0 ) return t;
4170   dx /= dist;
4171   dy /= dist;
4172   if( r > 0.5*dist ){
4173     r = 0.5*dist;
4174     *pbMid = 1;
4175   }else{
4176     *pbMid = 0;
4177   }
4178   m.x = t.x - r*dx;
4179   m.y = t.y - r*dy;
4180   return m;
4181 }
radiusPath(Pik * p,PObj * pObj,PNum r)4182 static void radiusPath(Pik *p, PObj *pObj, PNum r){
4183   int i;
4184   int n = pObj->nPath;
4185   const PPoint *a = pObj->aPath;
4186   PPoint m;
4187   PPoint an = a[n-1];
4188   int isMid = 0;
4189   int iLast = pObj->bClose ? n : n-1;
4190 
4191   pik_append_xy(p,"<path d=\"M", a[0].x, a[0].y);
4192   m = radiusMidpoint(a[0], a[1], r, &isMid);
4193   pik_append_xy(p," L ",m.x,m.y);
4194   for(i=1; i<iLast; i++){
4195     an = i<n-1 ? a[i+1] : a[0];
4196     m = radiusMidpoint(an,a[i],r, &isMid);
4197     pik_append_xy(p," Q ",a[i].x,a[i].y);
4198     pik_append_xy(p," ",m.x,m.y);
4199     if( !isMid ){
4200       m = radiusMidpoint(a[i],an,r, &isMid);
4201       pik_append_xy(p," L ",m.x,m.y);
4202     }
4203   }
4204   pik_append_xy(p," L ",an.x,an.y);
4205   if( pObj->bClose ){
4206     pik_append(p,"Z",1);
4207   }else{
4208     pObj->fill = -1.0;
4209   }
4210   pik_append(p,"\" ",-1);
4211   pik_append_style(p,pObj,pObj->bClose?3:0);
4212   pik_append(p,"\" />\n", -1);
4213 }
splineRender(Pik * p,PObj * pObj)4214 static void splineRender(Pik *p, PObj *pObj){
4215   if( pObj->sw>0.0 ){
4216     int n = pObj->nPath;
4217     PNum r = pObj->rad;
4218     if( n<3 || r<=0.0 ){
4219       lineRender(p,pObj);
4220       return;
4221     }
4222     if( pObj->larrow ){
4223       pik_draw_arrowhead(p,&pObj->aPath[1],&pObj->aPath[0],pObj);
4224     }
4225     if( pObj->rarrow ){
4226       pik_draw_arrowhead(p,&pObj->aPath[n-2],&pObj->aPath[n-1],pObj);
4227     }
4228     radiusPath(p,pObj,pObj->rad);
4229   }
4230   pik_append_txt(p, pObj, 0);
4231 }
4232 
4233 
4234 /* Methods for the "text" class */
textInit(Pik * p,PObj * pObj)4235 static void textInit(Pik *p, PObj *pObj){
4236   pik_value(p, "textwid",7,0);
4237   pik_value(p, "textht",6,0);
4238   pObj->sw = 0.0;
4239 }
textOffset(Pik * p,PObj * pObj,int cp)4240 static PPoint textOffset(Pik *p, PObj *pObj, int cp){
4241   /* Automatically slim-down the width and height of text
4242   ** statements so that the bounding box tightly encloses the text,
4243   ** then get boxOffset() to do the offset computation.
4244   */
4245   pik_size_to_fit(p, &pObj->errTok,3);
4246   return boxOffset(p, pObj, cp);
4247 }
4248 
4249 /* Methods for the "sublist" class */
sublistInit(Pik * p,PObj * pObj)4250 static void sublistInit(Pik *p, PObj *pObj){
4251   PList *pList = pObj->pSublist;
4252   int i;
4253   UNUSED_PARAMETER(p);
4254   pik_bbox_init(&pObj->bbox);
4255   for(i=0; i<pList->n; i++){
4256     pik_bbox_addbox(&pObj->bbox, &pList->a[i]->bbox);
4257   }
4258   pObj->w = pObj->bbox.ne.x - pObj->bbox.sw.x;
4259   pObj->h = pObj->bbox.ne.y - pObj->bbox.sw.y;
4260   pObj->ptAt.x = 0.5*(pObj->bbox.ne.x + pObj->bbox.sw.x);
4261   pObj->ptAt.y = 0.5*(pObj->bbox.ne.y + pObj->bbox.sw.y);
4262   pObj->mCalc |= A_WIDTH|A_HEIGHT|A_RADIUS;
4263 }
4264 
4265 
4266 /*
4267 ** The following array holds all the different kinds of objects.
4268 ** The special [] object is separate.
4269 */
4270 static const PClass aClass[] = {
4271    {  /* name */          "arc",
4272       /* isline */        1,
4273       /* eJust */         0,
4274       /* xInit */         arcInit,
4275       /* xNumProp */      0,
4276       /* xCheck */        arcCheck,
4277       /* xChop */         0,
4278       /* xOffset */       boxOffset,
4279       /* xFit */          0,
4280       /* xRender */       arcRender
4281    },
4282    {  /* name */          "arrow",
4283       /* isline */        1,
4284       /* eJust */         0,
4285       /* xInit */         arrowInit,
4286       /* xNumProp */      0,
4287       /* xCheck */        0,
4288       /* xChop */         0,
4289       /* xOffset */       lineOffset,
4290       /* xFit */          0,
4291       /* xRender */       splineRender
4292    },
4293    {  /* name */          "box",
4294       /* isline */        0,
4295       /* eJust */         1,
4296       /* xInit */         boxInit,
4297       /* xNumProp */      0,
4298       /* xCheck */        0,
4299       /* xChop */         boxChop,
4300       /* xOffset */       boxOffset,
4301       /* xFit */          boxFit,
4302       /* xRender */       boxRender
4303    },
4304    {  /* name */          "circle",
4305       /* isline */        0,
4306       /* eJust */         0,
4307       /* xInit */         circleInit,
4308       /* xNumProp */      circleNumProp,
4309       /* xCheck */        0,
4310       /* xChop */         circleChop,
4311       /* xOffset */       ellipseOffset,
4312       /* xFit */          circleFit,
4313       /* xRender */       circleRender
4314    },
4315    {  /* name */          "cylinder",
4316       /* isline */        0,
4317       /* eJust */         1,
4318       /* xInit */         cylinderInit,
4319       /* xNumProp */      0,
4320       /* xCheck */        0,
4321       /* xChop */         boxChop,
4322       /* xOffset */       cylinderOffset,
4323       /* xFit */          cylinderFit,
4324       /* xRender */       cylinderRender
4325    },
4326    {  /* name */          "dot",
4327       /* isline */        0,
4328       /* eJust */         0,
4329       /* xInit */         dotInit,
4330       /* xNumProp */      dotNumProp,
4331       /* xCheck */        dotCheck,
4332       /* xChop */         circleChop,
4333       /* xOffset */       dotOffset,
4334       /* xFit */          0,
4335       /* xRender */       dotRender
4336    },
4337    {  /* name */          "ellipse",
4338       /* isline */        0,
4339       /* eJust */         0,
4340       /* xInit */         ellipseInit,
4341       /* xNumProp */      0,
4342       /* xCheck */        0,
4343       /* xChop */         ellipseChop,
4344       /* xOffset */       ellipseOffset,
4345       /* xFit */          boxFit,
4346       /* xRender */       ellipseRender
4347    },
4348    {  /* name */          "file",
4349       /* isline */        0,
4350       /* eJust */         1,
4351       /* xInit */         fileInit,
4352       /* xNumProp */      0,
4353       /* xCheck */        0,
4354       /* xChop */         boxChop,
4355       /* xOffset */       fileOffset,
4356       /* xFit */          fileFit,
4357       /* xRender */       fileRender
4358    },
4359    {  /* name */          "line",
4360       /* isline */        1,
4361       /* eJust */         0,
4362       /* xInit */         lineInit,
4363       /* xNumProp */      0,
4364       /* xCheck */        0,
4365       /* xChop */         0,
4366       /* xOffset */       lineOffset,
4367       /* xFit */          0,
4368       /* xRender */       splineRender
4369    },
4370    {  /* name */          "move",
4371       /* isline */        1,
4372       /* eJust */         0,
4373       /* xInit */         moveInit,
4374       /* xNumProp */      0,
4375       /* xCheck */        0,
4376       /* xChop */         0,
4377       /* xOffset */       boxOffset,
4378       /* xFit */          0,
4379       /* xRender */       moveRender
4380    },
4381    {  /* name */          "oval",
4382       /* isline */        0,
4383       /* eJust */         1,
4384       /* xInit */         ovalInit,
4385       /* xNumProp */      ovalNumProp,
4386       /* xCheck */        0,
4387       /* xChop */         boxChop,
4388       /* xOffset */       boxOffset,
4389       /* xFit */          ovalFit,
4390       /* xRender */       boxRender
4391    },
4392    {  /* name */          "spline",
4393       /* isline */        1,
4394       /* eJust */         0,
4395       /* xInit */         splineInit,
4396       /* xNumProp */      0,
4397       /* xCheck */        0,
4398       /* xChop */         0,
4399       /* xOffset */       lineOffset,
4400       /* xFit */          0,
4401       /* xRender */       splineRender
4402    },
4403    {  /* name */          "text",
4404       /* isline */        0,
4405       /* eJust */         0,
4406       /* xInit */         textInit,
4407       /* xNumProp */      0,
4408       /* xCheck */        0,
4409       /* xChop */         boxChop,
4410       /* xOffset */       textOffset,
4411       /* xFit */          boxFit,
4412       /* xRender */       boxRender
4413    },
4414 };
4415 static const PClass sublistClass =
4416    {  /* name */          "[]",
4417       /* isline */        0,
4418       /* eJust */         0,
4419       /* xInit */         sublistInit,
4420       /* xNumProp */      0,
4421       /* xCheck */        0,
4422       /* xChop */         0,
4423       /* xOffset */       boxOffset,
4424       /* xFit */          0,
4425       /* xRender */       0
4426    };
4427 static const PClass noopClass =
4428    {  /* name */          "noop",
4429       /* isline */        0,
4430       /* eJust */         0,
4431       /* xInit */         0,
4432       /* xNumProp */      0,
4433       /* xCheck */        0,
4434       /* xChop */         0,
4435       /* xOffset */       boxOffset,
4436       /* xFit */          0,
4437       /* xRender */       0
4438    };
4439 
4440 
4441 /*
4442 ** Reduce the length of the line segment by amt (if possible) by
4443 ** modifying the location of *t.
4444 */
pik_chop(PPoint * f,PPoint * t,PNum amt)4445 static void pik_chop(PPoint *f, PPoint *t, PNum amt){
4446   PNum dx = t->x - f->x;
4447   PNum dy = t->y - f->y;
4448   PNum dist = hypot(dx,dy);
4449   PNum r;
4450   if( dist<=amt ){
4451     *t = *f;
4452     return;
4453   }
4454   r = 1.0 - amt/dist;
4455   t->x = f->x + r*dx;
4456   t->y = f->y + r*dy;
4457 }
4458 
4459 /*
4460 ** Draw an arrowhead on the end of the line segment from pFrom to pTo.
4461 ** Also, shorten the line segment (by changing the value of pTo) so that
4462 ** the shaft of the arrow does not extend into the arrowhead.
4463 */
pik_draw_arrowhead(Pik * p,PPoint * f,PPoint * t,PObj * pObj)4464 static void pik_draw_arrowhead(Pik *p, PPoint *f, PPoint *t, PObj *pObj){
4465   PNum dx = t->x - f->x;
4466   PNum dy = t->y - f->y;
4467   PNum dist = hypot(dx,dy);
4468   PNum h = p->hArrow * pObj->sw;
4469   PNum w = p->wArrow * pObj->sw;
4470   PNum e1, ddx, ddy;
4471   PNum bx, by;
4472   if( pObj->color<0.0 ) return;
4473   if( pObj->sw<=0.0 ) return;
4474   if( dist<=0.0 ) return;  /* Unable */
4475   dx /= dist;
4476   dy /= dist;
4477   e1 = dist - h;
4478   if( e1<0.0 ){
4479     e1 = 0.0;
4480     h = dist;
4481   }
4482   ddx = -w*dy;
4483   ddy = w*dx;
4484   bx = f->x + e1*dx;
4485   by = f->y + e1*dy;
4486   pik_append_xy(p,"<polygon points=\"", t->x, t->y);
4487   pik_append_xy(p," ",bx-ddx, by-ddy);
4488   pik_append_xy(p," ",bx+ddx, by+ddy);
4489   pik_append_clr(p,"\" style=\"fill:",pObj->color,"\"/>\n",0);
4490   pik_chop(f,t,h/2);
4491 }
4492 
4493 /*
4494 ** Compute the relative offset to an edge location from the reference for a
4495 ** an statement.
4496 */
pik_elem_offset(Pik * p,PObj * pObj,int cp)4497 static PPoint pik_elem_offset(Pik *p, PObj *pObj, int cp){
4498   return pObj->type->xOffset(p, pObj, cp);
4499 }
4500 
4501 
4502 /*
4503 ** Append raw text to zOut
4504 */
pik_append(Pik * p,const char * zText,int n)4505 static void pik_append(Pik *p, const char *zText, int n){
4506   if( n<0 ) n = (int)strlen(zText);
4507   if( p->nOut+n>=p->nOutAlloc ){
4508     int nNew = (p->nOut+n)*2 + 1;
4509     char *z = realloc(p->zOut, nNew);
4510     if( z==0 ){
4511       pik_error(p, 0, 0);
4512       return;
4513     }
4514     p->zOut = z;
4515     p->nOutAlloc = n;
4516   }
4517   memcpy(p->zOut+p->nOut, zText, n);
4518   p->nOut += n;
4519   p->zOut[p->nOut] = 0;
4520 }
4521 
4522 /*
4523 ** Append text to zOut with HTML characters escaped.
4524 **
4525 **   *  The space character is changed into non-breaking space (U+00a0)
4526 **      if mFlags has the 0x01 bit set. This is needed when outputting
4527 **      text to preserve leading and trailing whitespace.  Turns out we
4528 **      cannot use &nbsp; as that is an HTML-ism and is not valid in XML.
4529 **
4530 **   *  The "&" character is changed into "&amp;" if mFlags has the
4531 **      0x02 bit set.  This is needed when generating error message text.
4532 **
4533 **   *  Except for the above, only "<" and ">" are escaped.
4534 */
pik_append_text(Pik * p,const char * zText,int n,int mFlags)4535 static void pik_append_text(Pik *p, const char *zText, int n, int mFlags){
4536   int i;
4537   char c = 0;
4538   int bQSpace = mFlags & 1;
4539   int bQAmp = mFlags & 2;
4540   if( n<0 ) n = (int)strlen(zText);
4541   while( n>0 ){
4542     for(i=0; i<n; i++){
4543       c = zText[i];
4544       if( c=='<' || c=='>' ) break;
4545       if( c==' ' && bQSpace ) break;
4546       if( c=='&' && bQAmp ) break;
4547     }
4548     if( i ) pik_append(p, zText, i);
4549     if( i==n ) break;
4550     switch( c ){
4551       case '<': {  pik_append(p, "&lt;", 4);  break;  }
4552       case '>': {  pik_append(p, "&gt;", 4);  break;  }
4553       case '&': {  pik_append(p, "&amp;", 5);  break;  }
4554       case ' ': {  pik_append(p, "\302\240;", 2);  break;  }
4555     }
4556     i++;
4557     n -= i;
4558     zText += i;
4559     i = 0;
4560   }
4561 }
4562 
4563 /*
4564 ** Append error message text.  This is either a raw append, or an append
4565 ** with HTML escapes, depending on whether the PIKCHR_PLAINTEXT_ERRORS flag
4566 ** is set.
4567 */
pik_append_errtxt(Pik * p,const char * zText,int n)4568 static void pik_append_errtxt(Pik *p, const char *zText, int n){
4569   if( p->mFlags & PIKCHR_PLAINTEXT_ERRORS ){
4570     pik_append(p, zText, n);
4571   }else{
4572     pik_append_text(p, zText, n, 0);
4573   }
4574 }
4575 
4576 /* Append a PNum value
4577 */
pik_append_num(Pik * p,const char * z,PNum v)4578 static void pik_append_num(Pik *p, const char *z,PNum v){
4579   char buf[100];
4580   snprintf(buf, sizeof(buf)-1, "%.10g", (double)v);
4581   buf[sizeof(buf)-1] = 0;
4582   pik_append(p, z, -1);
4583   pik_append(p, buf, -1);
4584 }
4585 
4586 /* Append a PPoint value  (Used for debugging only)
4587 */
pik_append_point(Pik * p,const char * z,PPoint * pPt)4588 static void pik_append_point(Pik *p, const char *z, PPoint *pPt){
4589   char buf[100];
4590   snprintf(buf, sizeof(buf)-1, "%.10g,%.10g",
4591           (double)pPt->x, (double)pPt->y);
4592   buf[sizeof(buf)-1] = 0;
4593   pik_append(p, z, -1);
4594   pik_append(p, buf, -1);
4595 }
4596 
4597 /*
4598 ** Invert the RGB color so that it is appropriate for dark mode.
4599 ** Variable x hold the initial color.  The color is intended for use
4600 ** as a background color if isBg is true, and as a foreground color
4601 ** if isBg is false.
4602 */
pik_color_to_dark_mode(int x,int isBg)4603 static int pik_color_to_dark_mode(int x, int isBg){
4604   int r, g, b;
4605   int mn, mx;
4606   x = 0xffffff - x;
4607   r = (x>>16) & 0xff;
4608   g = (x>>8) & 0xff;
4609   b = x & 0xff;
4610   mx = r;
4611   if( g>mx ) mx = g;
4612   if( b>mx ) mx = b;
4613   mn = r;
4614   if( g<mn ) mn = g;
4615   if( b<mn ) mn = b;
4616   r = mn + (mx-r);
4617   g = mn + (mx-g);
4618   b = mn + (mx-b);
4619   if( isBg ){
4620     if( mx>127 ){
4621       r = (127*r)/mx;
4622       g = (127*g)/mx;
4623       b = (127*b)/mx;
4624     }
4625   }else{
4626     if( mn<128 && mx>mn ){
4627       r = 127 + ((r-mn)*128)/(mx-mn);
4628       g = 127 + ((g-mn)*128)/(mx-mn);
4629       b = 127 + ((b-mn)*128)/(mx-mn);
4630     }
4631   }
4632   return r*0x10000 + g*0x100 + b;
4633 }
4634 
4635 /* Append a PNum value surrounded by text.  Do coordinate transformations
4636 ** on the value.
4637 */
pik_append_x(Pik * p,const char * z1,PNum v,const char * z2)4638 static void pik_append_x(Pik *p, const char *z1, PNum v, const char *z2){
4639   char buf[200];
4640   v -= p->bbox.sw.x;
4641   snprintf(buf, sizeof(buf)-1, "%s%d%s", z1, (int)(p->rScale*v), z2);
4642   buf[sizeof(buf)-1] = 0;
4643   pik_append(p, buf, -1);
4644 }
pik_append_y(Pik * p,const char * z1,PNum v,const char * z2)4645 static void pik_append_y(Pik *p, const char *z1, PNum v, const char *z2){
4646   char buf[200];
4647   v = p->bbox.ne.y - v;
4648   snprintf(buf, sizeof(buf)-1, "%s%d%s", z1, (int)(p->rScale*v), z2);
4649   buf[sizeof(buf)-1] = 0;
4650   pik_append(p, buf, -1);
4651 }
pik_append_xy(Pik * p,const char * z1,PNum x,PNum y)4652 static void pik_append_xy(Pik *p, const char *z1, PNum x, PNum y){
4653   char buf[200];
4654   x = x - p->bbox.sw.x;
4655   y = p->bbox.ne.y - y;
4656   snprintf(buf, sizeof(buf)-1, "%s%d,%d", z1,
4657        (int)(p->rScale*x), (int)(p->rScale*y));
4658   buf[sizeof(buf)-1] = 0;
4659   pik_append(p, buf, -1);
4660 }
pik_append_dis(Pik * p,const char * z1,PNum v,const char * z2)4661 static void pik_append_dis(Pik *p, const char *z1, PNum v, const char *z2){
4662   char buf[200];
4663   snprintf(buf, sizeof(buf)-1, "%s%g%s", z1, p->rScale*v, z2);
4664   buf[sizeof(buf)-1] = 0;
4665   pik_append(p, buf, -1);
4666 }
4667 
4668 /* Append a color specification to the output.
4669 **
4670 ** In PIKCHR_DARK_MODE, the color is inverted.  The "bg" flags indicates that
4671 ** the color is intended for use as a background color if true, or as a
4672 ** foreground color if false.  The distinction only matters for color
4673 ** inversions in PIKCHR_DARK_MODE.
4674 */
pik_append_clr(Pik * p,const char * z1,PNum v,const char * z2,int bg)4675 static void pik_append_clr(Pik *p,const char *z1,PNum v,const char *z2,int bg){
4676   char buf[200];
4677   int x = (int)v;
4678   int r, g, b;
4679   if( x==0 && p->fgcolor>0 && !bg ){
4680     x = p->fgcolor;
4681   }else if( bg && x>=0xffffff && p->bgcolor>0 ){
4682     x = p->bgcolor;
4683   }else if( p->mFlags & PIKCHR_DARK_MODE ){
4684     x = pik_color_to_dark_mode(x,bg);
4685   }
4686   r = (x>>16) & 0xff;
4687   g = (x>>8) & 0xff;
4688   b = x & 0xff;
4689   snprintf(buf, sizeof(buf)-1, "%srgb(%d,%d,%d)%s", z1, r, g, b, z2);
4690   buf[sizeof(buf)-1] = 0;
4691   pik_append(p, buf, -1);
4692 }
4693 
4694 /* Append an SVG path A record:
4695 **
4696 **    A r1 r2 0 0 0 x y
4697 */
pik_append_arc(Pik * p,PNum r1,PNum r2,PNum x,PNum y)4698 static void pik_append_arc(Pik *p, PNum r1, PNum r2, PNum x, PNum y){
4699   char buf[200];
4700   x = x - p->bbox.sw.x;
4701   y = p->bbox.ne.y - y;
4702   snprintf(buf, sizeof(buf)-1, "A%d %d 0 0 0 %d %d",
4703      (int)(p->rScale*r1), (int)(p->rScale*r2),
4704      (int)(p->rScale*x), (int)(p->rScale*y));
4705   buf[sizeof(buf)-1] = 0;
4706   pik_append(p, buf, -1);
4707 }
4708 
4709 /* Append a style="..." text.  But, leave the quote unterminated, in case
4710 ** the caller wants to add some more.
4711 **
4712 ** eFill is non-zero to fill in the background, or 0 if no fill should
4713 ** occur.  Non-zero values of eFill determine the "bg" flag to pik_append_clr()
4714 ** for cases when pObj->fill==pObj->color
4715 **
4716 **     1        fill is background, and color is foreground.
4717 **     2        fill and color are both foreground.  (Used by "dot" objects)
4718 **     3        fill and color are both background.  (Used by most other objs)
4719 */
pik_append_style(Pik * p,PObj * pObj,int eFill)4720 static void pik_append_style(Pik *p, PObj *pObj, int eFill){
4721   int clrIsBg = 0;
4722   pik_append(p, " style=\"", -1);
4723   if( pObj->fill>=0 && eFill ){
4724     int fillIsBg = 1;
4725     if( pObj->fill==pObj->color ){
4726       if( eFill==2 ) fillIsBg = 0;
4727       if( eFill==3 ) clrIsBg = 1;
4728     }
4729     pik_append_clr(p, "fill:", pObj->fill, ";", fillIsBg);
4730   }else{
4731     pik_append(p,"fill:none;",-1);
4732   }
4733   if( pObj->sw>0.0 && pObj->color>=0.0 ){
4734     PNum sw = pObj->sw;
4735     pik_append_dis(p, "stroke-width:", sw, ";");
4736     if( pObj->nPath>2 && pObj->rad<=pObj->sw ){
4737       pik_append(p, "stroke-linejoin:round;", -1);
4738     }
4739     pik_append_clr(p, "stroke:",pObj->color,";",clrIsBg);
4740     if( pObj->dotted>0.0 ){
4741       PNum v = pObj->dotted;
4742       if( sw<2.1/p->rScale ) sw = 2.1/p->rScale;
4743       pik_append_dis(p,"stroke-dasharray:",sw,"");
4744       pik_append_dis(p,",",v,";");
4745     }else if( pObj->dashed>0.0 ){
4746       PNum v = pObj->dashed;
4747       pik_append_dis(p,"stroke-dasharray:",v,"");
4748       pik_append_dis(p,",",v,";");
4749     }
4750   }
4751 }
4752 
4753 /*
4754 ** Compute the vertical locations for all text items in the
4755 ** object pObj.  In other words, set every pObj->aTxt[*].eCode
4756 ** value to contain exactly one of: TP_ABOVE2, TP_ABOVE, TP_CENTER,
4757 ** TP_BELOW, or TP_BELOW2 is set.
4758 */
pik_txt_vertical_layout(PObj * pObj)4759 static void pik_txt_vertical_layout(PObj *pObj){
4760   int n, i;
4761   PToken *aTxt;
4762   n = pObj->nTxt;
4763   if( n==0 ) return;
4764   aTxt = pObj->aTxt;
4765   if( n==1 ){
4766     if( (aTxt[0].eCode & TP_VMASK)==0 ){
4767       aTxt[0].eCode |= TP_CENTER;
4768     }
4769   }else{
4770     int allSlots = 0;
4771     int aFree[5];
4772     int iSlot;
4773     int j, mJust;
4774     /* If there is more than one TP_ABOVE, change the first to TP_ABOVE2. */
4775     for(j=mJust=0, i=n-1; i>=0; i--){
4776       if( aTxt[i].eCode & TP_ABOVE ){
4777         if( j==0 ){
4778           j++;
4779           mJust = aTxt[i].eCode & TP_JMASK;
4780         }else if( j==1 && mJust!=0 && (aTxt[i].eCode & mJust)==0 ){
4781           j++;
4782         }else{
4783           aTxt[i].eCode = (aTxt[i].eCode & ~TP_VMASK) | TP_ABOVE2;
4784           break;
4785         }
4786       }
4787     }
4788     /* If there is more than one TP_BELOW, change the last to TP_BELOW2 */
4789     for(j=mJust=0, i=0; i<n; i++){
4790       if( aTxt[i].eCode & TP_BELOW ){
4791         if( j==0 ){
4792           j++;
4793           mJust = aTxt[i].eCode & TP_JMASK;
4794         }else if( j==1 && mJust!=0 && (aTxt[i].eCode & mJust)==0 ){
4795           j++;
4796         }else{
4797           aTxt[i].eCode = (aTxt[i].eCode & ~TP_VMASK) | TP_BELOW2;
4798           break;
4799         }
4800       }
4801     }
4802     /* Compute a mask of all slots used */
4803     for(i=0; i<n; i++) allSlots |= aTxt[i].eCode & TP_VMASK;
4804     /* Set of an array of available slots */
4805     if( n==2
4806      && ((aTxt[0].eCode|aTxt[1].eCode)&TP_JMASK)==(TP_LJUST|TP_RJUST)
4807     ){
4808       /* Special case of two texts that have opposite justification:
4809       ** Allow them both to float to center. */
4810       iSlot = 2;
4811       aFree[0] = aFree[1] = TP_CENTER;
4812     }else{
4813       /* Set up the arrow so that available slots are filled from top to
4814       ** bottom */
4815       iSlot = 0;
4816       if( n>=4 && (allSlots & TP_ABOVE2)==0 ) aFree[iSlot++] = TP_ABOVE2;
4817       if( (allSlots & TP_ABOVE)==0 ) aFree[iSlot++] = TP_ABOVE;
4818       if( (n&1)!=0 ) aFree[iSlot++] = TP_CENTER;
4819       if( (allSlots & TP_BELOW)==0 ) aFree[iSlot++] = TP_BELOW;
4820       if( n>=4 && (allSlots & TP_BELOW2)==0 ) aFree[iSlot++] = TP_BELOW2;
4821     }
4822     /* Set the VMASK for all unassigned texts */
4823     for(i=iSlot=0; i<n; i++){
4824       if( (aTxt[i].eCode & TP_VMASK)==0 ){
4825         aTxt[i].eCode |= aFree[iSlot++];
4826       }
4827     }
4828   }
4829 }
4830 
4831 /* Return the font scaling factor associated with the input text attribute.
4832 */
pik_font_scale(PToken * t)4833 static PNum pik_font_scale(PToken *t){
4834   PNum scale = 1.0;
4835   if( t->eCode & TP_BIG    ) scale *= 1.25;
4836   if( t->eCode & TP_SMALL  ) scale *= 0.8;
4837   if( t->eCode & TP_XTRA   ) scale *= scale;
4838   return scale;
4839 }
4840 
4841 /* Append multiple <text> SVG elements for the text fields of the PObj.
4842 ** Parameters:
4843 **
4844 **    p          The Pik object into which we are rendering
4845 **
4846 **    pObj       Object containing the text to be rendered
4847 **
4848 **    pBox       If not NULL, do no rendering at all.  Instead
4849 **               expand the box object so that it will include all
4850 **               of the text.
4851 */
pik_append_txt(Pik * p,PObj * pObj,PBox * pBox)4852 static void pik_append_txt(Pik *p, PObj *pObj, PBox *pBox){
4853   PNum jw;          /* Justification margin relative to center */
4854   PNum ha2 = 0.0;   /* Height of the top row of text */
4855   PNum ha1 = 0.0;   /* Height of the second "above" row */
4856   PNum hc = 0.0;    /* Height of the center row */
4857   PNum hb1 = 0.0;   /* Height of the first "below" row of text */
4858   PNum hb2 = 0.0;   /* Height of the second "below" row */
4859   PNum yBase = 0.0;
4860   int n, i, nz;
4861   PNum x, y, orig_y, s;
4862   const char *z;
4863   PToken *aTxt;
4864   unsigned allMask = 0;
4865 
4866   if( p->nErr ) return;
4867   if( pObj->nTxt==0 ) return;
4868   aTxt = pObj->aTxt;
4869   n = pObj->nTxt;
4870   pik_txt_vertical_layout(pObj);
4871   x = pObj->ptAt.x;
4872   for(i=0; i<n; i++) allMask |= pObj->aTxt[i].eCode;
4873   if( pObj->type->isLine ){
4874     hc = pObj->sw*1.5;
4875   }else if( pObj->rad>0.0 && pObj->type->xInit==cylinderInit ){
4876     yBase = -0.75*pObj->rad;
4877   }
4878   if( allMask & TP_CENTER ){
4879     for(i=0; i<n; i++){
4880       if( pObj->aTxt[i].eCode & TP_CENTER ){
4881         s = pik_font_scale(pObj->aTxt+i);
4882         if( hc<s*p->charHeight ) hc = s*p->charHeight;
4883       }
4884     }
4885   }
4886   if( allMask & TP_ABOVE ){
4887     for(i=0; i<n; i++){
4888       if( pObj->aTxt[i].eCode & TP_ABOVE ){
4889         s = pik_font_scale(pObj->aTxt+i)*p->charHeight;
4890         if( ha1<s ) ha1 = s;
4891       }
4892     }
4893     if( allMask & TP_ABOVE2 ){
4894       for(i=0; i<n; i++){
4895         if( pObj->aTxt[i].eCode & TP_ABOVE2 ){
4896           s = pik_font_scale(pObj->aTxt+i)*p->charHeight;
4897           if( ha2<s ) ha2 = s;
4898         }
4899       }
4900     }
4901   }
4902   if( allMask & TP_BELOW ){
4903     for(i=0; i<n; i++){
4904       if( pObj->aTxt[i].eCode & TP_BELOW ){
4905         s = pik_font_scale(pObj->aTxt+i)*p->charHeight;
4906         if( hb1<s ) hb1 = s;
4907       }
4908     }
4909     if( allMask & TP_BELOW2 ){
4910       for(i=0; i<n; i++){
4911         if( pObj->aTxt[i].eCode & TP_BELOW2 ){
4912           s = pik_font_scale(pObj->aTxt+i)*p->charHeight;
4913           if( hb2<s ) hb2 = s;
4914         }
4915       }
4916     }
4917   }
4918   if( pObj->type->eJust==1 ){
4919     jw = 0.5*(pObj->w - 0.5*(p->charWidth + pObj->sw));
4920   }else{
4921     jw = 0.0;
4922   }
4923   for(i=0; i<n; i++){
4924     PToken *t = &aTxt[i];
4925     PNum xtraFontScale = pik_font_scale(t);
4926     PNum nx = 0;
4927     orig_y = pObj->ptAt.y;
4928     y = yBase;
4929     if( t->eCode & TP_ABOVE2 ) y += 0.5*hc + ha1 + 0.5*ha2;
4930     if( t->eCode & TP_ABOVE  ) y += 0.5*hc + 0.5*ha1;
4931     if( t->eCode & TP_BELOW  ) y -= 0.5*hc + 0.5*hb1;
4932     if( t->eCode & TP_BELOW2 ) y -= 0.5*hc + hb1 + 0.5*hb2;
4933     if( t->eCode & TP_LJUST  ) nx -= jw;
4934     if( t->eCode & TP_RJUST  ) nx += jw;
4935 
4936     if( pBox!=0 ){
4937       /* If pBox is not NULL, do not draw any <text>.  Instead, just expand
4938       ** pBox to include the text */
4939       PNum cw = pik_text_length(t)*p->charWidth*xtraFontScale*0.01;
4940       PNum ch = p->charHeight*0.5*xtraFontScale;
4941       PNum x0, y0, x1, y1;  /* Boundary of text relative to pObj->ptAt */
4942       if( t->eCode & TP_BOLD ) cw *= 1.1;
4943       if( t->eCode & TP_RJUST ){
4944         x0 = nx;
4945         y0 = y-ch;
4946         x1 = nx-cw;
4947         y1 = y+ch;
4948       }else if( t->eCode & TP_LJUST ){
4949         x0 = nx;
4950         y0 = y-ch;
4951         x1 = nx+cw;
4952         y1 = y+ch;
4953       }else{
4954         x0 = nx+cw/2;
4955         y0 = y+ch;
4956         x1 = nx-cw/2;
4957         y1 = y-ch;
4958       }
4959       if( (t->eCode & TP_ALIGN)!=0 && pObj->nPath>=2 ){
4960         int nn = pObj->nPath;
4961         PNum dx = pObj->aPath[nn-1].x - pObj->aPath[0].x;
4962         PNum dy = pObj->aPath[nn-1].y - pObj->aPath[0].y;
4963         if( dx!=0 || dy!=0 ){
4964           PNum dist = hypot(dx,dy);
4965           PNum tt;
4966           dx /= dist;
4967           dy /= dist;
4968           tt = dx*x0 - dy*y0;
4969           y0 = dy*x0 - dx*y0;
4970           x0 = tt;
4971           tt = dx*x1 - dy*y1;
4972           y1 = dy*x1 - dx*y1;
4973           x1 = tt;
4974         }
4975       }
4976       pik_bbox_add_xy(pBox, x+x0, orig_y+y0);
4977       pik_bbox_add_xy(pBox, x+x1, orig_y+y1);
4978       continue;
4979     }
4980     nx += x;
4981     y += orig_y;
4982 
4983     pik_append_x(p, "<text x=\"", nx, "\"");
4984     pik_append_y(p, " y=\"", y, "\"");
4985     if( t->eCode & TP_RJUST ){
4986       pik_append(p, " text-anchor=\"end\"", -1);
4987     }else if( t->eCode & TP_LJUST ){
4988       pik_append(p, " text-anchor=\"start\"", -1);
4989     }else{
4990       pik_append(p, " text-anchor=\"middle\"", -1);
4991     }
4992     if( t->eCode & TP_ITALIC ){
4993       pik_append(p, " font-style=\"italic\"", -1);
4994     }
4995     if( t->eCode & TP_BOLD ){
4996       pik_append(p, " font-weight=\"bold\"", -1);
4997     }
4998     if( pObj->color>=0.0 ){
4999       pik_append_clr(p, " fill=\"", pObj->color, "\"",0);
5000     }
5001     xtraFontScale *= p->fontScale;
5002     if( xtraFontScale<=0.99 || xtraFontScale>=1.01 ){
5003       pik_append_num(p, " font-size=\"", xtraFontScale*100.0);
5004       pik_append(p, "%\"", 2);
5005     }
5006     if( (t->eCode & TP_ALIGN)!=0 && pObj->nPath>=2 ){
5007       int nn = pObj->nPath;
5008       PNum dx = pObj->aPath[nn-1].x - pObj->aPath[0].x;
5009       PNum dy = pObj->aPath[nn-1].y - pObj->aPath[0].y;
5010       if( dx!=0 || dy!=0 ){
5011         PNum ang = atan2(dy,dx)*-180/M_PI;
5012         pik_append_num(p, " transform=\"rotate(", ang);
5013         pik_append_xy(p, " ", x, orig_y);
5014         pik_append(p,")\"",2);
5015       }
5016     }
5017     pik_append(p," dominant-baseline=\"central\">",-1);
5018     if( t->n>=2 && t->z[0]=='"' ){
5019       z = t->z+1;
5020       nz = t->n-2;
5021     }else{
5022       z = t->z;
5023       nz = t->n;
5024     }
5025     while( nz>0 ){
5026       int j;
5027       for(j=0; j<nz && z[j]!='\\'; j++){}
5028       if( j ) pik_append_text(p, z, j, 1);
5029       if( j<nz && (j+1==nz || z[j+1]=='\\') ){
5030         pik_append(p, "&#92;", -1);
5031         j++;
5032       }
5033       nz -= j+1;
5034       z += j+1;
5035     }
5036     pik_append(p, "</text>\n", -1);
5037   }
5038 }
5039 
5040 /*
5041 ** Append text (that will go inside of a <pre>...</pre>) that
5042 ** shows the context of an error token.
5043 */
pik_error_context(Pik * p,PToken * pErr,int nContext)5044 static void pik_error_context(Pik *p, PToken *pErr, int nContext){
5045   int iErrPt;           /* Index of first byte of error from start of input */
5046   int iErrCol;          /* Column of the error token on its line */
5047   int iStart;           /* Start position of the error context */
5048   int iEnd;             /* End position of the error context */
5049   int iLineno;          /* Line number of the error */
5050   int iFirstLineno;     /* Line number of start of error context */
5051   int i;                /* Loop counter */
5052   int iBump = 0;        /* Bump the location of the error cursor */
5053   char zLineno[20];     /* Buffer in which to generate line numbers */
5054 
5055   iErrPt = (int)(pErr->z - p->sIn.z);
5056   if( iErrPt>=(int)p->sIn.n ){
5057     iErrPt = p->sIn.n-1;
5058     iBump = 1;
5059   }else{
5060     while( iErrPt>0 && (p->sIn.z[iErrPt]=='\n' || p->sIn.z[iErrPt]=='\r') ){
5061       iErrPt--;
5062       iBump = 1;
5063     }
5064   }
5065   iLineno = 1;
5066   for(i=0; i<iErrPt; i++){
5067     if( p->sIn.z[i]=='\n' ){
5068       iLineno++;
5069     }
5070   }
5071   iStart = 0;
5072   iFirstLineno = 1;
5073   while( iFirstLineno+nContext<iLineno ){
5074     while( p->sIn.z[iStart]!='\n' ){ iStart++; }
5075     iStart++;
5076     iFirstLineno++;
5077   }
5078   for(iEnd=iErrPt; p->sIn.z[iEnd]!=0 && p->sIn.z[iEnd]!='\n'; iEnd++){}
5079   i = iStart;
5080   while( iFirstLineno<=iLineno ){
5081     snprintf(zLineno,sizeof(zLineno)-1,"/* %4d */  ", iFirstLineno++);
5082     zLineno[sizeof(zLineno)-1] = 0;
5083     pik_append(p, zLineno, -1);
5084     for(i=iStart; p->sIn.z[i]!=0 && p->sIn.z[i]!='\n'; i++){}
5085     pik_append_errtxt(p, p->sIn.z+iStart, i-iStart);
5086     iStart = i+1;
5087     pik_append(p, "\n", 1);
5088   }
5089   for(iErrCol=0, i=iErrPt; i>0 && p->sIn.z[i]!='\n'; iErrCol++, i--){}
5090   for(i=0; i<iErrCol+11+iBump; i++){ pik_append(p, " ", 1); }
5091   for(i=0; i<(int)pErr->n; i++) pik_append(p, "^", 1);
5092   pik_append(p, "\n", 1);
5093 }
5094 
5095 
5096 /*
5097 ** Generate an error message for the output.  pErr is the token at which
5098 ** the error should point.  zMsg is the text of the error message. If
5099 ** either pErr or zMsg is NULL, generate an out-of-memory error message.
5100 **
5101 ** This routine is a no-op if there has already been an error reported.
5102 */
pik_error(Pik * p,PToken * pErr,const char * zMsg)5103 static void pik_error(Pik *p, PToken *pErr, const char *zMsg){
5104   int i;
5105   if( p==0 ) return;
5106   if( p->nErr ) return;
5107   p->nErr++;
5108   if( zMsg==0 ){
5109     if( p->mFlags & PIKCHR_PLAINTEXT_ERRORS ){
5110       pik_append(p, "\nOut of memory\n", -1);
5111     }else{
5112       pik_append(p, "\n<div><p>Out of memory</p></div>\n", -1);
5113     }
5114     return;
5115   }
5116   if( pErr==0 ){
5117     pik_append(p, "\n", 1);
5118     pik_append_errtxt(p, zMsg, -1);
5119     return;
5120   }
5121   if( (p->mFlags & PIKCHR_PLAINTEXT_ERRORS)==0 ){
5122     pik_append(p, "<div><pre>\n", -1);
5123   }
5124   pik_error_context(p, pErr, 5);
5125   pik_append(p, "ERROR: ", -1);
5126   pik_append_errtxt(p, zMsg, -1);
5127   pik_append(p, "\n", 1);
5128   for(i=p->nCtx-1; i>=0; i--){
5129     pik_append(p, "Called from:\n", -1);
5130     pik_error_context(p, &p->aCtx[i], 0);
5131   }
5132   if( (p->mFlags & PIKCHR_PLAINTEXT_ERRORS)==0 ){
5133     pik_append(p, "</pre></div>\n", -1);
5134   }
5135 }
5136 
5137 /*
5138 ** Process an "assert( e1 == e2 )" statement.  Always return NULL.
5139 */
pik_assert(Pik * p,PNum e1,PToken * pEq,PNum e2)5140 static PObj *pik_assert(Pik *p, PNum e1, PToken *pEq, PNum e2){
5141   char zE1[100], zE2[100], zMsg[300];
5142 
5143   /* Convert the numbers to strings using %g for comparison.  This
5144   ** limits the precision of the comparison to account for rounding error. */
5145   snprintf(zE1, sizeof(zE1), "%g", e1); zE1[sizeof(zE1)-1] = 0;
5146   snprintf(zE2, sizeof(zE2), "%g", e2); zE1[sizeof(zE2)-1] = 0;
5147   if( strcmp(zE1,zE2)!=0 ){
5148     snprintf(zMsg, sizeof(zMsg), "%.50s != %.50s", zE1, zE2);
5149     pik_error(p, pEq, zMsg);
5150   }
5151   return 0;
5152 }
5153 
5154 /*
5155 ** Process an "assert( place1 == place2 )" statement.  Always return NULL.
5156 */
pik_position_assert(Pik * p,PPoint * e1,PToken * pEq,PPoint * e2)5157 static PObj *pik_position_assert(Pik *p, PPoint *e1, PToken *pEq, PPoint *e2){
5158   char zE1[100], zE2[100], zMsg[210];
5159 
5160   /* Convert the numbers to strings using %g for comparison.  This
5161   ** limits the precision of the comparison to account for rounding error. */
5162   snprintf(zE1, sizeof(zE1), "(%g,%g)", e1->x, e1->y); zE1[sizeof(zE1)-1] = 0;
5163   snprintf(zE2, sizeof(zE2), "(%g,%g)", e2->x, e2->y); zE1[sizeof(zE2)-1] = 0;
5164   if( strcmp(zE1,zE2)!=0 ){
5165     snprintf(zMsg, sizeof(zMsg), "%s != %s", zE1, zE2);
5166     pik_error(p, pEq, zMsg);
5167   }
5168   return 0;
5169 }
5170 
5171 /* Free a complete list of objects */
pik_elist_free(Pik * p,PList * pList)5172 static void pik_elist_free(Pik *p, PList *pList){
5173   int i;
5174   if( pList==0 ) return;
5175   for(i=0; i<pList->n; i++){
5176     pik_elem_free(p, pList->a[i]);
5177   }
5178   free(pList->a);
5179   free(pList);
5180   return;
5181 }
5182 
5183 /* Free a single object, and its substructure */
pik_elem_free(Pik * p,PObj * pObj)5184 static void pik_elem_free(Pik *p, PObj *pObj){
5185   if( pObj==0 ) return;
5186   free(pObj->zName);
5187   pik_elist_free(p, pObj->pSublist);
5188   free(pObj->aPath);
5189   free(pObj);
5190 }
5191 
5192 /* Convert a numeric literal into a number.  Return that number.
5193 ** There is no error handling because the tokenizer has already
5194 ** assured us that the numeric literal is valid.
5195 **
5196 ** Allowed number forms:
5197 **
5198 **   (1)    Floating point literal
5199 **   (2)    Same as (1) but followed by a unit: "cm", "mm", "in",
5200 **          "px", "pt", or "pc".
5201 **   (3)    Hex integers: 0x000000
5202 **
5203 ** This routine returns the result in inches.  If a different unit
5204 ** is specified, the conversion happens automatically.
5205 */
pik_atof(PToken * num)5206 PNum pik_atof(PToken *num){
5207   char *endptr;
5208   PNum ans;
5209   if( num->n>=3 && num->z[0]=='0' && (num->z[1]=='x'||num->z[1]=='X') ){
5210     return (PNum)strtol(num->z+2, 0, 16);
5211   }
5212   ans = strtod(num->z, &endptr);
5213   if( (int)(endptr - num->z)==(int)num->n-2 ){
5214     char c1 = endptr[0];
5215     char c2 = endptr[1];
5216     if( c1=='c' && c2=='m' ){
5217       ans /= 2.54;
5218     }else if( c1=='m' && c2=='m' ){
5219       ans /= 25.4;
5220     }else if( c1=='p' && c2=='x' ){
5221       ans /= 96;
5222     }else if( c1=='p' && c2=='t' ){
5223       ans /= 72;
5224     }else if( c1=='p' && c2=='c' ){
5225       ans /= 6;
5226     }
5227   }
5228   return ans;
5229 }
5230 
5231 /*
5232 ** Compute the distance between two points
5233 */
pik_dist(PPoint * pA,PPoint * pB)5234 static PNum pik_dist(PPoint *pA, PPoint *pB){
5235   PNum dx, dy;
5236   dx = pB->x - pA->x;
5237   dy = pB->y - pA->y;
5238   return hypot(dx,dy);
5239 }
5240 
5241 /* Return true if a bounding box is empty.
5242 */
pik_bbox_isempty(PBox * p)5243 static int pik_bbox_isempty(PBox *p){
5244   return p->sw.x>p->ne.x;
5245 }
5246 
5247 /* Initialize a bounding box to an empty container
5248 */
pik_bbox_init(PBox * p)5249 static void pik_bbox_init(PBox *p){
5250   p->sw.x = 1.0;
5251   p->sw.y = 1.0;
5252   p->ne.x = 0.0;
5253   p->ne.y = 0.0;
5254 }
5255 
5256 /* Enlarge the PBox of the first argument so that it fully
5257 ** covers the second PBox
5258 */
pik_bbox_addbox(PBox * pA,PBox * pB)5259 static void pik_bbox_addbox(PBox *pA, PBox *pB){
5260   if( pik_bbox_isempty(pA) ){
5261     *pA = *pB;
5262   }
5263   if( pik_bbox_isempty(pB) ) return;
5264   if( pA->sw.x>pB->sw.x ) pA->sw.x = pB->sw.x;
5265   if( pA->sw.y>pB->sw.y ) pA->sw.y = pB->sw.y;
5266   if( pA->ne.x<pB->ne.x ) pA->ne.x = pB->ne.x;
5267   if( pA->ne.y<pB->ne.y ) pA->ne.y = pB->ne.y;
5268 }
5269 
5270 /* Enlarge the PBox of the first argument, if necessary, so that
5271 ** it contains the point described by the 2nd and 3rd arguments.
5272 */
pik_bbox_add_xy(PBox * pA,PNum x,PNum y)5273 static void pik_bbox_add_xy(PBox *pA, PNum x, PNum y){
5274   if( pik_bbox_isempty(pA) ){
5275     pA->ne.x = x;
5276     pA->ne.y = y;
5277     pA->sw.x = x;
5278     pA->sw.y = y;
5279     return;
5280   }
5281   if( pA->sw.x>x ) pA->sw.x = x;
5282   if( pA->sw.y>y ) pA->sw.y = y;
5283   if( pA->ne.x<x ) pA->ne.x = x;
5284   if( pA->ne.y<y ) pA->ne.y = y;
5285 }
5286 
5287 /* Enlarge the PBox so that it is able to contain an ellipse
5288 ** centered at x,y and with radiuses rx and ry.
5289 */
pik_bbox_addellipse(PBox * pA,PNum x,PNum y,PNum rx,PNum ry)5290 static void pik_bbox_addellipse(PBox *pA, PNum x, PNum y, PNum rx, PNum ry){
5291   if( pik_bbox_isempty(pA) ){
5292     pA->ne.x = x+rx;
5293     pA->ne.y = y+ry;
5294     pA->sw.x = x-rx;
5295     pA->sw.y = y-ry;
5296     return;
5297   }
5298   if( pA->sw.x>x-rx ) pA->sw.x = x-rx;
5299   if( pA->sw.y>y-ry ) pA->sw.y = y-ry;
5300   if( pA->ne.x<x+rx ) pA->ne.x = x+rx;
5301   if( pA->ne.y<y+ry ) pA->ne.y = y+ry;
5302 }
5303 
5304 
5305 
5306 /* Append a new object onto the end of an object list.  The
5307 ** object list is created if it does not already exist.  Return
5308 ** the new object list.
5309 */
pik_elist_append(Pik * p,PList * pList,PObj * pObj)5310 static PList *pik_elist_append(Pik *p, PList *pList, PObj *pObj){
5311   if( pObj==0 ) return pList;
5312   if( pList==0 ){
5313     pList = malloc(sizeof(*pList));
5314     if( pList==0 ){
5315       pik_error(p, 0, 0);
5316       pik_elem_free(p, pObj);
5317       return 0;
5318     }
5319     memset(pList, 0, sizeof(*pList));
5320   }
5321   if( pList->n>=pList->nAlloc ){
5322     int nNew = (pList->n+5)*2;
5323     PObj **pNew = realloc(pList->a, sizeof(PObj*)*nNew);
5324     if( pNew==0 ){
5325       pik_error(p, 0, 0);
5326       pik_elem_free(p, pObj);
5327       return pList;
5328     }
5329     pList->nAlloc = nNew;
5330     pList->a = pNew;
5331   }
5332   pList->a[pList->n++] = pObj;
5333   p->list = pList;
5334   return pList;
5335 }
5336 
5337 /* Convert an object class name into a PClass pointer
5338 */
pik_find_class(PToken * pId)5339 static const PClass *pik_find_class(PToken *pId){
5340   int first = 0;
5341   int last = count(aClass) - 1;
5342   do{
5343     int mid = (first+last)/2;
5344     int c = strncmp(aClass[mid].zName, pId->z, pId->n);
5345     if( c==0 ){
5346       c = aClass[mid].zName[pId->n]!=0;
5347       if( c==0 ) return &aClass[mid];
5348     }
5349     if( c<0 ){
5350       first = mid + 1;
5351     }else{
5352       last = mid - 1;
5353     }
5354   }while( first<=last );
5355   return 0;
5356 }
5357 
5358 /* Allocate and return a new PObj object.
5359 **
5360 ** If pId!=0 then pId is an identifier that defines the object class.
5361 ** If pStr!=0 then it is a STRING literal that defines a text object.
5362 ** If pSublist!=0 then this is a [...] object. If all three parameters
5363 ** are NULL then this is a no-op object used to define a PLACENAME.
5364 */
pik_elem_new(Pik * p,PToken * pId,PToken * pStr,PList * pSublist)5365 static PObj *pik_elem_new(Pik *p, PToken *pId, PToken *pStr,PList *pSublist){
5366   PObj *pNew;
5367   int miss = 0;
5368 
5369   if( p->nErr ) return 0;
5370   pNew = malloc( sizeof(*pNew) );
5371   if( pNew==0 ){
5372     pik_error(p,0,0);
5373     pik_elist_free(p, pSublist);
5374     return 0;
5375   }
5376   memset(pNew, 0, sizeof(*pNew));
5377   p->cur = pNew;
5378   p->nTPath = 1;
5379   p->thenFlag = 0;
5380   if( p->list==0 || p->list->n==0 ){
5381     pNew->ptAt.x = pNew->ptAt.y = 0.0;
5382     pNew->eWith = CP_C;
5383   }else{
5384     PObj *pPrior = p->list->a[p->list->n-1];
5385     pNew->ptAt = pPrior->ptExit;
5386     switch( p->eDir ){
5387       default:         pNew->eWith = CP_W;   break;
5388       case DIR_LEFT:   pNew->eWith = CP_E;   break;
5389       case DIR_UP:     pNew->eWith = CP_S;   break;
5390       case DIR_DOWN:   pNew->eWith = CP_N;   break;
5391     }
5392   }
5393   p->aTPath[0] = pNew->ptAt;
5394   pNew->with = pNew->ptAt;
5395   pNew->outDir = pNew->inDir = p->eDir;
5396   pNew->iLayer = (int)pik_value(p, "layer", 5, &miss);
5397   if( miss ) pNew->iLayer = 1000;
5398   if( pNew->iLayer<0 ) pNew->iLayer = 0;
5399   if( pSublist ){
5400     pNew->type = &sublistClass;
5401     pNew->pSublist = pSublist;
5402     sublistClass.xInit(p,pNew);
5403     return pNew;
5404   }
5405   if( pStr ){
5406     PToken n;
5407     n.z = "text";
5408     n.n = 4;
5409     pNew->type = pik_find_class(&n);
5410     assert( pNew->type!=0 );
5411     pNew->errTok = *pStr;
5412     pNew->type->xInit(p, pNew);
5413     pik_add_txt(p, pStr, pStr->eCode);
5414     return pNew;
5415   }
5416   if( pId ){
5417     const PClass *pClass;
5418     pNew->errTok = *pId;
5419     pClass = pik_find_class(pId);
5420     if( pClass ){
5421       pNew->type = pClass;
5422       pNew->sw = pik_value(p, "thickness",9,0);
5423       pNew->fill = pik_value(p, "fill",4,0);
5424       pNew->color = pik_value(p, "color",5,0);
5425       pClass->xInit(p, pNew);
5426       return pNew;
5427     }
5428     pik_error(p, pId, "unknown object type");
5429     pik_elem_free(p, pNew);
5430     return 0;
5431   }
5432   pNew->type = &noopClass;
5433   pNew->ptExit = pNew->ptEnter = pNew->ptAt;
5434   return pNew;
5435 }
5436 
5437 /*
5438 ** If the ID token in the argument is the name of a macro, return
5439 ** the PMacro object for that macro
5440 */
pik_find_macro(Pik * p,PToken * pId)5441 static PMacro *pik_find_macro(Pik *p, PToken *pId){
5442   PMacro *pMac;
5443   for(pMac = p->pMacros; pMac; pMac=pMac->pNext){
5444     if( pMac->macroName.n==pId->n
5445      && strncmp(pMac->macroName.z,pId->z,pId->n)==0
5446     ){
5447       return pMac;
5448     }
5449   }
5450   return 0;
5451 }
5452 
5453 /* Add a new macro
5454 */
pik_add_macro(Pik * p,PToken * pId,PToken * pCode)5455 static void pik_add_macro(
5456   Pik *p,          /* Current Pikchr diagram */
5457   PToken *pId,     /* The ID token that defines the macro name */
5458   PToken *pCode    /* Macro body inside of {...} */
5459 ){
5460   PMacro *pNew = pik_find_macro(p, pId);
5461   if( pNew==0 ){
5462     pNew = malloc( sizeof(*pNew) );
5463     if( pNew==0 ){
5464       pik_error(p, 0, 0);
5465       return;
5466     }
5467     pNew->pNext = p->pMacros;
5468     p->pMacros = pNew;
5469     pNew->macroName = *pId;
5470   }
5471   pNew->macroBody.z = pCode->z+1;
5472   pNew->macroBody.n = pCode->n-2;
5473   pNew->inUse = 0;
5474 }
5475 
5476 
5477 /*
5478 ** Set the output direction and exit point for an object
5479 */
pik_elem_set_exit(PObj * pObj,int eDir)5480 static void pik_elem_set_exit(PObj *pObj, int eDir){
5481   assert( ValidDir(eDir) );
5482   pObj->outDir = eDir;
5483   if( !pObj->type->isLine || pObj->bClose ){
5484     pObj->ptExit = pObj->ptAt;
5485     switch( pObj->outDir ){
5486       default:         pObj->ptExit.x += pObj->w*0.5;  break;
5487       case DIR_LEFT:   pObj->ptExit.x -= pObj->w*0.5;  break;
5488       case DIR_UP:     pObj->ptExit.y += pObj->h*0.5;  break;
5489       case DIR_DOWN:   pObj->ptExit.y -= pObj->h*0.5;  break;
5490     }
5491   }
5492 }
5493 
5494 /* Change the layout direction.
5495 */
pik_set_direction(Pik * p,int eDir)5496 static void pik_set_direction(Pik *p, int eDir){
5497   assert( ValidDir(eDir) );
5498   p->eDir = (unsigned char)eDir;
5499 
5500   /* It seems to make sense to reach back into the last object and
5501   ** change its exit point (its ".end") to correspond to the new
5502   ** direction.  Things just seem to work better this way.  However,
5503   ** legacy PIC does *not* do this.
5504   **
5505   ** The difference can be seen in a script like this:
5506   **
5507   **      arrow; circle; down; arrow
5508   **
5509   ** You can make pikchr render the above exactly like PIC
5510   ** by deleting the following three lines.  But I (drh) think
5511   ** it works better with those lines in place.
5512   */
5513   if( p->list && p->list->n ){
5514     pik_elem_set_exit(p->list->a[p->list->n-1], eDir);
5515   }
5516 }
5517 
5518 /* Move all coordinates contained within an object (and within its
5519 ** substructure) by dx, dy
5520 */
pik_elem_move(PObj * pObj,PNum dx,PNum dy)5521 static void pik_elem_move(PObj *pObj, PNum dx, PNum dy){
5522   int i;
5523   pObj->ptAt.x += dx;
5524   pObj->ptAt.y += dy;
5525   pObj->ptEnter.x += dx;
5526   pObj->ptEnter.y += dy;
5527   pObj->ptExit.x += dx;
5528   pObj->ptExit.y += dy;
5529   pObj->bbox.ne.x += dx;
5530   pObj->bbox.ne.y += dy;
5531   pObj->bbox.sw.x += dx;
5532   pObj->bbox.sw.y += dy;
5533   for(i=0; i<pObj->nPath; i++){
5534     pObj->aPath[i].x += dx;
5535     pObj->aPath[i].y += dy;
5536   }
5537   if( pObj->pSublist ){
5538     pik_elist_move(pObj->pSublist, dx, dy);
5539   }
5540 }
pik_elist_move(PList * pList,PNum dx,PNum dy)5541 static void pik_elist_move(PList *pList, PNum dx, PNum dy){
5542   int i;
5543   for(i=0; i<pList->n; i++){
5544     pik_elem_move(pList->a[i], dx, dy);
5545   }
5546 }
5547 
5548 /*
5549 ** Check to see if it is ok to set the value of paraemeter mThis.
5550 ** Return 0 if it is ok. If it not ok, generate an appropriate
5551 ** error message and return non-zero.
5552 **
5553 ** Flags are set in pObj so that the same object or conflicting
5554 ** objects may not be set again.
5555 **
5556 ** To be ok, bit mThis must be clear and no more than one of
5557 ** the bits identified by mBlockers may be set.
5558 */
pik_param_ok(Pik * p,PObj * pObj,PToken * pId,int mThis)5559 static int pik_param_ok(
5560   Pik *p,             /* For storing the error message (if any) */
5561   PObj *pObj,       /* The object under construction */
5562   PToken *pId,        /* Make the error point to this token */
5563   int mThis           /* Value we are trying to set */
5564 ){
5565   if( pObj->mProp & mThis ){
5566     pik_error(p, pId, "value is already set");
5567     return 1;
5568   }
5569   if( pObj->mCalc & mThis ){
5570     pik_error(p, pId, "value already fixed by prior constraints");
5571     return 1;
5572   }
5573   pObj->mProp |= mThis;
5574   return 0;
5575 }
5576 
5577 
5578 /*
5579 ** Set a numeric property like "width 7" or "radius 200%".
5580 **
5581 ** The rAbs term is an absolute value to add in.  rRel is
5582 ** a relative value by which to change the current value.
5583 */
pik_set_numprop(Pik * p,PToken * pId,PRel * pVal)5584 void pik_set_numprop(Pik *p, PToken *pId, PRel *pVal){
5585   PObj *pObj = p->cur;
5586   switch( pId->eType ){
5587     case T_HEIGHT:
5588       if( pik_param_ok(p, pObj, pId, A_HEIGHT) ) return;
5589       pObj->h = pObj->h*pVal->rRel + pVal->rAbs;
5590       break;
5591     case T_WIDTH:
5592       if( pik_param_ok(p, pObj, pId, A_WIDTH) ) return;
5593       pObj->w = pObj->w*pVal->rRel + pVal->rAbs;
5594       break;
5595     case T_RADIUS:
5596       if( pik_param_ok(p, pObj, pId, A_RADIUS) ) return;
5597       pObj->rad = pObj->rad*pVal->rRel + pVal->rAbs;
5598       break;
5599     case T_DIAMETER:
5600       if( pik_param_ok(p, pObj, pId, A_RADIUS) ) return;
5601       pObj->rad = pObj->rad*pVal->rRel + 0.5*pVal->rAbs; /* diam it 2x rad */
5602       break;
5603     case T_THICKNESS:
5604       if( pik_param_ok(p, pObj, pId, A_THICKNESS) ) return;
5605       pObj->sw = pObj->sw*pVal->rRel + pVal->rAbs;
5606       break;
5607   }
5608   if( pObj->type->xNumProp ){
5609     pObj->type->xNumProp(p, pObj, pId);
5610   }
5611   return;
5612 }
5613 
5614 /*
5615 ** Set a color property.  The argument is an RGB value.
5616 */
pik_set_clrprop(Pik * p,PToken * pId,PNum rClr)5617 void pik_set_clrprop(Pik *p, PToken *pId, PNum rClr){
5618   PObj *pObj = p->cur;
5619   switch( pId->eType ){
5620     case T_FILL:
5621       if( pik_param_ok(p, pObj, pId, A_FILL) ) return;
5622       pObj->fill = rClr;
5623       break;
5624     case T_COLOR:
5625       if( pik_param_ok(p, pObj, pId, A_COLOR) ) return;
5626       pObj->color = rClr;
5627       break;
5628   }
5629   if( pObj->type->xNumProp ){
5630     pObj->type->xNumProp(p, pObj, pId);
5631   }
5632   return;
5633 }
5634 
5635 /*
5636 ** Set a "dashed" property like "dash 0.05"
5637 **
5638 ** Use the value supplied by pVal if available.  If pVal==0, use
5639 ** a default.
5640 */
pik_set_dashed(Pik * p,PToken * pId,PNum * pVal)5641 void pik_set_dashed(Pik *p, PToken *pId, PNum *pVal){
5642   PObj *pObj = p->cur;
5643   PNum v;
5644   switch( pId->eType ){
5645     case T_DOTTED:  {
5646       v = pVal==0 ? pik_value(p,"dashwid",7,0) : *pVal;
5647       pObj->dotted = v;
5648       pObj->dashed = 0.0;
5649       break;
5650     }
5651     case T_DASHED:  {
5652       v = pVal==0 ? pik_value(p,"dashwid",7,0) : *pVal;
5653       pObj->dashed = v;
5654       pObj->dotted = 0.0;
5655       break;
5656     }
5657   }
5658 }
5659 
5660 /*
5661 ** If the current path information came from a "same" or "same as"
5662 ** reset it.
5663 */
pik_reset_samepath(Pik * p)5664 static void pik_reset_samepath(Pik *p){
5665   if( p->samePath ){
5666     p->samePath = 0;
5667     p->nTPath = 1;
5668   }
5669 }
5670 
5671 
5672 /* Add a new term to the path for a line-oriented object by transferring
5673 ** the information in the ptTo field over onto the path and into ptFrom
5674 ** resetting the ptTo.
5675 */
pik_then(Pik * p,PToken * pToken,PObj * pObj)5676 static void pik_then(Pik *p, PToken *pToken, PObj *pObj){
5677   int n;
5678   if( !pObj->type->isLine ){
5679     pik_error(p, pToken, "use with line-oriented objects only");
5680     return;
5681   }
5682   n = p->nTPath - 1;
5683   if( n<1 && (pObj->mProp & A_FROM)==0 ){
5684     pik_error(p, pToken, "no prior path points");
5685     return;
5686   }
5687   p->thenFlag = 1;
5688 }
5689 
5690 /* Advance to the next entry in p->aTPath.  Return its index.
5691 */
pik_next_rpath(Pik * p,PToken * pErr)5692 static int pik_next_rpath(Pik *p, PToken *pErr){
5693   int n = p->nTPath - 1;
5694   if( n+1>=(int)count(p->aTPath) ){
5695     pik_error(0, pErr, "too many path elements");
5696     return n;
5697   }
5698   n++;
5699   p->nTPath++;
5700   p->aTPath[n] = p->aTPath[n-1];
5701   p->mTPath = 0;
5702   return n;
5703 }
5704 
5705 /* Add a direction term to an object.  "up 0.5", or "left 3", or "down"
5706 ** or "down 50%".
5707 */
pik_add_direction(Pik * p,PToken * pDir,PRel * pVal)5708 static void pik_add_direction(Pik *p, PToken *pDir, PRel *pVal){
5709   PObj *pObj = p->cur;
5710   int n;
5711   int dir;
5712   if( !pObj->type->isLine ){
5713     if( pDir ){
5714       pik_error(p, pDir, "use with line-oriented objects only");
5715     }else{
5716       PToken x = pik_next_semantic_token(&pObj->errTok);
5717       pik_error(p, &x, "syntax error");
5718     }
5719     return;
5720   }
5721   pik_reset_samepath(p);
5722   n = p->nTPath - 1;
5723   if( p->thenFlag || p->mTPath==3 || n==0 ){
5724     n = pik_next_rpath(p, pDir);
5725     p->thenFlag = 0;
5726   }
5727   dir = pDir ? pDir->eCode : p->eDir;
5728   switch( dir ){
5729     case DIR_UP:
5730        if( p->mTPath & 2 ) n = pik_next_rpath(p, pDir);
5731        p->aTPath[n].y += pVal->rAbs + pObj->h*pVal->rRel;
5732        p->mTPath |= 2;
5733        break;
5734     case DIR_DOWN:
5735        if( p->mTPath & 2 ) n = pik_next_rpath(p, pDir);
5736        p->aTPath[n].y -= pVal->rAbs + pObj->h*pVal->rRel;
5737        p->mTPath |= 2;
5738        break;
5739     case DIR_RIGHT:
5740        if( p->mTPath & 1 ) n = pik_next_rpath(p, pDir);
5741        p->aTPath[n].x += pVal->rAbs + pObj->w*pVal->rRel;
5742        p->mTPath |= 1;
5743        break;
5744     case DIR_LEFT:
5745        if( p->mTPath & 1 ) n = pik_next_rpath(p, pDir);
5746        p->aTPath[n].x -= pVal->rAbs + pObj->w*pVal->rRel;
5747        p->mTPath |= 1;
5748        break;
5749   }
5750   pObj->outDir = dir;
5751 }
5752 
5753 /* Process a movement attribute of one of these forms:
5754 **
5755 **         pDist   pHdgKW  rHdg    pEdgept
5756 **     GO distance HEADING angle
5757 **     GO distance               compasspoint
5758 */
pik_move_hdg(Pik * p,PRel * pDist,PToken * pHeading,PNum rHdg,PToken * pEdgept,PToken * pErr)5759 static void pik_move_hdg(
5760   Pik *p,              /* The Pikchr context */
5761   PRel *pDist,         /* Distance to move */
5762   PToken *pHeading,    /* "heading" keyword if present */
5763   PNum rHdg,           /* Angle argument to "heading" keyword */
5764   PToken *pEdgept,     /* EDGEPT keyword "ne", "sw", etc... */
5765   PToken *pErr         /* Token to use for error messages */
5766 ){
5767   PObj *pObj = p->cur;
5768   int n;
5769   PNum rDist = pDist->rAbs + pik_value(p,"linewid",7,0)*pDist->rRel;
5770   if( !pObj->type->isLine ){
5771     pik_error(p, pErr, "use with line-oriented objects only");
5772     return;
5773   }
5774   pik_reset_samepath(p);
5775   do{
5776     n = pik_next_rpath(p, pErr);
5777   }while( n<1 );
5778   if( pHeading ){
5779     if( rHdg<0.0 || rHdg>360.0 ){
5780       pik_error(p, pHeading, "headings should be between 0 and 360");
5781       return;
5782     }
5783   }else if( pEdgept->eEdge==CP_C ){
5784     pik_error(p, pEdgept, "syntax error");
5785     return;
5786   }else{
5787     rHdg = pik_hdg_angle[pEdgept->eEdge];
5788   }
5789   if( rHdg<=45.0 ){
5790     pObj->outDir = DIR_UP;
5791   }else if( rHdg<=135.0 ){
5792     pObj->outDir = DIR_RIGHT;
5793   }else if( rHdg<=225.0 ){
5794     pObj->outDir = DIR_DOWN;
5795   }else if( rHdg<=315.0 ){
5796     pObj->outDir = DIR_LEFT;
5797   }else{
5798     pObj->outDir = DIR_UP;
5799   }
5800   rHdg *= 0.017453292519943295769;  /* degrees to radians */
5801   p->aTPath[n].x += rDist*sin(rHdg);
5802   p->aTPath[n].y += rDist*cos(rHdg);
5803   p->mTPath = 2;
5804 }
5805 
5806 
5807 /* Process a movement attribute of the form "right until even with ..."
5808 **
5809 ** pDir is the first keyword, "right" or "left" or "up" or "down".
5810 ** The movement is in that direction until its closest approach to
5811 ** the point specified by pPoint.
5812 */
pik_evenwith(Pik * p,PToken * pDir,PPoint * pPlace)5813 static void pik_evenwith(Pik *p, PToken *pDir, PPoint *pPlace){
5814   PObj *pObj = p->cur;
5815   int n;
5816   if( !pObj->type->isLine ){
5817     pik_error(p, pDir, "use with line-oriented objects only");
5818     return;
5819   }
5820   pik_reset_samepath(p);
5821   n = p->nTPath - 1;
5822   if( p->thenFlag || p->mTPath==3 || n==0 ){
5823     n = pik_next_rpath(p, pDir);
5824     p->thenFlag = 0;
5825   }
5826   switch( pDir->eCode ){
5827     case DIR_DOWN:
5828     case DIR_UP:
5829        if( p->mTPath & 2 ) n = pik_next_rpath(p, pDir);
5830        p->aTPath[n].y = pPlace->y;
5831        p->mTPath |= 2;
5832        break;
5833     case DIR_RIGHT:
5834     case DIR_LEFT:
5835        if( p->mTPath & 1 ) n = pik_next_rpath(p, pDir);
5836        p->aTPath[n].x = pPlace->x;
5837        p->mTPath |= 1;
5838        break;
5839   }
5840   pObj->outDir = pDir->eCode;
5841 }
5842 
5843 /* Set the "from" of an object
5844 */
pik_set_from(Pik * p,PObj * pObj,PToken * pTk,PPoint * pPt)5845 static void pik_set_from(Pik *p, PObj *pObj, PToken *pTk, PPoint *pPt){
5846   if( !pObj->type->isLine ){
5847     pik_error(p, pTk, "use \"at\" to position this object");
5848     return;
5849   }
5850   if( pObj->mProp & A_FROM ){
5851     pik_error(p, pTk, "line start location already fixed");
5852     return;
5853   }
5854   if( pObj->bClose ){
5855     pik_error(p, pTk, "polygon is closed");
5856     return;
5857   }
5858   if( p->nTPath>1 ){
5859     PNum dx = pPt->x - p->aTPath[0].x;
5860     PNum dy = pPt->y - p->aTPath[0].y;
5861     int i;
5862     for(i=1; i<p->nTPath; i++){
5863       p->aTPath[i].x += dx;
5864       p->aTPath[i].y += dy;
5865     }
5866   }
5867   p->aTPath[0] = *pPt;
5868   p->mTPath = 3;
5869   pObj->mProp |= A_FROM;
5870 }
5871 
5872 /* Set the "to" of an object
5873 */
pik_add_to(Pik * p,PObj * pObj,PToken * pTk,PPoint * pPt)5874 static void pik_add_to(Pik *p, PObj *pObj, PToken *pTk, PPoint *pPt){
5875   int n = p->nTPath-1;
5876   if( !pObj->type->isLine ){
5877     pik_error(p, pTk, "use \"at\" to position this object");
5878     return;
5879   }
5880   if( pObj->bClose ){
5881     pik_error(p, pTk, "polygon is closed");
5882     return;
5883   }
5884   pik_reset_samepath(p);
5885   if( n==0 || p->mTPath==3 || p->thenFlag ){
5886     n = pik_next_rpath(p, pTk);
5887   }
5888   p->aTPath[n] = *pPt;
5889   p->mTPath = 3;
5890 }
5891 
pik_close_path(Pik * p,PToken * pErr)5892 static void pik_close_path(Pik *p, PToken *pErr){
5893   PObj *pObj = p->cur;
5894   if( p->nTPath<3 ){
5895     pik_error(p, pErr,
5896       "need at least 3 vertexes in order to close the polygon");
5897     return;
5898   }
5899   if( pObj->bClose ){
5900     pik_error(p, pErr, "polygon already closed");
5901     return;
5902   }
5903   pObj->bClose = 1;
5904 }
5905 
5906 /* Lower the layer of the current object so that it is behind the
5907 ** given object.
5908 */
pik_behind(Pik * p,PObj * pOther)5909 static void pik_behind(Pik *p, PObj *pOther){
5910   PObj *pObj = p->cur;
5911   if( p->nErr==0 && pObj->iLayer>=pOther->iLayer ){
5912     pObj->iLayer = pOther->iLayer - 1;
5913   }
5914 }
5915 
5916 
5917 /* Set the "at" of an object
5918 */
pik_set_at(Pik * p,PToken * pEdge,PPoint * pAt,PToken * pErrTok)5919 static void pik_set_at(Pik *p, PToken *pEdge, PPoint *pAt, PToken *pErrTok){
5920   PObj *pObj;
5921   static unsigned char eDirToCp[] = { CP_E, CP_S, CP_W, CP_N };
5922   if( p->nErr ) return;
5923   pObj = p->cur;
5924 
5925   if( pObj->type->isLine ){
5926     pik_error(p, pErrTok, "use \"from\" and \"to\" to position this object");
5927     return;
5928   }
5929   if( pObj->mProp & A_AT ){
5930     pik_error(p, pErrTok, "location fixed by prior \"at\"");
5931     return;
5932   }
5933   pObj->mProp |= A_AT;
5934   pObj->eWith = pEdge ? pEdge->eEdge : CP_C;
5935   if( pObj->eWith>=CP_END ){
5936     int dir = pObj->eWith==CP_END ? pObj->outDir : pObj->inDir;
5937     pObj->eWith = eDirToCp[dir];
5938   }
5939   pObj->with = *pAt;
5940 }
5941 
5942 /*
5943 ** Try to add a text attribute to an object
5944 */
pik_add_txt(Pik * p,PToken * pTxt,int iPos)5945 static void pik_add_txt(Pik *p, PToken *pTxt, int iPos){
5946   PObj *pObj = p->cur;
5947   PToken *pT;
5948   if( pObj->nTxt >= count(pObj->aTxt) ){
5949     pik_error(p, pTxt, "too many text terms");
5950     return;
5951   }
5952   pT = &pObj->aTxt[pObj->nTxt++];
5953   *pT = *pTxt;
5954   pT->eCode = (short)iPos;
5955 }
5956 
5957 /* Merge "text-position" flags
5958 */
pik_text_position(int iPrev,PToken * pFlag)5959 static int pik_text_position(int iPrev, PToken *pFlag){
5960   int iRes = iPrev;
5961   switch( pFlag->eType ){
5962     case T_LJUST:    iRes = (iRes&~TP_JMASK) | TP_LJUST;  break;
5963     case T_RJUST:    iRes = (iRes&~TP_JMASK) | TP_RJUST;  break;
5964     case T_ABOVE:    iRes = (iRes&~TP_VMASK) | TP_ABOVE;  break;
5965     case T_CENTER:   iRes = (iRes&~TP_VMASK) | TP_CENTER; break;
5966     case T_BELOW:    iRes = (iRes&~TP_VMASK) | TP_BELOW;  break;
5967     case T_ITALIC:   iRes |= TP_ITALIC;                   break;
5968     case T_BOLD:     iRes |= TP_BOLD;                     break;
5969     case T_ALIGNED:  iRes |= TP_ALIGN;                    break;
5970     case T_BIG:      if( iRes & TP_BIG ) iRes |= TP_XTRA;
5971                      else iRes = (iRes &~TP_SZMASK)|TP_BIG;   break;
5972     case T_SMALL:    if( iRes & TP_SMALL ) iRes |= TP_XTRA;
5973                      else iRes = (iRes &~TP_SZMASK)|TP_SMALL; break;
5974   }
5975   return iRes;
5976 }
5977 
5978 /*
5979 ** Table of scale-factor estimates for variable-width characters.
5980 ** Actual character widths vary by font.  These numbers are only
5981 ** guesses.  And this table only provides data for ASCII.
5982 **
5983 ** 100 means normal width.
5984 */
5985 static const unsigned char awChar[] = {
5986   /* Skip initial 32 control characters */
5987   /* ' ' */  45,
5988   /* '!' */  55,
5989   /* '"' */  62,
5990   /* '#' */  115,
5991   /* '$' */  90,
5992   /* '%' */  132,
5993   /* '&' */  125,
5994   /* '\''*/  40,
5995 
5996   /* '(' */  55,
5997   /* ')' */  55,
5998   /* '*' */  71,
5999   /* '+' */  115,
6000   /* ',' */  45,
6001   /* '-' */  48,
6002   /* '.' */  45,
6003   /* '/' */  50,
6004 
6005   /* '0' */  91,
6006   /* '1' */  91,
6007   /* '2' */  91,
6008   /* '3' */  91,
6009   /* '4' */  91,
6010   /* '5' */  91,
6011   /* '6' */  91,
6012   /* '7' */  91,
6013 
6014   /* '8' */  91,
6015   /* '9' */  91,
6016   /* ':' */  50,
6017   /* ';' */  50,
6018   /* '<' */ 120,
6019   /* '=' */ 120,
6020   /* '>' */ 120,
6021   /* '?' */  78,
6022 
6023   /* '@' */ 142,
6024   /* 'A' */ 102,
6025   /* 'B' */ 105,
6026   /* 'C' */ 110,
6027   /* 'D' */ 115,
6028   /* 'E' */ 105,
6029   /* 'F' */  98,
6030   /* 'G' */ 105,
6031 
6032   /* 'H' */ 125,
6033   /* 'I' */  58,
6034   /* 'J' */  58,
6035   /* 'K' */ 107,
6036   /* 'L' */  95,
6037   /* 'M' */ 145,
6038   /* 'N' */ 125,
6039   /* 'O' */ 115,
6040 
6041   /* 'P' */  95,
6042   /* 'Q' */ 115,
6043   /* 'R' */ 107,
6044   /* 'S' */  95,
6045   /* 'T' */  97,
6046   /* 'U' */ 118,
6047   /* 'V' */ 102,
6048   /* 'W' */ 150,
6049 
6050   /* 'X' */ 100,
6051   /* 'Y' */  93,
6052   /* 'Z' */ 100,
6053   /* '[' */  58,
6054   /* '\\'*/  50,
6055   /* ']' */  58,
6056   /* '^' */ 119,
6057   /* '_' */  72,
6058 
6059   /* '`' */  72,
6060   /* 'a' */  86,
6061   /* 'b' */  92,
6062   /* 'c' */  80,
6063   /* 'd' */  92,
6064   /* 'e' */  85,
6065   /* 'f' */  52,
6066   /* 'g' */  92,
6067 
6068   /* 'h' */  92,
6069   /* 'i' */  47,
6070   /* 'j' */  47,
6071   /* 'k' */  88,
6072   /* 'l' */  48,
6073   /* 'm' */ 135,
6074   /* 'n' */  92,
6075   /* 'o' */  86,
6076 
6077   /* 'p' */  92,
6078   /* 'q' */  92,
6079   /* 'r' */  69,
6080   /* 's' */  75,
6081   /* 't' */  58,
6082   /* 'u' */  92,
6083   /* 'v' */  80,
6084   /* 'w' */ 121,
6085 
6086   /* 'x' */  81,
6087   /* 'y' */  80,
6088   /* 'z' */  76,
6089   /* '{' */  91,
6090   /* '|'*/   49,
6091   /* '}' */  91,
6092   /* '~' */ 118,
6093 };
6094 
6095 /* Return an estimate of the width of the displayed characters
6096 ** in a character string.  The returned value is 100 times the
6097 ** average character width.
6098 **
6099 ** Omit "\" used to escape characters.  And count entities like
6100 ** "&lt;" as a single character.  Multi-byte UTF8 characters count
6101 ** as a single character.
6102 **
6103 ** Attempt to scale the answer by the actual characters seen.  Wide
6104 ** characters count more than narrow characters.  But the widths are
6105 ** only guesses.
6106 */
pik_text_length(const PToken * pToken)6107 static int pik_text_length(const PToken *pToken){
6108   int n = pToken->n;
6109   const char *z = pToken->z;
6110   int cnt, j;
6111   for(j=1, cnt=0; j<n-1; j++){
6112     char c = z[j];
6113     if( c=='\\' && z[j+1]!='&' ){
6114       c = z[++j];
6115     }else if( c=='&' ){
6116       int k;
6117       for(k=j+1; k<j+7 && z[k]!=0 && z[k]!=';'; k++){}
6118       if( z[k]==';' ) j = k;
6119       cnt += 150;
6120       continue;
6121     }
6122     if( (c & 0xc0)==0xc0 ){
6123       while( j+1<n-1 && (z[j+1]&0xc0)==0x80 ){ j++; }
6124       cnt += 100;
6125       continue;
6126     }
6127     if( c>=0x20 && c<=0x7e ){
6128       cnt += awChar[c-0x20];
6129     }else{
6130       cnt += 100;
6131     }
6132   }
6133   return cnt;
6134 }
6135 
6136 /* Adjust the width, height, and/or radius of the object so that
6137 ** it fits around the text that has been added so far.
6138 **
6139 **    (1) Only text specified prior to this attribute is considered.
6140 **    (2) The text size is estimated based on the charht and charwid
6141 **        variable settings.
6142 **    (3) The fitted attributes can be changed again after this
6143 **        attribute, for example using "width 110%" if this auto-fit
6144 **        underestimates the text size.
6145 **    (4) Previously set attributes will not be altered.  In other words,
6146 **        "width 1in fit" might cause the height to change, but the
6147 **        width is now set.
6148 **    (5) This only works for attributes that have an xFit method.
6149 **
6150 ** The eWhich parameter is:
6151 **
6152 **    1:   Fit horizontally only
6153 **    2:   Fit vertically only
6154 **    3:   Fit both ways
6155 */
pik_size_to_fit(Pik * p,PToken * pFit,int eWhich)6156 static void pik_size_to_fit(Pik *p, PToken *pFit, int eWhich){
6157   PObj *pObj;
6158   PNum w, h;
6159   PBox bbox;
6160   if( p->nErr ) return;
6161   pObj = p->cur;
6162 
6163   if( pObj->nTxt==0 ){
6164     pik_error(0, pFit, "no text to fit to");
6165     return;
6166   }
6167   if( pObj->type->xFit==0 ) return;
6168   pik_bbox_init(&bbox);
6169   pik_compute_layout_settings(p);
6170   pik_append_txt(p, pObj, &bbox);
6171   w = (eWhich & 1)!=0 ? (bbox.ne.x - bbox.sw.x) + p->charWidth : 0;
6172   if( eWhich & 2 ){
6173     PNum h1, h2;
6174     h1 = (bbox.ne.y - pObj->ptAt.y);
6175     h2 = (pObj->ptAt.y - bbox.sw.y);
6176     h = 2.0*( h1<h2 ? h2 : h1 ) + 0.5*p->charHeight;
6177   }else{
6178     h = 0;
6179   }
6180   pObj->type->xFit(p, pObj, w, h);
6181   pObj->mProp |= A_FIT;
6182 }
6183 
6184 /* Set a local variable name to "val".
6185 **
6186 ** The name might be a built-in variable or a color name.  In either case,
6187 ** a new application-defined variable is set.  Since app-defined variables
6188 ** are searched first, this will override any built-in variables.
6189 */
pik_set_var(Pik * p,PToken * pId,PNum val,PToken * pOp)6190 static void pik_set_var(Pik *p, PToken *pId, PNum val, PToken *pOp){
6191   PVar *pVar = p->pVar;
6192   while( pVar ){
6193     if( pik_token_eq(pId,pVar->zName)==0 ) break;
6194     pVar = pVar->pNext;
6195   }
6196   if( pVar==0 ){
6197     char *z;
6198     pVar = malloc( pId->n+1 + sizeof(*pVar) );
6199     if( pVar==0 ){
6200       pik_error(p, 0, 0);
6201       return;
6202     }
6203     pVar->zName = z = (char*)&pVar[1];
6204     memcpy(z, pId->z, pId->n);
6205     z[pId->n] = 0;
6206     pVar->pNext = p->pVar;
6207     pVar->val = pik_value(p, pId->z, pId->n, 0);
6208     p->pVar = pVar;
6209   }
6210   switch( pOp->eCode ){
6211     case T_PLUS:  pVar->val += val; break;
6212     case T_STAR:  pVar->val *= val; break;
6213     case T_MINUS: pVar->val -= val; break;
6214     case T_SLASH:
6215       if( val==0.0 ){
6216         pik_error(p, pOp, "division by zero");
6217       }else{
6218         pVar->val /= val;
6219       }
6220       break;
6221     default:      pVar->val = val; break;
6222   }
6223   p->bLayoutVars = 0;  /* Clear the layout setting cache */
6224 }
6225 
6226 /*
6227 ** Search for the variable named z[0..n-1] in:
6228 **
6229 **   * Application defined variables
6230 **   * Built-in variables
6231 **
6232 ** Return the value of the variable if found.  If not found
6233 ** return 0.0.  Also if pMiss is not NULL, then set it to 1
6234 ** if not found.
6235 **
6236 ** This routine is a subroutine to pik_get_var().  But it is also
6237 ** used by object implementations to look up (possibly overwritten)
6238 ** values for built-in variables like "boxwid".
6239 */
pik_value(Pik * p,const char * z,int n,int * pMiss)6240 static PNum pik_value(Pik *p, const char *z, int n, int *pMiss){
6241   PVar *pVar;
6242   int first, last, mid, c;
6243   for(pVar=p->pVar; pVar; pVar=pVar->pNext){
6244     if( strncmp(pVar->zName,z,n)==0 && pVar->zName[n]==0 ){
6245       return pVar->val;
6246     }
6247   }
6248   first = 0;
6249   last = count(aBuiltin)-1;
6250   while( first<=last ){
6251     mid = (first+last)/2;
6252     c = strncmp(z,aBuiltin[mid].zName,n);
6253     if( c==0 && aBuiltin[mid].zName[n] ) c = 1;
6254     if( c==0 ) return aBuiltin[mid].val;
6255     if( c>0 ){
6256       first = mid+1;
6257     }else{
6258       last = mid-1;
6259     }
6260   }
6261   if( pMiss ) *pMiss = 1;
6262   return 0.0;
6263 }
6264 
6265 /*
6266 ** Look up a color-name.  Unlike other names in this program, the
6267 ** color-names are not case sensitive.  So "DarkBlue" and "darkblue"
6268 ** and "DARKBLUE" all find the same value (139).
6269 **
6270 ** If not found, return -99.0.  Also post an error if p!=NULL.
6271 **
6272 ** Special color names "None" and "Off" return -1.0 without causing
6273 ** an error.
6274 */
pik_lookup_color(Pik * p,PToken * pId)6275 static PNum pik_lookup_color(Pik *p, PToken *pId){
6276   int first, last, mid, c = 0;
6277   first = 0;
6278   last = count(aColor)-1;
6279   while( first<=last ){
6280     const char *zClr;
6281     int c1, c2;
6282     unsigned int i;
6283     mid = (first+last)/2;
6284     zClr = aColor[mid].zName;
6285     for(i=0; i<pId->n; i++){
6286       c1 = zClr[i]&0x7f;
6287       if( isupper(c1) ) c1 = tolower(c1);
6288       c2 = pId->z[i]&0x7f;
6289       if( isupper(c2) ) c2 = tolower(c2);
6290       c = c2 - c1;
6291       if( c ) break;
6292     }
6293     if( c==0 && aColor[mid].zName[pId->n] ) c = -1;
6294     if( c==0 ) return (double)aColor[mid].val;
6295     if( c>0 ){
6296       first = mid+1;
6297     }else{
6298       last = mid-1;
6299     }
6300   }
6301   if( p ) pik_error(p, pId, "not a known color name");
6302   return -99.0;
6303 }
6304 
6305 /* Get the value of a variable.
6306 **
6307 ** Search in order:
6308 **
6309 **    *  Application defined variables
6310 **    *  Built-in variables
6311 **    *  Color names
6312 **
6313 ** If no such variable is found, throw an error.
6314 */
pik_get_var(Pik * p,PToken * pId)6315 static PNum pik_get_var(Pik *p, PToken *pId){
6316   int miss = 0;
6317   PNum v = pik_value(p, pId->z, pId->n, &miss);
6318   if( miss==0 ) return v;
6319   v = pik_lookup_color(0, pId);
6320   if( v>-90.0 ) return v;
6321   pik_error(p,pId,"no such variable");
6322   return 0.0;
6323 }
6324 
6325 /* Convert a T_NTH token (ex: "2nd", "5th"} into a numeric value and
6326 ** return that value.  Throw an error if the value is too big.
6327 */
pik_nth_value(Pik * p,PToken * pNth)6328 static short int pik_nth_value(Pik *p, PToken *pNth){
6329   int i = atoi(pNth->z);
6330   if( i>1000 ){
6331     pik_error(p, pNth, "value too big - max '1000th'");
6332     i = 1;
6333   }
6334   if( i==0 && pik_token_eq(pNth,"first")==0 ) i = 1;
6335   return (short int)i;
6336 }
6337 
6338 /* Search for the NTH object.
6339 **
6340 ** If pBasis is not NULL then it should be a [] object.  Use the
6341 ** sublist of that [] object for the search.  If pBasis is not a []
6342 ** object, then throw an error.
6343 **
6344 ** The pNth token describes the N-th search.  The pNth->eCode value
6345 ** is one more than the number of items to skip.  It is negative
6346 ** to search backwards.  If pNth->eType==T_ID, then it is the name
6347 ** of a class to search for.  If pNth->eType==T_LB, then
6348 ** search for a [] object.  If pNth->eType==T_LAST, then search for
6349 ** any type.
6350 **
6351 ** Raise an error if the item is not found.
6352 */
pik_find_nth(Pik * p,PObj * pBasis,PToken * pNth)6353 static PObj *pik_find_nth(Pik *p, PObj *pBasis, PToken *pNth){
6354   PList *pList;
6355   int i, n;
6356   const PClass *pClass;
6357   if( pBasis==0 ){
6358     pList = p->list;
6359   }else{
6360     pList = pBasis->pSublist;
6361   }
6362   if( pList==0 ){
6363     pik_error(p, pNth, "no such object");
6364     return 0;
6365   }
6366   if( pNth->eType==T_LAST ){
6367     pClass = 0;
6368   }else if( pNth->eType==T_LB ){
6369     pClass = &sublistClass;
6370   }else{
6371     pClass = pik_find_class(pNth);
6372     if( pClass==0 ){
6373       pik_error(0, pNth, "no such object type");
6374       return 0;
6375     }
6376   }
6377   n = pNth->eCode;
6378   if( n<0 ){
6379     for(i=pList->n-1; i>=0; i--){
6380       PObj *pObj = pList->a[i];
6381       if( pClass && pObj->type!=pClass ) continue;
6382       n++;
6383       if( n==0 ){ return pObj; }
6384     }
6385   }else{
6386     for(i=0; i<pList->n; i++){
6387       PObj *pObj = pList->a[i];
6388       if( pClass && pObj->type!=pClass ) continue;
6389       n--;
6390       if( n==0 ){ return pObj; }
6391     }
6392   }
6393   pik_error(p, pNth, "no such object");
6394   return 0;
6395 }
6396 
6397 /* Search for an object by name.
6398 **
6399 ** Search in pBasis->pSublist if pBasis is not NULL.  If pBasis is NULL
6400 ** then search in p->list.
6401 */
pik_find_byname(Pik * p,PObj * pBasis,PToken * pName)6402 static PObj *pik_find_byname(Pik *p, PObj *pBasis, PToken *pName){
6403   PList *pList;
6404   int i, j;
6405   if( pBasis==0 ){
6406     pList = p->list;
6407   }else{
6408     pList = pBasis->pSublist;
6409   }
6410   if( pList==0 ){
6411     pik_error(p, pName, "no such object");
6412     return 0;
6413   }
6414   /* First look explicitly tagged objects */
6415   for(i=pList->n-1; i>=0; i--){
6416     PObj *pObj = pList->a[i];
6417     if( pObj->zName && pik_token_eq(pName,pObj->zName)==0 ){
6418       return pObj;
6419     }
6420   }
6421   /* If not found, do a second pass looking for any object containing
6422   ** text which exactly matches pName */
6423   for(i=pList->n-1; i>=0; i--){
6424     PObj *pObj = pList->a[i];
6425     for(j=0; j<pObj->nTxt; j++){
6426       if( pObj->aTxt[j].n==pName->n+2
6427        && memcmp(pObj->aTxt[j].z+1,pName->z,pName->n)==0 ){
6428         return pObj;
6429       }
6430     }
6431   }
6432   pik_error(p, pName, "no such object");
6433   return 0;
6434 }
6435 
6436 /* Change most of the settings for the current object to be the
6437 ** same as the pOther object, or the most recent object of the same
6438 ** type if pOther is NULL.
6439 */
pik_same(Pik * p,PObj * pOther,PToken * pErrTok)6440 static void pik_same(Pik *p, PObj *pOther, PToken *pErrTok){
6441   PObj *pObj = p->cur;
6442   if( p->nErr ) return;
6443   if( pOther==0 ){
6444     int i;
6445     for(i=(p->list ? p->list->n : 0)-1; i>=0; i--){
6446       pOther = p->list->a[i];
6447       if( pOther->type==pObj->type ) break;
6448     }
6449     if( i<0 ){
6450       pik_error(p, pErrTok, "no prior objects of the same type");
6451       return;
6452     }
6453   }
6454   if( pOther->nPath && pObj->type->isLine ){
6455     PNum dx, dy;
6456     int i;
6457     dx = p->aTPath[0].x - pOther->aPath[0].x;
6458     dy = p->aTPath[0].y - pOther->aPath[0].y;
6459     for(i=1; i<pOther->nPath; i++){
6460       p->aTPath[i].x = pOther->aPath[i].x + dx;
6461       p->aTPath[i].y = pOther->aPath[i].y + dy;
6462     }
6463     p->nTPath = pOther->nPath;
6464     p->mTPath = 3;
6465     p->samePath = 1;
6466   }
6467   if( !pObj->type->isLine ){
6468     pObj->w = pOther->w;
6469     pObj->h = pOther->h;
6470   }
6471   pObj->rad = pOther->rad;
6472   pObj->sw = pOther->sw;
6473   pObj->dashed = pOther->dashed;
6474   pObj->dotted = pOther->dotted;
6475   pObj->fill = pOther->fill;
6476   pObj->color = pOther->color;
6477   pObj->cw = pOther->cw;
6478   pObj->larrow = pOther->larrow;
6479   pObj->rarrow = pOther->rarrow;
6480   pObj->bClose = pOther->bClose;
6481   pObj->bChop = pOther->bChop;
6482   pObj->inDir = pOther->inDir;
6483   pObj->outDir = pOther->outDir;
6484   pObj->iLayer = pOther->iLayer;
6485 }
6486 
6487 
6488 /* Return a "Place" associated with object pObj.  If pEdge is NULL
6489 ** return the center of the object.  Otherwise, return the corner
6490 ** described by pEdge.
6491 */
pik_place_of_elem(Pik * p,PObj * pObj,PToken * pEdge)6492 static PPoint pik_place_of_elem(Pik *p, PObj *pObj, PToken *pEdge){
6493   PPoint pt = cZeroPoint;
6494   const PClass *pClass;
6495   if( pObj==0 ) return pt;
6496   if( pEdge==0 ){
6497     return pObj->ptAt;
6498   }
6499   pClass = pObj->type;
6500   if( pEdge->eType==T_EDGEPT || (pEdge->eEdge>0 && pEdge->eEdge<CP_END) ){
6501     pt = pClass->xOffset(p, pObj, pEdge->eEdge);
6502     pt.x += pObj->ptAt.x;
6503     pt.y += pObj->ptAt.y;
6504     return pt;
6505   }
6506   if( pEdge->eType==T_START ){
6507     return pObj->ptEnter;
6508   }else{
6509     return pObj->ptExit;
6510   }
6511 }
6512 
6513 /* Do a linear interpolation of two positions.
6514 */
pik_position_between(PNum x,PPoint p1,PPoint p2)6515 static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2){
6516   PPoint out;
6517   out.x = p2.x*x + p1.x*(1.0 - x);
6518   out.y = p2.y*x + p1.y*(1.0 - x);
6519   return out;
6520 }
6521 
6522 /* Compute the position that is dist away from pt at an heading angle of r
6523 **
6524 ** The angle is a compass heading in degrees.  North is 0 (or 360).
6525 ** East is 90.  South is 180.  West is 270.  And so forth.
6526 */
pik_position_at_angle(PNum dist,PNum r,PPoint pt)6527 static PPoint pik_position_at_angle(PNum dist, PNum r, PPoint pt){
6528   r *= 0.017453292519943295769;  /* degrees to radians */
6529   pt.x += dist*sin(r);
6530   pt.y += dist*cos(r);
6531   return pt;
6532 }
6533 
6534 /* Compute the position that is dist away at a compass point
6535 */
pik_position_at_hdg(PNum dist,PToken * pD,PPoint pt)6536 static PPoint pik_position_at_hdg(PNum dist, PToken *pD, PPoint pt){
6537   return pik_position_at_angle(dist, pik_hdg_angle[pD->eEdge], pt);
6538 }
6539 
6540 /* Return the coordinates for the n-th vertex of a line.
6541 */
pik_nth_vertex(Pik * p,PToken * pNth,PToken * pErr,PObj * pObj)6542 static PPoint pik_nth_vertex(Pik *p, PToken *pNth, PToken *pErr, PObj *pObj){
6543   static const PPoint zero = {0, 0};
6544   int n;
6545   if( p->nErr || pObj==0 ) return p->aTPath[0];
6546   if( !pObj->type->isLine ){
6547     pik_error(p, pErr, "object is not a line");
6548     return zero;
6549   }
6550   n = atoi(pNth->z);
6551   if( n<1 || n>pObj->nPath ){
6552     pik_error(p, pNth, "no such vertex");
6553     return zero;
6554   }
6555   return pObj->aPath[n-1];
6556 }
6557 
6558 /* Return the value of a property of an object.
6559 */
pik_property_of(PObj * pObj,PToken * pProp)6560 static PNum pik_property_of(PObj *pObj, PToken *pProp){
6561   PNum v = 0.0;
6562   switch( pProp->eType ){
6563     case T_HEIGHT:    v = pObj->h;            break;
6564     case T_WIDTH:     v = pObj->w;            break;
6565     case T_RADIUS:    v = pObj->rad;          break;
6566     case T_DIAMETER:  v = pObj->rad*2.0;      break;
6567     case T_THICKNESS: v = pObj->sw;           break;
6568     case T_DASHED:    v = pObj->dashed;       break;
6569     case T_DOTTED:    v = pObj->dotted;       break;
6570     case T_FILL:      v = pObj->fill;         break;
6571     case T_COLOR:     v = pObj->color;        break;
6572     case T_X:         v = pObj->ptAt.x;       break;
6573     case T_Y:         v = pObj->ptAt.y;       break;
6574     case T_TOP:       v = pObj->bbox.ne.y;    break;
6575     case T_BOTTOM:    v = pObj->bbox.sw.y;    break;
6576     case T_LEFT:      v = pObj->bbox.sw.x;    break;
6577     case T_RIGHT:     v = pObj->bbox.ne.x;    break;
6578   }
6579   return v;
6580 }
6581 
6582 /* Compute one of the built-in functions
6583 */
pik_func(Pik * p,PToken * pFunc,PNum x,PNum y)6584 static PNum pik_func(Pik *p, PToken *pFunc, PNum x, PNum y){
6585   PNum v = 0.0;
6586   switch( pFunc->eCode ){
6587     case FN_ABS:  v = v<0.0 ? -v : v;  break;
6588     case FN_COS:  v = cos(x);          break;
6589     case FN_INT:  v = rint(x);         break;
6590     case FN_SIN:  v = sin(x);          break;
6591     case FN_SQRT:
6592       if( x<0.0 ){
6593         pik_error(p, pFunc, "sqrt of negative value");
6594         v = 0.0;
6595       }else{
6596         v = sqrt(x);
6597       }
6598       break;
6599     case FN_MAX:  v = x>y ? x : y;   break;
6600     case FN_MIN:  v = x<y ? x : y;   break;
6601     default:      v = 0.0;
6602   }
6603   return v;
6604 }
6605 
6606 /* Attach a name to an object
6607 */
pik_elem_setname(Pik * p,PObj * pObj,PToken * pName)6608 static void pik_elem_setname(Pik *p, PObj *pObj, PToken *pName){
6609   if( pObj==0 ) return;
6610   if( pName==0 ) return;
6611   free(pObj->zName);
6612   pObj->zName = malloc(pName->n+1);
6613   if( pObj->zName==0 ){
6614     pik_error(p,0,0);
6615   }else{
6616     memcpy(pObj->zName,pName->z,pName->n);
6617     pObj->zName[pName->n] = 0;
6618   }
6619   return;
6620 }
6621 
6622 /*
6623 ** Search for object located at *pCenter that has an xChop method.
6624 ** Return a pointer to the object, or NULL if not found.
6625 */
pik_find_chopper(PList * pList,PPoint * pCenter)6626 static PObj *pik_find_chopper(PList *pList, PPoint *pCenter){
6627   int i;
6628   if( pList==0 ) return 0;
6629   for(i=pList->n-1; i>=0; i--){
6630     PObj *pObj = pList->a[i];
6631     if( pObj->type->xChop!=0
6632      && pObj->ptAt.x==pCenter->x
6633      && pObj->ptAt.y==pCenter->y
6634     ){
6635       return pObj;
6636     }else if( pObj->pSublist ){
6637       pObj = pik_find_chopper(pObj->pSublist,pCenter);
6638       if( pObj ) return pObj;
6639     }
6640   }
6641   return 0;
6642 }
6643 
6644 /*
6645 ** There is a line traveling from pFrom to pTo.
6646 **
6647 ** If point pTo is the exact enter of a choppable object,
6648 ** then adjust pTo by the appropriate amount in the direction
6649 ** of pFrom.
6650 */
pik_autochop(Pik * p,PPoint * pFrom,PPoint * pTo)6651 static void pik_autochop(Pik *p, PPoint *pFrom, PPoint *pTo){
6652   PObj *pObj = pik_find_chopper(p->list, pTo);
6653   if( pObj ){
6654     *pTo = pObj->type->xChop(p, pObj, pFrom);
6655   }
6656 }
6657 
6658 /* This routine runs after all attributes have been received
6659 ** on an object.
6660 */
pik_after_adding_attributes(Pik * p,PObj * pObj)6661 static void pik_after_adding_attributes(Pik *p, PObj *pObj){
6662   int i;
6663   PPoint ofst;
6664   PNum dx, dy;
6665 
6666   if( p->nErr ) return;
6667 
6668   /* Position block objects */
6669   if( pObj->type->isLine==0 ){
6670     /* A height or width less than or equal to zero means "autofit".
6671     ** Change the height or width to be big enough to contain the text,
6672     */
6673     if( pObj->h<=0.0 ){
6674       if( pObj->nTxt==0 ){
6675         pObj->h = 0.0;
6676       }else if( pObj->w<=0.0 ){
6677         pik_size_to_fit(p, &pObj->errTok, 3);
6678       }else{
6679         pik_size_to_fit(p, &pObj->errTok, 2);
6680       }
6681     }
6682     if( pObj->w<=0.0 ){
6683       if( pObj->nTxt==0 ){
6684         pObj->w = 0.0;
6685       }else{
6686         pik_size_to_fit(p, &pObj->errTok, 1);
6687       }
6688     }
6689     ofst = pik_elem_offset(p, pObj, pObj->eWith);
6690     dx = (pObj->with.x - ofst.x) - pObj->ptAt.x;
6691     dy = (pObj->with.y - ofst.y) - pObj->ptAt.y;
6692     if( dx!=0 || dy!=0 ){
6693       pik_elem_move(pObj, dx, dy);
6694     }
6695   }
6696 
6697   /* For a line object with no movement specified, a single movement
6698   ** of the default length in the current direction
6699   */
6700   if( pObj->type->isLine && p->nTPath<2 ){
6701     pik_next_rpath(p, 0);
6702     assert( p->nTPath==2 );
6703     switch( pObj->inDir ){
6704       default:        p->aTPath[1].x += pObj->w; break;
6705       case DIR_DOWN:  p->aTPath[1].y -= pObj->h; break;
6706       case DIR_LEFT:  p->aTPath[1].x -= pObj->w; break;
6707       case DIR_UP:    p->aTPath[1].y += pObj->h; break;
6708     }
6709     if( pObj->type->xInit==arcInit ){
6710       pObj->outDir = (pObj->inDir + (pObj->cw ? 1 : 3))%4;
6711       p->eDir = (unsigned char)pObj->outDir;
6712       switch( pObj->outDir ){
6713         default:        p->aTPath[1].x += pObj->w; break;
6714         case DIR_DOWN:  p->aTPath[1].y -= pObj->h; break;
6715         case DIR_LEFT:  p->aTPath[1].x -= pObj->w; break;
6716         case DIR_UP:    p->aTPath[1].y += pObj->h; break;
6717       }
6718     }
6719   }
6720 
6721   /* Initialize the bounding box prior to running xCheck */
6722   pik_bbox_init(&pObj->bbox);
6723 
6724   /* Run object-specific code */
6725   if( pObj->type->xCheck!=0 ){
6726     pObj->type->xCheck(p,pObj);
6727     if( p->nErr ) return;
6728   }
6729 
6730   /* Compute final bounding box, entry and exit points, center
6731   ** point (ptAt) and path for the object
6732   */
6733   if( pObj->type->isLine ){
6734     pObj->aPath = malloc( sizeof(PPoint)*p->nTPath );
6735     if( pObj->aPath==0 ){
6736       pik_error(p, 0, 0);
6737       return;
6738     }else{
6739       pObj->nPath = p->nTPath;
6740       for(i=0; i<p->nTPath; i++){
6741         pObj->aPath[i] = p->aTPath[i];
6742       }
6743     }
6744 
6745     /* "chop" processing:
6746     ** If the line goes to the center of an object with an
6747     ** xChop method, then use the xChop method to trim the line.
6748     */
6749     if( pObj->bChop && pObj->nPath>=2 ){
6750       int n = pObj->nPath;
6751       pik_autochop(p, &pObj->aPath[n-2], &pObj->aPath[n-1]);
6752       pik_autochop(p, &pObj->aPath[1], &pObj->aPath[0]);
6753     }
6754 
6755     pObj->ptEnter = pObj->aPath[0];
6756     pObj->ptExit = pObj->aPath[pObj->nPath-1];
6757 
6758     /* Compute the center of the line based on the bounding box over
6759     ** the vertexes.  This is a difference from PIC.  In Pikchr, the
6760     ** center of a line is the center of its bounding box. In PIC, the
6761     ** center of a line is halfway between its .start and .end.  For
6762     ** straight lines, this is the same point, but for multi-segment
6763     ** lines the result is usually diferent */
6764     for(i=0; i<pObj->nPath; i++){
6765       pik_bbox_add_xy(&pObj->bbox, pObj->aPath[i].x, pObj->aPath[i].y);
6766     }
6767     pObj->ptAt.x = (pObj->bbox.ne.x + pObj->bbox.sw.x)/2.0;
6768     pObj->ptAt.y = (pObj->bbox.ne.y + pObj->bbox.sw.y)/2.0;
6769 
6770     /* Reset the width and height of the object to be the width and height
6771     ** of the bounding box over vertexes */
6772     pObj->w = pObj->bbox.ne.x - pObj->bbox.sw.x;
6773     pObj->h = pObj->bbox.ne.y - pObj->bbox.sw.y;
6774 
6775     /* If this is a polygon (if it has the "close" attribute), then
6776     ** adjust the exit point */
6777     if( pObj->bClose ){
6778       /* For "closed" lines, the .end is one of the .e, .s, .w, or .n
6779       ** points of the bounding box, as with block objects. */
6780       pik_elem_set_exit(pObj, pObj->inDir);
6781     }
6782   }else{
6783     PNum w2 = pObj->w/2.0;
6784     PNum h2 = pObj->h/2.0;
6785     pObj->ptEnter = pObj->ptAt;
6786     pObj->ptExit = pObj->ptAt;
6787     switch( pObj->inDir ){
6788       default:         pObj->ptEnter.x -= w2;  break;
6789       case DIR_LEFT:   pObj->ptEnter.x += w2;  break;
6790       case DIR_UP:     pObj->ptEnter.y -= h2;  break;
6791       case DIR_DOWN:   pObj->ptEnter.y += h2;  break;
6792     }
6793     switch( pObj->outDir ){
6794       default:         pObj->ptExit.x += w2;  break;
6795       case DIR_LEFT:   pObj->ptExit.x -= w2;  break;
6796       case DIR_UP:     pObj->ptExit.y += h2;  break;
6797       case DIR_DOWN:   pObj->ptExit.y -= h2;  break;
6798     }
6799     pik_bbox_add_xy(&pObj->bbox, pObj->ptAt.x - w2, pObj->ptAt.y - h2);
6800     pik_bbox_add_xy(&pObj->bbox, pObj->ptAt.x + w2, pObj->ptAt.y + h2);
6801   }
6802   p->eDir = (unsigned char)pObj->outDir;
6803 }
6804 
6805 /* Show basic information about each object as a comment in the
6806 ** generated HTML.  Used for testing and debugging.  Activated
6807 ** by the (undocumented) "debug = 1;"
6808 ** command.
6809 */
pik_elem_render(Pik * p,PObj * pObj)6810 static void pik_elem_render(Pik *p, PObj *pObj){
6811   char *zDir;
6812   if( pObj==0 ) return;
6813   pik_append(p,"<!-- ", -1);
6814   if( pObj->zName ){
6815     pik_append_text(p, pObj->zName, -1, 0);
6816     pik_append(p, ": ", 2);
6817   }
6818   pik_append_text(p, pObj->type->zName, -1, 0);
6819   if( pObj->nTxt ){
6820     pik_append(p, " \"", 2);
6821     pik_append_text(p, pObj->aTxt[0].z+1, pObj->aTxt[0].n-2, 1);
6822     pik_append(p, "\"", 1);
6823   }
6824   pik_append_num(p, " w=", pObj->w);
6825   pik_append_num(p, " h=", pObj->h);
6826   pik_append_point(p, " center=", &pObj->ptAt);
6827   pik_append_point(p, " enter=", &pObj->ptEnter);
6828   switch( pObj->outDir ){
6829     default:        zDir = " right";  break;
6830     case DIR_LEFT:  zDir = " left";   break;
6831     case DIR_UP:    zDir = " up";     break;
6832     case DIR_DOWN:  zDir = " down";   break;
6833   }
6834   pik_append_point(p, " exit=", &pObj->ptExit);
6835   pik_append(p, zDir, -1);
6836   pik_append(p, " -->\n", -1);
6837 }
6838 
6839 /* Render a list of objects
6840 */
pik_elist_render(Pik * p,PList * pList)6841 void pik_elist_render(Pik *p, PList *pList){
6842   int i;
6843   int iNextLayer = 0;
6844   int iThisLayer;
6845   int bMoreToDo;
6846   int miss = 0;
6847   int mDebug = (int)pik_value(p, "debug", 5, 0);
6848   PNum colorLabel;
6849   do{
6850     bMoreToDo = 0;
6851     iThisLayer = iNextLayer;
6852     iNextLayer = 0x7fffffff;
6853     for(i=0; i<pList->n; i++){
6854       PObj *pObj = pList->a[i];
6855       void (*xRender)(Pik*,PObj*);
6856       if( pObj->iLayer>iThisLayer ){
6857         if( pObj->iLayer<iNextLayer ) iNextLayer = pObj->iLayer;
6858         bMoreToDo = 1;
6859         continue; /* Defer until another round */
6860       }else if( pObj->iLayer<iThisLayer ){
6861         continue;
6862       }
6863       if( mDebug & 1 ) pik_elem_render(p, pObj);
6864       xRender = pObj->type->xRender;
6865       if( xRender ){
6866         xRender(p, pObj);
6867       }
6868       if( pObj->pSublist ){
6869         pik_elist_render(p, pObj->pSublist);
6870       }
6871     }
6872   }while( bMoreToDo );
6873 
6874   /* If the color_debug_label value is defined, then go through
6875   ** and paint a dot at every label location */
6876   colorLabel = pik_value(p, "debug_label_color", 17, &miss);
6877   if( miss==0 && colorLabel>=0.0 ){
6878     PObj dot;
6879     memset(&dot, 0, sizeof(dot));
6880     dot.type = &noopClass;
6881     dot.rad = 0.015;
6882     dot.sw = 0.015;
6883     dot.fill = colorLabel;
6884     dot.color = colorLabel;
6885     dot.nTxt = 1;
6886     dot.aTxt[0].eCode = TP_ABOVE;
6887     for(i=0; i<pList->n; i++){
6888       PObj *pObj = pList->a[i];
6889       if( pObj->zName==0 ) continue;
6890       dot.ptAt = pObj->ptAt;
6891       dot.aTxt[0].z = pObj->zName;
6892       dot.aTxt[0].n = (int)strlen(pObj->zName);
6893       dotRender(p, &dot);
6894     }
6895   }
6896 }
6897 
6898 /* Add all objects of the list pList to the bounding box
6899 */
pik_bbox_add_elist(Pik * p,PList * pList,PNum wArrow)6900 static void pik_bbox_add_elist(Pik *p, PList *pList, PNum wArrow){
6901   int i;
6902   for(i=0; i<pList->n; i++){
6903     PObj *pObj = pList->a[i];
6904     if( pObj->sw>0.0 ) pik_bbox_addbox(&p->bbox, &pObj->bbox);
6905     pik_append_txt(p, pObj, &p->bbox);
6906     if( pObj->pSublist ) pik_bbox_add_elist(p, pObj->pSublist, wArrow);
6907 
6908 
6909     /* Expand the bounding box to account for arrowheads on lines */
6910     if( pObj->type->isLine && pObj->nPath>0 ){
6911       if( pObj->larrow ){
6912         pik_bbox_addellipse(&p->bbox, pObj->aPath[0].x, pObj->aPath[0].y,
6913                             wArrow, wArrow);
6914       }
6915       if( pObj->rarrow ){
6916         int j = pObj->nPath-1;
6917         pik_bbox_addellipse(&p->bbox, pObj->aPath[j].x, pObj->aPath[j].y,
6918                             wArrow, wArrow);
6919       }
6920     }
6921   }
6922 }
6923 
6924 /* Recompute key layout parameters from variables. */
pik_compute_layout_settings(Pik * p)6925 static void pik_compute_layout_settings(Pik *p){
6926   PNum thickness;  /* Line thickness */
6927   PNum wArrow;     /* Width of arrowheads */
6928 
6929   /* Set up rendering parameters */
6930   if( p->bLayoutVars ) return;
6931   thickness = pik_value(p,"thickness",9,0);
6932   if( thickness<=0.01 ) thickness = 0.01;
6933   wArrow = 0.5*pik_value(p,"arrowwid",8,0);
6934   p->wArrow = wArrow/thickness;
6935   p->hArrow = pik_value(p,"arrowht",7,0)/thickness;
6936   p->fontScale = pik_value(p,"fontscale",9,0);
6937   if( p->fontScale<=0.0 ) p->fontScale = 1.0;
6938   p->rScale = 144.0;
6939   p->charWidth = pik_value(p,"charwid",7,0)*p->fontScale;
6940   p->charHeight = pik_value(p,"charht",6,0)*p->fontScale;
6941   p->bLayoutVars = 1;
6942 }
6943 
6944 /* Render a list of objects.  Write the SVG into p->zOut.
6945 ** Delete the input object_list before returnning.
6946 */
pik_render(Pik * p,PList * pList)6947 static void pik_render(Pik *p, PList *pList){
6948   if( pList==0 ) return;
6949   if( p->nErr==0 ){
6950     PNum thickness;  /* Stroke width */
6951     PNum margin;     /* Extra bounding box margin */
6952     PNum w, h;       /* Drawing width and height */
6953     PNum wArrow;
6954     PNum pikScale;   /* Value of the "scale" variable */
6955     int miss = 0;
6956 
6957     /* Set up rendering parameters */
6958     pik_compute_layout_settings(p);
6959     thickness = pik_value(p,"thickness",9,0);
6960     if( thickness<=0.01 ) thickness = 0.01;
6961     margin = pik_value(p,"margin",6,0);
6962     margin += thickness;
6963     wArrow = p->wArrow*thickness;
6964     miss = 0;
6965     p->fgcolor = (int)pik_value(p,"fgcolor",7,&miss);
6966     if( miss ){
6967       PToken t;
6968       t.z = "fgcolor";
6969       t.n = 7;
6970       p->fgcolor = (int)pik_lookup_color(0, &t);
6971     }
6972     miss = 0;
6973     p->bgcolor = (int)pik_value(p,"bgcolor",7,&miss);
6974     if( miss ){
6975       PToken t;
6976       t.z = "bgcolor";
6977       t.n = 7;
6978       p->bgcolor = (int)pik_lookup_color(0, &t);
6979     }
6980 
6981     /* Compute a bounding box over all objects so that we can know
6982     ** how big to declare the SVG canvas */
6983     pik_bbox_init(&p->bbox);
6984     pik_bbox_add_elist(p, pList, wArrow);
6985 
6986     /* Expand the bounding box slightly to account for line thickness
6987     ** and the optional "margin = EXPR" setting. */
6988     p->bbox.ne.x += margin + pik_value(p,"rightmargin",11,0);
6989     p->bbox.ne.y += margin + pik_value(p,"topmargin",9,0);
6990     p->bbox.sw.x -= margin + pik_value(p,"leftmargin",10,0);
6991     p->bbox.sw.y -= margin + pik_value(p,"bottommargin",12,0);
6992 
6993     /* Output the SVG */
6994     pik_append(p, "<svg xmlns='http://www.w3.org/2000/svg'",-1);
6995     if( p->zClass ){
6996       pik_append(p, " class=\"", -1);
6997       pik_append(p, p->zClass, -1);
6998       pik_append(p, "\"", 1);
6999     }
7000     w = p->bbox.ne.x - p->bbox.sw.x;
7001     h = p->bbox.ne.y - p->bbox.sw.y;
7002     p->wSVG = (int)(p->rScale*w);
7003     p->hSVG = (int)(p->rScale*h);
7004     pikScale = pik_value(p,"scale",5,0);
7005     if( pikScale>=0.001 && pikScale<=1000.0
7006      && (pikScale<0.99 || pikScale>1.01)
7007     ){
7008       p->wSVG = (int)(p->wSVG*pikScale);
7009       p->hSVG = (int)(p->hSVG*pikScale);
7010       pik_append_num(p, " width=\"", p->wSVG);
7011       pik_append_num(p, "\" height=\"", p->hSVG);
7012       pik_append(p, "\"", 1);
7013     }
7014     pik_append_dis(p, " viewBox=\"0 0 ",w,"");
7015     pik_append_dis(p, " ",h,"\">\n");
7016     pik_elist_render(p, pList);
7017     pik_append(p,"</svg>\n", -1);
7018   }else{
7019     p->wSVG = -1;
7020     p->hSVG = -1;
7021   }
7022   pik_elist_free(p, pList);
7023 }
7024 
7025 
7026 
7027 /*
7028 ** An array of this structure defines a list of keywords.
7029 */
7030 typedef struct PikWord {
7031   char *zWord;             /* Text of the keyword */
7032   unsigned char nChar;     /* Length of keyword text in bytes */
7033   unsigned char eType;     /* Token code */
7034   unsigned char eCode;     /* Extra code for the token */
7035   unsigned char eEdge;     /* CP_* code for corner/edge keywords */
7036 } PikWord;
7037 
7038 /*
7039 ** Keywords
7040 */
7041 static const PikWord pik_keywords[] = {
7042   { "above",      5,   T_ABOVE,     0,         0        },
7043   { "abs",        3,   T_FUNC1,     FN_ABS,    0        },
7044   { "aligned",    7,   T_ALIGNED,   0,         0        },
7045   { "and",        3,   T_AND,       0,         0        },
7046   { "as",         2,   T_AS,        0,         0        },
7047   { "assert",     6,   T_ASSERT,    0,         0        },
7048   { "at",         2,   T_AT,        0,         0        },
7049   { "behind",     6,   T_BEHIND,    0,         0        },
7050   { "below",      5,   T_BELOW,     0,         0        },
7051   { "between",    7,   T_BETWEEN,   0,         0        },
7052   { "big",        3,   T_BIG,       0,         0        },
7053   { "bold",       4,   T_BOLD,      0,         0        },
7054   { "bot",        3,   T_EDGEPT,    0,         CP_S     },
7055   { "bottom",     6,   T_BOTTOM,    0,         CP_S     },
7056   { "c",          1,   T_EDGEPT,    0,         CP_C     },
7057   { "ccw",        3,   T_CCW,       0,         0        },
7058   { "center",     6,   T_CENTER,    0,         CP_C     },
7059   { "chop",       4,   T_CHOP,      0,         0        },
7060   { "close",      5,   T_CLOSE,     0,         0        },
7061   { "color",      5,   T_COLOR,     0,         0        },
7062   { "cos",        3,   T_FUNC1,     FN_COS,    0        },
7063   { "cw",         2,   T_CW,        0,         0        },
7064   { "dashed",     6,   T_DASHED,    0,         0        },
7065   { "define",     6,   T_DEFINE,    0,         0        },
7066   { "diameter",   8,   T_DIAMETER,  0,         0        },
7067   { "dist",       4,   T_DIST,      0,         0        },
7068   { "dotted",     6,   T_DOTTED,    0,         0        },
7069   { "down",       4,   T_DOWN,      DIR_DOWN,  0        },
7070   { "e",          1,   T_EDGEPT,    0,         CP_E     },
7071   { "east",       4,   T_EDGEPT,    0,         CP_E     },
7072   { "end",        3,   T_END,       0,         CP_END   },
7073   { "even",       4,   T_EVEN,      0,         0        },
7074   { "fill",       4,   T_FILL,      0,         0        },
7075   { "first",      5,   T_NTH,       0,         0        },
7076   { "fit",        3,   T_FIT,       0,         0        },
7077   { "from",       4,   T_FROM,      0,         0        },
7078   { "go",         2,   T_GO,        0,         0        },
7079   { "heading",    7,   T_HEADING,   0,         0        },
7080   { "height",     6,   T_HEIGHT,    0,         0        },
7081   { "ht",         2,   T_HEIGHT,    0,         0        },
7082   { "in",         2,   T_IN,        0,         0        },
7083   { "int",        3,   T_FUNC1,     FN_INT,    0        },
7084   { "invis",      5,   T_INVIS,     0,         0        },
7085   { "invisible",  9,   T_INVIS,     0,         0        },
7086   { "italic",     6,   T_ITALIC,    0,         0        },
7087   { "last",       4,   T_LAST,      0,         0        },
7088   { "left",       4,   T_LEFT,      DIR_LEFT,  CP_W     },
7089   { "ljust",      5,   T_LJUST,     0,         0        },
7090   { "max",        3,   T_FUNC2,     FN_MAX,    0        },
7091   { "min",        3,   T_FUNC2,     FN_MIN,    0        },
7092   { "n",          1,   T_EDGEPT,    0,         CP_N     },
7093   { "ne",         2,   T_EDGEPT,    0,         CP_NE    },
7094   { "north",      5,   T_EDGEPT,    0,         CP_N     },
7095   { "nw",         2,   T_EDGEPT,    0,         CP_NW    },
7096   { "of",         2,   T_OF,        0,         0        },
7097   { "previous",   8,   T_LAST,      0,         0,       },
7098   { "print",      5,   T_PRINT,     0,         0        },
7099   { "rad",        3,   T_RADIUS,    0,         0        },
7100   { "radius",     6,   T_RADIUS,    0,         0        },
7101   { "right",      5,   T_RIGHT,     DIR_RIGHT, CP_E     },
7102   { "rjust",      5,   T_RJUST,     0,         0        },
7103   { "s",          1,   T_EDGEPT,    0,         CP_S     },
7104   { "same",       4,   T_SAME,      0,         0        },
7105   { "se",         2,   T_EDGEPT,    0,         CP_SE    },
7106   { "sin",        3,   T_FUNC1,     FN_SIN,    0        },
7107   { "small",      5,   T_SMALL,     0,         0        },
7108   { "solid",      5,   T_SOLID,     0,         0        },
7109   { "south",      5,   T_EDGEPT,    0,         CP_S     },
7110   { "sqrt",       4,   T_FUNC1,     FN_SQRT,   0        },
7111   { "start",      5,   T_START,     0,         CP_START },
7112   { "sw",         2,   T_EDGEPT,    0,         CP_SW    },
7113   { "t",          1,   T_TOP,       0,         CP_N     },
7114   { "the",        3,   T_THE,       0,         0        },
7115   { "then",       4,   T_THEN,      0,         0        },
7116   { "thick",      5,   T_THICK,     0,         0        },
7117   { "thickness",  9,   T_THICKNESS, 0,         0        },
7118   { "thin",       4,   T_THIN,      0,         0        },
7119   { "this",       4,   T_THIS,      0,         0        },
7120   { "to",         2,   T_TO,        0,         0        },
7121   { "top",        3,   T_TOP,       0,         CP_N     },
7122   { "until",      5,   T_UNTIL,     0,         0        },
7123   { "up",         2,   T_UP,        DIR_UP,    0        },
7124   { "vertex",     6,   T_VERTEX,    0,         0        },
7125   { "w",          1,   T_EDGEPT,    0,         CP_W     },
7126   { "way",        3,   T_WAY,       0,         0        },
7127   { "west",       4,   T_EDGEPT,    0,         CP_W     },
7128   { "wid",        3,   T_WIDTH,     0,         0        },
7129   { "width",      5,   T_WIDTH,     0,         0        },
7130   { "with",       4,   T_WITH,      0,         0        },
7131   { "x",          1,   T_X,         0,         0        },
7132   { "y",          1,   T_Y,         0,         0        },
7133 };
7134 
7135 /*
7136 ** Search a PikWordlist for the given keyword.  Return a pointer to the
7137 ** keyword entry found.  Or return 0 if not found.
7138 */
pik_find_word(const char * zIn,int n,const PikWord * aList,int nList)7139 static const PikWord *pik_find_word(
7140   const char *zIn,              /* Word to search for */
7141   int n,                        /* Length of zIn */
7142   const PikWord *aList,         /* List to search */
7143   int nList                     /* Number of entries in aList */
7144 ){
7145   int first = 0;
7146   int last = nList-1;
7147   while( first<=last ){
7148     int mid = (first + last)/2;
7149     int sz = aList[mid].nChar;
7150     int c = strncmp(zIn, aList[mid].zWord, sz<n ? sz : n);
7151     if( c==0 ){
7152       c = n - sz;
7153       if( c==0 ) return &aList[mid];
7154     }
7155     if( c<0 ){
7156       last = mid-1;
7157     }else{
7158       first = mid+1;
7159     }
7160   }
7161   return 0;
7162 }
7163 
7164 /*
7165 ** Set a symbolic debugger breakpoint on this routine to receive a
7166 ** breakpoint when the "#breakpoint" token is parsed.
7167 */
pik_breakpoint(const unsigned char * z)7168 static void pik_breakpoint(const unsigned char *z){
7169   /* Prevent C compilers from optimizing out this routine. */
7170   if( z[2]=='X' ) exit(1);
7171 }
7172 
7173 
7174 /*
7175 ** Return the length of next token.  The token starts on
7176 ** the pToken->z character.  Fill in other fields of the
7177 ** pToken object as appropriate.
7178 */
pik_token_length(PToken * pToken,int bAllowCodeBlock)7179 static int pik_token_length(PToken *pToken, int bAllowCodeBlock){
7180   const unsigned char *z = (const unsigned char*)pToken->z;
7181   int i;
7182   unsigned char c, c2;
7183   switch( z[0] ){
7184     case '\\': {
7185       pToken->eType = T_WHITESPACE;
7186       for(i=1; z[i]=='\r' || z[i]==' ' || z[i]=='\t'; i++){}
7187       if( z[i]=='\n'  ) return i+1;
7188       pToken->eType = T_ERROR;
7189       return 1;
7190     }
7191     case ';':
7192     case '\n': {
7193       pToken->eType = T_EOL;
7194       return 1;
7195     }
7196     case '"': {
7197       for(i=1; (c = z[i])!=0; i++){
7198         if( c=='\\' ){
7199           if( z[i+1]==0 ) break;
7200           i++;
7201           continue;
7202         }
7203         if( c=='"' ){
7204           pToken->eType = T_STRING;
7205           return i+1;
7206         }
7207       }
7208       pToken->eType = T_ERROR;
7209       return i;
7210     }
7211     case ' ':
7212     case '\t':
7213     case '\f':
7214     case '\r': {
7215       for(i=1; (c = z[i])==' ' || c=='\t' || c=='\r' || c=='\t'; i++){}
7216       pToken->eType = T_WHITESPACE;
7217       return i;
7218     }
7219     case '#': {
7220       for(i=1; (c = z[i])!=0 && c!='\n'; i++){}
7221       pToken->eType = T_WHITESPACE;
7222       /* If the comment is "#breakpoint" then invoke the pik_breakpoint()
7223       ** routine.  The pik_breakpoint() routie is a no-op that serves as
7224       ** a convenient place to set a gdb breakpoint when debugging. */
7225       if( strncmp((const char*)z,"#breakpoint",11)==0 ) pik_breakpoint(z);
7226       return i;
7227     }
7228     case '/': {
7229       if( z[1]=='*' ){
7230         for(i=2; z[i]!=0 && (z[i]!='*' || z[i+1]!='/'); i++){}
7231         if( z[i]=='*' ){
7232           pToken->eType = T_WHITESPACE;
7233           return i+2;
7234         }else{
7235           pToken->eType = T_ERROR;
7236           return i;
7237         }
7238       }else if( z[1]=='/' ){
7239         for(i=2; z[i]!=0 && z[i]!='\n'; i++){}
7240         pToken->eType = T_WHITESPACE;
7241         return i;
7242       }else if( z[1]=='=' ){
7243         pToken->eType = T_ASSIGN;
7244         pToken->eCode = T_SLASH;
7245         return 2;
7246       }else{
7247         pToken->eType = T_SLASH;
7248         return 1;
7249       }
7250     }
7251     case '+': {
7252       if( z[1]=='=' ){
7253         pToken->eType = T_ASSIGN;
7254         pToken->eCode = T_PLUS;
7255         return 2;
7256       }
7257       pToken->eType = T_PLUS;
7258       return 1;
7259     }
7260     case '*': {
7261       if( z[1]=='=' ){
7262         pToken->eType = T_ASSIGN;
7263         pToken->eCode = T_STAR;
7264         return 2;
7265       }
7266       pToken->eType = T_STAR;
7267       return 1;
7268     }
7269     case '%': {   pToken->eType = T_PERCENT; return 1; }
7270     case '(': {   pToken->eType = T_LP;      return 1; }
7271     case ')': {   pToken->eType = T_RP;      return 1; }
7272     case '[': {   pToken->eType = T_LB;      return 1; }
7273     case ']': {   pToken->eType = T_RB;      return 1; }
7274     case ',': {   pToken->eType = T_COMMA;   return 1; }
7275     case ':': {   pToken->eType = T_COLON;   return 1; }
7276     case '>': {   pToken->eType = T_GT;      return 1; }
7277     case '=': {
7278        if( z[1]=='=' ){
7279          pToken->eType = T_EQ;
7280          return 2;
7281        }
7282        pToken->eType = T_ASSIGN;
7283        pToken->eCode = T_ASSIGN;
7284        return 1;
7285     }
7286     case '-': {
7287       if( z[1]=='>' ){
7288         pToken->eType = T_RARROW;
7289         return 2;
7290       }else if( z[1]=='=' ){
7291         pToken->eType = T_ASSIGN;
7292         pToken->eCode = T_MINUS;
7293         return 2;
7294       }else{
7295         pToken->eType = T_MINUS;
7296         return 1;
7297       }
7298     }
7299     case '<': {
7300       if( z[1]=='-' ){
7301          if( z[2]=='>' ){
7302            pToken->eType = T_LRARROW;
7303            return 3;
7304          }else{
7305            pToken->eType = T_LARROW;
7306            return 2;
7307          }
7308       }else{
7309         pToken->eType = T_LT;
7310         return 1;
7311       }
7312     }
7313     case '{': {
7314       int len, depth;
7315       i = 1;
7316       if( bAllowCodeBlock ){
7317         depth = 1;
7318         while( z[i] && depth>0 ){
7319           PToken x;
7320           x.z = (char*)(z+i);
7321           len = pik_token_length(&x, 0);
7322           if( len==1 ){
7323             if( z[i]=='{' ) depth++;
7324             if( z[i]=='}' ) depth--;
7325           }
7326           i += len;
7327         }
7328       }else{
7329         depth = 0;
7330       }
7331       if( depth ){
7332         pToken->eType = T_ERROR;
7333         return 1;
7334       }
7335       pToken->eType = T_CODEBLOCK;
7336       return i;
7337     }
7338     default: {
7339       c = z[0];
7340       if( c=='.' ){
7341         unsigned char c1 = z[1];
7342         if( islower(c1) ){
7343           const PikWord *pFound;
7344           for(i=2; (c = z[i])>='a' && c<='z'; i++){}
7345           pFound = pik_find_word((const char*)z+1, i-1,
7346                                     pik_keywords, count(pik_keywords));
7347           if( pFound && (pFound->eEdge>0 ||
7348                          pFound->eType==T_EDGEPT ||
7349                          pFound->eType==T_START ||
7350                          pFound->eType==T_END )
7351           ){
7352             /* Dot followed by something that is a 2-D place value */
7353             pToken->eType = T_DOT_E;
7354           }else if( pFound && (pFound->eType==T_X || pFound->eType==T_Y) ){
7355             /* Dot followed by "x" or "y" */
7356             pToken->eType = T_DOT_XY;
7357           }else{
7358             /* Any other "dot" */
7359             pToken->eType = T_DOT_L;
7360           }
7361           return 1;
7362         }else if( isdigit(c1) ){
7363           i = 0;
7364           /* no-op.  Fall through to number handling */
7365         }else if( isupper(c1) ){
7366           for(i=2; (c = z[i])!=0 && (isalnum(c) || c=='_'); i++){}
7367           pToken->eType = T_DOT_U;
7368           return 1;
7369         }else{
7370           pToken->eType = T_ERROR;
7371           return 1;
7372         }
7373       }
7374       if( (c>='0' && c<='9') || c=='.' ){
7375         int nDigit;
7376         int isInt = 1;
7377         if( c!='.' ){
7378           nDigit = 1;
7379           for(i=1; (c = z[i])>='0' && c<='9'; i++){ nDigit++; }
7380           if( i==1 && (c=='x' || c=='X') ){
7381             for(i=2; (c = z[i])!=0 && isxdigit(c); i++){}
7382             pToken->eType = T_NUMBER;
7383             return i;
7384           }
7385         }else{
7386           isInt = 0;
7387           nDigit = 0;
7388           i = 0;
7389         }
7390         if( c=='.' ){
7391           isInt = 0;
7392           for(i++; (c = z[i])>='0' && c<='9'; i++){ nDigit++; }
7393         }
7394         if( nDigit==0 ){
7395           pToken->eType = T_ERROR;
7396           return i;
7397         }
7398         if( c=='e' || c=='E' ){
7399           int iBefore = i;
7400           i++;
7401           c2 = z[i];
7402           if( c2=='+' || c2=='-' ){
7403             i++;
7404             c2 = z[i];
7405           }
7406           if( c2<'0' || c>'9' ){
7407             /* This is not an exp */
7408             i = iBefore;
7409           }else{
7410             i++;
7411             isInt = 0;
7412             while( (c = z[i])>='0' && c<='9' ){ i++; }
7413           }
7414         }
7415         c2 = c ? z[i+1] : 0;
7416         if( isInt ){
7417           if( (c=='t' && c2=='h')
7418            || (c=='r' && c2=='d')
7419            || (c=='n' && c2=='d')
7420            || (c=='s' && c2=='t')
7421           ){
7422             pToken->eType = T_NTH;
7423             return i+2;
7424           }
7425         }
7426         if( (c=='i' && c2=='n')
7427          || (c=='c' && c2=='m')
7428          || (c=='m' && c2=='m')
7429          || (c=='p' && c2=='t')
7430          || (c=='p' && c2=='x')
7431          || (c=='p' && c2=='c')
7432         ){
7433           i += 2;
7434         }
7435         pToken->eType = T_NUMBER;
7436         return i;
7437       }else if( islower(c) ){
7438         const PikWord *pFound;
7439         for(i=1; (c =  z[i])!=0 && (isalnum(c) || c=='_'); i++){}
7440         pFound = pik_find_word((const char*)z, i,
7441                                pik_keywords, count(pik_keywords));
7442         if( pFound ){
7443           pToken->eType = pFound->eType;
7444           pToken->eCode = pFound->eCode;
7445           pToken->eEdge = pFound->eEdge;
7446           return i;
7447         }
7448         pToken->n = i;
7449         if( pik_find_class(pToken)!=0 ){
7450           pToken->eType = T_CLASSNAME;
7451         }else{
7452           pToken->eType = T_ID;
7453         }
7454         return i;
7455       }else if( c>='A' && c<='Z' ){
7456         for(i=1; (c =  z[i])!=0 && (isalnum(c) || c=='_'); i++){}
7457         pToken->eType = T_PLACENAME;
7458         return i;
7459       }else if( c=='$' && z[1]>='1' && z[1]<='9' && !isdigit(z[2]) ){
7460         pToken->eType = T_PARAMETER;
7461         pToken->eCode = z[1] - '1';
7462         return 2;
7463       }else if( c=='_' || c=='$' || c=='@' ){
7464         for(i=1; (c =  z[i])!=0 && (isalnum(c) || c=='_'); i++){}
7465         pToken->eType = T_ID;
7466         return i;
7467       }else{
7468         pToken->eType = T_ERROR;
7469         return 1;
7470       }
7471     }
7472   }
7473 }
7474 
7475 /*
7476 ** Return a pointer to the next non-whitespace token after pThis.
7477 ** This is used to help form error messages.
7478 */
pik_next_semantic_token(PToken * pThis)7479 static PToken pik_next_semantic_token(PToken *pThis){
7480   PToken x;
7481   int sz;
7482   int i = pThis->n;
7483   memset(&x, 0, sizeof(x));
7484   x.z = pThis->z;
7485   while(1){
7486     x.z = pThis->z + i;
7487     sz = pik_token_length(&x, 1);
7488     if( x.eType!=T_WHITESPACE ){
7489       x.n = sz;
7490       return x;
7491     }
7492     i += sz;
7493   }
7494 }
7495 
7496 /* Parser arguments to a macro invocation
7497 **
7498 **     (arg1, arg2, ...)
7499 **
7500 ** Arguments are comma-separated, except that commas within string
7501 ** literals or with (...), {...}, or [...] do not count.  The argument
7502 ** list begins and ends with parentheses.  There can be at most 9
7503 ** arguments.
7504 **
7505 ** Return the number of bytes in the argument list.
7506 */
pik_parse_macro_args(Pik * p,const char * z,int n,PToken * args,PToken * pOuter)7507 static unsigned int pik_parse_macro_args(
7508   Pik *p,
7509   const char *z,     /* Start of the argument list */
7510   int n,             /* Available bytes */
7511   PToken *args,      /* Fill in with the arguments */
7512   PToken *pOuter     /* Arguments of the next outer context, or NULL */
7513 ){
7514   int nArg = 0;
7515   int i, j, sz;
7516   int iStart;
7517   int depth = 0;
7518   PToken x;
7519   if( z[0]!='(' ) return 0;
7520   args[0].z = z+1;
7521   iStart = 1;
7522   for(i=1; i<n && z[i]!=')'; i+=sz){
7523     x.z = z+i;
7524     sz = pik_token_length(&x, 0);
7525     if( sz!=1 ) continue;
7526     if( z[i]==',' && depth<=0 ){
7527       args[nArg].n = i - iStart;
7528       if( nArg==8 ){
7529         x.z = z;
7530         x.n = 1;
7531         pik_error(p, &x, "too many macro arguments - max 9");
7532         return 0;
7533       }
7534       nArg++;
7535       args[nArg].z = z+i+1;
7536       iStart = i+1;
7537       depth = 0;
7538     }else if( z[i]=='(' || z[i]=='{' || z[i]=='[' ){
7539       depth++;
7540     }else if( z[i]==')' || z[i]=='}' || z[i]==']' ){
7541       depth--;
7542     }
7543   }
7544   if( z[i]==')' ){
7545     args[nArg].n = i - iStart;
7546     /* Remove leading and trailing whitespace from each argument.
7547     ** If what remains is one of $1, $2, ... $9 then transfer the
7548     ** corresponding argument from the outer context */
7549     for(j=0; j<=nArg; j++){
7550       PToken *t = &args[j];
7551       while( t->n>0 && isspace(t->z[0]) ){ t->n--; t->z++; }
7552       while( t->n>0 && isspace(t->z[t->n-1]) ){ t->n--; }
7553       if( t->n==2 && t->z[0]=='$' && t->z[1]>='1' && t->z[1]<='9' ){
7554         if( pOuter ) *t = pOuter[t->z[1]-'1'];
7555         else t->n = 0;
7556       }
7557     }
7558     return i+1;
7559   }
7560   x.z = z;
7561   x.n = 1;
7562   pik_error(p, &x, "unterminated macro argument list");
7563   return 0;
7564 }
7565 
7566 /*
7567 ** Split up the content of a PToken into multiple tokens and
7568 ** send each to the parser.
7569 */
pik_tokenize(Pik * p,PToken * pIn,yyParser * pParser,PToken * aParam)7570 void pik_tokenize(Pik *p, PToken *pIn, yyParser *pParser, PToken *aParam){
7571   unsigned int i;
7572   int sz = 0;
7573   PToken token;
7574   PMacro *pMac;
7575   for(i=0; i<pIn->n && pIn->z[i] && p->nErr==0; i+=sz){
7576     token.eCode = 0;
7577     token.eEdge = 0;
7578     token.z = pIn->z + i;
7579     sz = pik_token_length(&token, 1);
7580     if( token.eType==T_WHITESPACE ){
7581       /* no-op */
7582     }else if( sz>50000 ){
7583       token.n = 1;
7584       pik_error(p, &token, "token is too long - max length 50000 bytes");
7585       break;
7586     }else if( token.eType==T_ERROR ){
7587       token.n = (unsigned short)(sz & 0xffff);
7588       pik_error(p, &token, "unrecognized token");
7589       break;
7590     }else if( sz+i>pIn->n ){
7591       token.n = pIn->n - i;
7592       pik_error(p, &token, "syntax error");
7593       break;
7594     }else if( token.eType==T_PARAMETER ){
7595       /* Substitute a parameter into the input stream */
7596       if( aParam==0 || aParam[token.eCode].n==0 ){
7597         continue;
7598       }
7599       token.n = (unsigned short)(sz & 0xffff);
7600       if( p->nCtx>=count(p->aCtx) ){
7601         pik_error(p, &token, "macros nested too deep");
7602       }else{
7603         p->aCtx[p->nCtx++] = token;
7604         pik_tokenize(p, &aParam[token.eCode], pParser, 0);
7605         p->nCtx--;
7606       }
7607     }else if( token.eType==T_ID
7608                && (token.n = (unsigned short)(sz & 0xffff),
7609                    (pMac = pik_find_macro(p,&token))!=0)
7610     ){
7611       PToken args[9];
7612       unsigned int j = i+sz;
7613       if( pMac->inUse ){
7614         pik_error(p, &pMac->macroName, "recursive macro definition");
7615         break;
7616       }
7617       token.n = (short int)(sz & 0xffff);
7618       if( p->nCtx>=count(p->aCtx) ){
7619         pik_error(p, &token, "macros nested too deep");
7620         break;
7621       }
7622       pMac->inUse = 1;
7623       memset(args, 0, sizeof(args));
7624       p->aCtx[p->nCtx++] = token;
7625       sz += pik_parse_macro_args(p, pIn->z+j, pIn->n-j, args, aParam);
7626       pik_tokenize(p, &pMac->macroBody, pParser, args);
7627       p->nCtx--;
7628       pMac->inUse = 0;
7629     }else{
7630 #if 0
7631       printf("******** Token %s (%d): \"%.*s\" **************\n",
7632              yyTokenName[token.eType], token.eType,
7633              (int)(isspace(token.z[0]) ? 0 : sz), token.z);
7634 #endif
7635       token.n = (unsigned short)(sz & 0xffff);
7636       pik_parser(pParser, token.eType, token);
7637     }
7638   }
7639 }
7640 
7641 /*
7642 ** Parse the PIKCHR script contained in zText[].  Return a rendering.  Or
7643 ** if an error is encountered, return the error text.  The error message
7644 ** is HTML formatted.  So regardless of what happens, the return text
7645 ** is safe to be insertd into an HTML output stream.
7646 **
7647 ** If pnWidth and pnHeight are not NULL, then this routine writes the
7648 ** width and height of the <SVG> object into the integers that they
7649 ** point to.  A value of -1 is written if an error is seen.
7650 **
7651 ** If zClass is not NULL, then it is a class name to be included in
7652 ** the <SVG> markup.
7653 **
7654 ** The returned string is contained in memory obtained from malloc()
7655 ** and should be released by the caller.
7656 */
pikchr(const char * zText,const char * zClass,unsigned int mFlags,int * pnWidth,int * pnHeight)7657 char *pikchr(
7658   const char *zText,     /* Input PIKCHR source text.  zero-terminated */
7659   const char *zClass,    /* Add class="%s" to <svg> markup */
7660   unsigned int mFlags,   /* Flags used to influence rendering behavior */
7661   int *pnWidth,          /* Write width of <svg> here, if not NULL */
7662   int *pnHeight          /* Write height here, if not NULL */
7663 ){
7664   Pik s;
7665   yyParser sParse;
7666 
7667   memset(&s, 0, sizeof(s));
7668   s.sIn.z = zText;
7669   s.sIn.n = (unsigned int)strlen(zText);
7670   s.eDir = DIR_RIGHT;
7671   s.zClass = zClass;
7672   s.mFlags = mFlags;
7673   pik_parserInit(&sParse, &s);
7674 #if 0
7675   pik_parserTrace(stdout, "parser: ");
7676 #endif
7677   pik_tokenize(&s, &s.sIn, &sParse, 0);
7678   if( s.nErr==0 ){
7679     PToken token;
7680     memset(&token,0,sizeof(token));
7681     token.z = zText + (s.sIn.n>0 ? s.sIn.n-1 : 0);
7682     token.n = 1;
7683     pik_parser(&sParse, 0, token);
7684   }
7685   pik_parserFinalize(&sParse);
7686   if( s.zOut==0 && s.nErr==0 ){
7687     pik_append(&s, "<!-- empty pikchr diagram -->\n", -1);
7688   }
7689   while( s.pVar ){
7690     PVar *pNext = s.pVar->pNext;
7691     free(s.pVar);
7692     s.pVar = pNext;
7693   }
7694   while( s.pMacros ){
7695     PMacro *pNext = s.pMacros->pNext;
7696     free(s.pMacros);
7697     s.pMacros = pNext;
7698   }
7699   if( pnWidth ) *pnWidth = s.nErr ? -1 : s.wSVG;
7700   if( pnHeight ) *pnHeight = s.nErr ? -1 : s.hSVG;
7701   if( s.zOut ){
7702     s.zOut[s.nOut] = 0;
7703     s.zOut = realloc(s.zOut, s.nOut+1);
7704   }
7705   return s.zOut;
7706 }
7707 
7708 #if defined(PIKCHR_FUZZ)
7709 #include <stdint.h>
LLVMFuzzerTestOneInput(const uint8_t * aData,size_t nByte)7710 int LLVMFuzzerTestOneInput(const uint8_t *aData, size_t nByte){
7711   int w,h;
7712   char *zIn, *zOut;
7713   zIn = malloc( nByte + 1 );
7714   if( zIn==0 ) return 0;
7715   memcpy(zIn, aData, nByte);
7716   zIn[nByte] = 0;
7717   zOut = pikchr(zIn, "pikchr", 0, &w, &h);
7718   free(zIn);
7719   free(zOut);
7720   return 0;
7721 }
7722 #endif /* PIKCHR_FUZZ */
7723 
7724 #if defined(PIKCHR_SHELL)
7725 /* Print a usage comment for the shell and exit. */
usage(const char * argv0)7726 static void usage(const char *argv0){
7727   fprintf(stderr, "usage: %s [OPTIONS] FILE ...\n", argv0);
7728   fprintf(stderr,
7729     "Convert Pikchr input files into SVG.  Filename \"-\" means stdin.\n"
7730     "Options:\n"
7731     "   --dont-stop      Process all files even if earlier files have errors\n"
7732     "   --svg-only       Omit raw SVG without the HTML wrapper\n"
7733   );
7734   exit(1);
7735 }
7736 
7737 /* Send text to standard output, but escape HTML markup */
print_escape_html(const char * z)7738 static void print_escape_html(const char *z){
7739   int j;
7740   char c;
7741   while( z[0]!=0 ){
7742     for(j=0; (c = z[j])!=0 && c!='<' && c!='>' && c!='&'; j++){}
7743     if( j ) printf("%.*s", j, z);
7744     z += j+1;
7745     j = -1;
7746     if( c=='<' ){
7747       printf("&lt;");
7748     }else if( c=='>' ){
7749       printf("&gt;");
7750     }else if( c=='&' ){
7751       printf("&amp;");
7752     }else if( c==0 ){
7753       break;
7754     }
7755   }
7756 }
7757 
7758 /* Read the content of file zFilename into memory obtained from malloc()
7759 ** Return the memory.  If something goes wrong (ex: the file does not exist
7760 ** or cannot be opened) put an error message on stderr and return NULL.
7761 **
7762 ** If the filename is "-" read stdin.
7763 */
readFile(const char * zFilename)7764 static char *readFile(const char *zFilename){
7765   FILE *in;
7766   size_t n;
7767   size_t nUsed = 0;
7768   size_t nAlloc = 0;
7769   char *z = 0, *zNew = 0;
7770   in = strcmp(zFilename,"-")==0 ? stdin : fopen(zFilename, "rb");
7771   if( in==0 ){
7772     fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename);
7773     return 0;
7774   }
7775   while(1){
7776     if( nUsed+2>=nAlloc ){
7777       nAlloc = nAlloc*2 + 4000;
7778       zNew = realloc(z, nAlloc);
7779     }
7780     if( zNew==0 ){
7781       free(z);
7782       fprintf(stderr, "out of memory trying to allocate %lld bytes\n",
7783               (long long int)nAlloc);
7784       exit(1);
7785     }
7786     z = zNew;
7787     n = fread(z+nUsed, 1, nAlloc-nUsed-1, in);
7788     if( n<=0 ){
7789       break;
7790     }
7791     nUsed += n;
7792   }
7793   if( in!=stdin ) fclose(in);
7794   z[nUsed] = 0;
7795   return z;
7796 }
7797 
7798 
7799 /* Testing interface
7800 **
7801 ** Generate HTML on standard output that displays both the original
7802 ** input text and the rendered SVG for all files named on the command
7803 ** line.
7804 */
main(int argc,char ** argv)7805 int main(int argc, char **argv){
7806   int i;
7807   int bSvgOnly = 0;            /* Output SVG only.  No HTML wrapper */
7808   int bDontStop = 0;           /* Continue in spite of errors */
7809   int exitCode = 0;            /* What to return */
7810   int mFlags = 0;              /* mFlags argument to pikchr() */
7811   const char *zStyle = "";     /* Extra styling */
7812   const char *zHtmlHdr =
7813     "<!DOCTYPE html>\n"
7814     "<html lang=\"en-US\">\n"
7815     "<head>\n<title>PIKCHR Test</title>\n"
7816     "<style>\n"
7817     "  .hidden {\n"
7818     "     position: absolute !important;\n"
7819     "     opacity: 0 !important;\n"
7820     "     pointer-events: none !important;\n"
7821     "     display: none !important;\n"
7822     "  }\n"
7823     "</style>\n"
7824     "<script>\n"
7825     "  function toggleHidden(id){\n"
7826     "    for(var c of document.getElementById(id).children){\n"
7827     "      c.classList.toggle('hidden');\n"
7828     "    }\n"
7829     "  }\n"
7830     "</script>\n"
7831     "<meta charset=\"utf-8\">\n"
7832     "</head>\n"
7833     "<body>\n"
7834   ;
7835   if( argc<2 ) usage(argv[0]);
7836   for(i=1; i<argc; i++){
7837     char *zIn;
7838     char *zOut;
7839     int w, h;
7840 
7841     if( argv[i][0]=='-' && argv[i][1]!=0 ){
7842       char *z = argv[i];
7843       z++;
7844       if( z[0]=='-' ) z++;
7845       if( strcmp(z,"dont-stop")==0 ){
7846         bDontStop = 1;
7847       }else
7848       if( strcmp(z,"dark-mode")==0 ){
7849         zStyle = "color:white;background-color:black;";
7850         mFlags |= PIKCHR_DARK_MODE;
7851       }else
7852       if( strcmp(z,"svg-only")==0 ){
7853         if( zHtmlHdr==0 ){
7854           fprintf(stderr, "the \"%s\" option must come first\n",argv[i]);
7855           exit(1);
7856         }
7857         bSvgOnly = 1;
7858         mFlags |= PIKCHR_PLAINTEXT_ERRORS;
7859       }else
7860       {
7861         fprintf(stderr,"unknown option: \"%s\"\n", argv[i]);
7862         usage(argv[0]);
7863       }
7864       continue;
7865     }
7866     zIn = readFile(argv[i]);
7867     if( zIn==0 ) continue;
7868     zOut = pikchr(zIn, "pikchr", mFlags, &w, &h);
7869     if( w<0 ) exitCode = 1;
7870     if( zOut==0 ){
7871       fprintf(stderr, "pikchr() returns NULL.  Out of memory?\n");
7872       if( !bDontStop ) exit(1);
7873     }else if( bSvgOnly ){
7874       printf("%s\n", zOut);
7875     }else{
7876       if( zHtmlHdr ){
7877         printf("%s", zHtmlHdr);
7878         zHtmlHdr = 0;
7879       }
7880       printf("<h1>File %s</h1>\n", argv[i]);
7881       if( w<0 ){
7882         printf("<p>ERROR</p>\n%s\n", zOut);
7883       }else{
7884         printf("<div id=\"svg-%d\" onclick=\"toggleHidden('svg-%d')\">\n",i,i);
7885         printf("<div style='border:3px solid lightgray;max-width:%dpx;%s'>\n",
7886                w,zStyle);
7887         printf("%s</div>\n", zOut);
7888         printf("<pre class='hidden'>");
7889         print_escape_html(zIn);
7890         printf("</pre>\n</div>\n");
7891       }
7892     }
7893     free(zOut);
7894     free(zIn);
7895   }
7896   if( !bSvgOnly ){
7897     printf("</body></html>\n");
7898   }
7899   return exitCode ? EXIT_FAILURE : EXIT_SUCCESS;
7900 }
7901 #endif /* PIKCHR_SHELL */
7902 
7903 #ifdef PIKCHR_TCL
7904 #include <tcl.h>
7905 /*
7906 ** An interface to TCL
7907 **
7908 ** TCL command:     pikchr $INPUTTEXT
7909 **
7910 ** Returns a list of 3 elements which are the output text, the width, and
7911 ** the height.
7912 **
7913 ** Register the "pikchr" command by invoking Pikchr_Init(Tcl_Interp*).  Or
7914 ** compile this source file as a shared library and load it using the
7915 ** "load" command of Tcl.
7916 **
7917 ** Compile this source-code file into a shared library using a command
7918 ** similar to this:
7919 **
7920 **    gcc -c pikchr.so -DPIKCHR_TCL -shared pikchr.c
7921 */
pik_tcl_command(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])7922 static int pik_tcl_command(
7923   ClientData clientData, /* Not Used */
7924   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
7925   int objc,              /* Number of arguments */
7926   Tcl_Obj *CONST objv[]  /* Command arguments */
7927 ){
7928   int w, h;              /* Width and height of the pikchr */
7929   const char *zIn;       /* Source text input */
7930   char *zOut;            /* SVG output text */
7931   Tcl_Obj *pRes;         /* The result TCL object */
7932 
7933   (void)clientData;
7934   if( objc!=2 ){
7935     Tcl_WrongNumArgs(interp, 1, objv, "PIKCHR_SOURCE_TEXT");
7936     return TCL_ERROR;
7937   }
7938   zIn = Tcl_GetString(objv[1]);
7939   w = h = 0;
7940   zOut = pikchr(zIn, "pikchr", 0, &w, &h);
7941   if( zOut==0 ){
7942     return TCL_ERROR;  /* Out of memory */
7943   }
7944   pRes = Tcl_NewObj();
7945   Tcl_ListObjAppendElement(0, pRes, Tcl_NewStringObj(zOut, -1));
7946   free(zOut);
7947   Tcl_ListObjAppendElement(0, pRes, Tcl_NewIntObj(w));
7948   Tcl_ListObjAppendElement(0, pRes, Tcl_NewIntObj(h));
7949   Tcl_SetObjResult(interp, pRes);
7950   return TCL_OK;
7951 }
7952 
7953 /* Invoke this routine to register the "pikchr" command with the interpreter
7954 ** given in the argument */
Pikchr_Init(Tcl_Interp * interp)7955 int Pikchr_Init(Tcl_Interp *interp){
7956   Tcl_CreateObjCommand(interp, "pikchr", pik_tcl_command, 0, 0);
7957   Tcl_PkgProvide (interp, PACKAGE_NAME, PACKAGE_VERSION);
7958   return TCL_OK;
7959 }
7960 
7961 
7962 #endif /* PIKCHR_TCL */
7963 
7964 
7965 #line 7990 "pikchr.c"
7966