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 as that is an HTML-ism and is not valid in XML.
4529 **
4530 ** * The "&" character is changed into "&" 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, "<", 4); break; }
4552 case '>': { pik_append(p, ">", 4); break; }
4553 case '&': { pik_append(p, "&", 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, "\", -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 ** "<" 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("<");
7748 }else if( c=='>' ){
7749 printf(">");
7750 }else if( c=='&' ){
7751 printf("&");
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