Fossil SCM

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

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button