/* This file is automatically generated by Lemon from input grammar ** source file "pikchr.y". */ /* ** Zero-Clause BSD license: ** ** Copyright (C) 2020-09-01 by D. Richard Hipp <drh@sqlite.org> ** ** Permission to use, copy, modify, and/or distribute this software for ** any purpose with or without fee is hereby granted. ** **************************************************************************** ** ** This software translates a PIC-inspired diagram language into SVG. ** ** PIKCHR (pronounced like "picture") is *mostly* backwards compatible ** with legacy PIC, though some features of legacy PIC are removed ** (for example, the "sh" command is removed for security) and ** many enhancements are added. ** ** PIKCHR is designed for use in an internet facing web environment. ** In particular, PIKCHR is designed to safely generate benign SVG from ** source text that provided by a hostile agent. ** ** This code was originally written by D. Richard Hipp using documentation ** from prior PIC implementations but without reference to prior code. ** All of the code in this project is original. ** ** This file implements a C-language subroutine that accepts a string ** of PIKCHR language text and generates a second string of SVG output that ** renders the drawing defined by the input. Space to hold the returned ** string is obtained from malloc() and should be freed by the caller. ** NULL might be returned if there is a memory allocation error. ** ** If there are errors in the PIKCHR input, the output will consist of an ** error message and the original PIKCHR input text (inside of <pre>...</pre>). ** ** The subroutine implemented by this file is intended to be stand-alone. ** It uses no external routines other than routines commonly found in ** the standard C library. ** **************************************************************************** ** COMPILING: ** ** The original source text is a mixture of C99 and "Lemon" ** (See https://sqlite.org/src/file/doc/lemon.html). Lemon is an LALR(1) ** parser generator program, similar to Yacc. The grammar of the ** input language is specified in Lemon. C-code is attached. Lemon ** runs to generate a single output file ("pikchr.c") which is then ** compiled to generate the Pikchr library. This header comment is ** preserved in the Lemon output, so you might be reading this in either ** the generated "pikchr.c" file that is output by Lemon, or in the ** "pikchr.y" source file that is input into Lemon. If you make changes, ** you should change the input source file "pikchr.y", not the ** Lemon-generated output file. ** ** Basic compilation steps: ** ** lemon pikchr.y ** cc pikchr.c -o pikchr.o ** ** Add -DPIKCHR_SHELL to add a main() routine that reads input files ** and sends them through Pikchr, for testing. Add -DPIKCHR_FUZZ for ** -fsanitizer=fuzzer testing. ** **************************************************************************** ** IMPLEMENTATION NOTES (for people who want to understand the internal ** operation of this software, perhaps to extend the code or to fix bugs): ** ** Each call to pikchr() uses a single instance of the Pik structure to ** track its internal state. The Pik structure lives for the duration ** of the pikchr() call. ** ** The input is a sequence of objects or "statements". Each statement is ** parsed into a PObj object. These are stored on an extensible array ** called PList. All parameters to each PObj are computed as the ** object is parsed. (Hence, the parameters to a PObj may only refer ** to prior statements.) Once the PObj is completely assembled, it is ** added to the end of a PList and never changes thereafter - except, ** PObj objects that are part of a "[...]" block might have their ** absolute position shifted when the outer [...] block is positioned. ** But apart from this repositioning, PObj objects are unchanged once ** they are added to the list. The order of statements on a PList does ** not change. ** ** After all input has been parsed, the top-level PList is walked to ** generate output. Sub-lists resulting from [...] blocks are scanned ** as they are encountered. All input must be collected and parsed ahead ** of output generation because the size and position of statements must be ** known in order to compute a bounding box on the output. ** ** Each PObj is on a "layer". (The common case is that all PObj's are ** on a single layer, but multiple layers are possible.) A separate pass ** is made through the list for each layer. ** ** After all output is generated, the Pik object and all the PList ** and PObj objects are deallocated and the generated output string is ** returned. Upon any error, the Pik.nErr flag is set, processing quickly ** stops, and the stack unwinds. No attempt is made to continue reading ** input after an error. ** ** Most statements begin with a class name like "box" or "arrow" or "move". ** There is a class named "text" which is used for statements that begin ** with a string literal. You can also specify the "text" class. ** A Sublist ("[...]") is a single object that contains a pointer to ** its substatements, all gathered onto a separate PList object. ** ** Variables go into PVar objects that form a linked list. ** ** Each PObj has zero or one names. Input constructs that attempt ** to assign a new name from an older name, for example: ** ** Abc: Abc + (0.5cm, 0) ** ** Statements like these generate a new "noop" object at the specified ** place and with the given name. As place-names are searched by scanning ** the list in reverse order, this has the effect of overriding the "Abc" ** name when referenced by subsequent objects. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <math.h> #include <assert.h> /* Begin inclusion of sha1 from SQLite ** ** This is included at the top for minimum disruption of program flow. ** ** It is included at all, rather than #included from a separate file, to ** keep pikchr a single-file program. The code is not interspersed with ** the rest of the program because it was sourced from elsewhere, and in ** the event of changes to the original, it's better to keep it in one place. */ /* ** 2017-01-27 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ****************************************************************************** */ /****************************************************************************** ** The Hash Engine */ /* Context for the SHA1 hash */ typedef struct SHA1Context SHA1Context; struct SHA1Context { unsigned int state[5]; unsigned int count[2]; unsigned char buffer[64]; }; #define SHA_ROT(x,l,r) ((x) << (l) | (x) >> (r)) #define rol(x,k) SHA_ROT(x,k,32-(k)) #define ror(x,k) SHA_ROT(x,32-(k),k) #define blk0le(i) (block[i] = (ror(block[i],8)&0xFF00FF00) \ |(rol(block[i],8)&0x00FF00FF)) #define blk0be(i) block[i] #define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \ ^block[(i+2)&15]^block[i&15],1)) /* * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 * * Rl0() for little-endian and Rb0() for big-endian. Endianness is * determined at run-time. */ #define Rl0(v,w,x,y,z,i) \ z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=ror(w,2); #define Rb0(v,w,x,y,z,i) \ z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v,5);w=ror(w,2); #define R1(v,w,x,y,z,i) \ z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=ror(w,2); #define R2(v,w,x,y,z,i) \ z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=ror(w,2); #define R3(v,w,x,y,z,i) \ z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=ror(w,2); #define R4(v,w,x,y,z,i) \ z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=ror(w,2); /* * Hash a single 512-bit block. This is the core of the algorithm. */ static void SHA1Transform(unsigned int state[5], const unsigned char buffer[64]){ unsigned int qq[5]; /* a, b, c, d, e; */ static int one = 1; unsigned int block[16]; memcpy(block, buffer, 64); memcpy(qq,state,5*sizeof(unsigned int)); #define a qq[0] #define b qq[1] #define c qq[2] #define d qq[3] #define e qq[4] /* Copy p->state[] to working vars */ /* a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; */ /* 4 rounds of 20 operations each. Loop unrolled. */ if( 1 == *(unsigned char*)&one ){ Rl0(a,b,c,d,e, 0); Rl0(e,a,b,c,d, 1); Rl0(d,e,a,b,c, 2); Rl0(c,d,e,a,b, 3); Rl0(b,c,d,e,a, 4); Rl0(a,b,c,d,e, 5); Rl0(e,a,b,c,d, 6); Rl0(d,e,a,b,c, 7); Rl0(c,d,e,a,b, 8); Rl0(b,c,d,e,a, 9); Rl0(a,b,c,d,e,10); Rl0(e,a,b,c,d,11); Rl0(d,e,a,b,c,12); Rl0(c,d,e,a,b,13); Rl0(b,c,d,e,a,14); Rl0(a,b,c,d,e,15); }else{ Rb0(a,b,c,d,e, 0); Rb0(e,a,b,c,d, 1); Rb0(d,e,a,b,c, 2); Rb0(c,d,e,a,b, 3); Rb0(b,c,d,e,a, 4); Rb0(a,b,c,d,e, 5); Rb0(e,a,b,c,d, 6); Rb0(d,e,a,b,c, 7); Rb0(c,d,e,a,b, 8); Rb0(b,c,d,e,a, 9); Rb0(a,b,c,d,e,10); Rb0(e,a,b,c,d,11); Rb0(d,e,a,b,c,12); Rb0(c,d,e,a,b,13); Rb0(b,c,d,e,a,14); Rb0(a,b,c,d,e,15); } R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; #undef a #undef b #undef c #undef d #undef e } /* Initialize a SHA1 context */ static void hash_init(SHA1Context *p){ /* SHA1 initialization constants */ p->state[0] = 0x67452301; p->state[1] = 0xEFCDAB89; p->state[2] = 0x98BADCFE; p->state[3] = 0x10325476; p->state[4] = 0xC3D2E1F0; p->count[0] = p->count[1] = 0; } /* Add new content to the SHA1 hash */ static void hash_step( SHA1Context *p, /* Add content to this context */ const unsigned char *data, /* Data to be added */ unsigned int len /* Number of bytes in data */ ){ unsigned int i, j; j = p->count[0]; if( (p->count[0] += len << 3) < j ){ p->count[1] += (len>>29)+1; } j = (j >> 3) & 63; if( (j + len) > 63 ){ (void)memcpy(&p->buffer[j], data, (i = 64-j)); SHA1Transform(p->state, p->buffer); for(; i + 63 < len; i += 64){ SHA1Transform(p->state, &data[i]); } j = 0; }else{ i = 0; } (void)memcpy(&p->buffer[j], &data[i], len - i); } /* Add padding and compute the message digest. Render the ** message digest as lower-case hexadecimal and put it into ** zOut[]. zOut[] must be at least 41 bytes long. */ static void hash_finish( SHA1Context *p, /* The SHA1 context to finish and render */ char *zOut /* Store hexadecimal hash here */ ){ unsigned int i; unsigned char finalcount[8]; unsigned char digest[20]; static const char zEncode[] = "0123456789abcdef"; for (i = 0; i < 8; i++){ finalcount[i] = (unsigned char)((p->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ } hash_step(p, (const unsigned char *)"\200", 1); while ((p->count[0] & 504) != 448){ hash_step(p, (const unsigned char *)"\0", 1); } hash_step(p, finalcount, 8); /* Should cause a SHA1Transform() */ for (i = 0; i < 20; i++){ digest[i] = (unsigned char)((p->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); } for(i=0; i<20; i++){ zOut[i*2] = zEncode[(digest[i]>>4)&0xf]; zOut[i*2+1] = zEncode[digest[i] & 0xf]; } zOut[i*2]= 0; } /* End of sha1 code */ #define count(X) (sizeof(X)/sizeof(X[0])) #ifndef M_PI # define M_PI 3.1415926535897932385 #endif /* XXX These macros will be removed */ #define PIKDEV_NO_ELEMENTS #define PIKDEV_MFLAG_ARGV /**/ /* Limit the number of tokens in a single script to avoid run-away ** macro expansion attacks. See forum post ** https://pikchr.org/home/forumpost/ef8684c6955a411a */ #ifndef PIKCHR_TOKEN_LIMIT # define PIKCHR_TOKEN_LIMIT 100000 #endif /* Tag intentionally unused parameters with this macro to prevent ** compiler warnings with -Wextra */ #define UNUSED_PARAMETER(X) (void)(X) typedef struct Pik Pik; /* Complete parsing context */ typedef struct PToken PToken; /* A single token */ typedef struct PObj PObj; /* A single diagram object */ typedef struct PList PList; /* A list of diagram objects */ typedef struct PClass PClass; /* Description of statements types */ typedef double PNum; /* Numeric value */ typedef struct PRel PRel; /* Absolute or percentage value */ typedef struct PPoint PPoint; /* A position in 2-D space */ typedef struct PXmlClass PXmlClass; /* An XML class */ typedef struct PVar PVar; /* script-defined variable */ typedef struct PBox PBox; /* A bounding box */ typedef struct PMacro PMacro; /* A "define" macro */ /* Compass points */ #define CP_N 1 #define CP_NE 2 #define CP_E 3 #define CP_SE 4 #define CP_S 5 #define CP_SW 6 #define CP_W 7 #define CP_NW 8 #define CP_C 9 /* .center or .c */ #define CP_END 10 /* .end */ #define CP_START 11 /* .start */ /* Heading angles corresponding to compass points */ static const PNum pik_hdg_angle[] = { /* none */ 0.0, /* N */ 0.0, /* NE */ 45.0, /* E */ 90.0, /* SE */ 135.0, /* S */ 180.0, /* SW */ 225.0, /* W */ 270.0, /* NW */ 315.0, /* C */ 0.0, }; /* Built-in functions */ #define FN_ABS 0 #define FN_COS 1 #define FN_INT 2 #define FN_MAX 3 #define FN_MIN 4 #define FN_SIN 5 #define FN_SQRT 6 /* Text position and style flags. Stored in PToken.eCode so limited ** to 15 bits. */ #define TP_LJUST 0x0001 /* left justify...... */ #define TP_RJUST 0x0002 /* ...Right justify */ #define TP_JMASK 0x0003 /* Mask for justification bits */ #define TP_ABOVE2 0x0004 /* Position text way above PObj.ptAt */ #define TP_ABOVE 0x0008 /* Position text above PObj.ptAt */ #define TP_CENTER 0x0010 /* On the line */ #define TP_BELOW 0x0020 /* Position text below PObj.ptAt */ #define TP_BELOW2 0x0040 /* Position text way below PObj.ptAt */ #define TP_VMASK 0x007c /* Mask for text positioning flags */ #define TP_BIG 0x0100 /* Larger font */ #define TP_SMALL 0x0200 /* Smaller font */ #define TP_XTRA 0x0400 /* Amplify TP_BIG or TP_SMALL */ #define TP_SZMASK 0x0700 /* Font size mask */ #define TP_ITALIC 0x1000 /* Italic font */ #define TP_BOLD 0x2000 /* Bold font */ #define TP_MONO 0x4000 /* Monospace font family */ #define TP_FMASK 0x7000 /* Mask for font style */ #define TP_ALIGN 0x8000 /* Rotate to align with the line */ /* Style attribute flags. These track when a presentation attribute ** such as dominant-baseline is needed. */ #define ATTR_BASELINE 0x01 /* Include dominant-baseline="central" */ #define ATTR_T_START 0x02 /* text-anchor="start" */ #define ATTR_T_MIDDLE 0x04 /* text-anchor="middle" */ #define ATTR_T_END 0x08 /* text-anchor="end" */ #define ATTR_F_ITALIC 0x10 /* font-style="italic" */ #define ATTR_F_BOLD 0x20 /* font-weight="bold" */ #define ATTR_F_MONO 0x40 /* font-family="monospace" */ #define ATTR_FILL_NO 0x80 /* fill="transparent" */ /* An object to hold a position in 2-D space */ struct PPoint { PNum x, y; /* X and Y coordinates */ }; static const PPoint cZeroPoint = {0.0,0.0}; /* A bounding box */ struct PBox { PPoint sw, ne; /* Lower-left and top-right corners */ }; /* An Absolute or a relative distance. The absolute distance ** is stored in rAbs and the relative distance is stored in rRel. ** Usually, one or the other will be 0.0. When using a PRel to ** update an existing value, the computation is usually something ** like this: ** ** value = PRel.rAbs + value*PRel.rRel ** */ struct PRel { PNum rAbs; /* Absolute value */ PNum rRel; /* Value relative to current value */ }; /* A node in a linked list of SVG classes. ** */ struct PXmlClass { const char *zClass; /* Class identifier */ PXmlClass *pNext; /* Next class in an element's list */ }; /* A variable created by the ID = EXPR construct of the PIKCHR script ** ** PIKCHR (and PIC) scripts do not use many variables, so it is reasonable ** to store them all on a linked list. */ struct PVar { const char *zName; /* Name of the variable */ PNum val; /* Value of the variable */ PVar *pNext; /* Next variable in a list of them all */ }; /* A single token in the parser input stream */ struct PToken { const char *z; /* Pointer to the token text */ unsigned int n; /* Length of the token in bytes */ short int eCode; /* Auxiliary code */ unsigned char eType; /* The numeric parser code */ unsigned char eEdge; /* Corner value for corner keywords */ }; /* Return negative, zero, or positive if pToken is less than, equal to ** or greater than the zero-terminated string z[] */ static int pik_token_eq(PToken *pToken, const char *z){ int c = strncmp(pToken->z,z,pToken->n); if( c==0 && z[pToken->n]!=0 ) c = -1; return c; } /* Extra token types not generated by LEMON but needed by the ** tokenizer */ #define T_PARAMETER 253 /* $1, $2, ..., $9 */ #define T_WHITESPACE 254 /* Whitespace or comments */ #define T_ERROR 255 /* Any text that is not a valid token */ /* Directions of movement */ #define DIR_RIGHT 0 #define DIR_DOWN 1 #define DIR_LEFT 2 #define DIR_UP 3 #define ValidDir(X) ((X)>=0 && (X)<=3) #define IsUpDown(X) (((X)&1)==1) #define IsLeftRight(X) (((X)&1)==0) /* Bitmask for the various attributes for PObj. These bits are ** collected in PObj.mProp and PObj.mCalc to check for constraint ** errors. */ #define A_WIDTH 0x0001 #define A_HEIGHT 0x0002 #define A_RADIUS 0x0004 #define A_THICKNESS 0x0008 #define A_DASHED 0x0010 /* Includes "dotted" */ #define A_FILL 0x0020 #define A_COLOR 0x0040 #define A_ARROW 0x0080 #define A_FROM 0x0100 #define A_CW 0x0200 #define A_AT 0x0400 #define A_TO 0x0800 /* one or more movement attributes */ #define A_FIT 0x1000 #define A_TEXTCOLOR 0x2000 #define A_CLASS 0x4000 /* A single graphics object */ struct PObj { const PClass *type; /* Object type or class */ PToken errTok; /* Reference token for error messages */ PPoint ptAt; /* Reference point for the object */ PPoint ptEnter, ptExit; /* Entry and exit points */ PList *pSublist; /* Substructure for [...] objects */ PXmlClass *pXmlClass; /* Optional list of assigned XML classes */ char *zName; /* Name assigned to this statement */ PNum w; /* "width" property */ PNum h; /* "height" property */ PNum rad; /* "radius" property */ PNum sw; /* "thickness" property. (Mnemonic: "stroke width")*/ PNum dotted; /* "dotted" property. <=0.0 for off */ PNum dashed; /* "dashed" property. <=0.0 for off */ PNum fill; /* "fill" property. Negative for off */ PNum color; /* "color" property */ PNum textcolor; /* "textcolor" property*/ PPoint with; /* Position constraint from WITH clause */ char eWith; /* Type of heading point on WITH clause */ char cw; /* True for clockwise arc */ char larrow; /* Arrow at beginning (<- or <->) */ char rarrow; /* Arrow at end (-> or <->) */ char bClose; /* True if "close" is seen */ char bChop; /* True if "chop" is seen */ char bAltAutoFit; /* Always send both h and w into xFit() */ unsigned char nTxt; /* Number of text values */ unsigned mProp; /* Masks of properties set so far */ unsigned mCalc; /* Values computed from other constraints */ PToken aTxt[5]; /* Text with .eCode holding TP flags */ int iLayer; /* Rendering order */ int inDir, outDir; /* Entry and exit directions */ int nPath; /* Number of path points */ PPoint *aPath; /* Array of path points */ PObj *pFrom, *pTo; /* End-point objects of a path */ PBox bbox; /* Bounding box */ }; /* A list of graphics objects */ struct PList { int n; /* Number of statements in the list */ int nAlloc; /* Allocated slots in a[] */ PObj **a; /* Pointers to individual objects */ }; /* A macro definition */ struct PMacro { PMacro *pNext; /* Next in the list */ PToken macroName; /* Name of the macro */ PToken macroBody; /* Body of the macro */ int inUse; /* Do not allow recursion */ }; /* Each call to the pikchr() subroutine uses an instance of the following ** object to pass around context to all of its subroutines. */ struct Pik { unsigned nErr; /* Number of errors seen */ unsigned nToken; /* Number of tokens parsed */ PToken sIn; /* Input Pikchr-language text */ char *zOut; /* Result accumulates here */ unsigned int nOut; /* Bytes written to zOut[] so far */ unsigned int nOutAlloc; /* Space allocated to zOut[] */ SHA1Context *shaDigest; /* The hashing context for tokens */ unsigned char eDir; /* Current direction */ unsigned int mFlags; /* Flags passed to pikchr() */ PObj *cur; /* Object under construction */ PObj *lastRef; /* Last object references by name */ PList *list; /* Object list under construction */ PMacro *pMacros; /* List of all defined macros */ PVar *pVar; /* Application-defined variables */ PBox bbox; /* Bounding box around all statements */ /* Cache of layout values. <=0.0 for unknown... */ PNum rScale; /* Multiply to convert inches to pixels */ PNum fontScale; /* Scale fonts by this percent */ PNum charWidth; /* Character width */ PNum charHeight; /* Character height */ PNum wArrow; /* Width of arrowhead at the fat end */ PNum hArrow; /* Ht of arrowhead - dist from tip to fat end */ char bLayoutVars; /* True if cache is valid */ char thenFlag; /* True if "then" seen */ char bLabel; /* True if zTitle should be rendered as a label */ char samePath; /* aTPath copied by "same" */ char bEOL; /* Flag for if we're in a run of T_EOL (hashing) */ unsigned char mAttr; /* Flags for when to include certain styles */ const char *zClass; /* Class name for the <svg> */ char *zID; /* Suffix for id fields */ char *zTitle; /* Text of title or label */ char *zDesc; /* Text of extended description */ int wSVG, hSVG; /* Width and height of the <svg> */ int fgcolor; /* foreground color value, or -1 for none */ int bgcolor; /* background color value, or -1 for none */ /* Paths for lines are constructed here first, then transferred into ** the PObj object at the end: */ int nTPath; /* Number of entries on aTPath[] */ int mTPath; /* For last entry, 1: x set, 2: y set */ PPoint aTPath[1000]; /* Path under construction */ /* Error contexts */ unsigned int nCtx; /* Number of error contexts */ PToken aCtx[10]; /* Nested error contexts */ }; /* Include PIKCHR_PLAINTEXT_ERRORS among the bits of mFlags on the 3rd ** argument to pikchr() in order to cause error message text to come out ** as text/plain instead of as text/html */ #define PIKCHR_PLAINTEXT_ERRORS 0x0001 /* Include PIKCHR_DARK_MODE among the mFlag bits to invert colors. */ #define PIKCHR_DARK_MODE 0x0002 /* Include PIKCHR_EXTRA_UNIQUE_ID among the mFlag bits to add a value ** to each hash of a call to `char *pikchr`. This value is taken from ** a counter, meaning the id is based both on the tokens of the Pikchr ** script, and the order in which the diagram is produced. This could ** be useful in settings like forums, where more than one copy of a ** diagram may appear. */ #define PIKCHR_EXTRA_UNIQUE_ID 0x0004 /* ** The behavior of an object class is defined by an instance of ** this structure. This is the "virtual method" table. */ struct PClass { const char *zName; /* Name of class */ char isLine; /* True if a line class */ char eJust; /* Use box-style text justification */ void (*xInit)(Pik*,PObj*); /* Initializer */ void (*xNumProp)(Pik*,PObj*,PToken*); /* Value change notification */ void (*xCheck)(Pik*,PObj*); /* Checks to do after parsing */ PPoint (*xChop)(Pik*,PObj*,PPoint*); /* Chopper */ PPoint (*xOffset)(Pik*,PObj*,int); /* Offset from .c to edge point */ void (*xFit)(Pik*,PObj*,PNum w,PNum h); /* Size to fit text */ void (*xRender)(Pik*,PObj*); /* Render */ }; /* Forward declarations */ static void pik_append(Pik*,const char*,int); static void pik_append_tag_open(Pik*,PObj*,const char*); static int pik_append_group(Pik*,PObj*); static void pik_append_str(Pik*,int,const char*,int); static void pik_append_text(Pik*,const char*,int,int); static void pik_append_num(Pik*,const char*,PNum); static void pik_append_point(Pik*,const char*,PPoint*); static void pik_append_x(Pik*,const char*,PNum,const char*); static void pik_append_y(Pik*,const char*,PNum,const char*); static void pik_append_xy(Pik*,const char*,PNum,PNum); static void pik_append_dis(Pik*,const char*,PNum,const char*); static void pik_append_arc(Pik*,PNum,PNum,PNum,PNum); static void pik_append_clr(Pik*,const char*,PNum,const char*,int); static void pik_append_style(Pik*,PObj*,int); static void pik_append_txt(Pik*,PObj*, PBox*); static void pik_draw_arrowhead(Pik*,PPoint*pFrom,PPoint*pTo,PObj*); static void pik_chop(PPoint*pFrom,PPoint*pTo,PNum); static void pik_error(Pik*,PToken*,const char*); static void pik_elist_free(Pik*,PList*); static void pik_elem_free(Pik*,PObj*); static void pik_render(Pik*,PList*); static PList *pik_elist_append(Pik*,PList*,PObj*); static PObj *pik_elem_new(Pik*,PToken*,PToken*,PList*); static void pik_set_title(Pik*,PToken*,int); static void pik_set_description(Pik*,PToken*); static void pik_set_direction(Pik*,int); static void pik_elem_setname(Pik*,PObj*,PToken*); static int pik_round(PNum); static void pik_set_var(Pik*,PToken*,PNum,PToken*); static PNum pik_value(Pik*,const char*,int,int*); static int pik_value_int(Pik*,const char*,int,int*); static PNum pik_lookup_color(Pik*,PToken*); static PNum pik_get_var(Pik*,PToken*); static PNum pik_atof(PToken*); static void pik_after_adding_attributes(Pik*,PObj*); static void pik_elem_move(PObj*,PNum dx, PNum dy); static void pik_elist_move(PList*,PNum dx, PNum dy); static void pik_set_numprop(Pik*,PToken*,PRel*); static void pik_set_clrprop(Pik*,PToken*,PNum); static void pik_set_dashed(Pik*,PToken*,PNum*); static void pik_then(Pik*,PToken*,PObj*); static void pik_add_direction(Pik*,PToken*,PRel*); static void pik_move_hdg(Pik*,PRel*,PToken*,PNum,PToken*,PToken*); static void pik_evenwith(Pik*,PToken*,PPoint*); static void pik_set_from(Pik*,PObj*,PToken*,PPoint*); static void pik_add_to(Pik*,PObj*,PToken*,PPoint*); static void pik_close_path(Pik*,PToken*); static void pik_set_at(Pik*,PToken*,PPoint*,PToken*); static short int pik_nth_value(Pik*,PToken*); static PObj *pik_find_nth(Pik*,PObj*,PToken*); static PObj *pik_find_byname(Pik*,PObj*,PToken*); static PPoint pik_place_of_elem(Pik*,PObj*,PToken*); static int pik_bbox_isempty(PBox*); static int pik_bbox_contains_point(PBox*,PPoint*); static void pik_bbox_init(PBox*); static void pik_bbox_addbox(PBox*,PBox*); static void pik_bbox_add_xy(PBox*,PNum,PNum); static void pik_bbox_addellipse(PBox*,PNum x,PNum y,PNum rx,PNum ry); static void pik_add_txt(Pik*,PToken*,int); static int pik_text_length(const PToken *pToken, const int isMonospace); static void pik_size_to_fit(Pik*,PToken*,int); static int pik_text_position(int,PToken*); static PNum pik_property_of(PObj*,PToken*); static PNum pik_func(Pik*,PToken*,PNum,PNum); static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2); static PPoint pik_position_at_angle(PNum dist, PNum r, PPoint pt); static PPoint pik_position_at_hdg(PNum dist, PToken *pD, PPoint pt); static void pik_same(Pik *p, PObj*, PToken*); static PPoint pik_nth_vertex(Pik *p, PToken *pNth, PToken *pErr, PObj *pObj); static PToken pik_next_semantic_token(PToken *pThis); static void pik_compute_layout_settings(Pik*); static void pik_behind(Pik*,PObj*); static PObj *pik_assert(Pik*,PNum,PToken*,PNum); static PObj *pik_position_assert(Pik*,PPoint*,PToken*,PPoint*); static PNum pik_dist(PPoint*,PPoint*); static void pik_add_macro(Pik*,PToken *pId,PToken *pCode); static void pik_set_xml_class(Pik*,PToken *pXToken); static void pik_set_xml_classes(Pik*,PToken *pXToken); static void pik_xml_class_prepend(PObj*,PXmlClass *pXNext); #line 781 "pikchr.c" /**************** End of %include directives **********************************/ /* These constants specify the various numeric values for terminal symbols. ***************** Begin token definitions *************************************/ #ifndef T_ID #define T_ID 1 #define T_EDGEPT 2 #define T_OF 3 #define T_PLUS 4 #define T_MINUS 5 #define T_STAR 6 #define T_SLASH 7 #define T_PERCENT 8 #define T_UMINUS 9 #define T_EOL 10 #define T_ASSIGN 11 #define T_PLACENAME 12 #define T_COLON 13 #define T_TITLE 14 #define T_STRING 15 #define T_LABEL 16 #define T_DESCRIBE 17 #define T_ASSERT 18 #define T_LP 19 #define T_EQ 20 #define T_RP 21 #define T_DEFINE 22 #define T_CODEBLOCK 23 #define T_FILL 24 #define T_COLOR 25 #define T_TEXTCOLOR 26 #define T_THICKNESS 27 #define T_PRINT 28 #define T_COMMA 29 #define T_CLASSNAME 30 #define T_LB 31 #define T_RB 32 #define T_UP 33 #define T_DOWN 34 #define T_LEFT 35 #define T_RIGHT 36 #define T_CLASS 37 #define T_XML_CLASSES 38 #define T_CLOSE 39 #define T_CHOP 40 #define T_FROM 41 #define T_TO 42 #define T_THEN 43 #define T_HEADING 44 #define T_GO 45 #define T_AT 46 #define T_WITH 47 #define T_SAME 48 #define T_AS 49 #define T_FIT 50 #define T_BEHIND 51 #define T_UNTIL 52 #define T_EVEN 53 #define T_DOT_E 54 #define T_HEIGHT 55 #define T_WIDTH 56 #define T_RADIUS 57 #define T_DIAMETER 58 #define T_DOTTED 59 #define T_DASHED 60 #define T_CW 61 #define T_CCW 62 #define T_LARROW 63 #define T_RARROW 64 #define T_LRARROW 65 #define T_INVIS 66 #define T_THICK 67 #define T_THIN 68 #define T_SOLID 69 #define T_CENTER 70 #define T_LJUST 71 #define T_RJUST 72 #define T_ABOVE 73 #define T_BELOW 74 #define T_ITALIC 75 #define T_BOLD 76 #define T_MONO 77 #define T_ALIGNED 78 #define T_BIG 79 #define T_SMALL 80 #define T_AND 81 #define T_LT 82 #define T_GT 83 #define T_ON 84 #define T_WAY 85 #define T_BETWEEN 86 #define T_THE 87 #define T_NTH 88 #define T_VERTEX 89 #define T_TOP 90 #define T_BOTTOM 91 #define T_START 92 #define T_END 93 #define T_IN 94 #define T_THIS 95 #define T_DOT_U 96 #define T_LAST 97 #define T_NUMBER 98 #define T_FUNC1 99 #define T_FUNC2 100 #define T_DIST 101 #define T_DOT_XY 102 #define T_X 103 #define T_Y 104 #define T_DOT_L 105 #endif /**************** End token definitions ***************************************/ /* The next sections is a series of control #defines. ** various aspects of the generated parser. ** YYCODETYPE is the data type used to store the integer codes ** that represent terminal and non-terminal symbols. ** "unsigned char" is used if there are fewer than ** 256 symbols. Larger types otherwise. ** YYNOCODE is a number of type YYCODETYPE that is not used for ** any terminal or nonterminal symbol. ** YYFALLBACK If defined, this indicates that one or more tokens ** (also known as: "terminal symbols") have fall-back ** values which should be used if the original symbol ** would not parse. This permits keywords to sometimes ** be used as identifiers, for example. ** YYACTIONTYPE is the data type used for "action codes" - numbers ** that indicate what to do in response to the next ** token. ** pik_parserTOKENTYPE is the data type used for minor type for terminal ** symbols. Background: A "minor type" is a semantic ** value associated with a terminal or non-terminal ** symbols. For example, for an "ID" terminal symbol, ** the minor type might be the name of the identifier. ** Each non-terminal can have a different minor type. ** Terminal symbols all have the same minor type, though. ** This macros defines the minor type for terminal ** symbols. ** YYMINORTYPE is the data type used for all minor types. ** This is typically a union of many types, one of ** which is pik_parserTOKENTYPE. The entry in the union ** for terminal symbols is called "yy0". ** YYSTACKDEPTH is the maximum depth of the parser's stack. If ** zero the stack is dynamically sized using realloc() ** pik_parserARG_SDECL A static variable declaration for the %extra_argument ** pik_parserARG_PDECL A parameter declaration for the %extra_argument ** pik_parserARG_PARAM Code to pass %extra_argument as a subroutine parameter ** pik_parserARG_STORE Code to store %extra_argument into yypParser ** pik_parserARG_FETCH Code to extract %extra_argument from yypParser ** pik_parserCTX_* As pik_parserARG_ except for %extra_context ** YYREALLOC Name of the realloc() function to use ** YYFREE Name of the free() function to use ** YYDYNSTACK True if stack space should be extended on heap ** YYERRORSYMBOL is the code number of the error symbol. If not ** defined, then do no error processing. ** YYNSTATE the combined number of states. ** YYNRULE the number of rules in the grammar ** YYNTOKEN Number of terminal symbols ** YY_MAX_SHIFT Maximum value for shift actions ** YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions ** YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions ** YY_ERROR_ACTION The yy_action[] code for syntax error ** YY_ACCEPT_ACTION The yy_action[] code for accept ** YY_NO_ACTION The yy_action[] code for no-op ** YY_MIN_REDUCE Minimum value for reduce actions ** YY_MAX_REDUCE Maximum value for reduce actions ** YY_MIN_DSTRCTR Minimum symbol value that has a destructor ** YY_MAX_DSTRCTR Maximum symbol value that has a destructor */ #ifndef INTERFACE # define INTERFACE 1 #endif /************* Begin control #defines *****************************************/ #define YYCODETYPE unsigned char #define YYNOCODE 142 #define YYACTIONTYPE unsigned short int #define pik_parserTOKENTYPE PToken typedef union { int yyinit; pik_parserTOKENTYPE yy0; PList* yy51; PPoint yy67; short int yy88; PRel yy132; PNum yy253; PObj* yy258; } YYMINORTYPE; #ifndef YYSTACKDEPTH #define YYSTACKDEPTH 100 #endif #define pik_parserARG_SDECL #define pik_parserARG_PDECL #define pik_parserARG_PARAM #define pik_parserARG_FETCH #define pik_parserARG_STORE #define YYREALLOC realloc #define YYFREE free #define YYDYNSTACK 0 #define pik_parserCTX_SDECL Pik *p; #define pik_parserCTX_PDECL ,Pik *p #define pik_parserCTX_PARAM ,p #define pik_parserCTX_FETCH Pik *p=yypParser->p; #define pik_parserCTX_STORE yypParser->p=p; #define YYFALLBACK 1 #define YYNSTATE 168 #define YYNRULE 163 #define YYNRULE_WITH_ACTION 121 #define YYNTOKEN 106 #define YY_MAX_SHIFT 167 #define YY_MIN_SHIFTREDUCE 298 #define YY_MAX_SHIFTREDUCE 460 #define YY_ERROR_ACTION 461 #define YY_ACCEPT_ACTION 462 #define YY_NO_ACTION 463 #define YY_MIN_REDUCE 464 #define YY_MAX_REDUCE 626 #define YY_MIN_DSTRCTR 106 #define YY_MAX_DSTRCTR 109 /************* End control #defines *******************************************/ #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) /* Define the yytestcase() macro to be a no-op if is not already defined ** otherwise. ** ** Applications can choose to define yytestcase() in the %include section ** to a macro that can assist in verifying code coverage. For production ** code the yytestcase() macro should be turned off. But it is useful ** for testing. */ #ifndef yytestcase # define yytestcase(X) #endif /* Macro to determine if stack space has the ability to grow using ** heap memory. */ #if YYSTACKDEPTH<=0 || YYDYNSTACK # define YYGROWABLESTACK 1 #else # define YYGROWABLESTACK 0 #endif /* Guarantee a minimum number of initial stack slots. */ #if YYSTACKDEPTH<=0 # undef YYSTACKDEPTH # define YYSTACKDEPTH 2 /* Need a minimum stack size */ #endif /* Next are the tables used to determine what action to take based on the ** current state and lookahead token. These tables are used to implement ** functions that take a state number and lookahead value and return an ** action integer. ** ** Suppose the action integer is N. Then the action is determined as ** follows ** ** 0 <= N <= YY_MAX_SHIFT Shift N. That is, push the lookahead ** token onto the stack and goto state N. ** ** N between YY_MIN_SHIFTREDUCE Shift to an arbitrary state then ** and YY_MAX_SHIFTREDUCE reduce by rule N-YY_MIN_SHIFTREDUCE. ** ** N == YY_ERROR_ACTION A syntax error has occurred. ** ** N == YY_ACCEPT_ACTION The parser accepts its input. ** ** N == YY_NO_ACTION No such action. Denotes unused ** slots in the yy_action[] table. ** ** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE ** and YY_MAX_REDUCE ** ** The action table is constructed as a single large table named yy_action[]. ** Given state S and lookahead X, the action is computed as either: ** ** (A) N = yy_action[ yy_shift_ofst[S] + X ] ** (B) N = yy_default[S] ** ** The (A) formula is preferred. The B formula is used instead if ** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X. ** ** The formulas above are for computing the action when the lookahead is ** a terminal symbol. If the lookahead is a non-terminal (as occurs after ** a reduce action) then the yy_reduce_ofst[] array is used in place of ** the yy_shift_ofst[] array. ** ** The following are the tables generated in this section: ** ** yy_action[] A single table containing all actions. ** yy_lookahead[] A table containing the lookahead for each entry in ** yy_action. Used to detect hash collisions. ** yy_shift_ofst[] For each state, the offset into yy_action for ** shifting terminals. ** yy_reduce_ofst[] For each state, the offset into yy_action for ** shifting non-terminals after a reduce. ** yy_default[] Default action for each state. ** *********** Begin parsing tables **********************************************/ #define YY_ACTTAB_COUNT (1291) static const YYACTIONTYPE yy_action[] = { /* 0 */ 599, 518, 165, 120, 132, 64, 63, 62, 61, 464, /* 10 */ 599, 122, 465, 475, 29, 81, 36, 582, 468, 36, /* 20 */ 583, 584, 407, 392, 444, 445, 446, 355, 69, 167, /* 30 */ 49, 603, 599, 462, 27, 25, 336, 112, 324, 338, /* 40 */ 339, 9, 8, 33, 346, 32, 7, 71, 131, 38, /* 50 */ 351, 66, 48, 37, 133, 355, 355, 355, 355, 442, /* 60 */ 443, 356, 357, 358, 359, 360, 361, 362, 363, 364, /* 70 */ 495, 553, 88, 337, 601, 77, 601, 515, 165, 120, /* 80 */ 495, 121, 165, 120, 28, 81, 46, 10, 500, 500, /* 90 */ 428, 429, 430, 431, 444, 445, 446, 355, 320, 108, /* 100 */ 344, 31, 495, 156, 54, 51, 391, 112, 118, 338, /* 110 */ 339, 9, 8, 33, 30, 32, 7, 71, 131, 69, /* 120 */ 351, 66, 551, 165, 120, 355, 355, 355, 355, 442, /* 130 */ 443, 356, 357, 358, 359, 360, 361, 362, 363, 364, /* 140 */ 410, 453, 47, 59, 60, 83, 64, 63, 62, 61, /* 150 */ 85, 392, 556, 165, 120, 414, 415, 35, 2, 121, /* 160 */ 165, 120, 123, 160, 160, 160, 160, 84, 444, 445, /* 170 */ 446, 355, 62, 61, 459, 458, 410, 453, 313, 59, /* 180 */ 60, 156, 64, 63, 62, 61, 327, 392, 373, 312, /* 190 */ 80, 466, 475, 29, 2, 4, 13, 468, 308, 355, /* 200 */ 355, 355, 355, 442, 443, 322, 79, 3, 167, 452, /* 210 */ 459, 458, 307, 27, 146, 144, 64, 63, 62, 61, /* 220 */ 64, 63, 62, 61, 394, 162, 306, 106, 76, 454, /* 230 */ 455, 456, 457, 407, 391, 67, 118, 409, 159, 158, /* 240 */ 157, 55, 558, 441, 5, 452, 6, 151, 150, 470, /* 250 */ 29, 74, 440, 152, 396, 161, 43, 15, 471, 114, /* 260 */ 121, 165, 120, 106, 1, 454, 455, 456, 457, 413, /* 270 */ 391, 135, 118, 409, 159, 158, 157, 410, 453, 65, /* 280 */ 59, 60, 153, 166, 558, 22, 21, 11, 392, 12, /* 290 */ 558, 119, 372, 558, 24, 2, 149, 145, 449, 450, /* 300 */ 366, 366, 366, 366, 366, 366, 366, 366, 366, 366, /* 310 */ 366, 459, 458, 73, 142, 152, 64, 63, 62, 61, /* 320 */ 113, 114, 121, 165, 120, 74, 143, 152, 448, 107, /* 330 */ 14, 16, 125, 114, 121, 165, 120, 18, 121, 165, /* 340 */ 120, 72, 499, 152, 153, 44, 452, 19, 126, 114, /* 350 */ 121, 165, 120, 17, 394, 162, 153, 554, 165, 120, /* 360 */ 156, 20, 68, 115, 106, 375, 454, 455, 456, 457, /* 370 */ 419, 391, 153, 118, 409, 159, 158, 157, 408, 395, /* 380 */ 163, 137, 26, 130, 80, 129, 128, 127, 400, 23, /* 390 */ 57, 124, 58, 420, 421, 422, 423, 425, 398, 322, /* 400 */ 79, 393, 428, 429, 430, 431, 146, 144, 64, 63, /* 410 */ 62, 61, 410, 399, 164, 59, 60, 70, 88, 39, /* 420 */ 463, 119, 463, 102, 45, 463, 319, 121, 165, 120, /* 430 */ 42, 463, 463, 55, 496, 315, 316, 463, 317, 151, /* 440 */ 150, 410, 463, 463, 59, 60, 463, 463, 43, 156, /* 450 */ 463, 463, 392, 109, 465, 475, 29, 463, 463, 42, /* 460 */ 468, 463, 463, 86, 160, 160, 160, 160, 453, 463, /* 470 */ 463, 167, 121, 165, 120, 463, 27, 22, 21, 463, /* 480 */ 146, 144, 64, 63, 62, 61, 24, 463, 149, 145, /* 490 */ 449, 463, 463, 463, 156, 463, 463, 463, 463, 106, /* 500 */ 463, 459, 458, 463, 463, 463, 391, 55, 118, 409, /* 510 */ 159, 158, 157, 151, 150, 410, 463, 463, 59, 60, /* 520 */ 75, 463, 43, 463, 463, 463, 392, 463, 106, 463, /* 530 */ 463, 463, 463, 42, 463, 391, 452, 118, 409, 159, /* 540 */ 158, 157, 463, 410, 494, 463, 59, 60, 463, 463, /* 550 */ 463, 22, 21, 463, 392, 463, 454, 455, 456, 457, /* 560 */ 24, 42, 149, 145, 449, 88, 136, 134, 463, 410, /* 570 */ 147, 463, 59, 60, 121, 165, 120, 463, 463, 463, /* 580 */ 392, 110, 110, 463, 463, 463, 494, 42, 410, 148, /* 590 */ 463, 59, 60, 463, 107, 463, 156, 463, 463, 392, /* 600 */ 463, 463, 106, 121, 165, 120, 42, 469, 463, 391, /* 610 */ 463, 118, 409, 159, 158, 157, 410, 463, 463, 59, /* 620 */ 60, 64, 63, 62, 61, 156, 463, 392, 463, 463, /* 630 */ 106, 463, 463, 463, 42, 463, 463, 391, 407, 118, /* 640 */ 409, 159, 158, 157, 410, 463, 52, 59, 60, 463, /* 650 */ 74, 463, 152, 463, 463, 102, 106, 520, 114, 121, /* 660 */ 165, 120, 42, 391, 463, 118, 409, 159, 158, 157, /* 670 */ 463, 463, 463, 463, 463, 106, 463, 463, 463, 463, /* 680 */ 463, 153, 391, 463, 118, 409, 159, 158, 157, 410, /* 690 */ 463, 463, 59, 60, 64, 63, 62, 61, 463, 463, /* 700 */ 392, 463, 463, 106, 463, 463, 463, 40, 463, 463, /* 710 */ 391, 311, 118, 409, 159, 158, 157, 410, 463, 463, /* 720 */ 59, 60, 463, 98, 463, 463, 463, 463, 392, 463, /* 730 */ 463, 106, 121, 165, 120, 41, 89, 463, 391, 463, /* 740 */ 118, 409, 159, 158, 157, 121, 165, 120, 74, 463, /* 750 */ 152, 463, 463, 463, 156, 519, 114, 121, 165, 120, /* 760 */ 463, 74, 463, 152, 463, 463, 463, 156, 513, 114, /* 770 */ 121, 165, 120, 463, 463, 463, 106, 463, 463, 153, /* 780 */ 463, 463, 463, 391, 463, 118, 409, 159, 158, 157, /* 790 */ 463, 463, 153, 463, 64, 63, 62, 61, 463, 74, /* 800 */ 463, 152, 463, 463, 106, 463, 507, 114, 121, 165, /* 810 */ 120, 391, 463, 118, 409, 159, 158, 157, 74, 50, /* 820 */ 152, 463, 463, 463, 463, 506, 114, 121, 165, 120, /* 830 */ 153, 74, 463, 152, 90, 463, 463, 463, 501, 114, /* 840 */ 121, 165, 120, 121, 165, 120, 74, 463, 152, 153, /* 850 */ 463, 463, 463, 138, 114, 121, 165, 120, 463, 74, /* 860 */ 463, 152, 153, 463, 463, 156, 540, 114, 121, 165, /* 870 */ 120, 463, 74, 463, 152, 463, 463, 153, 463, 141, /* 880 */ 114, 121, 165, 120, 463, 463, 463, 74, 463, 152, /* 890 */ 153, 463, 463, 463, 548, 114, 121, 165, 120, 74, /* 900 */ 463, 152, 463, 153, 463, 463, 550, 114, 121, 165, /* 910 */ 120, 463, 74, 463, 152, 463, 463, 463, 153, 547, /* 920 */ 114, 121, 165, 120, 74, 463, 152, 463, 463, 463, /* 930 */ 153, 549, 114, 121, 165, 120, 463, 74, 463, 152, /* 940 */ 463, 463, 463, 153, 546, 114, 121, 165, 120, 74, /* 950 */ 463, 152, 87, 463, 463, 153, 545, 114, 121, 165, /* 960 */ 120, 121, 165, 120, 74, 463, 152, 463, 153, 463, /* 970 */ 463, 544, 114, 121, 165, 120, 463, 74, 463, 152, /* 980 */ 153, 463, 463, 156, 543, 114, 121, 165, 120, 453, /* 990 */ 74, 463, 152, 463, 463, 153, 463, 542, 114, 121, /* 1000 */ 165, 120, 463, 463, 463, 74, 463, 152, 153, 463, /* 1010 */ 463, 463, 154, 114, 121, 165, 120, 74, 463, 152, /* 1020 */ 463, 153, 459, 458, 155, 114, 121, 165, 120, 463, /* 1030 */ 74, 463, 152, 463, 463, 463, 153, 140, 114, 121, /* 1040 */ 165, 120, 74, 463, 152, 107, 463, 463, 153, 139, /* 1050 */ 114, 121, 165, 120, 121, 165, 120, 452, 484, 88, /* 1060 */ 463, 153, 463, 463, 463, 78, 78, 463, 121, 165, /* 1070 */ 120, 463, 463, 153, 463, 82, 156, 454, 455, 456, /* 1080 */ 457, 463, 487, 107, 34, 463, 463, 463, 99, 463, /* 1090 */ 156, 88, 121, 165, 120, 463, 484, 121, 165, 120, /* 1100 */ 121, 165, 120, 463, 593, 463, 100, 111, 111, 64, /* 1110 */ 63, 62, 61, 463, 156, 121, 165, 120, 101, 156, /* 1120 */ 463, 463, 156, 91, 463, 463, 371, 121, 165, 120, /* 1130 */ 103, 463, 121, 165, 120, 92, 463, 156, 463, 121, /* 1140 */ 165, 120, 463, 463, 121, 165, 120, 93, 463, 156, /* 1150 */ 463, 463, 463, 104, 156, 463, 121, 165, 120, 94, /* 1160 */ 463, 156, 121, 165, 120, 105, 156, 463, 121, 165, /* 1170 */ 120, 463, 95, 463, 121, 165, 120, 96, 156, 463, /* 1180 */ 463, 121, 165, 120, 156, 463, 121, 165, 120, 97, /* 1190 */ 156, 463, 463, 463, 463, 572, 156, 463, 121, 165, /* 1200 */ 120, 571, 463, 156, 121, 165, 120, 570, 156, 463, /* 1210 */ 121, 165, 120, 569, 463, 463, 121, 165, 120, 116, /* 1220 */ 156, 463, 121, 165, 120, 117, 156, 463, 121, 165, /* 1230 */ 120, 463, 156, 463, 121, 165, 120, 463, 156, 463, /* 1240 */ 64, 63, 62, 61, 156, 64, 63, 62, 61, 463, /* 1250 */ 156, 64, 63, 62, 61, 463, 156, 370, 64, 63, /* 1260 */ 62, 61, 64, 63, 62, 61, 463, 463, 412, 463, /* 1270 */ 53, 463, 463, 64, 63, 62, 61, 463, 463, 411, /* 1280 */ 463, 463, 463, 56, 463, 463, 463, 463, 463, 463, /* 1290 */ 407, }; static const YYCODETYPE yy_lookahead[] = { /* 0 */ 0, 119, 120, 121, 112, 4, 5, 6, 7, 0, /* 10 */ 10, 106, 107, 108, 109, 15, 10, 111, 113, 10, /* 20 */ 114, 115, 21, 12, 24, 25, 26, 27, 3, 124, /* 30 */ 29, 139, 32, 128, 129, 140, 1, 37, 32, 39, /* 40 */ 40, 41, 42, 43, 2, 45, 46, 47, 48, 111, /* 50 */ 50, 51, 114, 115, 112, 55, 56, 57, 58, 59, /* 60 */ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, /* 70 */ 0, 112, 110, 38, 136, 137, 138, 119, 120, 121, /* 80 */ 10, 119, 120, 121, 113, 15, 44, 125, 126, 127, /* 90 */ 33, 34, 35, 36, 24, 25, 26, 27, 29, 88, /* 100 */ 2, 132, 32, 141, 4, 5, 95, 37, 97, 39, /* 110 */ 40, 41, 42, 43, 134, 45, 46, 47, 48, 94, /* 120 */ 50, 51, 119, 120, 121, 55, 56, 57, 58, 59, /* 130 */ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, /* 140 */ 1, 2, 44, 4, 5, 122, 4, 5, 6, 7, /* 150 */ 110, 12, 119, 120, 121, 103, 104, 135, 19, 119, /* 160 */ 120, 121, 1, 24, 25, 26, 27, 122, 24, 25, /* 170 */ 26, 27, 6, 7, 35, 36, 1, 2, 23, 4, /* 180 */ 5, 141, 4, 5, 6, 7, 8, 12, 21, 21, /* 190 */ 15, 107, 108, 109, 19, 19, 29, 113, 15, 55, /* 200 */ 56, 57, 58, 59, 60, 30, 31, 20, 124, 70, /* 210 */ 35, 36, 15, 129, 2, 3, 4, 5, 6, 7, /* 220 */ 4, 5, 6, 7, 30, 31, 15, 88, 54, 90, /* 230 */ 91, 92, 93, 21, 95, 49, 97, 98, 99, 100, /* 240 */ 101, 29, 54, 47, 46, 70, 46, 35, 36, 108, /* 250 */ 109, 110, 47, 112, 30, 31, 44, 41, 117, 118, /* 260 */ 119, 120, 121, 88, 13, 90, 91, 92, 93, 21, /* 270 */ 95, 53, 97, 98, 99, 100, 101, 1, 2, 105, /* 280 */ 4, 5, 141, 89, 96, 73, 74, 29, 12, 81, /* 290 */ 102, 97, 21, 105, 82, 19, 84, 85, 86, 86, /* 300 */ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 310 */ 80, 35, 36, 110, 85, 112, 4, 5, 6, 7, /* 320 */ 117, 118, 119, 120, 121, 110, 87, 112, 86, 110, /* 330 */ 3, 3, 117, 118, 119, 120, 121, 3, 119, 120, /* 340 */ 121, 110, 123, 112, 141, 44, 70, 3, 117, 118, /* 350 */ 119, 120, 121, 41, 30, 31, 141, 119, 120, 121, /* 360 */ 141, 3, 3, 102, 88, 83, 90, 91, 92, 93, /* 370 */ 1, 95, 141, 97, 98, 99, 100, 101, 21, 30, /* 380 */ 31, 12, 19, 14, 15, 16, 17, 18, 32, 29, /* 390 */ 19, 22, 19, 24, 25, 26, 27, 28, 32, 30, /* 400 */ 31, 12, 33, 34, 35, 36, 2, 3, 4, 5, /* 410 */ 6, 7, 1, 32, 96, 4, 5, 3, 110, 11, /* 420 */ 142, 97, 142, 12, 20, 142, 15, 119, 120, 121, /* 430 */ 19, 142, 142, 29, 126, 24, 25, 142, 27, 35, /* 440 */ 36, 1, 142, 142, 4, 5, 142, 142, 44, 141, /* 450 */ 142, 142, 12, 106, 107, 108, 109, 142, 142, 19, /* 460 */ 113, 142, 142, 110, 24, 25, 26, 27, 2, 142, /* 470 */ 142, 124, 119, 120, 121, 142, 129, 73, 74, 142, /* 480 */ 2, 3, 4, 5, 6, 7, 82, 142, 84, 85, /* 490 */ 86, 142, 142, 142, 141, 142, 142, 142, 142, 88, /* 500 */ 142, 35, 36, 142, 142, 142, 95, 29, 97, 98, /* 510 */ 99, 100, 101, 35, 36, 1, 142, 142, 4, 5, /* 520 */ 54, 142, 44, 142, 142, 142, 12, 142, 88, 142, /* 530 */ 142, 142, 142, 19, 142, 95, 70, 97, 98, 99, /* 540 */ 100, 101, 142, 1, 2, 142, 4, 5, 142, 142, /* 550 */ 142, 73, 74, 142, 12, 142, 90, 91, 92, 93, /* 560 */ 82, 19, 84, 85, 86, 110, 52, 53, 142, 1, /* 570 */ 2, 142, 4, 5, 119, 120, 121, 142, 142, 142, /* 580 */ 12, 126, 127, 142, 142, 142, 44, 19, 1, 2, /* 590 */ 142, 4, 5, 142, 110, 142, 141, 142, 142, 12, /* 600 */ 142, 142, 88, 119, 120, 121, 19, 123, 142, 95, /* 610 */ 142, 97, 98, 99, 100, 101, 1, 142, 142, 4, /* 620 */ 5, 4, 5, 6, 7, 141, 142, 12, 142, 142, /* 630 */ 88, 142, 142, 142, 19, 142, 142, 95, 21, 97, /* 640 */ 98, 99, 100, 101, 1, 142, 29, 4, 5, 142, /* 650 */ 110, 142, 112, 142, 142, 12, 88, 117, 118, 119, /* 660 */ 120, 121, 19, 95, 142, 97, 98, 99, 100, 101, /* 670 */ 142, 142, 142, 142, 142, 88, 142, 142, 142, 142, /* 680 */ 142, 141, 95, 142, 97, 98, 99, 100, 101, 1, /* 690 */ 142, 142, 4, 5, 4, 5, 6, 7, 142, 142, /* 700 */ 12, 142, 142, 88, 142, 142, 142, 19, 142, 142, /* 710 */ 95, 21, 97, 98, 99, 100, 101, 1, 142, 142, /* 720 */ 4, 5, 142, 110, 142, 142, 142, 142, 12, 142, /* 730 */ 142, 88, 119, 120, 121, 19, 110, 142, 95, 142, /* 740 */ 97, 98, 99, 100, 101, 119, 120, 121, 110, 142, /* 750 */ 112, 142, 142, 142, 141, 117, 118, 119, 120, 121, /* 760 */ 142, 110, 142, 112, 142, 142, 142, 141, 117, 118, /* 770 */ 119, 120, 121, 142, 142, 142, 88, 142, 142, 141, /* 780 */ 142, 142, 142, 95, 142, 97, 98, 99, 100, 101, /* 790 */ 142, 142, 141, 142, 4, 5, 6, 7, 142, 110, /* 800 */ 142, 112, 142, 142, 88, 142, 117, 118, 119, 120, /* 810 */ 121, 95, 142, 97, 98, 99, 100, 101, 110, 29, /* 820 */ 112, 142, 142, 142, 142, 117, 118, 119, 120, 121, /* 830 */ 141, 110, 142, 112, 110, 142, 142, 142, 117, 118, /* 840 */ 119, 120, 121, 119, 120, 121, 110, 142, 112, 141, /* 850 */ 142, 142, 142, 117, 118, 119, 120, 121, 142, 110, /* 860 */ 142, 112, 141, 142, 142, 141, 117, 118, 119, 120, /* 870 */ 121, 142, 110, 142, 112, 142, 142, 141, 142, 117, /* 880 */ 118, 119, 120, 121, 142, 142, 142, 110, 142, 112, /* 890 */ 141, 142, 142, 142, 117, 118, 119, 120, 121, 110, /* 900 */ 142, 112, 142, 141, 142, 142, 117, 118, 119, 120, /* 910 */ 121, 142, 110, 142, 112, 142, 142, 142, 141, 117, /* 920 */ 118, 119, 120, 121, 110, 142, 112, 142, 142, 142, /* 930 */ 141, 117, 118, 119, 120, 121, 142, 110, 142, 112, /* 940 */ 142, 142, 142, 141, 117, 118, 119, 120, 121, 110, /* 950 */ 142, 112, 110, 142, 142, 141, 117, 118, 119, 120, /* 960 */ 121, 119, 120, 121, 110, 142, 112, 142, 141, 142, /* 970 */ 142, 117, 118, 119, 120, 121, 142, 110, 142, 112, /* 980 */ 141, 142, 142, 141, 117, 118, 119, 120, 121, 2, /* 990 */ 110, 142, 112, 142, 142, 141, 142, 117, 118, 119, /* 1000 */ 120, 121, 142, 142, 142, 110, 142, 112, 141, 142, /* 1010 */ 142, 142, 117, 118, 119, 120, 121, 110, 142, 112, /* 1020 */ 142, 141, 35, 36, 117, 118, 119, 120, 121, 142, /* 1030 */ 110, 142, 112, 142, 142, 142, 141, 117, 118, 119, /* 1040 */ 120, 121, 110, 142, 112, 110, 142, 142, 141, 117, /* 1050 */ 118, 119, 120, 121, 119, 120, 121, 70, 123, 110, /* 1060 */ 142, 141, 142, 142, 142, 130, 131, 142, 119, 120, /* 1070 */ 121, 142, 142, 141, 142, 126, 141, 90, 91, 92, /* 1080 */ 93, 142, 133, 110, 135, 142, 142, 142, 110, 142, /* 1090 */ 141, 110, 119, 120, 121, 142, 123, 119, 120, 121, /* 1100 */ 119, 120, 121, 142, 131, 142, 110, 126, 127, 4, /* 1110 */ 5, 6, 7, 142, 141, 119, 120, 121, 110, 141, /* 1120 */ 142, 142, 141, 110, 142, 142, 21, 119, 120, 121, /* 1130 */ 110, 142, 119, 120, 121, 110, 142, 141, 142, 119, /* 1140 */ 120, 121, 142, 142, 119, 120, 121, 110, 142, 141, /* 1150 */ 142, 142, 142, 110, 141, 142, 119, 120, 121, 110, /* 1160 */ 142, 141, 119, 120, 121, 110, 141, 142, 119, 120, /* 1170 */ 121, 142, 110, 142, 119, 120, 121, 110, 141, 142, /* 1180 */ 142, 119, 120, 121, 141, 142, 119, 120, 121, 110, /* 1190 */ 141, 142, 142, 142, 142, 110, 141, 142, 119, 120, /* 1200 */ 121, 110, 142, 141, 119, 120, 121, 110, 141, 142, /* 1210 */ 119, 120, 121, 110, 142, 142, 119, 120, 121, 110, /* 1220 */ 141, 142, 119, 120, 121, 110, 141, 142, 119, 120, /* 1230 */ 121, 142, 141, 142, 119, 120, 121, 142, 141, 142, /* 1240 */ 4, 5, 6, 7, 141, 4, 5, 6, 7, 142, /* 1250 */ 141, 4, 5, 6, 7, 142, 141, 21, 4, 5, /* 1260 */ 6, 7, 4, 5, 6, 7, 142, 142, 21, 142, /* 1270 */ 29, 142, 142, 4, 5, 6, 7, 142, 142, 21, /* 1280 */ 142, 142, 142, 29, 142, 142, 142, 142, 142, 142, /* 1290 */ 21, 142, 142, 142, 142, 142, 142, 142, 142, 142, /* 1300 */ 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, /* 1310 */ 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, /* 1320 */ 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, /* 1330 */ 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, /* 1340 */ 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, /* 1350 */ 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, /* 1360 */ 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, /* 1370 */ 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, /* 1380 */ 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, /* 1390 */ 106, 106, 106, 106, 106, 106, 106, }; #define YY_SHIFT_COUNT (167) #define YY_SHIFT_MIN (0) #define YY_SHIFT_MAX (1269) static const unsigned short int yy_shift_ofst[] = { /* 0 */ 369, 175, 139, 276, 276, 276, 276, 276, 276, 276, /* 10 */ 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, /* 20 */ 276, 276, 276, 276, 276, 276, 276, 411, 514, 615, /* 30 */ 369, 411, 542, 542, 0, 70, 369, 643, 615, 643, /* 40 */ 440, 440, 440, 568, 587, 615, 615, 615, 615, 615, /* 50 */ 615, 688, 615, 615, 716, 615, 615, 615, 615, 615, /* 60 */ 615, 615, 615, 615, 615, 144, 11, 11, 11, 11, /* 70 */ 11, 466, 404, 212, 478, 987, 987, 57, 69, 1291, /* 80 */ 1291, 1291, 1291, 230, 230, 1, 617, 690, 178, 216, /* 90 */ 312, 1105, 790, 1236, 1241, 1247, 1254, 1258, 1269, 142, /* 100 */ 142, 142, 188, 142, 142, 142, 194, 142, 324, 6, /* 110 */ 42, 98, 35, 167, 100, 52, 166, 166, 224, 349, /* 120 */ 25, 174, 9, 155, 161, 168, 187, 176, 183, 197, /* 130 */ 211, 186, 198, 200, 196, 205, 218, 251, 248, 258, /* 140 */ 208, 271, 213, 229, 239, 242, 327, 328, 334, 301, /* 150 */ 344, 358, 359, 261, 282, 360, 261, 363, 371, 373, /* 160 */ 357, 356, 366, 381, 389, 318, 414, 408, }; #define YY_REDUCE_COUNT (82) #define YY_REDUCE_MIN (-118) #define YY_REDUCE_MAX (1115) static const short yy_reduce_ofst[] = { /* 0 */ -95, 141, 203, 215, 231, 540, 638, 651, 689, 708, /* 10 */ 721, 736, 749, 762, 777, 789, 802, 814, 827, 839, /* 20 */ 854, 867, 880, 895, 907, 920, 932, 935, -38, 949, /* 30 */ 347, 973, 455, 981, -62, -62, 84, 219, 308, 484, /* 40 */ 40, 353, 613, 626, 724, 842, 978, 996, 1008, 1013, /* 50 */ 1020, 1025, 1037, 1043, 1049, 1055, 1062, 1067, 1079, 1085, /* 60 */ 1091, 1097, 1103, 1109, 1115, -94, -118, -42, 3, 33, /* 70 */ 238, -108, -105, -105, -105, -58, -41, -29, -31, -20, /* 80 */ 23, 45, 22, }; static const YYACTIONTYPE yy_default[] = { /* 0 */ 467, 461, 461, 461, 461, 461, 461, 461, 461, 461, /* 10 */ 461, 461, 461, 461, 461, 461, 461, 461, 461, 461, /* 20 */ 461, 461, 461, 461, 461, 461, 461, 461, 494, 600, /* 30 */ 467, 461, 604, 508, 605, 605, 467, 461, 461, 461, /* 40 */ 461, 461, 461, 461, 461, 461, 461, 461, 498, 461, /* 50 */ 461, 461, 461, 461, 461, 461, 461, 461, 461, 461, /* 60 */ 461, 461, 461, 461, 461, 461, 461, 461, 461, 461, /* 70 */ 461, 461, 461, 461, 461, 461, 461, 461, 476, 491, /* 80 */ 531, 531, 600, 489, 516, 461, 461, 461, 492, 461, /* 90 */ 461, 461, 461, 461, 461, 461, 461, 461, 461, 511, /* 100 */ 509, 497, 480, 535, 534, 533, 461, 590, 461, 461, /* 110 */ 461, 461, 461, 461, 613, 461, 568, 567, 563, 461, /* 120 */ 555, 552, 461, 461, 461, 461, 461, 461, 461, 461, /* 130 */ 461, 514, 461, 461, 461, 461, 461, 461, 461, 461, /* 140 */ 461, 461, 461, 461, 461, 461, 461, 461, 461, 461, /* 150 */ 461, 461, 461, 617, 461, 461, 461, 461, 461, 461, /* 160 */ 461, 461, 461, 461, 461, 626, 461, 461, }; /********** End of lemon-generated parsing tables *****************************/ /* The next table maps tokens (terminal symbols) into fallback tokens. ** If a construct like the following: ** ** %fallback ID X Y Z. ** ** appears in the grammar, then ID becomes a fallback token for X, Y, ** and Z. Whenever one of the tokens X, Y, or Z is input to the parser ** but it does not parse, the type of the token is changed to ID and ** the parse is retried before an error is thrown. ** ** This feature can be used, for example, to cause some keywords in a language ** to revert to identifiers if they keyword does not apply in the context where ** it appears. */ #ifdef YYFALLBACK static const YYCODETYPE yyFallback[] = { 0, /* $ => nothing */ 0, /* ID => nothing */ 1, /* EDGEPT => ID */ 0, /* OF => nothing */ 0, /* PLUS => nothing */ 0, /* MINUS => nothing */ 0, /* STAR => nothing */ 0, /* SLASH => nothing */ 0, /* PERCENT => nothing */ 0, /* UMINUS => nothing */ 0, /* EOL => nothing */ 0, /* ASSIGN => nothing */ 0, /* PLACENAME => nothing */ 0, /* COLON => nothing */ 0, /* TITLE => nothing */ 0, /* STRING => nothing */ 0, /* LABEL => nothing */ 0, /* DESCRIBE => nothing */ 0, /* ASSERT => nothing */ 0, /* LP => nothing */ 0, /* EQ => nothing */ 0, /* RP => nothing */ 0, /* DEFINE => nothing */ 0, /* CODEBLOCK => nothing */ 0, /* FILL => nothing */ 0, /* COLOR => nothing */ 0, /* TEXTCOLOR => nothing */ 0, /* THICKNESS => nothing */ 0, /* PRINT => nothing */ 0, /* COMMA => nothing */ 0, /* CLASSNAME => nothing */ 0, /* LB => nothing */ 0, /* RB => nothing */ 0, /* UP => nothing */ 0, /* DOWN => nothing */ 0, /* LEFT => nothing */ 0, /* RIGHT => nothing */ 0, /* CLASS => nothing */ 0, /* XML_CLASSES => nothing */ 0, /* CLOSE => nothing */ 0, /* CHOP => nothing */ 0, /* FROM => nothing */ 0, /* TO => nothing */ 0, /* THEN => nothing */ 0, /* HEADING => nothing */ 0, /* GO => nothing */ 0, /* AT => nothing */ 0, /* WITH => nothing */ 0, /* SAME => nothing */ 0, /* AS => nothing */ 0, /* FIT => nothing */ 0, /* BEHIND => nothing */ 0, /* UNTIL => nothing */ 0, /* EVEN => nothing */ 0, /* DOT_E => nothing */ 0, /* HEIGHT => nothing */ 0, /* WIDTH => nothing */ 0, /* RADIUS => nothing */ 0, /* DIAMETER => nothing */ 0, /* DOTTED => nothing */ 0, /* DASHED => nothing */ 0, /* CW => nothing */ 0, /* CCW => nothing */ 0, /* LARROW => nothing */ 0, /* RARROW => nothing */ 0, /* LRARROW => nothing */ 0, /* INVIS => nothing */ 0, /* THICK => nothing */ 0, /* THIN => nothing */ 0, /* SOLID => nothing */ 0, /* CENTER => nothing */ 0, /* LJUST => nothing */ 0, /* RJUST => nothing */ 0, /* ABOVE => nothing */ 0, /* BELOW => nothing */ 0, /* ITALIC => nothing */ 0, /* BOLD => nothing */ 0, /* MONO => nothing */ 0, /* ALIGNED => nothing */ 0, /* BIG => nothing */ 0, /* SMALL => nothing */ 0, /* AND => nothing */ 0, /* LT => nothing */ 0, /* GT => nothing */ 0, /* ON => nothing */ 0, /* WAY => nothing */ 0, /* BETWEEN => nothing */ 0, /* THE => nothing */ 0, /* NTH => nothing */ 0, /* VERTEX => nothing */ 0, /* TOP => nothing */ 0, /* BOTTOM => nothing */ 0, /* START => nothing */ 0, /* END => nothing */ 0, /* IN => nothing */ 0, /* THIS => nothing */ 0, /* DOT_U => nothing */ 0, /* LAST => nothing */ 0, /* NUMBER => nothing */ 0, /* FUNC1 => nothing */ 0, /* FUNC2 => nothing */ 0, /* DIST => nothing */ 0, /* DOT_XY => nothing */ 0, /* X => nothing */ 0, /* Y => nothing */ 0, /* DOT_L => nothing */ }; #endif /* YYFALLBACK */ /* The following structure represents a single element of the ** parser's stack. Information stored includes: ** ** + The state number for the parser at this level of the stack. ** ** + The value of the token stored at this level of the stack. ** (In other words, the "major" token.) ** ** + The semantic value stored at this level of the stack. This is ** the information used by the action routines in the grammar. ** It is sometimes called the "minor" token. ** ** After the "shift" half of a SHIFTREDUCE action, the stateno field ** actually contains the reduce action for the second half of the ** SHIFTREDUCE. */ struct yyStackEntry { YYACTIONTYPE stateno; /* The state-number, or reduce action in SHIFTREDUCE */ YYCODETYPE major; /* The major token value. This is the code ** number for the token at this stack level */ YYMINORTYPE minor; /* The user-supplied minor token value. This ** is the value of the token */ }; typedef struct yyStackEntry yyStackEntry; /* The state of the parser is completely contained in an instance of ** the following structure */ struct yyParser { yyStackEntry *yytos; /* Pointer to top element of the stack */ #ifdef YYTRACKMAXSTACKDEPTH int yyhwm; /* High-water mark of the stack */ #endif #ifndef YYNOERRORRECOVERY int yyerrcnt; /* Shifts left before out of the error */ #endif pik_parserARG_SDECL /* A place to hold %extra_argument */ pik_parserCTX_SDECL /* A place to hold %extra_context */ yyStackEntry *yystackEnd; /* Last entry in the stack */ yyStackEntry *yystack; /* The parser stack */ yyStackEntry yystk0[YYSTACKDEPTH]; /* Initial stack space */ }; typedef struct yyParser yyParser; #include <assert.h> #ifndef NDEBUG #include <stdio.h> static FILE *yyTraceFILE = 0; static char *yyTracePrompt = 0; #endif /* NDEBUG */ #ifndef NDEBUG /* ** Turn parser tracing on by giving a stream to which to write the trace ** and a prompt to preface each trace message. Tracing is turned off ** by making either argument NULL ** ** Inputs: ** <ul> ** <li> A FILE* to which trace output should be written. ** If NULL, then tracing is turned off. ** <li> A prefix string written at the beginning of every ** line of trace output. If NULL, then tracing is ** turned off. ** </ul> ** ** Outputs: ** None. */ void pik_parserTrace(FILE *TraceFILE, char *zTracePrompt){ yyTraceFILE = TraceFILE; yyTracePrompt = zTracePrompt; if( yyTraceFILE==0 ) yyTracePrompt = 0; else if( yyTracePrompt==0 ) yyTraceFILE = 0; } #endif /* NDEBUG */ #if defined(YYCOVERAGE) || !defined(NDEBUG) /* For tracing shifts, the names of all terminals and nonterminals ** are required. The following table supplies these names */ static const char *const yyTokenName[] = { /* 0 */ "$", /* 1 */ "ID", /* 2 */ "EDGEPT", /* 3 */ "OF", /* 4 */ "PLUS", /* 5 */ "MINUS", /* 6 */ "STAR", /* 7 */ "SLASH", /* 8 */ "PERCENT", /* 9 */ "UMINUS", /* 10 */ "EOL", /* 11 */ "ASSIGN", /* 12 */ "PLACENAME", /* 13 */ "COLON", /* 14 */ "TITLE", /* 15 */ "STRING", /* 16 */ "LABEL", /* 17 */ "DESCRIBE", /* 18 */ "ASSERT", /* 19 */ "LP", /* 20 */ "EQ", /* 21 */ "RP", /* 22 */ "DEFINE", /* 23 */ "CODEBLOCK", /* 24 */ "FILL", /* 25 */ "COLOR", /* 26 */ "TEXTCOLOR", /* 27 */ "THICKNESS", /* 28 */ "PRINT", /* 29 */ "COMMA", /* 30 */ "CLASSNAME", /* 31 */ "LB", /* 32 */ "RB", /* 33 */ "UP", /* 34 */ "DOWN", /* 35 */ "LEFT", /* 36 */ "RIGHT", /* 37 */ "CLASS", /* 38 */ "XML_CLASSES", /* 39 */ "CLOSE", /* 40 */ "CHOP", /* 41 */ "FROM", /* 42 */ "TO", /* 43 */ "THEN", /* 44 */ "HEADING", /* 45 */ "GO", /* 46 */ "AT", /* 47 */ "WITH", /* 48 */ "SAME", /* 49 */ "AS", /* 50 */ "FIT", /* 51 */ "BEHIND", /* 52 */ "UNTIL", /* 53 */ "EVEN", /* 54 */ "DOT_E", /* 55 */ "HEIGHT", /* 56 */ "WIDTH", /* 57 */ "RADIUS", /* 58 */ "DIAMETER", /* 59 */ "DOTTED", /* 60 */ "DASHED", /* 61 */ "CW", /* 62 */ "CCW", /* 63 */ "LARROW", /* 64 */ "RARROW", /* 65 */ "LRARROW", /* 66 */ "INVIS", /* 67 */ "THICK", /* 68 */ "THIN", /* 69 */ "SOLID", /* 70 */ "CENTER", /* 71 */ "LJUST", /* 72 */ "RJUST", /* 73 */ "ABOVE", /* 74 */ "BELOW", /* 75 */ "ITALIC", /* 76 */ "BOLD", /* 77 */ "MONO", /* 78 */ "ALIGNED", /* 79 */ "BIG", /* 80 */ "SMALL", /* 81 */ "AND", /* 82 */ "LT", /* 83 */ "GT", /* 84 */ "ON", /* 85 */ "WAY", /* 86 */ "BETWEEN", /* 87 */ "THE", /* 88 */ "NTH", /* 89 */ "VERTEX", /* 90 */ "TOP", /* 91 */ "BOTTOM", /* 92 */ "START", /* 93 */ "END", /* 94 */ "IN", /* 95 */ "THIS", /* 96 */ "DOT_U", /* 97 */ "LAST", /* 98 */ "NUMBER", /* 99 */ "FUNC1", /* 100 */ "FUNC2", /* 101 */ "DIST", /* 102 */ "DOT_XY", /* 103 */ "X", /* 104 */ "Y", /* 105 */ "DOT_L", /* 106 */ "statement_list", /* 107 */ "statement", /* 108 */ "unnamed_statement", /* 109 */ "basetype", /* 110 */ "expr", /* 111 */ "numproperty", /* 112 */ "edge", /* 113 */ "direction", /* 114 */ "dashproperty", /* 115 */ "colorproperty", /* 116 */ "locproperty", /* 117 */ "position", /* 118 */ "place", /* 119 */ "object", /* 120 */ "objectname", /* 121 */ "nth", /* 122 */ "textposition", /* 123 */ "rvalue", /* 124 */ "lvalue", /* 125 */ "even", /* 126 */ "relexpr", /* 127 */ "optrelexpr", /* 128 */ "document", /* 129 */ "print", /* 130 */ "prlist", /* 131 */ "pritem", /* 132 */ "prsep", /* 133 */ "attribute_list", /* 134 */ "savelist", /* 135 */ "alist", /* 136 */ "attribute", /* 137 */ "go", /* 138 */ "boolproperty", /* 139 */ "withclause", /* 140 */ "between", /* 141 */ "place2", }; #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ #ifndef NDEBUG /* For tracing reduce actions, the names of all rules are required. */ static const char *const yyRuleName[] = { /* 0 */ "document ::= statement_list", /* 1 */ "statement_list ::= statement", /* 2 */ "statement_list ::= statement_list EOL statement", /* 3 */ "statement ::=", /* 4 */ "statement ::= direction", /* 5 */ "statement ::= lvalue ASSIGN rvalue", /* 6 */ "statement ::= PLACENAME COLON unnamed_statement", /* 7 */ "statement ::= PLACENAME COLON position", /* 8 */ "statement ::= TITLE STRING", /* 9 */ "statement ::= LABEL STRING", /* 10 */ "statement ::= DESCRIBE STRING", /* 11 */ "statement ::= unnamed_statement", /* 12 */ "statement ::= print prlist", /* 13 */ "statement ::= ASSERT LP expr EQ expr RP", /* 14 */ "statement ::= ASSERT LP position EQ position RP", /* 15 */ "statement ::= DEFINE ID CODEBLOCK", /* 16 */ "rvalue ::= PLACENAME", /* 17 */ "pritem ::= FILL", /* 18 */ "pritem ::= COLOR", /* 19 */ "pritem ::= THICKNESS", /* 20 */ "pritem ::= rvalue", /* 21 */ "pritem ::= STRING", /* 22 */ "prsep ::= COMMA", /* 23 */ "unnamed_statement ::= basetype attribute_list", /* 24 */ "basetype ::= CLASSNAME", /* 25 */ "basetype ::= STRING textposition", /* 26 */ "basetype ::= LB savelist statement_list RB", /* 27 */ "savelist ::=", /* 28 */ "relexpr ::= expr", /* 29 */ "relexpr ::= expr PERCENT", /* 30 */ "optrelexpr ::=", /* 31 */ "attribute_list ::= relexpr alist", /* 32 */ "attribute ::= numproperty relexpr", /* 33 */ "attribute ::= dashproperty expr", /* 34 */ "attribute ::= dashproperty", /* 35 */ "attribute ::= colorproperty rvalue", /* 36 */ "attribute ::= go direction optrelexpr", /* 37 */ "attribute ::= go direction even position", /* 38 */ "attribute ::= CLASS ID", /* 39 */ "attribute ::= CLASS XML_CLASSES", /* 40 */ "attribute ::= CLOSE", /* 41 */ "attribute ::= CHOP", /* 42 */ "attribute ::= FROM position", /* 43 */ "attribute ::= TO position", /* 44 */ "attribute ::= THEN", /* 45 */ "attribute ::= THEN optrelexpr HEADING expr", /* 46 */ "attribute ::= THEN optrelexpr EDGEPT", /* 47 */ "attribute ::= GO optrelexpr HEADING expr", /* 48 */ "attribute ::= GO optrelexpr EDGEPT", /* 49 */ "attribute ::= AT position", /* 50 */ "attribute ::= SAME", /* 51 */ "attribute ::= SAME AS object", /* 52 */ "attribute ::= STRING textposition", /* 53 */ "attribute ::= FIT", /* 54 */ "attribute ::= BEHIND object", /* 55 */ "withclause ::= DOT_E edge AT position", /* 56 */ "withclause ::= edge AT position", /* 57 */ "numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS", /* 58 */ "boolproperty ::= CW", /* 59 */ "boolproperty ::= CCW", /* 60 */ "boolproperty ::= LARROW", /* 61 */ "boolproperty ::= RARROW", /* 62 */ "boolproperty ::= LRARROW", /* 63 */ "boolproperty ::= INVIS", /* 64 */ "boolproperty ::= THICK", /* 65 */ "boolproperty ::= THIN", /* 66 */ "boolproperty ::= SOLID", /* 67 */ "textposition ::=", /* 68 */ "textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL", /* 69 */ "position ::= expr COMMA expr", /* 70 */ "position ::= place PLUS expr COMMA expr", /* 71 */ "position ::= place MINUS expr COMMA expr", /* 72 */ "position ::= place PLUS LP expr COMMA expr RP", /* 73 */ "position ::= place MINUS LP expr COMMA expr RP", /* 74 */ "position ::= LP position COMMA position RP", /* 75 */ "position ::= LP position RP", /* 76 */ "position ::= expr between position AND position", /* 77 */ "position ::= expr LT position COMMA position GT", /* 78 */ "position ::= expr ABOVE position", /* 79 */ "position ::= expr BELOW position", /* 80 */ "position ::= expr LEFT OF position", /* 81 */ "position ::= expr RIGHT OF position", /* 82 */ "position ::= expr ON HEADING EDGEPT OF position", /* 83 */ "position ::= expr HEADING EDGEPT OF position", /* 84 */ "position ::= expr EDGEPT OF position", /* 85 */ "position ::= expr ON HEADING expr FROM position", /* 86 */ "position ::= expr HEADING expr FROM position", /* 87 */ "place ::= edge OF object", /* 88 */ "place2 ::= object", /* 89 */ "place2 ::= object DOT_E edge", /* 90 */ "place2 ::= NTH VERTEX OF object", /* 91 */ "object ::= nth", /* 92 */ "object ::= nth OF|IN object", /* 93 */ "objectname ::= THIS", /* 94 */ "objectname ::= PLACENAME", /* 95 */ "objectname ::= objectname DOT_U PLACENAME", /* 96 */ "nth ::= NTH CLASSNAME", /* 97 */ "nth ::= NTH LAST CLASSNAME", /* 98 */ "nth ::= LAST CLASSNAME", /* 99 */ "nth ::= LAST", /* 100 */ "nth ::= NTH LB RB", /* 101 */ "nth ::= NTH LAST LB RB", /* 102 */ "nth ::= LAST LB RB", /* 103 */ "expr ::= expr PLUS expr", /* 104 */ "expr ::= expr MINUS expr", /* 105 */ "expr ::= expr STAR expr", /* 106 */ "expr ::= expr SLASH expr", /* 107 */ "expr ::= MINUS expr", /* 108 */ "expr ::= PLUS expr", /* 109 */ "expr ::= LP expr RP", /* 110 */ "expr ::= LP FILL|COLOR|TEXTCOLOR|THICKNESS RP", /* 111 */ "expr ::= NUMBER", /* 112 */ "expr ::= ID", /* 113 */ "expr ::= FUNC1 LP expr RP", /* 114 */ "expr ::= FUNC2 LP expr COMMA expr RP", /* 115 */ "expr ::= DIST LP position COMMA position RP", /* 116 */ "expr ::= place2 DOT_XY X", /* 117 */ "expr ::= place2 DOT_XY Y", /* 118 */ "expr ::= object DOT_L numproperty", /* 119 */ "expr ::= object DOT_L dashproperty", /* 120 */ "expr ::= object DOT_L colorproperty", /* 121 */ "lvalue ::= ID", /* 122 */ "lvalue ::= FILL", /* 123 */ "lvalue ::= COLOR", /* 124 */ "lvalue ::= TEXTCOLOR", /* 125 */ "lvalue ::= THICKNESS", /* 126 */ "rvalue ::= expr", /* 127 */ "print ::= PRINT", /* 128 */ "prlist ::= pritem", /* 129 */ "prlist ::= prlist prsep pritem", /* 130 */ "direction ::= UP", /* 131 */ "direction ::= DOWN", /* 132 */ "direction ::= LEFT", /* 133 */ "direction ::= RIGHT", /* 134 */ "optrelexpr ::= relexpr", /* 135 */ "attribute_list ::= alist", /* 136 */ "alist ::=", /* 137 */ "alist ::= alist attribute", /* 138 */ "attribute ::= boolproperty", /* 139 */ "attribute ::= WITH withclause", /* 140 */ "go ::= GO", /* 141 */ "go ::=", /* 142 */ "even ::= UNTIL EVEN WITH", /* 143 */ "even ::= EVEN WITH", /* 144 */ "dashproperty ::= DOTTED", /* 145 */ "dashproperty ::= DASHED", /* 146 */ "colorproperty ::= FILL", /* 147 */ "colorproperty ::= COLOR", /* 148 */ "colorproperty ::= TEXTCOLOR", /* 149 */ "position ::= place", /* 150 */ "between ::= WAY BETWEEN", /* 151 */ "between ::= BETWEEN", /* 152 */ "between ::= OF THE WAY BETWEEN", /* 153 */ "place ::= place2", /* 154 */ "edge ::= CENTER", /* 155 */ "edge ::= EDGEPT", /* 156 */ "edge ::= TOP", /* 157 */ "edge ::= BOTTOM", /* 158 */ "edge ::= START", /* 159 */ "edge ::= END", /* 160 */ "edge ::= RIGHT", /* 161 */ "edge ::= LEFT", /* 162 */ "object ::= objectname", }; #endif /* NDEBUG */ #if YYGROWABLESTACK /* ** Try to increase the size of the parser stack. Return the number ** of errors. Return 0 on success. */ static int yyGrowStack(yyParser *p){ int oldSize = 1 + (int)(p->yystackEnd - p->yystack); int newSize; int idx; yyStackEntry *pNew; newSize = oldSize*2 + 100; idx = (int)(p->yytos - p->yystack); if( p->yystack==p->yystk0 ){ pNew = YYREALLOC(0, newSize*sizeof(pNew[0])); if( pNew==0 ) return 1; memcpy(pNew, p->yystack, oldSize*sizeof(pNew[0])); }else{ pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0])); if( pNew==0 ) return 1; } p->yystack = pNew; p->yytos = &p->yystack[idx]; #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n", yyTracePrompt, oldSize, newSize); } #endif p->yystackEnd = &p->yystack[newSize-1]; return 0; } #endif /* YYGROWABLESTACK */ #if !YYGROWABLESTACK /* For builds that do no have a growable stack, yyGrowStack always ** returns an error. */ # define yyGrowStack(X) 1 #endif /* Datatype of the argument to the memory allocated passed as the ** second argument to pik_parserAlloc() below. This can be changed by ** putting an appropriate #define in the %include section of the input ** grammar. */ #ifndef YYMALLOCARGTYPE # define YYMALLOCARGTYPE size_t #endif /* Initialize a new parser that has already been allocated. */ void pik_parserInit(void *yypRawParser pik_parserCTX_PDECL){ yyParser *yypParser = (yyParser*)yypRawParser; pik_parserCTX_STORE #ifdef YYTRACKMAXSTACKDEPTH yypParser->yyhwm = 0; #endif yypParser->yystack = yypParser->yystk0; yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1]; #ifndef YYNOERRORRECOVERY yypParser->yyerrcnt = -1; #endif yypParser->yytos = yypParser->yystack; yypParser->yystack[0].stateno = 0; yypParser->yystack[0].major = 0; } #ifndef pik_parser_ENGINEALWAYSONSTACK /* ** This function allocates a new parser. ** The only argument is a pointer to a function which works like ** malloc. ** ** Inputs: ** A pointer to the function used to allocate memory. ** ** Outputs: ** A pointer to a parser. This pointer is used in subsequent calls ** to pik_parser and pik_parserFree. */ void *pik_parserAlloc(void *(*mallocProc)(YYMALLOCARGTYPE) pik_parserCTX_PDECL){ yyParser *yypParser; yypParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) ); if( yypParser ){ pik_parserCTX_STORE pik_parserInit(yypParser pik_parserCTX_PARAM); } return (void*)yypParser; } #endif /* pik_parser_ENGINEALWAYSONSTACK */ /* The following function deletes the "minor type" or semantic value ** associated with a symbol. The symbol can be either a terminal ** or nonterminal. "yymajor" is the symbol code, and "yypminor" is ** a pointer to the value to be deleted. The code used to do the ** deletions is derived from the %destructor and/or %token_destructor ** directives of the input grammar. */ static void yy_destructor( yyParser *yypParser, /* The parser */ YYCODETYPE yymajor, /* Type code for object to destroy */ YYMINORTYPE *yypminor /* The object to be destroyed */ ){ pik_parserARG_FETCH pik_parserCTX_FETCH switch( yymajor ){ /* Here is inserted the actions which take place when a ** terminal or non-terminal is destroyed. This can happen ** when the symbol is popped from the stack during a ** reduce or during error processing or when a parser is ** being destroyed before it is finished parsing. ** ** Note: during a reduce, the only symbols destroyed are those ** which appear on the RHS of the rule, but which are *not* used ** inside the C code. */ /********* Begin destructor definitions ***************************************/ case 106: /* statement_list */ { #line 769 "pikchr.y" pik_elist_free(p,(yypminor->yy51)); #line 2056 "pikchr.c" } break; case 107: /* statement */ case 108: /* unnamed_statement */ case 109: /* basetype */ { #line 771 "pikchr.y" pik_elem_free(p,(yypminor->yy258)); #line 2065 "pikchr.c" } break; /********* End destructor definitions *****************************************/ default: break; /* If no destructor action specified: do nothing */ } } /* ** Pop the parser's stack once. ** ** If there is a destructor routine associated with the token which ** is popped from the stack, then call it. */ static void yy_pop_parser_stack(yyParser *pParser){ yyStackEntry *yytos; assert( pParser->yytos!=0 ); assert( pParser->yytos > pParser->yystack ); yytos = pParser->yytos--; #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sPopping %s\n", yyTracePrompt, yyTokenName[yytos->major]); } #endif yy_destructor(pParser, yytos->major, &yytos->minor); } /* ** Clear all secondary memory allocations from the parser */ void pik_parserFinalize(void *p){ yyParser *pParser = (yyParser*)p; /* In-lined version of calling yy_pop_parser_stack() for each ** element left in the stack */ yyStackEntry *yytos = pParser->yytos; while( yytos>pParser->yystack ){ #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sPopping %s\n", yyTracePrompt, yyTokenName[yytos->major]); } #endif if( yytos->major>=YY_MIN_DSTRCTR ){ yy_destructor(pParser, yytos->major, &yytos->minor); } yytos--; } #if YYGROWABLESTACK if( pParser->yystack!=pParser->yystk0 ) YYFREE(pParser->yystack); #endif } #ifndef pik_parser_ENGINEALWAYSONSTACK /* ** Deallocate and destroy a parser. Destructors are called for ** all stack elements before shutting the parser down. ** ** If the YYPARSEFREENEVERNULL macro exists (for example because it ** is defined in a %include section of the input grammar) then it is ** assumed that the input pointer is never NULL. */ void pik_parserFree( void *p, /* The parser to be deleted */ void (*freeProc)(void*) /* Function used to reclaim memory */ ){ #ifndef YYPARSEFREENEVERNULL if( p==0 ) return; #endif pik_parserFinalize(p); (*freeProc)(p); } #endif /* pik_parser_ENGINEALWAYSONSTACK */ /* ** Return the peak depth of the stack for a parser. */ #ifdef YYTRACKMAXSTACKDEPTH int pik_parserStackPeak(void *p){ yyParser *pParser = (yyParser*)p; return pParser->yyhwm; } #endif /* This array of booleans keeps track of the parser statement ** coverage. The element yycoverage[X][Y] is set when the parser ** is in state X and has a lookahead token Y. In a well-tested ** systems, every element of this matrix should end up being set. */ #if defined(YYCOVERAGE) static unsigned char yycoverage[YYNSTATE][YYNTOKEN]; #endif /* ** Write into out a description of every state/lookahead combination that ** ** (1) has not been used by the parser, and ** (2) is not a syntax error. ** ** Return the number of missed state/lookahead combinations. */ #if defined(YYCOVERAGE) int pik_parserCoverage(FILE *out){ int stateno, iLookAhead, i; int nMissed = 0; for(stateno=0; stateno<YYNSTATE; stateno++){ i = yy_shift_ofst[stateno]; for(iLookAhead=0; iLookAhead<YYNTOKEN; iLookAhead++){ if( yy_lookahead[i+iLookAhead]!=iLookAhead ) continue; if( yycoverage[stateno][iLookAhead]==0 ) nMissed++; if( out ){ fprintf(out,"State %d lookahead %s %s\n", stateno, yyTokenName[iLookAhead], yycoverage[stateno][iLookAhead] ? "ok" : "missed"); } } } return nMissed; } #endif /* ** Find the appropriate action for a parser given the terminal ** look-ahead token iLookAhead. */ static YYACTIONTYPE yy_find_shift_action( YYCODETYPE iLookAhead, /* The look-ahead token */ YYACTIONTYPE stateno /* Current state number */ ){ int i; if( stateno>YY_MAX_SHIFT ) return stateno; assert( stateno <= YY_SHIFT_COUNT ); #if defined(YYCOVERAGE) yycoverage[stateno][iLookAhead] = 1; #endif do{ i = yy_shift_ofst[stateno]; assert( i>=0 ); assert( i<=YY_ACTTAB_COUNT ); assert( i+YYNTOKEN<=(int)YY_NLOOKAHEAD ); assert( iLookAhead!=YYNOCODE ); assert( iLookAhead < YYNTOKEN ); i += iLookAhead; assert( i<(int)YY_NLOOKAHEAD ); if( yy_lookahead[i]!=iLookAhead ){ #ifdef YYFALLBACK YYCODETYPE iFallback; /* Fallback token */ assert( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0]) ); iFallback = yyFallback[iLookAhead]; if( iFallback!=0 ){ #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n", yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); } #endif assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */ iLookAhead = iFallback; continue; } #endif #ifdef YYWILDCARD { int j = i - iLookAhead + YYWILDCARD; assert( j<(int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])) ); if( yy_lookahead[j]==YYWILDCARD && iLookAhead>0 ){ #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n", yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]); } #endif /* NDEBUG */ return yy_action[j]; } } #endif /* YYWILDCARD */ return yy_default[stateno]; }else{ assert( i>=0 && i<(int)(sizeof(yy_action)/sizeof(yy_action[0])) ); return yy_action[i]; } }while(1); } /* ** Find the appropriate action for a parser given the non-terminal ** look-ahead token iLookAhead. */ static YYACTIONTYPE yy_find_reduce_action( YYACTIONTYPE stateno, /* Current state number */ YYCODETYPE iLookAhead /* The look-ahead token */ ){ int i; #ifdef YYERRORSYMBOL if( stateno>YY_REDUCE_COUNT ){ return yy_default[stateno]; } #else assert( stateno<=YY_REDUCE_COUNT ); #endif i = yy_reduce_ofst[stateno]; assert( iLookAhead!=YYNOCODE ); i += iLookAhead; #ifdef YYERRORSYMBOL if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){ return yy_default[stateno]; } #else assert( i>=0 && i<YY_ACTTAB_COUNT ); assert( yy_lookahead[i]==iLookAhead ); #endif return yy_action[i]; } /* ** The following routine is called if the stack overflows. */ static void yyStackOverflow(yyParser *yypParser){ pik_parserARG_FETCH pik_parserCTX_FETCH #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); } #endif while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser); /* Here code is inserted which will execute if the parser ** stack every overflows */ /******** Begin %stack_overflow code ******************************************/ #line 803 "pikchr.y" pik_error(p, 0, "parser stack overflow"); #line 2303 "pikchr.c" /******** End %stack_overflow code ********************************************/ pik_parserARG_STORE /* Suppress warning about unused %extra_argument var */ pik_parserCTX_STORE } /* ** Print tracing information for a SHIFT action */ #ifndef NDEBUG static void yyTraceShift(yyParser *yypParser, int yyNewState, const char *zTag){ if( yyTraceFILE ){ if( yyNewState<YYNSTATE ){ fprintf(yyTraceFILE,"%s%s '%s', go to state %d\n", yyTracePrompt, zTag, yyTokenName[yypParser->yytos->major], yyNewState); }else{ fprintf(yyTraceFILE,"%s%s '%s', pending reduce %d\n", yyTracePrompt, zTag, yyTokenName[yypParser->yytos->major], yyNewState - YY_MIN_REDUCE); } } } #else # define yyTraceShift(X,Y,Z) #endif /* ** Perform a shift action. */ static void yy_shift( yyParser *yypParser, /* The parser to be shifted */ YYACTIONTYPE yyNewState, /* The new state to shift in */ YYCODETYPE yyMajor, /* The major token to shift in */ pik_parserTOKENTYPE yyMinor /* The minor token to shift in */ ){ yyStackEntry *yytos; yypParser->yytos++; #ifdef YYTRACKMAXSTACKDEPTH if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){ yypParser->yyhwm++; assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) ); } #endif yytos = yypParser->yytos; if( yytos>yypParser->yystackEnd ){ if( yyGrowStack(yypParser) ){ yypParser->yytos--; yyStackOverflow(yypParser); return; } yytos = yypParser->yytos; assert( yytos <= yypParser->yystackEnd ); } if( yyNewState > YY_MAX_SHIFT ){ yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE; } yytos->stateno = yyNewState; yytos->major = yyMajor; yytos->minor.yy0 = yyMinor; yyTraceShift(yypParser, yyNewState, "Shift"); } /* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side ** of that rule */ static const YYCODETYPE yyRuleInfoLhs[] = { 128, /* (0) document ::= statement_list */ 106, /* (1) statement_list ::= statement */ 106, /* (2) statement_list ::= statement_list EOL statement */ 107, /* (3) statement ::= */ 107, /* (4) statement ::= direction */ 107, /* (5) statement ::= lvalue ASSIGN rvalue */ 107, /* (6) statement ::= PLACENAME COLON unnamed_statement */ 107, /* (7) statement ::= PLACENAME COLON position */ 107, /* (8) statement ::= TITLE STRING */ 107, /* (9) statement ::= LABEL STRING */ 107, /* (10) statement ::= DESCRIBE STRING */ 107, /* (11) statement ::= unnamed_statement */ 107, /* (12) statement ::= print prlist */ 107, /* (13) statement ::= ASSERT LP expr EQ expr RP */ 107, /* (14) statement ::= ASSERT LP position EQ position RP */ 107, /* (15) statement ::= DEFINE ID CODEBLOCK */ 123, /* (16) rvalue ::= PLACENAME */ 131, /* (17) pritem ::= FILL */ 131, /* (18) pritem ::= COLOR */ 131, /* (19) pritem ::= THICKNESS */ 131, /* (20) pritem ::= rvalue */ 131, /* (21) pritem ::= STRING */ 132, /* (22) prsep ::= COMMA */ 108, /* (23) unnamed_statement ::= basetype attribute_list */ 109, /* (24) basetype ::= CLASSNAME */ 109, /* (25) basetype ::= STRING textposition */ 109, /* (26) basetype ::= LB savelist statement_list RB */ 134, /* (27) savelist ::= */ 126, /* (28) relexpr ::= expr */ 126, /* (29) relexpr ::= expr PERCENT */ 127, /* (30) optrelexpr ::= */ 133, /* (31) attribute_list ::= relexpr alist */ 136, /* (32) attribute ::= numproperty relexpr */ 136, /* (33) attribute ::= dashproperty expr */ 136, /* (34) attribute ::= dashproperty */ 136, /* (35) attribute ::= colorproperty rvalue */ 136, /* (36) attribute ::= go direction optrelexpr */ 136, /* (37) attribute ::= go direction even position */ 136, /* (38) attribute ::= CLASS ID */ 136, /* (39) attribute ::= CLASS XML_CLASSES */ 136, /* (40) attribute ::= CLOSE */ 136, /* (41) attribute ::= CHOP */ 136, /* (42) attribute ::= FROM position */ 136, /* (43) attribute ::= TO position */ 136, /* (44) attribute ::= THEN */ 136, /* (45) attribute ::= THEN optrelexpr HEADING expr */ 136, /* (46) attribute ::= THEN optrelexpr EDGEPT */ 136, /* (47) attribute ::= GO optrelexpr HEADING expr */ 136, /* (48) attribute ::= GO optrelexpr EDGEPT */ 136, /* (49) attribute ::= AT position */ 136, /* (50) attribute ::= SAME */ 136, /* (51) attribute ::= SAME AS object */ 136, /* (52) attribute ::= STRING textposition */ 136, /* (53) attribute ::= FIT */ 136, /* (54) attribute ::= BEHIND object */ 139, /* (55) withclause ::= DOT_E edge AT position */ 139, /* (56) withclause ::= edge AT position */ 111, /* (57) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */ 138, /* (58) boolproperty ::= CW */ 138, /* (59) boolproperty ::= CCW */ 138, /* (60) boolproperty ::= LARROW */ 138, /* (61) boolproperty ::= RARROW */ 138, /* (62) boolproperty ::= LRARROW */ 138, /* (63) boolproperty ::= INVIS */ 138, /* (64) boolproperty ::= THICK */ 138, /* (65) boolproperty ::= THIN */ 138, /* (66) boolproperty ::= SOLID */ 122, /* (67) textposition ::= */ 122, /* (68) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */ 117, /* (69) position ::= expr COMMA expr */ 117, /* (70) position ::= place PLUS expr COMMA expr */ 117, /* (71) position ::= place MINUS expr COMMA expr */ 117, /* (72) position ::= place PLUS LP expr COMMA expr RP */ 117, /* (73) position ::= place MINUS LP expr COMMA expr RP */ 117, /* (74) position ::= LP position COMMA position RP */ 117, /* (75) position ::= LP position RP */ 117, /* (76) position ::= expr between position AND position */ 117, /* (77) position ::= expr LT position COMMA position GT */ 117, /* (78) position ::= expr ABOVE position */ 117, /* (79) position ::= expr BELOW position */ 117, /* (80) position ::= expr LEFT OF position */ 117, /* (81) position ::= expr RIGHT OF position */ 117, /* (82) position ::= expr ON HEADING EDGEPT OF position */ 117, /* (83) position ::= expr HEADING EDGEPT OF position */ 117, /* (84) position ::= expr EDGEPT OF position */ 117, /* (85) position ::= expr ON HEADING expr FROM position */ 117, /* (86) position ::= expr HEADING expr FROM position */ 118, /* (87) place ::= edge OF object */ 141, /* (88) place2 ::= object */ 141, /* (89) place2 ::= object DOT_E edge */ 141, /* (90) place2 ::= NTH VERTEX OF object */ 119, /* (91) object ::= nth */ 119, /* (92) object ::= nth OF|IN object */ 120, /* (93) objectname ::= THIS */ 120, /* (94) objectname ::= PLACENAME */ 120, /* (95) objectname ::= objectname DOT_U PLACENAME */ 121, /* (96) nth ::= NTH CLASSNAME */ 121, /* (97) nth ::= NTH LAST CLASSNAME */ 121, /* (98) nth ::= LAST CLASSNAME */ 121, /* (99) nth ::= LAST */ 121, /* (100) nth ::= NTH LB RB */ 121, /* (101) nth ::= NTH LAST LB RB */ 121, /* (102) nth ::= LAST LB RB */ 110, /* (103) expr ::= expr PLUS expr */ 110, /* (104) expr ::= expr MINUS expr */ 110, /* (105) expr ::= expr STAR expr */ 110, /* (106) expr ::= expr SLASH expr */ 110, /* (107) expr ::= MINUS expr */ 110, /* (108) expr ::= PLUS expr */ 110, /* (109) expr ::= LP expr RP */ 110, /* (110) expr ::= LP FILL|COLOR|TEXTCOLOR|THICKNESS RP */ 110, /* (111) expr ::= NUMBER */ 110, /* (112) expr ::= ID */ 110, /* (113) expr ::= FUNC1 LP expr RP */ 110, /* (114) expr ::= FUNC2 LP expr COMMA expr RP */ 110, /* (115) expr ::= DIST LP position COMMA position RP */ 110, /* (116) expr ::= place2 DOT_XY X */ 110, /* (117) expr ::= place2 DOT_XY Y */ 110, /* (118) expr ::= object DOT_L numproperty */ 110, /* (119) expr ::= object DOT_L dashproperty */ 110, /* (120) expr ::= object DOT_L colorproperty */ 124, /* (121) lvalue ::= ID */ 124, /* (122) lvalue ::= FILL */ 124, /* (123) lvalue ::= COLOR */ 124, /* (124) lvalue ::= TEXTCOLOR */ 124, /* (125) lvalue ::= THICKNESS */ 123, /* (126) rvalue ::= expr */ 129, /* (127) print ::= PRINT */ 130, /* (128) prlist ::= pritem */ 130, /* (129) prlist ::= prlist prsep pritem */ 113, /* (130) direction ::= UP */ 113, /* (131) direction ::= DOWN */ 113, /* (132) direction ::= LEFT */ 113, /* (133) direction ::= RIGHT */ 127, /* (134) optrelexpr ::= relexpr */ 133, /* (135) attribute_list ::= alist */ 135, /* (136) alist ::= */ 135, /* (137) alist ::= alist attribute */ 136, /* (138) attribute ::= boolproperty */ 136, /* (139) attribute ::= WITH withclause */ 137, /* (140) go ::= GO */ 137, /* (141) go ::= */ 125, /* (142) even ::= UNTIL EVEN WITH */ 125, /* (143) even ::= EVEN WITH */ 114, /* (144) dashproperty ::= DOTTED */ 114, /* (145) dashproperty ::= DASHED */ 115, /* (146) colorproperty ::= FILL */ 115, /* (147) colorproperty ::= COLOR */ 115, /* (148) colorproperty ::= TEXTCOLOR */ 117, /* (149) position ::= place */ 140, /* (150) between ::= WAY BETWEEN */ 140, /* (151) between ::= BETWEEN */ 140, /* (152) between ::= OF THE WAY BETWEEN */ 118, /* (153) place ::= place2 */ 112, /* (154) edge ::= CENTER */ 112, /* (155) edge ::= EDGEPT */ 112, /* (156) edge ::= TOP */ 112, /* (157) edge ::= BOTTOM */ 112, /* (158) edge ::= START */ 112, /* (159) edge ::= END */ 112, /* (160) edge ::= RIGHT */ 112, /* (161) edge ::= LEFT */ 119, /* (162) object ::= objectname */ }; /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number ** of symbols on the right-hand side of that rule. */ static const signed char yyRuleInfoNRhs[] = { -1, /* (0) document ::= statement_list */ -1, /* (1) statement_list ::= statement */ -3, /* (2) statement_list ::= statement_list EOL statement */ 0, /* (3) statement ::= */ -1, /* (4) statement ::= direction */ -3, /* (5) statement ::= lvalue ASSIGN rvalue */ -3, /* (6) statement ::= PLACENAME COLON unnamed_statement */ -3, /* (7) statement ::= PLACENAME COLON position */ -2, /* (8) statement ::= TITLE STRING */ -2, /* (9) statement ::= LABEL STRING */ -2, /* (10) statement ::= DESCRIBE STRING */ -1, /* (11) statement ::= unnamed_statement */ -2, /* (12) statement ::= print prlist */ -6, /* (13) statement ::= ASSERT LP expr EQ expr RP */ -6, /* (14) statement ::= ASSERT LP position EQ position RP */ -3, /* (15) statement ::= DEFINE ID CODEBLOCK */ -1, /* (16) rvalue ::= PLACENAME */ -1, /* (17) pritem ::= FILL */ -1, /* (18) pritem ::= COLOR */ -1, /* (19) pritem ::= THICKNESS */ -1, /* (20) pritem ::= rvalue */ -1, /* (21) pritem ::= STRING */ -1, /* (22) prsep ::= COMMA */ -2, /* (23) unnamed_statement ::= basetype attribute_list */ -1, /* (24) basetype ::= CLASSNAME */ -2, /* (25) basetype ::= STRING textposition */ -4, /* (26) basetype ::= LB savelist statement_list RB */ 0, /* (27) savelist ::= */ -1, /* (28) relexpr ::= expr */ -2, /* (29) relexpr ::= expr PERCENT */ 0, /* (30) optrelexpr ::= */ -2, /* (31) attribute_list ::= relexpr alist */ -2, /* (32) attribute ::= numproperty relexpr */ -2, /* (33) attribute ::= dashproperty expr */ -1, /* (34) attribute ::= dashproperty */ -2, /* (35) attribute ::= colorproperty rvalue */ -3, /* (36) attribute ::= go direction optrelexpr */ -4, /* (37) attribute ::= go direction even position */ -2, /* (38) attribute ::= CLASS ID */ -2, /* (39) attribute ::= CLASS XML_CLASSES */ -1, /* (40) attribute ::= CLOSE */ -1, /* (41) attribute ::= CHOP */ -2, /* (42) attribute ::= FROM position */ -2, /* (43) attribute ::= TO position */ -1, /* (44) attribute ::= THEN */ -4, /* (45) attribute ::= THEN optrelexpr HEADING expr */ -3, /* (46) attribute ::= THEN optrelexpr EDGEPT */ -4, /* (47) attribute ::= GO optrelexpr HEADING expr */ -3, /* (48) attribute ::= GO optrelexpr EDGEPT */ -2, /* (49) attribute ::= AT position */ -1, /* (50) attribute ::= SAME */ -3, /* (51) attribute ::= SAME AS object */ -2, /* (52) attribute ::= STRING textposition */ -1, /* (53) attribute ::= FIT */ -2, /* (54) attribute ::= BEHIND object */ -4, /* (55) withclause ::= DOT_E edge AT position */ -3, /* (56) withclause ::= edge AT position */ -1, /* (57) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */ -1, /* (58) boolproperty ::= CW */ -1, /* (59) boolproperty ::= CCW */ -1, /* (60) boolproperty ::= LARROW */ -1, /* (61) boolproperty ::= RARROW */ -1, /* (62) boolproperty ::= LRARROW */ -1, /* (63) boolproperty ::= INVIS */ -1, /* (64) boolproperty ::= THICK */ -1, /* (65) boolproperty ::= THIN */ -1, /* (66) boolproperty ::= SOLID */ 0, /* (67) textposition ::= */ -2, /* (68) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */ -3, /* (69) position ::= expr COMMA expr */ -5, /* (70) position ::= place PLUS expr COMMA expr */ -5, /* (71) position ::= place MINUS expr COMMA expr */ -7, /* (72) position ::= place PLUS LP expr COMMA expr RP */ -7, /* (73) position ::= place MINUS LP expr COMMA expr RP */ -5, /* (74) position ::= LP position COMMA position RP */ -3, /* (75) position ::= LP position RP */ -5, /* (76) position ::= expr between position AND position */ -6, /* (77) position ::= expr LT position COMMA position GT */ -3, /* (78) position ::= expr ABOVE position */ -3, /* (79) position ::= expr BELOW position */ -4, /* (80) position ::= expr LEFT OF position */ -4, /* (81) position ::= expr RIGHT OF position */ -6, /* (82) position ::= expr ON HEADING EDGEPT OF position */ -5, /* (83) position ::= expr HEADING EDGEPT OF position */ -4, /* (84) position ::= expr EDGEPT OF position */ -6, /* (85) position ::= expr ON HEADING expr FROM position */ -5, /* (86) position ::= expr HEADING expr FROM position */ -3, /* (87) place ::= edge OF object */ -1, /* (88) place2 ::= object */ -3, /* (89) place2 ::= object DOT_E edge */ -4, /* (90) place2 ::= NTH VERTEX OF object */ -1, /* (91) object ::= nth */ -3, /* (92) object ::= nth OF|IN object */ -1, /* (93) objectname ::= THIS */ -1, /* (94) objectname ::= PLACENAME */ -3, /* (95) objectname ::= objectname DOT_U PLACENAME */ -2, /* (96) nth ::= NTH CLASSNAME */ -3, /* (97) nth ::= NTH LAST CLASSNAME */ -2, /* (98) nth ::= LAST CLASSNAME */ -1, /* (99) nth ::= LAST */ -3, /* (100) nth ::= NTH LB RB */ -4, /* (101) nth ::= NTH LAST LB RB */ -3, /* (102) nth ::= LAST LB RB */ -3, /* (103) expr ::= expr PLUS expr */ -3, /* (104) expr ::= expr MINUS expr */ -3, /* (105) expr ::= expr STAR expr */ -3, /* (106) expr ::= expr SLASH expr */ -2, /* (107) expr ::= MINUS expr */ -2, /* (108) expr ::= PLUS expr */ -3, /* (109) expr ::= LP expr RP */ -3, /* (110) expr ::= LP FILL|COLOR|TEXTCOLOR|THICKNESS RP */ -1, /* (111) expr ::= NUMBER */ -1, /* (112) expr ::= ID */ -4, /* (113) expr ::= FUNC1 LP expr RP */ -6, /* (114) expr ::= FUNC2 LP expr COMMA expr RP */ -6, /* (115) expr ::= DIST LP position COMMA position RP */ -3, /* (116) expr ::= place2 DOT_XY X */ -3, /* (117) expr ::= place2 DOT_XY Y */ -3, /* (118) expr ::= object DOT_L numproperty */ -3, /* (119) expr ::= object DOT_L dashproperty */ -3, /* (120) expr ::= object DOT_L colorproperty */ -1, /* (121) lvalue ::= ID */ -1, /* (122) lvalue ::= FILL */ -1, /* (123) lvalue ::= COLOR */ -1, /* (124) lvalue ::= TEXTCOLOR */ -1, /* (125) lvalue ::= THICKNESS */ -1, /* (126) rvalue ::= expr */ -1, /* (127) print ::= PRINT */ -1, /* (128) prlist ::= pritem */ -3, /* (129) prlist ::= prlist prsep pritem */ -1, /* (130) direction ::= UP */ -1, /* (131) direction ::= DOWN */ -1, /* (132) direction ::= LEFT */ -1, /* (133) direction ::= RIGHT */ -1, /* (134) optrelexpr ::= relexpr */ -1, /* (135) attribute_list ::= alist */ 0, /* (136) alist ::= */ -2, /* (137) alist ::= alist attribute */ -1, /* (138) attribute ::= boolproperty */ -2, /* (139) attribute ::= WITH withclause */ -1, /* (140) go ::= GO */ 0, /* (141) go ::= */ -3, /* (142) even ::= UNTIL EVEN WITH */ -2, /* (143) even ::= EVEN WITH */ -1, /* (144) dashproperty ::= DOTTED */ -1, /* (145) dashproperty ::= DASHED */ -1, /* (146) colorproperty ::= FILL */ -1, /* (147) colorproperty ::= COLOR */ -1, /* (148) colorproperty ::= TEXTCOLOR */ -1, /* (149) position ::= place */ -2, /* (150) between ::= WAY BETWEEN */ -1, /* (151) between ::= BETWEEN */ -4, /* (152) between ::= OF THE WAY BETWEEN */ -1, /* (153) place ::= place2 */ -1, /* (154) edge ::= CENTER */ -1, /* (155) edge ::= EDGEPT */ -1, /* (156) edge ::= TOP */ -1, /* (157) edge ::= BOTTOM */ -1, /* (158) edge ::= START */ -1, /* (159) edge ::= END */ -1, /* (160) edge ::= RIGHT */ -1, /* (161) edge ::= LEFT */ -1, /* (162) object ::= objectname */ }; static void yy_accept(yyParser*); /* Forward Declaration */ /* ** Perform a reduce action and the shift that must immediately ** follow the reduce. ** ** The yyLookahead and yyLookaheadToken parameters provide reduce actions ** access to the lookahead token (if any). The yyLookahead will be YYNOCODE ** if the lookahead token has already been consumed. As this procedure is ** only called from one place, optimizing compilers will in-line it, which ** means that the extra parameters have no performance impact. */ static YYACTIONTYPE yy_reduce( yyParser *yypParser, /* The parser */ unsigned int yyruleno, /* Number of the rule by which to reduce */ int yyLookahead, /* Lookahead token, or YYNOCODE if none */ pik_parserTOKENTYPE yyLookaheadToken /* Value of the lookahead token */ pik_parserCTX_PDECL /* %extra_context */ ){ int yygoto; /* The next state */ YYACTIONTYPE yyact; /* The next action */ yyStackEntry *yymsp; /* The top of the parser's stack */ int yysize; /* Amount to pop the stack */ pik_parserARG_FETCH (void)yyLookahead; (void)yyLookaheadToken; yymsp = yypParser->yytos; switch( yyruleno ){ /* Beginning here are the reduction cases. A typical example ** follows: ** case 0: ** #line <lineno> <grammarfile> ** { ... } // User supplied code ** #line <lineno> <thisfile> ** break; */ /********** Begin reduce actions **********************************************/ YYMINORTYPE yylhsminor; case 0: /* document ::= statement_list */ #line 807 "pikchr.y" {pik_render(p,yymsp[0].minor.yy51);} #line 2744 "pikchr.c" break; case 1: /* statement_list ::= statement */ #line 810 "pikchr.y" { yylhsminor.yy51 = pik_elist_append(p,0,yymsp[0].minor.yy258); } #line 2749 "pikchr.c" yymsp[0].minor.yy51 = yylhsminor.yy51; break; case 2: /* statement_list ::= statement_list EOL statement */ #line 812 "pikchr.y" { yylhsminor.yy51 = pik_elist_append(p,yymsp[-2].minor.yy51,yymsp[0].minor.yy258); } #line 2755 "pikchr.c" yymsp[-2].minor.yy51 = yylhsminor.yy51; break; case 3: /* statement ::= */ #line 815 "pikchr.y" { yymsp[1].minor.yy258 = 0; } #line 2761 "pikchr.c" break; case 4: /* statement ::= direction */ #line 816 "pikchr.y" { pik_set_direction(p,yymsp[0].minor.yy0.eCode); yylhsminor.yy258=0; } #line 2766 "pikchr.c" yymsp[0].minor.yy258 = yylhsminor.yy258; break; case 5: /* statement ::= lvalue ASSIGN rvalue */ #line 817 "pikchr.y" {pik_set_var(p,&yymsp[-2].minor.yy0,yymsp[0].minor.yy253,&yymsp[-1].minor.yy0); yylhsminor.yy258=0;} #line 2772 "pikchr.c" yymsp[-2].minor.yy258 = yylhsminor.yy258; break; case 6: /* statement ::= PLACENAME COLON unnamed_statement */ #line 819 "pikchr.y" { yylhsminor.yy258 = yymsp[0].minor.yy258; pik_elem_setname(p,yymsp[0].minor.yy258,&yymsp[-2].minor.yy0); } #line 2778 "pikchr.c" yymsp[-2].minor.yy258 = yylhsminor.yy258; break; case 7: /* statement ::= PLACENAME COLON position */ #line 821 "pikchr.y" { yylhsminor.yy258 = pik_elem_new(p,0,0,0); if(yylhsminor.yy258){ yylhsminor.yy258->ptAt = yymsp[0].minor.yy67; pik_elem_setname(p,yylhsminor.yy258,&yymsp[-2].minor.yy0); }} #line 2785 "pikchr.c" yymsp[-2].minor.yy258 = yylhsminor.yy258; break; case 8: /* statement ::= TITLE STRING */ #line 823 "pikchr.y" { pik_set_title(p, &yymsp[0].minor.yy0, 0); yymsp[-1].minor.yy258=0; } #line 2791 "pikchr.c" break; case 9: /* statement ::= LABEL STRING */ #line 824 "pikchr.y" { pik_set_title(p, &yymsp[0].minor.yy0, 1); yymsp[-1].minor.yy258=0; } #line 2796 "pikchr.c" break; case 10: /* statement ::= DESCRIBE STRING */ #line 825 "pikchr.y" { pik_set_description(p, &yymsp[0].minor.yy0); yymsp[-1].minor.yy258=0; } #line 2801 "pikchr.c" break; case 11: /* statement ::= unnamed_statement */ #line 826 "pikchr.y" {yylhsminor.yy258 = yymsp[0].minor.yy258;} #line 2806 "pikchr.c" yymsp[0].minor.yy258 = yylhsminor.yy258; break; case 12: /* statement ::= print prlist */ #line 827 "pikchr.y" {pik_append(p,"<br>\n",5); yymsp[-1].minor.yy258=0;} #line 2812 "pikchr.c" break; case 13: /* statement ::= ASSERT LP expr EQ expr RP */ #line 832 "pikchr.y" {yymsp[-5].minor.yy258=pik_assert(p,yymsp[-3].minor.yy253,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy253);} #line 2817 "pikchr.c" break; case 14: /* statement ::= ASSERT LP position EQ position RP */ #line 834 "pikchr.y" {yymsp[-5].minor.yy258=pik_position_assert(p,&yymsp[-3].minor.yy67,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy67);} #line 2822 "pikchr.c" break; case 15: /* statement ::= DEFINE ID CODEBLOCK */ #line 835 "pikchr.y" {yymsp[-2].minor.yy258=0; pik_add_macro(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);} #line 2827 "pikchr.c" break; case 16: /* rvalue ::= PLACENAME */ #line 847 "pikchr.y" {yylhsminor.yy253 = pik_lookup_color(p,&yymsp[0].minor.yy0);} #line 2832 "pikchr.c" yymsp[0].minor.yy253 = yylhsminor.yy253; break; case 17: /* pritem ::= FILL */ case 18: /* pritem ::= COLOR */ yytestcase(yyruleno==18); case 19: /* pritem ::= THICKNESS */ yytestcase(yyruleno==19); #line 852 "pikchr.y" {pik_append_num(p,"",pik_value(p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.n,0));} #line 2840 "pikchr.c" break; case 20: /* pritem ::= rvalue */ #line 855 "pikchr.y" {pik_append_num(p,"",yymsp[0].minor.yy253);} #line 2845 "pikchr.c" break; case 21: /* pritem ::= STRING */ #line 856 "pikchr.y" {pik_append_text(p,yymsp[0].minor.yy0.z+1,yymsp[0].minor.yy0.n-2,0);} #line 2850 "pikchr.c" break; case 22: /* prsep ::= COMMA */ #line 857 "pikchr.y" {pik_append(p, " ", 1);} #line 2855 "pikchr.c" break; case 23: /* unnamed_statement ::= basetype attribute_list */ #line 860 "pikchr.y" {yylhsminor.yy258 = yymsp[-1].minor.yy258; pik_after_adding_attributes(p,yylhsminor.yy258);} #line 2860 "pikchr.c" yymsp[-1].minor.yy258 = yylhsminor.yy258; break; case 24: /* basetype ::= CLASSNAME */ #line 862 "pikchr.y" {yylhsminor.yy258 = pik_elem_new(p,&yymsp[0].minor.yy0,0,0); } #line 2866 "pikchr.c" yymsp[0].minor.yy258 = yylhsminor.yy258; break; case 25: /* basetype ::= STRING textposition */ #line 864 "pikchr.y" {yymsp[-1].minor.yy0.eCode = yymsp[0].minor.yy88; yylhsminor.yy258 = pik_elem_new(p,0,&yymsp[-1].minor.yy0,0); } #line 2872 "pikchr.c" yymsp[-1].minor.yy258 = yylhsminor.yy258; break; case 26: /* basetype ::= LB savelist statement_list RB */ #line 866 "pikchr.y" { p->list = yymsp[-2].minor.yy51; yymsp[-3].minor.yy258 = pik_elem_new(p,0,0,yymsp[-1].minor.yy51); if(yymsp[-3].minor.yy258) yymsp[-3].minor.yy258->errTok = yymsp[0].minor.yy0; } #line 2878 "pikchr.c" break; case 27: /* savelist ::= */ #line 871 "pikchr.y" {yymsp[1].minor.yy51 = p->list; p->list = 0;} #line 2883 "pikchr.c" break; case 28: /* relexpr ::= expr */ #line 878 "pikchr.y" {yylhsminor.yy132.rAbs = yymsp[0].minor.yy253; yylhsminor.yy132.rRel = 0;} #line 2888 "pikchr.c" yymsp[0].minor.yy132 = yylhsminor.yy132; break; case 29: /* relexpr ::= expr PERCENT */ #line 879 "pikchr.y" {yylhsminor.yy132.rAbs = 0; yylhsminor.yy132.rRel = yymsp[-1].minor.yy253/100;} #line 2894 "pikchr.c" yymsp[-1].minor.yy132 = yylhsminor.yy132; break; case 30: /* optrelexpr ::= */ #line 881 "pikchr.y" {yymsp[1].minor.yy132.rAbs = 0; yymsp[1].minor.yy132.rRel = 1.0;} #line 2900 "pikchr.c" break; case 31: /* attribute_list ::= relexpr alist */ #line 883 "pikchr.y" {pik_add_direction(p,0,&yymsp[-1].minor.yy132);} #line 2905 "pikchr.c" break; case 32: /* attribute ::= numproperty relexpr */ #line 887 "pikchr.y" { pik_set_numprop(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy132); } #line 2910 "pikchr.c" break; case 33: /* attribute ::= dashproperty expr */ #line 888 "pikchr.y" { pik_set_dashed(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy253); } #line 2915 "pikchr.c" break; case 34: /* attribute ::= dashproperty */ #line 889 "pikchr.y" { pik_set_dashed(p,&yymsp[0].minor.yy0,0); } #line 2920 "pikchr.c" break; case 35: /* attribute ::= colorproperty rvalue */ #line 890 "pikchr.y" { pik_set_clrprop(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy253); } #line 2925 "pikchr.c" break; case 36: /* attribute ::= go direction optrelexpr */ #line 891 "pikchr.y" { pik_add_direction(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy132);} #line 2930 "pikchr.c" break; case 37: /* attribute ::= go direction even position */ #line 892 "pikchr.y" {pik_evenwith(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy67);} #line 2935 "pikchr.c" break; case 38: /* attribute ::= CLASS ID */ #line 893 "pikchr.y" {pik_set_xml_class(p, &yymsp[0].minor.yy0);} #line 2940 "pikchr.c" break; case 39: /* attribute ::= CLASS XML_CLASSES */ #line 894 "pikchr.y" {pik_set_xml_classes(p, &yymsp[0].minor.yy0);} #line 2945 "pikchr.c" break; case 40: /* attribute ::= CLOSE */ #line 895 "pikchr.y" { pik_close_path(p,&yymsp[0].minor.yy0); } #line 2950 "pikchr.c" break; case 41: /* attribute ::= CHOP */ #line 896 "pikchr.y" { p->cur->bChop = 1; } #line 2955 "pikchr.c" break; case 42: /* attribute ::= FROM position */ #line 897 "pikchr.y" { pik_set_from(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy67); } #line 2960 "pikchr.c" break; case 43: /* attribute ::= TO position */ #line 898 "pikchr.y" { pik_add_to(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy67); } #line 2965 "pikchr.c" break; case 44: /* attribute ::= THEN */ #line 899 "pikchr.y" { pik_then(p, &yymsp[0].minor.yy0, p->cur); } #line 2970 "pikchr.c" break; case 45: /* attribute ::= THEN optrelexpr HEADING expr */ case 47: /* attribute ::= GO optrelexpr HEADING expr */ yytestcase(yyruleno==47); #line 901 "pikchr.y" {pik_move_hdg(p,&yymsp[-2].minor.yy132,&yymsp[-1].minor.yy0,yymsp[0].minor.yy253,0,&yymsp[-3].minor.yy0);} #line 2976 "pikchr.c" break; case 46: /* attribute ::= THEN optrelexpr EDGEPT */ case 48: /* attribute ::= GO optrelexpr EDGEPT */ yytestcase(yyruleno==48); #line 902 "pikchr.y" {pik_move_hdg(p,&yymsp[-1].minor.yy132,0,0,&yymsp[0].minor.yy0,&yymsp[-2].minor.yy0);} #line 2982 "pikchr.c" break; case 49: /* attribute ::= AT position */ #line 907 "pikchr.y" { pik_set_at(p,0,&yymsp[0].minor.yy67,&yymsp[-1].minor.yy0); } #line 2987 "pikchr.c" break; case 50: /* attribute ::= SAME */ #line 909 "pikchr.y" {pik_same(p,0,&yymsp[0].minor.yy0);} #line 2992 "pikchr.c" break; case 51: /* attribute ::= SAME AS object */ #line 910 "pikchr.y" {pik_same(p,yymsp[0].minor.yy258,&yymsp[-2].minor.yy0);} #line 2997 "pikchr.c" break; case 52: /* attribute ::= STRING textposition */ #line 911 "pikchr.y" {pik_add_txt(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy88);} #line 3002 "pikchr.c" break; case 53: /* attribute ::= FIT */ #line 912 "pikchr.y" {pik_size_to_fit(p,&yymsp[0].minor.yy0,3); } #line 3007 "pikchr.c" break; case 54: /* attribute ::= BEHIND object */ #line 913 "pikchr.y" {pik_behind(p,yymsp[0].minor.yy258);} #line 3012 "pikchr.c" break; case 55: /* withclause ::= DOT_E edge AT position */ case 56: /* withclause ::= edge AT position */ yytestcase(yyruleno==56); #line 921 "pikchr.y" { pik_set_at(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy67,&yymsp[-1].minor.yy0); } #line 3018 "pikchr.c" break; case 57: /* numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */ #line 925 "pikchr.y" {yylhsminor.yy0 = yymsp[0].minor.yy0;} #line 3023 "pikchr.c" yymsp[0].minor.yy0 = yylhsminor.yy0; break; case 58: /* boolproperty ::= CW */ #line 937 "pikchr.y" {p->cur->cw = 1;} #line 3029 "pikchr.c" break; case 59: /* boolproperty ::= CCW */ #line 938 "pikchr.y" {p->cur->cw = 0;} #line 3034 "pikchr.c" break; case 60: /* boolproperty ::= LARROW */ #line 939 "pikchr.y" {p->cur->larrow=1; p->cur->rarrow=0; } #line 3039 "pikchr.c" break; case 61: /* boolproperty ::= RARROW */ #line 940 "pikchr.y" {p->cur->larrow=0; p->cur->rarrow=1; } #line 3044 "pikchr.c" break; case 62: /* boolproperty ::= LRARROW */ #line 941 "pikchr.y" {p->cur->larrow=1; p->cur->rarrow=1; } #line 3049 "pikchr.c" break; case 63: /* boolproperty ::= INVIS */ #line 942 "pikchr.y" {p->cur->sw = -0.00001;} #line 3054 "pikchr.c" break; case 64: /* boolproperty ::= THICK */ #line 943 "pikchr.y" {p->cur->sw *= 1.5;} #line 3059 "pikchr.c" break; case 65: /* boolproperty ::= THIN */ #line 944 "pikchr.y" {p->cur->sw *= 0.67;} #line 3064 "pikchr.c" break; case 66: /* boolproperty ::= SOLID */ #line 945 "pikchr.y" {p->cur->sw = pik_value(p,"thickness",9,0); p->cur->dotted = p->cur->dashed = 0.0;} #line 3070 "pikchr.c" break; case 67: /* textposition ::= */ #line 948 "pikchr.y" {yymsp[1].minor.yy88 = 0;} #line 3075 "pikchr.c" break; case 68: /* textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */ #line 951 "pikchr.y" {yylhsminor.yy88 = (short int)pik_text_position(yymsp[-1].minor.yy88,&yymsp[0].minor.yy0);} #line 3080 "pikchr.c" yymsp[-1].minor.yy88 = yylhsminor.yy88; break; case 69: /* position ::= expr COMMA expr */ #line 954 "pikchr.y" {yylhsminor.yy67.x=yymsp[-2].minor.yy253; yylhsminor.yy67.y=yymsp[0].minor.yy253;} #line 3086 "pikchr.c" yymsp[-2].minor.yy67 = yylhsminor.yy67; break; case 70: /* position ::= place PLUS expr COMMA expr */ #line 956 "pikchr.y" {yylhsminor.yy67.x=yymsp[-4].minor.yy67.x+yymsp[-2].minor.yy253; yylhsminor.yy67.y=yymsp[-4].minor.yy67.y+yymsp[0].minor.yy253;} #line 3092 "pikchr.c" yymsp[-4].minor.yy67 = yylhsminor.yy67; break; case 71: /* position ::= place MINUS expr COMMA expr */ #line 957 "pikchr.y" {yylhsminor.yy67.x=yymsp[-4].minor.yy67.x-yymsp[-2].minor.yy253; yylhsminor.yy67.y=yymsp[-4].minor.yy67.y-yymsp[0].minor.yy253;} #line 3098 "pikchr.c" yymsp[-4].minor.yy67 = yylhsminor.yy67; break; case 72: /* position ::= place PLUS LP expr COMMA expr RP */ #line 959 "pikchr.y" {yylhsminor.yy67.x=yymsp[-6].minor.yy67.x+yymsp[-3].minor.yy253; yylhsminor.yy67.y=yymsp[-6].minor.yy67.y+yymsp[-1].minor.yy253;} #line 3104 "pikchr.c" yymsp[-6].minor.yy67 = yylhsminor.yy67; break; case 73: /* position ::= place MINUS LP expr COMMA expr RP */ #line 961 "pikchr.y" {yylhsminor.yy67.x=yymsp[-6].minor.yy67.x-yymsp[-3].minor.yy253; yylhsminor.yy67.y=yymsp[-6].minor.yy67.y-yymsp[-1].minor.yy253;} #line 3110 "pikchr.c" yymsp[-6].minor.yy67 = yylhsminor.yy67; break; case 74: /* position ::= LP position COMMA position RP */ #line 962 "pikchr.y" {yymsp[-4].minor.yy67.x=yymsp[-3].minor.yy67.x; yymsp[-4].minor.yy67.y=yymsp[-1].minor.yy67.y;} #line 3116 "pikchr.c" break; case 75: /* position ::= LP position RP */ #line 963 "pikchr.y" {yymsp[-2].minor.yy67=yymsp[-1].minor.yy67;} #line 3121 "pikchr.c" break; case 76: /* position ::= expr between position AND position */ #line 965 "pikchr.y" {yylhsminor.yy67 = pik_position_between(yymsp[-4].minor.yy253,yymsp[-2].minor.yy67,yymsp[0].minor.yy67);} #line 3126 "pikchr.c" yymsp[-4].minor.yy67 = yylhsminor.yy67; break; case 77: /* position ::= expr LT position COMMA position GT */ #line 967 "pikchr.y" {yylhsminor.yy67 = pik_position_between(yymsp[-5].minor.yy253,yymsp[-3].minor.yy67,yymsp[-1].minor.yy67);} #line 3132 "pikchr.c" yymsp[-5].minor.yy67 = yylhsminor.yy67; break; case 78: /* position ::= expr ABOVE position */ #line 968 "pikchr.y" {yylhsminor.yy67=yymsp[0].minor.yy67; yylhsminor.yy67.y += yymsp[-2].minor.yy253;} #line 3138 "pikchr.c" yymsp[-2].minor.yy67 = yylhsminor.yy67; break; case 79: /* position ::= expr BELOW position */ #line 969 "pikchr.y" {yylhsminor.yy67=yymsp[0].minor.yy67; yylhsminor.yy67.y -= yymsp[-2].minor.yy253;} #line 3144 "pikchr.c" yymsp[-2].minor.yy67 = yylhsminor.yy67; break; case 80: /* position ::= expr LEFT OF position */ #line 970 "pikchr.y" {yylhsminor.yy67=yymsp[0].minor.yy67; yylhsminor.yy67.x -= yymsp[-3].minor.yy253;} #line 3150 "pikchr.c" yymsp[-3].minor.yy67 = yylhsminor.yy67; break; case 81: /* position ::= expr RIGHT OF position */ #line 971 "pikchr.y" {yylhsminor.yy67=yymsp[0].minor.yy67; yylhsminor.yy67.x += yymsp[-3].minor.yy253;} #line 3156 "pikchr.c" yymsp[-3].minor.yy67 = yylhsminor.yy67; break; case 82: /* position ::= expr ON HEADING EDGEPT OF position */ #line 973 "pikchr.y" {yylhsminor.yy67 = pik_position_at_hdg(yymsp[-5].minor.yy253,&yymsp[-2].minor.yy0,yymsp[0].minor.yy67);} #line 3162 "pikchr.c" yymsp[-5].minor.yy67 = yylhsminor.yy67; break; case 83: /* position ::= expr HEADING EDGEPT OF position */ #line 975 "pikchr.y" {yylhsminor.yy67 = pik_position_at_hdg(yymsp[-4].minor.yy253,&yymsp[-2].minor.yy0,yymsp[0].minor.yy67);} #line 3168 "pikchr.c" yymsp[-4].minor.yy67 = yylhsminor.yy67; break; case 84: /* position ::= expr EDGEPT OF position */ #line 977 "pikchr.y" {yylhsminor.yy67 = pik_position_at_hdg(yymsp[-3].minor.yy253,&yymsp[-2].minor.yy0,yymsp[0].minor.yy67);} #line 3174 "pikchr.c" yymsp[-3].minor.yy67 = yylhsminor.yy67; break; case 85: /* position ::= expr ON HEADING expr FROM position */ #line 979 "pikchr.y" {yylhsminor.yy67 = pik_position_at_angle(yymsp[-5].minor.yy253,yymsp[-2].minor.yy253,yymsp[0].minor.yy67);} #line 3180 "pikchr.c" yymsp[-5].minor.yy67 = yylhsminor.yy67; break; case 86: /* position ::= expr HEADING expr FROM position */ #line 981 "pikchr.y" {yylhsminor.yy67 = pik_position_at_angle(yymsp[-4].minor.yy253,yymsp[-2].minor.yy253,yymsp[0].minor.yy67);} #line 3186 "pikchr.c" yymsp[-4].minor.yy67 = yylhsminor.yy67; break; case 87: /* place ::= edge OF object */ #line 993 "pikchr.y" {yylhsminor.yy67 = pik_place_of_elem(p,yymsp[0].minor.yy258,&yymsp[-2].minor.yy0);} #line 3192 "pikchr.c" yymsp[-2].minor.yy67 = yylhsminor.yy67; break; case 88: /* place2 ::= object */ #line 994 "pikchr.y" {yylhsminor.yy67 = pik_place_of_elem(p,yymsp[0].minor.yy258,0);} #line 3198 "pikchr.c" yymsp[0].minor.yy67 = yylhsminor.yy67; break; case 89: /* place2 ::= object DOT_E edge */ #line 995 "pikchr.y" {yylhsminor.yy67 = pik_place_of_elem(p,yymsp[-2].minor.yy258,&yymsp[0].minor.yy0);} #line 3204 "pikchr.c" yymsp[-2].minor.yy67 = yylhsminor.yy67; break; case 90: /* place2 ::= NTH VERTEX OF object */ #line 996 "pikchr.y" {yylhsminor.yy67 = pik_nth_vertex(p,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,yymsp[0].minor.yy258);} #line 3210 "pikchr.c" yymsp[-3].minor.yy67 = yylhsminor.yy67; break; case 91: /* object ::= nth */ #line 1008 "pikchr.y" {yylhsminor.yy258 = pik_find_nth(p,0,&yymsp[0].minor.yy0);} #line 3216 "pikchr.c" yymsp[0].minor.yy258 = yylhsminor.yy258; break; case 92: /* object ::= nth OF|IN object */ #line 1009 "pikchr.y" {yylhsminor.yy258 = pik_find_nth(p,yymsp[0].minor.yy258,&yymsp[-2].minor.yy0);} #line 3222 "pikchr.c" yymsp[-2].minor.yy258 = yylhsminor.yy258; break; case 93: /* objectname ::= THIS */ #line 1011 "pikchr.y" {yymsp[0].minor.yy258 = p->cur;} #line 3228 "pikchr.c" break; case 94: /* objectname ::= PLACENAME */ #line 1012 "pikchr.y" {yylhsminor.yy258 = pik_find_byname(p,0,&yymsp[0].minor.yy0);} #line 3233 "pikchr.c" yymsp[0].minor.yy258 = yylhsminor.yy258; break; case 95: /* objectname ::= objectname DOT_U PLACENAME */ #line 1014 "pikchr.y" {yylhsminor.yy258 = pik_find_byname(p,yymsp[-2].minor.yy258,&yymsp[0].minor.yy0);} #line 3239 "pikchr.c" yymsp[-2].minor.yy258 = yylhsminor.yy258; break; case 96: /* nth ::= NTH CLASSNAME */ #line 1016 "pikchr.y" {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-1].minor.yy0); } #line 3245 "pikchr.c" yymsp[-1].minor.yy0 = yylhsminor.yy0; break; case 97: /* nth ::= NTH LAST CLASSNAME */ #line 1017 "pikchr.y" {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-2].minor.yy0); } #line 3251 "pikchr.c" yymsp[-2].minor.yy0 = yylhsminor.yy0; break; case 98: /* nth ::= LAST CLASSNAME */ #line 1018 "pikchr.y" {yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.eCode = -1;} #line 3257 "pikchr.c" break; case 99: /* nth ::= LAST */ #line 1019 "pikchr.y" {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -1;} #line 3262 "pikchr.c" yymsp[0].minor.yy0 = yylhsminor.yy0; break; case 100: /* nth ::= NTH LB RB */ #line 1020 "pikchr.y" {yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-2].minor.yy0);} #line 3268 "pikchr.c" yymsp[-2].minor.yy0 = yylhsminor.yy0; break; case 101: /* nth ::= NTH LAST LB RB */ #line 1021 "pikchr.y" {yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-3].minor.yy0);} #line 3274 "pikchr.c" yymsp[-3].minor.yy0 = yylhsminor.yy0; break; case 102: /* nth ::= LAST LB RB */ #line 1022 "pikchr.y" {yymsp[-2].minor.yy0=yymsp[-1].minor.yy0; yymsp[-2].minor.yy0.eCode = -1; } #line 3280 "pikchr.c" break; case 103: /* expr ::= expr PLUS expr */ #line 1024 "pikchr.y" {yylhsminor.yy253=yymsp[-2].minor.yy253+yymsp[0].minor.yy253;} #line 3285 "pikchr.c" yymsp[-2].minor.yy253 = yylhsminor.yy253; break; case 104: /* expr ::= expr MINUS expr */ #line 1025 "pikchr.y" {yylhsminor.yy253=yymsp[-2].minor.yy253-yymsp[0].minor.yy253;} #line 3291 "pikchr.c" yymsp[-2].minor.yy253 = yylhsminor.yy253; break; case 105: /* expr ::= expr STAR expr */ #line 1026 "pikchr.y" {yylhsminor.yy253=yymsp[-2].minor.yy253*yymsp[0].minor.yy253;} #line 3297 "pikchr.c" yymsp[-2].minor.yy253 = yylhsminor.yy253; break; case 106: /* expr ::= expr SLASH expr */ #line 1027 "pikchr.y" { if( yymsp[0].minor.yy253==0.0 ){ pik_error(p, &yymsp[-1].minor.yy0, "division by zero"); yylhsminor.yy253 = 0.0; } else{ yylhsminor.yy253 = yymsp[-2].minor.yy253/yymsp[0].minor.yy253; } } #line 3306 "pikchr.c" yymsp[-2].minor.yy253 = yylhsminor.yy253; break; case 107: /* expr ::= MINUS expr */ #line 1031 "pikchr.y" {yymsp[-1].minor.yy253=-yymsp[0].minor.yy253;} #line 3312 "pikchr.c" break; case 108: /* expr ::= PLUS expr */ #line 1032 "pikchr.y" {yymsp[-1].minor.yy253=yymsp[0].minor.yy253;} #line 3317 "pikchr.c" break; case 109: /* expr ::= LP expr RP */ #line 1033 "pikchr.y" {yymsp[-2].minor.yy253=yymsp[-1].minor.yy253;} #line 3322 "pikchr.c" break; case 110: /* expr ::= LP FILL|COLOR|TEXTCOLOR|THICKNESS RP */ #line 1034 "pikchr.y" {yymsp[-2].minor.yy253=pik_get_var(p,&yymsp[-1].minor.yy0);} #line 3327 "pikchr.c" break; case 111: /* expr ::= NUMBER */ #line 1035 "pikchr.y" {yylhsminor.yy253=pik_atof(&yymsp[0].minor.yy0);} #line 3332 "pikchr.c" yymsp[0].minor.yy253 = yylhsminor.yy253; break; case 112: /* expr ::= ID */ #line 1036 "pikchr.y" {yylhsminor.yy253=pik_get_var(p,&yymsp[0].minor.yy0);} #line 3338 "pikchr.c" yymsp[0].minor.yy253 = yylhsminor.yy253; break; case 113: /* expr ::= FUNC1 LP expr RP */ #line 1037 "pikchr.y" {yylhsminor.yy253 = pik_func(p,&yymsp[-3].minor.yy0,yymsp[-1].minor.yy253,0.0);} #line 3344 "pikchr.c" yymsp[-3].minor.yy253 = yylhsminor.yy253; break; case 114: /* expr ::= FUNC2 LP expr COMMA expr RP */ #line 1038 "pikchr.y" {yylhsminor.yy253 = pik_func(p,&yymsp[-5].minor.yy0,yymsp[-3].minor.yy253,yymsp[-1].minor.yy253);} #line 3350 "pikchr.c" yymsp[-5].minor.yy253 = yylhsminor.yy253; break; case 115: /* expr ::= DIST LP position COMMA position RP */ #line 1039 "pikchr.y" {yymsp[-5].minor.yy253 = pik_dist(&yymsp[-3].minor.yy67,&yymsp[-1].minor.yy67);} #line 3356 "pikchr.c" break; case 116: /* expr ::= place2 DOT_XY X */ #line 1040 "pikchr.y" {yylhsminor.yy253 = yymsp[-2].minor.yy67.x;} #line 3361 "pikchr.c" yymsp[-2].minor.yy253 = yylhsminor.yy253; break; case 117: /* expr ::= place2 DOT_XY Y */ #line 1041 "pikchr.y" {yylhsminor.yy253 = yymsp[-2].minor.yy67.y;} #line 3367 "pikchr.c" yymsp[-2].minor.yy253 = yylhsminor.yy253; break; case 118: /* expr ::= object DOT_L numproperty */ case 119: /* expr ::= object DOT_L dashproperty */ yytestcase(yyruleno==119); case 120: /* expr ::= object DOT_L colorproperty */ yytestcase(yyruleno==120); #line 1042 "pikchr.y" {yylhsminor.yy253=pik_property_of(yymsp[-2].minor.yy258,&yymsp[0].minor.yy0);} #line 3375 "pikchr.c" yymsp[-2].minor.yy253 = yylhsminor.yy253; break; default: /* (121) lvalue ::= ID */ yytestcase(yyruleno==121); /* (122) lvalue ::= FILL */ yytestcase(yyruleno==122); /* (123) lvalue ::= COLOR */ yytestcase(yyruleno==123); /* (124) lvalue ::= TEXTCOLOR */ yytestcase(yyruleno==124); /* (125) lvalue ::= THICKNESS */ yytestcase(yyruleno==125); /* (126) rvalue ::= expr */ yytestcase(yyruleno==126); /* (127) print ::= PRINT */ yytestcase(yyruleno==127); /* (128) prlist ::= pritem (OPTIMIZED OUT) */ assert(yyruleno!=128); /* (129) prlist ::= prlist prsep pritem */ yytestcase(yyruleno==129); /* (130) direction ::= UP */ yytestcase(yyruleno==130); /* (131) direction ::= DOWN */ yytestcase(yyruleno==131); /* (132) direction ::= LEFT */ yytestcase(yyruleno==132); /* (133) direction ::= RIGHT */ yytestcase(yyruleno==133); /* (134) optrelexpr ::= relexpr (OPTIMIZED OUT) */ assert(yyruleno!=134); /* (135) attribute_list ::= alist */ yytestcase(yyruleno==135); /* (136) alist ::= */ yytestcase(yyruleno==136); /* (137) alist ::= alist attribute */ yytestcase(yyruleno==137); /* (138) attribute ::= boolproperty (OPTIMIZED OUT) */ assert(yyruleno!=138); /* (139) attribute ::= WITH withclause */ yytestcase(yyruleno==139); /* (140) go ::= GO */ yytestcase(yyruleno==140); /* (141) go ::= */ yytestcase(yyruleno==141); /* (142) even ::= UNTIL EVEN WITH */ yytestcase(yyruleno==142); /* (143) even ::= EVEN WITH */ yytestcase(yyruleno==143); /* (144) dashproperty ::= DOTTED */ yytestcase(yyruleno==144); /* (145) dashproperty ::= DASHED */ yytestcase(yyruleno==145); /* (146) colorproperty ::= FILL */ yytestcase(yyruleno==146); /* (147) colorproperty ::= COLOR */ yytestcase(yyruleno==147); /* (148) colorproperty ::= TEXTCOLOR */ yytestcase(yyruleno==148); /* (149) position ::= place */ yytestcase(yyruleno==149); /* (150) between ::= WAY BETWEEN */ yytestcase(yyruleno==150); /* (151) between ::= BETWEEN */ yytestcase(yyruleno==151); /* (152) between ::= OF THE WAY BETWEEN */ yytestcase(yyruleno==152); /* (153) place ::= place2 */ yytestcase(yyruleno==153); /* (154) edge ::= CENTER */ yytestcase(yyruleno==154); /* (155) edge ::= EDGEPT */ yytestcase(yyruleno==155); /* (156) edge ::= TOP */ yytestcase(yyruleno==156); /* (157) edge ::= BOTTOM */ yytestcase(yyruleno==157); /* (158) edge ::= START */ yytestcase(yyruleno==158); /* (159) edge ::= END */ yytestcase(yyruleno==159); /* (160) edge ::= RIGHT */ yytestcase(yyruleno==160); /* (161) edge ::= LEFT */ yytestcase(yyruleno==161); /* (162) object ::= objectname */ yytestcase(yyruleno==162); break; /********** End reduce actions ************************************************/ }; assert( yyruleno<sizeof(yyRuleInfoLhs)/sizeof(yyRuleInfoLhs[0]) ); yygoto = yyRuleInfoLhs[yyruleno]; yysize = yyRuleInfoNRhs[yyruleno]; yyact = yy_find_reduce_action(yymsp[yysize].stateno,(YYCODETYPE)yygoto); /* There are no SHIFTREDUCE actions on nonterminals because the table ** generator has simplified them to pure REDUCE actions. */ assert( !(yyact>YY_MAX_SHIFT && yyact<=YY_MAX_SHIFTREDUCE) ); /* It is not possible for a REDUCE to be followed by an error */ assert( yyact!=YY_ERROR_ACTION ); yymsp += yysize+1; yypParser->yytos = yymsp; yymsp->stateno = (YYACTIONTYPE)yyact; yymsp->major = (YYCODETYPE)yygoto; yyTraceShift(yypParser, yyact, "... then shift"); return yyact; } /* ** The following code executes when the parse fails */ #ifndef YYNOERRORRECOVERY static void yy_parse_failed( yyParser *yypParser /* The parser */ ){ pik_parserARG_FETCH pik_parserCTX_FETCH #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); } #endif while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser); /* Here code is inserted which will be executed whenever the ** parser fails */ /************ Begin %parse_failure code ***************************************/ /************ End %parse_failure code *****************************************/ pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */ pik_parserCTX_STORE } #endif /* YYNOERRORRECOVERY */ /* ** The following code executes when a syntax error first occurs. */ static void yy_syntax_error( yyParser *yypParser, /* The parser */ int yymajor, /* The major type of the error token */ pik_parserTOKENTYPE yyminor /* The minor type of the error token */ ){ pik_parserARG_FETCH pik_parserCTX_FETCH #define TOKEN yyminor /************ Begin %syntax_error code ****************************************/ #line 795 "pikchr.y" if( TOKEN.z && TOKEN.z[0] ){ pik_error(p, &TOKEN, "syntax error"); }else{ pik_error(p, 0, "syntax error"); } UNUSED_PARAMETER(yymajor); #line 3488 "pikchr.c" /************ End %syntax_error code ******************************************/ pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */ pik_parserCTX_STORE } /* ** The following is executed when the parser accepts */ static void yy_accept( yyParser *yypParser /* The parser */ ){ pik_parserARG_FETCH pik_parserCTX_FETCH #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); } #endif #ifndef YYNOERRORRECOVERY yypParser->yyerrcnt = -1; #endif assert( yypParser->yytos==yypParser->yystack ); /* Here code is inserted which will be executed whenever the ** parser accepts */ /*********** Begin %parse_accept code *****************************************/ /*********** End %parse_accept code *******************************************/ pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */ pik_parserCTX_STORE } /* The main parser program. ** The first argument is a pointer to a structure obtained from ** "pik_parserAlloc" which describes the current state of the parser. ** The second argument is the major token number. The third is ** the minor token. The fourth optional argument is whatever the ** user wants (and specified in the grammar) and is available for ** use by the action routines. ** ** Inputs: ** <ul> ** <li> A pointer to the parser (an opaque structure.) ** <li> The major token number. ** <li> The minor token number. ** <li> An option argument of a grammar-specified type. ** </ul> ** ** Outputs: ** None. */ void pik_parser( void *yyp, /* The parser */ int yymajor, /* The major token code number */ pik_parserTOKENTYPE yyminor /* The value for the token */ pik_parserARG_PDECL /* Optional %extra_argument parameter */ ){ YYMINORTYPE yyminorunion; YYACTIONTYPE yyact; /* The parser action. */ #if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY) int yyendofinput; /* True if we are at the end of input */ #endif #ifdef YYERRORSYMBOL int yyerrorhit = 0; /* True if yymajor has invoked an error */ #endif yyParser *yypParser = (yyParser*)yyp; /* The parser */ pik_parserCTX_FETCH pik_parserARG_STORE assert( yypParser->yytos!=0 ); #if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY) yyendofinput = (yymajor==0); #endif yyact = yypParser->yytos->stateno; #ifndef NDEBUG if( yyTraceFILE ){ if( yyact < YY_MIN_REDUCE ){ fprintf(yyTraceFILE,"%sInput '%s' in state %d\n", yyTracePrompt,yyTokenName[yymajor],yyact); }else{ fprintf(yyTraceFILE,"%sInput '%s' with pending reduce %d\n", yyTracePrompt,yyTokenName[yymajor],yyact-YY_MIN_REDUCE); } } #endif while(1){ /* Exit by "break" */ assert( yypParser->yytos>=yypParser->yystack ); assert( yyact==yypParser->yytos->stateno ); yyact = yy_find_shift_action((YYCODETYPE)yymajor,yyact); if( yyact >= YY_MIN_REDUCE ){ unsigned int yyruleno = yyact - YY_MIN_REDUCE; /* Reduce by this rule */ #ifndef NDEBUG assert( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ); if( yyTraceFILE ){ int yysize = yyRuleInfoNRhs[yyruleno]; if( yysize ){ fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n", yyTracePrompt, yyruleno, yyRuleName[yyruleno], yyruleno<YYNRULE_WITH_ACTION ? "" : " without external action", yypParser->yytos[yysize].stateno); }else{ fprintf(yyTraceFILE, "%sReduce %d [%s]%s.\n", yyTracePrompt, yyruleno, yyRuleName[yyruleno], yyruleno<YYNRULE_WITH_ACTION ? "" : " without external action"); } } #endif /* NDEBUG */ /* Check that the stack is large enough to grow by a single entry ** if the RHS of the rule is empty. This ensures that there is room ** enough on the stack to push the LHS value */ if( yyRuleInfoNRhs[yyruleno]==0 ){ #ifdef YYTRACKMAXSTACKDEPTH if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){ yypParser->yyhwm++; assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack)); } #endif if( yypParser->yytos>=yypParser->yystackEnd ){ if( yyGrowStack(yypParser) ){ yyStackOverflow(yypParser); break; } } } yyact = yy_reduce(yypParser,yyruleno,yymajor,yyminor pik_parserCTX_PARAM); }else if( yyact <= YY_MAX_SHIFTREDUCE ){ yy_shift(yypParser,yyact,(YYCODETYPE)yymajor,yyminor); #ifndef YYNOERRORRECOVERY yypParser->yyerrcnt--; #endif break; }else if( yyact==YY_ACCEPT_ACTION ){ yypParser->yytos--; yy_accept(yypParser); return; }else{ assert( yyact == YY_ERROR_ACTION ); yyminorunion.yy0 = yyminor; #ifdef YYERRORSYMBOL int yymx; #endif #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); } #endif #ifdef YYERRORSYMBOL /* A syntax error has occurred. ** The response to an error depends upon whether or not the ** grammar defines an error token "ERROR". ** ** This is what we do if the grammar does define ERROR: ** ** * Call the %syntax_error function. ** ** * Begin popping the stack until we enter a state where ** it is legal to shift the error symbol, then shift ** the error symbol. ** ** * Set the error count to three. ** ** * Begin accepting and shifting new tokens. No new error ** processing will occur until three tokens have been ** shifted successfully. ** */ if( yypParser->yyerrcnt<0 ){ yy_syntax_error(yypParser,yymajor,yyminor); } yymx = yypParser->yytos->major; if( yymx==YYERRORSYMBOL || yyerrorhit ){ #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sDiscard input token %s\n", yyTracePrompt,yyTokenName[yymajor]); } #endif yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion); yymajor = YYNOCODE; }else{ while( yypParser->yytos > yypParser->yystack ){ yyact = yy_find_reduce_action(yypParser->yytos->stateno, YYERRORSYMBOL); if( yyact<=YY_MAX_SHIFTREDUCE ) break; yy_pop_parser_stack(yypParser); } if( yypParser->yytos <= yypParser->yystack || yymajor==0 ){ yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); yy_parse_failed(yypParser); #ifndef YYNOERRORRECOVERY yypParser->yyerrcnt = -1; #endif yymajor = YYNOCODE; }else if( yymx!=YYERRORSYMBOL ){ yy_shift(yypParser,yyact,YYERRORSYMBOL,yyminor); } } yypParser->yyerrcnt = 3; yyerrorhit = 1; if( yymajor==YYNOCODE ) break; yyact = yypParser->yytos->stateno; #elif defined(YYNOERRORRECOVERY) /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to ** do any kind of error recovery. Instead, simply invoke the syntax ** error routine and continue going as if nothing had happened. ** ** Applications can set this macro (for example inside %include) if ** they intend to abandon the parse upon the first syntax error seen. */ yy_syntax_error(yypParser,yymajor, yyminor); yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); break; #else /* YYERRORSYMBOL is not defined */ /* This is what we do if the grammar does not define ERROR: ** ** * Report an error message, and throw away the input token. ** ** * If the input token is $, then fail the parse. ** ** As before, subsequent error messages are suppressed until ** three input tokens have been successfully shifted. */ if( yypParser->yyerrcnt<=0 ){ yy_syntax_error(yypParser,yymajor, yyminor); } yypParser->yyerrcnt = 3; yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); if( yyendofinput ){ yy_parse_failed(yypParser); #ifndef YYNOERRORRECOVERY yypParser->yyerrcnt = -1; #endif } break; #endif } } #ifndef NDEBUG if( yyTraceFILE ){ yyStackEntry *i; char cDiv = '['; fprintf(yyTraceFILE,"%sReturn. Stack=",yyTracePrompt); for(i=&yypParser->yystack[1]; i<=yypParser->yytos; i++){ fprintf(yyTraceFILE,"%c%s", cDiv, yyTokenName[i->major]); cDiv = ' '; } fprintf(yyTraceFILE,"]\n"); } #endif return; } /* ** Return the fallback token corresponding to canonical token iToken, or ** 0 if iToken has no fallback. */ int pik_parserFallback(int iToken){ #ifdef YYFALLBACK assert( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) ); return yyFallback[iToken]; #else (void)iToken; return 0; #endif } #line 1047 "pikchr.y" /* Chart of the 148 official CSS color names with their ** corresponding RGB values thru Color Module Level 4: ** https://developer.mozilla.org/en-US/docs/Web/CSS/color_value ** ** Two new names "None" and "Off" are added with a value ** of -1. */ static const struct { const char *zName; /* Name of the color */ int val; /* RGB value */ } aColor[] = { { "AliceBlue", 0xf0f8ff }, { "AntiqueWhite", 0xfaebd7 }, { "Aqua", 0x00ffff }, { "Aquamarine", 0x7fffd4 }, { "Azure", 0xf0ffff }, { "Beige", 0xf5f5dc }, { "Bisque", 0xffe4c4 }, { "Black", 0x000000 }, { "BlanchedAlmond", 0xffebcd }, { "Blue", 0x0000ff }, { "BlueViolet", 0x8a2be2 }, { "Brown", 0xa52a2a }, { "BurlyWood", 0xdeb887 }, { "CadetBlue", 0x5f9ea0 }, { "Chartreuse", 0x7fff00 }, { "Chocolate", 0xd2691e }, { "Coral", 0xff7f50 }, { "CornflowerBlue", 0x6495ed }, { "Cornsilk", 0xfff8dc }, { "Crimson", 0xdc143c }, { "Cyan", 0x00ffff }, { "DarkBlue", 0x00008b }, { "DarkCyan", 0x008b8b }, { "DarkGoldenrod", 0xb8860b }, { "DarkGray", 0xa9a9a9 }, { "DarkGreen", 0x006400 }, { "DarkGrey", 0xa9a9a9 }, { "DarkKhaki", 0xbdb76b }, { "DarkMagenta", 0x8b008b }, { "DarkOliveGreen", 0x556b2f }, { "DarkOrange", 0xff8c00 }, { "DarkOrchid", 0x9932cc }, { "DarkRed", 0x8b0000 }, { "DarkSalmon", 0xe9967a }, { "DarkSeaGreen", 0x8fbc8f }, { "DarkSlateBlue", 0x483d8b }, { "DarkSlateGray", 0x2f4f4f }, { "DarkSlateGrey", 0x2f4f4f }, { "DarkTurquoise", 0x00ced1 }, { "DarkViolet", 0x9400d3 }, { "DeepPink", 0xff1493 }, { "DeepSkyBlue", 0x00bfff }, { "DimGray", 0x696969 }, { "DimGrey", 0x696969 }, { "DodgerBlue", 0x1e90ff }, { "Firebrick", 0xb22222 }, { "FloralWhite", 0xfffaf0 }, { "ForestGreen", 0x228b22 }, { "Fuchsia", 0xff00ff }, { "Gainsboro", 0xdcdcdc }, { "GhostWhite", 0xf8f8ff }, { "Gold", 0xffd700 }, { "Goldenrod", 0xdaa520 }, { "Gray", 0x808080 }, { "Green", 0x008000 }, { "GreenYellow", 0xadff2f }, { "Grey", 0x808080 }, { "Honeydew", 0xf0fff0 }, { "HotPink", 0xff69b4 }, { "IndianRed", 0xcd5c5c }, { "Indigo", 0x4b0082 }, { "Ivory", 0xfffff0 }, { "Khaki", 0xf0e68c }, { "Lavender", 0xe6e6fa }, { "LavenderBlush", 0xfff0f5 }, { "LawnGreen", 0x7cfc00 }, { "LemonChiffon", 0xfffacd }, { "LightBlue", 0xadd8e6 }, { "LightCoral", 0xf08080 }, { "LightCyan", 0xe0ffff }, { "LightGoldenrodYellow", 0xfafad2 }, { "LightGray", 0xd3d3d3 }, { "LightGreen", 0x90ee90 }, { "LightGrey", 0xd3d3d3 }, { "LightPink", 0xffb6c1 }, { "LightSalmon", 0xffa07a }, { "LightSeaGreen", 0x20b2aa }, { "LightSkyBlue", 0x87cefa }, { "LightSlateGray", 0x778899 }, { "LightSlateGrey", 0x778899 }, { "LightSteelBlue", 0xb0c4de }, { "LightYellow", 0xffffe0 }, { "Lime", 0x00ff00 }, { "LimeGreen", 0x32cd32 }, { "Linen", 0xfaf0e6 }, { "Magenta", 0xff00ff }, { "Maroon", 0x800000 }, { "MediumAquamarine", 0x66cdaa }, { "MediumBlue", 0x0000cd }, { "MediumOrchid", 0xba55d3 }, { "MediumPurple", 0x9370db }, { "MediumSeaGreen", 0x3cb371 }, { "MediumSlateBlue", 0x7b68ee }, { "MediumSpringGreen", 0x00fa9a }, { "MediumTurquoise", 0x48d1cc }, { "MediumVioletRed", 0xc71585 }, { "MidnightBlue", 0x191970 }, { "MintCream", 0xf5fffa }, { "MistyRose", 0xffe4e1 }, { "Moccasin", 0xffe4b5 }, { "NavajoWhite", 0xffdead }, { "Navy", 0x000080 }, { "None", -1 }, /* Non-standard addition */ { "Off", -1 }, /* Non-standard addition */ { "OldLace", 0xfdf5e6 }, { "Olive", 0x808000 }, { "OliveDrab", 0x6b8e23 }, { "Orange", 0xffa500 }, { "OrangeRed", 0xff4500 }, { "Orchid", 0xda70d6 }, { "PaleGoldenrod", 0xeee8aa }, { "PaleGreen", 0x98fb98 }, { "PaleTurquoise", 0xafeeee }, { "PaleVioletRed", 0xdb7093 }, { "PapayaWhip", 0xffefd5 }, { "PeachPuff", 0xffdab9 }, { "Peru", 0xcd853f }, { "Pink", 0xffc0cb }, { "Plum", 0xdda0dd }, { "PowderBlue", 0xb0e0e6 }, { "Purple", 0x800080 }, { "RebeccaPurple", 0x663399 }, { "Red", 0xff0000 }, { "RosyBrown", 0xbc8f8f }, { "RoyalBlue", 0x4169e1 }, { "SaddleBrown", 0x8b4513 }, { "Salmon", 0xfa8072 }, { "SandyBrown", 0xf4a460 }, { "SeaGreen", 0x2e8b57 }, { "Seashell", 0xfff5ee }, { "Sienna", 0xa0522d }, { "Silver", 0xc0c0c0 }, { "SkyBlue", 0x87ceeb }, { "SlateBlue", 0x6a5acd }, { "SlateGray", 0x708090 }, { "SlateGrey", 0x708090 }, { "Snow", 0xfffafa }, { "SpringGreen", 0x00ff7f }, { "SteelBlue", 0x4682b4 }, { "Tan", 0xd2b48c }, { "Teal", 0x008080 }, { "Thistle", 0xd8bfd8 }, { "Tomato", 0xff6347 }, { "Turquoise", 0x40e0d0 }, { "Violet", 0xee82ee }, { "Wheat", 0xf5deb3 }, { "White", 0xffffff }, { "WhiteSmoke", 0xf5f5f5 }, { "Yellow", 0xffff00 }, { "YellowGreen", 0x9acd32 }, }; /* Built-in variable names. ** ** This array is constant. When a script changes the value of one of ** these built-ins, a new PVar record is added at the head of ** the Pik.pVar list, which is searched first. Thus the new PVar entry ** will override this default value. ** ** Units are in inches, except for "color" and "fill" which are ** interpreted as 24-bit RGB values. ** ** Binary search used. Must be kept in sorted order. */ static const struct { const char *zName; PNum val; } aBuiltin[] = { { "arcrad", 0.25 }, { "arrowhead", 2.0 }, { "arrowht", 0.08 }, { "arrowwid", 0.06 }, { "boxht", 0.5 }, { "boxrad", 0.0 }, { "boxwid", 0.75 }, { "charht", 0.14 }, { "charwid", 0.08 }, { "circlerad", 0.25 }, { "color", 0.0 }, { "cylht", 0.5 }, { "cylrad", 0.075 }, { "cylwid", 0.75 }, { "dashwid", 0.05 }, { "diamondht", 0.75 }, { "diamondwid", 1.0 }, { "dotrad", 0.015 }, { "ellipseht", 0.5 }, { "ellipsewid", 0.75 }, { "fileht", 0.75 }, { "filerad", 0.15 }, { "filewid", 0.5 }, { "fill", -1.0 }, { "lineht", 0.5 }, { "linewid", 0.5 }, { "movewid", 0.5 }, { "ovalht", 0.5 }, { "ovalwid", 1.0 }, { "scale", 1.0 }, { "textcolor", 0.0 }, { "textht", 0.5 }, { "textwid", 0.75 }, { "thickness", 0.015 }, }; /* XML Class Methods */ /* Set a single XML class on an Object. ** */ static void pik_set_xml_class(Pik *p, PToken *pXToken) { if( pXToken==0 ) return; PObj *pObj = p->cur; if( pObj->mProp & A_CLASS ){ pik_error(p, pXToken, "value already set"); return; } char z0 = pXToken->z[0]; if( z0=='_' || z0=='$' || z0=='@'){ pik_error(p, pXToken, "illegal start for class token"); return; } PXmlClass *pXNext = (PXmlClass *) malloc(pXToken->n+1 + sizeof(PXmlClass)); if( pXNext==0 ){ pik_error(p, 0, 0); return; } char *z; pXNext->zClass = z = (char*)&pXNext[1]; memcpy(z, pXToken->z, pXToken->n); z[pXToken->n] = 0; for( int i = 0; z[i] != 0; i++) { if( z[i] == '_' ) { z[i] = '-'; } } pObj->mProp |= A_CLASS; pik_xml_class_prepend(pObj, pXNext); } /* Set a list of XML classes on an object. ** */ static void pik_set_xml_classes(Pik *p, PToken *pXToken) { if( pXToken==0 ) return; PObj *pObj = p->cur; if( pObj->mProp & A_CLASS ){ pik_error(p, pXToken, "value already set"); return; } int st = 1; char *z = malloc(pXToken->n + 1); memcpy(z, pXToken->z, pXToken->n); z[pXToken->n] = 0; // counter so that each class counts as a token int tok_count = -1; // one token already counted for( int i = 1; z[i] != 0; i++) { if ( z[i] == '_' ) { z[i] = '-'; } else if( z[i] == ' ' || z[i] == '\'' ){ // skip runs of whitespace if( i == st){ st = i + 1; continue; } PXmlClass *pXNext = (PXmlClass *) malloc(i - st + 1 + sizeof(PXmlClass)); if( pXNext==0 ){ pik_error(p, 0, 0); free(z); return; } tok_count++; char *cl; int tok_len = i - st; pXNext->zClass = cl = (char*)&pXNext[1]; memcpy(cl, &z[st], tok_len); cl[tok_len] = 0; // We only hash the tokens in a class list, because the single-class // token is hashed in the tokenizer hash_step(p->shaDigest, (unsigned char *)pXNext->zClass, tok_len); pik_xml_class_prepend(pObj, pXNext); st = i + 1; } } free(z); if (tok_count > 0){ pObj->mProp |= A_CLASS; // `class ''` is bad style but does not set a class p->nToken += tok_count; } return; } /* Free an XML class list. ** */ static void pik_xml_class_free(PObj *pObj){ PXmlClass *pXClass = pObj->pXmlClass; while( pXClass ){ PXmlClass *pXNext = pXClass->pNext; free(pXClass); pXClass = pXNext; } pObj->pXmlClass = 0; } /* Prepend an XML class to a PObj. ** ** We prepend, rather than appending, because it's an O(1) operation, and the ** order of class names in a class attribute is not significant. */ static void pik_xml_class_prepend(PObj *pObj, PXmlClass *pXPrepend) { if( pXPrepend==0 ) return; PXmlClass *pXNext = pXPrepend; pXNext->pNext = 0; if( pObj->pXmlClass==0 ){ pObj->pXmlClass = pXNext; return; } // Prepend the new class to the list PXmlClass *pXCurrent = pObj->pXmlClass; pObj->pXmlClass = pXNext; pXNext->pNext = pXCurrent; } /* Methods for the "arc" class */ static void arcInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "arcrad",6,0); pObj->h = pObj->w; } /* Hack: Arcs are here rendered as quadratic Bezier curves rather ** than true arcs. Multiple reasons: (1) the legacy-PIC parameters ** that control arcs are obscure and I could not figure out what they ** mean based on available documentation. (2) Arcs are rarely used, ** and so do not seem that important. */ static PPoint arcControlPoint(int cw, PPoint f, PPoint t, PNum rScale){ PPoint m; PNum dx, dy; m.x = 0.5*(f.x+t.x); m.y = 0.5*(f.y+t.y); dx = t.x - f.x; dy = t.y - f.y; if( cw ){ m.x -= 0.5*rScale*dy; m.y += 0.5*rScale*dx; }else{ m.x += 0.5*rScale*dy; m.y -= 0.5*rScale*dx; } return m; } static void arcCheck(Pik *p, PObj *pObj){ PPoint m; if( p->nTPath>2 ){ pik_error(p, &pObj->errTok, "arc geometry error"); return; } m = arcControlPoint(pObj->cw, p->aTPath[0], p->aTPath[1], 0.5); pik_bbox_add_xy(&pObj->bbox, m.x, m.y); } static void arcRender(Pik *p, PObj *pObj){ PPoint f, m, t; if( pObj->nPath<2 ) return; if( pObj->sw<0.0 ) return; f = pObj->aPath[0]; t = pObj->aPath[1]; m = arcControlPoint(pObj->cw,f,t,1.0); if( pObj->larrow ){ pik_draw_arrowhead(p,&m,&f,pObj); } if( pObj->rarrow ){ pik_draw_arrowhead(p,&m,&t,pObj); } pik_append_tag_open(p, pObj, "path"); if( pObj->fill<0.0 ) { pik_append(p, " _T", 3); } pik_append(p, "\" ", 2); pik_append_xy(p,"d=\"M", f.x, f.y); pik_append_xy(p,"Q", m.x, m.y); pik_append_xy(p," ", t.x, t.y); pik_append(p,"\" ",2); pik_append_style(p,pObj,0); pik_append(p," />\n", -1); pik_append_txt(p, pObj, 0); } /* Methods for the "arrow" class */ static void arrowInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "linewid",7,0); pObj->h = pik_value(p, "lineht",6,0); pObj->rad = pik_value(p, "linerad",7,0); pObj->rarrow = 1; } /* Methods for the "box" class */ static void boxInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "boxwid",6,0); pObj->h = pik_value(p, "boxht",5,0); pObj->rad = pik_value(p, "boxrad",6,0); } /* Return offset from the center of the box to the compass point ** given by parameter cp */ static PPoint boxOffset(Pik *p, PObj *pObj, int cp){ PPoint pt = cZeroPoint; PNum w2 = 0.5*pObj->w; PNum h2 = 0.5*pObj->h; PNum rad = pObj->rad; PNum rx; if( rad<=0.0 ){ rx = 0.0; }else{ if( rad>w2 ) rad = w2; if( rad>h2 ) rad = h2; rx = 0.29289321881345252392*rad; } switch( cp ){ case CP_C: break; case CP_N: pt.x = 0.0; pt.y = h2; break; case CP_NE: pt.x = w2-rx; pt.y = h2-rx; break; case CP_E: pt.x = w2; pt.y = 0.0; break; case CP_SE: pt.x = w2-rx; pt.y = rx-h2; break; case CP_S: pt.x = 0.0; pt.y = -h2; break; case CP_SW: pt.x = rx-w2; pt.y = rx-h2; break; case CP_W: pt.x = -w2; pt.y = 0.0; break; case CP_NW: pt.x = rx-w2; pt.y = h2-rx; break; default: assert(0); } UNUSED_PARAMETER(p); return pt; } static PPoint boxChop(Pik *p, PObj *pObj, PPoint *pPt){ PNum dx, dy; int cp = CP_C; PPoint chop = pObj->ptAt; if( pObj->w<=0.0 ) return chop; if( pObj->h<=0.0 ) return chop; dx = (pPt->x - pObj->ptAt.x)*pObj->h/pObj->w; dy = (pPt->y - pObj->ptAt.y); if( dx>0.0 ){ if( dy>=2.414*dx ){ cp = CP_N; }else if( dy>=0.414*dx ){ cp = CP_NE; }else if( dy>=-0.414*dx ){ cp = CP_E; }else if( dy>-2.414*dx ){ cp = CP_SE; }else{ cp = CP_S; } }else{ if( dy>=-2.414*dx ){ cp = CP_N; }else if( dy>=-0.414*dx ){ cp = CP_NW; }else if( dy>=0.414*dx ){ cp = CP_W; }else if( dy>2.414*dx ){ cp = CP_SW; }else{ cp = CP_S; } } chop = pObj->type->xOffset(p,pObj,cp); chop.x += pObj->ptAt.x; chop.y += pObj->ptAt.y; return chop; } static void boxFit(Pik *p, PObj *pObj, PNum w, PNum h){ if( w>0 ) pObj->w = w; if( h>0 ) pObj->h = h; UNUSED_PARAMETER(p); } static void boxRender(Pik *p, PObj *pObj){ PNum w2 = 0.5*pObj->w; PNum h2 = 0.5*pObj->h; PNum rad = pObj->rad; PPoint pt = pObj->ptAt; if( pObj->sw>=0.0 ){ if( rad<=0.0 ){ pik_append_tag_open(p, pObj, "path"); if( pObj->fill<0.0 ) { pik_append(p, " _T", 3); } pik_append(p, "\" ", 2); pik_append_xy(p,"d=\"M", pt.x-w2,pt.y-h2); pik_append_xy(p,"L", pt.x+w2,pt.y-h2); pik_append_xy(p,"L", pt.x+w2,pt.y+h2); pik_append_xy(p,"L", pt.x-w2,pt.y+h2); pik_append(p,"Z\" ",-1); }else{ /* ** ---- - y3 ** / \ ** / \ _ y2 ** | | ** | | _ y1 ** \ / ** \ / ** ---- _ y0 ** ** ' ' ' ' ** x0 x1 x2 x3 */ PNum x0,x1,x2,x3,y0,y1,y2,y3; if( rad>w2 ) rad = w2; if( rad>h2 ) rad = h2; x0 = pt.x - w2; x1 = x0 + rad; x3 = pt.x + w2; x2 = x3 - rad; y0 = pt.y - h2; y1 = y0 + rad; y3 = pt.y + h2; y2 = y3 - rad; pik_append_tag_open(p, pObj, "path"); if( pObj->fill<0.0 ) { pik_append(p, " _T", 3); } pik_append(p, "\" ", 2); pik_append_xy(p,"d=\"M", x1, y0); if( x2>x1 ) pik_append_xy(p, "L", x2, y0); pik_append_arc(p, rad, rad, x3, y1); if( y2>y1 ) pik_append_xy(p, "L", x3, y2); pik_append_arc(p, rad, rad, x2, y3); if( x2>x1 ) pik_append_xy(p, "L", x1, y3); pik_append_arc(p, rad, rad, x0, y2); if( y2>y1 ) pik_append_xy(p, "L", x0, y1); pik_append_arc(p, rad, rad, x1, y0); pik_append(p,"Z\" ",-1); } pik_append_style(p,pObj,3); pik_append(p," />\n", -1); } pik_append_txt(p, pObj, 0); } /* Methods for the "circle" class */ static void circleInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "circlerad",9,0)*2; pObj->h = pObj->w; pObj->rad = 0.5*pObj->w; } static void circleNumProp(Pik *p, PObj *pObj, PToken *pId){ /* For a circle, the width must equal the height and both must ** be twice the radius. Enforce those constraints. */ switch( pId->eType ){ case T_DIAMETER: case T_RADIUS: pObj->w = pObj->h = 2.0*pObj->rad; break; case T_WIDTH: pObj->h = pObj->w; pObj->rad = 0.5*pObj->w; break; case T_HEIGHT: pObj->w = pObj->h; pObj->rad = 0.5*pObj->w; break; } UNUSED_PARAMETER(p); } static PPoint circleChop(Pik *p, PObj *pObj, PPoint *pPt){ PPoint chop; PNum dx = pPt->x - pObj->ptAt.x; PNum dy = pPt->y - pObj->ptAt.y; PNum dist = hypot(dx,dy); if( dist<pObj->rad || dist<=0 ) return pObj->ptAt; chop.x = pObj->ptAt.x + dx*pObj->rad/dist; chop.y = pObj->ptAt.y + dy*pObj->rad/dist; UNUSED_PARAMETER(p); return chop; } static void circleFit(Pik *p, PObj *pObj, PNum w, PNum h){ PNum mx = 0.0; if( w>0 ) mx = w; if( h>mx ) mx = h; if( w*h>0 && (w*w + h*h) > mx*mx ){ mx = hypot(w,h); } if( mx>0.0 ){ pObj->rad = 0.5*mx; pObj->w = pObj->h = mx; } UNUSED_PARAMETER(p); } static void circleRender(Pik *p, PObj *pObj){ PNum r = pObj->rad; PPoint pt = pObj->ptAt; if( pObj->sw>=0.0 ){ pik_append_tag_open(p, pObj, "circle"); if( pObj->fill<0.0 ) { pik_append(p, " _T", 3); } pik_append(p, "\" ", 2); pik_append_x(p,"cx=\"", pt.x, "\""); pik_append_y(p," cy=\"", pt.y, "\""); pik_append_dis(p," r=\"", r, "\" "); pik_append_style(p,pObj,3); pik_append(p," />\n", -1); } pik_append_txt(p, pObj, 0); } /* Methods for the "cylinder" class */ static void cylinderInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "cylwid",6,0); pObj->h = pik_value(p, "cylht",5,0); pObj->rad = pik_value(p, "cylrad",6,0); /* Minor radius of ellipses */ } static void cylinderFit(Pik *p, PObj *pObj, PNum w, PNum h){ if( w>0 ) pObj->w = w; if( h>0 ) pObj->h = h + 0.25*pObj->rad + pObj->sw; UNUSED_PARAMETER(p); } static void cylinderRender(Pik *p, PObj *pObj){ PNum w2 = 0.5*pObj->w; PNum h2 = 0.5*pObj->h; PNum rad = pObj->rad; PPoint pt = pObj->ptAt; if( pObj->sw>=0.0 ){ if( rad>h2 ){ rad = h2; }else if( rad<0 ){ rad = 0; } pik_append_tag_open(p, pObj, "path"); if( pObj->fill<0.0 ) { pik_append(p, " _T", 3); } pik_append(p, "\" ", 2); pik_append_xy(p,"d=\"M", pt.x-w2,pt.y+h2-rad); pik_append_xy(p,"L", pt.x-w2,pt.y-h2+rad); pik_append_arc(p,w2,rad,pt.x+w2,pt.y-h2+rad); pik_append_xy(p,"L", pt.x+w2,pt.y+h2-rad); pik_append_arc(p,w2,rad,pt.x-w2,pt.y+h2-rad); pik_append_arc(p,w2,rad,pt.x+w2,pt.y+h2-rad); pik_append(p,"\" ",-1); pik_append_style(p,pObj,3); pik_append(p," />\n", -1); } pik_append_txt(p, pObj, 0); } static PPoint cylinderOffset(Pik *p, PObj *pObj, int cp){ PPoint pt = cZeroPoint; PNum w2 = pObj->w*0.5; PNum h1 = pObj->h*0.5; PNum h2 = h1 - pObj->rad; switch( cp ){ case CP_C: break; case CP_N: pt.x = 0.0; pt.y = h1; break; case CP_NE: pt.x = w2; pt.y = h2; break; case CP_E: pt.x = w2; pt.y = 0.0; break; case CP_SE: pt.x = w2; pt.y = -h2; break; case CP_S: pt.x = 0.0; pt.y = -h1; break; case CP_SW: pt.x = -w2; pt.y = -h2; break; case CP_W: pt.x = -w2; pt.y = 0.0; break; case CP_NW: pt.x = -w2; pt.y = h2; break; default: assert(0); } UNUSED_PARAMETER(p); return pt; } /* Methods for the "dot" class */ static void dotInit(Pik *p, PObj *pObj){ pObj->rad = pik_value(p, "dotrad",6,0); pObj->h = pObj->w = pObj->rad*6; pObj->fill = pObj->color; } static void dotNumProp(Pik *p, PObj *pObj, PToken *pId){ switch( pId->eType ){ case T_COLOR: pObj->fill = pObj->color; break; case T_FILL: pObj->color = pObj->fill; break; } UNUSED_PARAMETER(p); } static void dotCheck(Pik *p, PObj *pObj){ pObj->w = pObj->h = 0; pik_bbox_addellipse(&pObj->bbox, pObj->ptAt.x, pObj->ptAt.y, pObj->rad, pObj->rad); UNUSED_PARAMETER(p); } static PPoint dotOffset(Pik *p, PObj *pObj, int cp){ UNUSED_PARAMETER(p); UNUSED_PARAMETER(pObj); UNUSED_PARAMETER(cp); return cZeroPoint; } static void dotRender(Pik *p, PObj *pObj){ PNum r = pObj->rad; PPoint pt = pObj->ptAt; if( pObj->sw>=0.0 ){ pik_append_tag_open(p, pObj, "circle"); if( pObj->fill<0.0 ) { pik_append(p, " _T", 3); } pik_append(p, "\" ", 2); pik_append_x(p,"cx=\"", pt.x, "\""); pik_append_y(p," cy=\"", pt.y, "\""); pik_append_dis(p," r=\"", r, "\" "); pik_append_style(p,pObj,2); pik_append(p," />\n", -1); } pik_append_txt(p, pObj, 0); } /* Methods for the "diamond" class */ static void diamondInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "diamondwid",10,0); pObj->h = pik_value(p, "diamondht",9,0); pObj->bAltAutoFit = 1; } /* Return offset from the center of the box to the compass point ** given by parameter cp */ static PPoint diamondOffset(Pik *p, PObj *pObj, int cp){ PPoint pt = cZeroPoint; PNum w2 = 0.5*pObj->w; PNum w4 = 0.25*pObj->w; PNum h2 = 0.5*pObj->h; PNum h4 = 0.25*pObj->h; switch( cp ){ case CP_C: break; case CP_N: pt.x = 0.0; pt.y = h2; break; case CP_NE: pt.x = w4; pt.y = h4; break; case CP_E: pt.x = w2; pt.y = 0.0; break; case CP_SE: pt.x = w4; pt.y = -h4; break; case CP_S: pt.x = 0.0; pt.y = -h2; break; case CP_SW: pt.x = -w4; pt.y = -h4; break; case CP_W: pt.x = -w2; pt.y = 0.0; break; case CP_NW: pt.x = -w4; pt.y = h4; break; default: assert(0); } UNUSED_PARAMETER(p); return pt; } static void diamondFit(Pik *p, PObj *pObj, PNum w, PNum h){ if( pObj->w<=0 ) pObj->w = w*1.5; if( pObj->h<=0 ) pObj->h = h*1.5; if( pObj->w>0 && pObj->h>0 ){ PNum x = pObj->w*h/pObj->h + w; PNum y = pObj->h*x/pObj->w; pObj->w = x; pObj->h = y; } UNUSED_PARAMETER(p); } static void diamondRender(Pik *p, PObj *pObj){ PNum w2 = 0.5*pObj->w; PNum h2 = 0.5*pObj->h; PPoint pt = pObj->ptAt; if( pObj->sw>=0.0 ){ pik_append_tag_open(p, pObj, "path"); if( pObj->fill<0.0 ) { pik_append(p, " _T", 3); } pik_append(p, "\" ", 2); pik_append_xy(p,"d=\"M", pt.x-w2,pt.y); pik_append_xy(p,"L", pt.x,pt.y-h2); pik_append_xy(p,"L", pt.x+w2,pt.y); pik_append_xy(p,"L", pt.x,pt.y+h2); pik_append(p,"Z\" ",-1); pik_append_style(p,pObj,3); pik_append(p," />\n", -1); } pik_append_txt(p, pObj, 0); } /* Methods for the "ellipse" class */ static void ellipseInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "ellipsewid",10,0); pObj->h = pik_value(p, "ellipseht",9,0); } static PPoint ellipseChop(Pik *p, PObj *pObj, PPoint *pPt){ PPoint chop; PNum s, dq, dist; PNum dx = pPt->x - pObj->ptAt.x; PNum dy = pPt->y - pObj->ptAt.y; if( pObj->w<=0.0 ) return pObj->ptAt; if( pObj->h<=0.0 ) return pObj->ptAt; s = pObj->h/pObj->w; dq = dx*s; dist = hypot(dq,dy); if( dist<pObj->h ) return pObj->ptAt; chop.x = pObj->ptAt.x + 0.5*dq*pObj->h/(dist*s); chop.y = pObj->ptAt.y + 0.5*dy*pObj->h/dist; UNUSED_PARAMETER(p); return chop; } static PPoint ellipseOffset(Pik *p, PObj *pObj, int cp){ PPoint pt = cZeroPoint; PNum w = pObj->w*0.5; PNum w2 = w*0.70710678118654747608; PNum h = pObj->h*0.5; PNum h2 = h*0.70710678118654747608; switch( cp ){ case CP_C: break; case CP_N: pt.x = 0.0; pt.y = h; break; case CP_NE: pt.x = w2; pt.y = h2; break; case CP_E: pt.x = w; pt.y = 0.0; break; case CP_SE: pt.x = w2; pt.y = -h2; break; case CP_S: pt.x = 0.0; pt.y = -h; break; case CP_SW: pt.x = -w2; pt.y = -h2; break; case CP_W: pt.x = -w; pt.y = 0.0; break; case CP_NW: pt.x = -w2; pt.y = h2; break; default: assert(0); } UNUSED_PARAMETER(p); return pt; } static void ellipseRender(Pik *p, PObj *pObj){ PNum w = pObj->w; PNum h = pObj->h; PPoint pt = pObj->ptAt; if( pObj->sw>=0.0 ){ pik_append_tag_open(p, pObj, "ellipse"); if( pObj->fill<0.0 ) { pik_append(p, " _T", 3); } pik_append(p, "\" ", 2); pik_append_x(p,"cx=\"", pt.x, "\""); pik_append_y(p," cy=\"", pt.y, "\""); pik_append_dis(p," rx=\"", w/2.0, "\""); pik_append_dis(p," ry=\"", h/2.0, "\" "); pik_append_style(p,pObj,3); pik_append(p," />\n", -1); } pik_append_txt(p, pObj, 0); } /* Methods for the "file" object */ static void fileInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "filewid",7,0); pObj->h = pik_value(p, "fileht",6,0); pObj->rad = pik_value(p, "filerad",7,0); } /* Return offset from the center of the file to the compass point ** given by parameter cp */ static PPoint fileOffset(Pik *p, PObj *pObj, int cp){ PPoint pt = cZeroPoint; PNum w2 = 0.5*pObj->w; PNum h2 = 0.5*pObj->h; PNum rx = pObj->rad; PNum mn = w2<h2 ? w2 : h2; if( rx>mn ) rx = mn; if( rx<mn*0.25 ) rx = mn*0.25; pt.x = pt.y = 0.0; rx *= 0.5; switch( cp ){ case CP_C: break; case CP_N: pt.x = 0.0; pt.y = h2; break; case CP_NE: pt.x = w2-rx; pt.y = h2-rx; break; case CP_E: pt.x = w2; pt.y = 0.0; break; case CP_SE: pt.x = w2; pt.y = -h2; break; case CP_S: pt.x = 0.0; pt.y = -h2; break; case CP_SW: pt.x = -w2; pt.y = -h2; break; case CP_W: pt.x = -w2; pt.y = 0.0; break; case CP_NW: pt.x = -w2; pt.y = h2; break; default: assert(0); } UNUSED_PARAMETER(p); return pt; } static void fileFit(Pik *p, PObj *pObj, PNum w, PNum h){ if( w>0 ) pObj->w = w; if( h>0 ) pObj->h = h + 2*pObj->rad; UNUSED_PARAMETER(p); } static void fileRender(Pik *p, PObj *pObj){ PNum w2 = 0.5*pObj->w; PNum h2 = 0.5*pObj->h; PNum rad = pObj->rad; PPoint pt = pObj->ptAt; PNum mn = w2<h2 ? w2 : h2; if( rad>mn ) rad = mn; if( rad<mn*0.25 ) rad = mn*0.25; if( pObj->sw>=0.0 ){ pik_append_tag_open(p, pObj, "path"); if( pObj->fill<0.0 ) { pik_append(p, " _T", 3); } pik_append(p, "\" ", 2); pik_append_xy(p,"d=\"M", pt.x-w2,pt.y-h2); pik_append_xy(p,"L", pt.x+w2,pt.y-h2); pik_append_xy(p,"L", pt.x+w2,pt.y+(h2-rad)); pik_append_xy(p,"L", pt.x+(w2-rad),pt.y+h2); pik_append_xy(p,"L", pt.x-w2,pt.y+h2); pik_append(p,"Z\" ",-1); pik_append_style(p,pObj,1); pik_append(p," />\n",-1); pik_append_tag_open(p, pObj, "path"); pik_append(p, " _T", 3); pik_append(p, "\" ", 2); pik_append_xy(p,"d=\"M", pt.x+(w2-rad), pt.y+h2); pik_append_xy(p,"L", pt.x+(w2-rad),pt.y+(h2-rad)); pik_append_xy(p,"L", pt.x+w2, pt.y+(h2-rad)); pik_append(p,"\" ",-1); pik_append_style(p,pObj,0); pik_append(p," />\n",-1); } pik_append_txt(p, pObj, 0); } /* Methods for the "line" class */ static void lineInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "linewid",7,0); pObj->h = pik_value(p, "lineht",6,0); pObj->rad = pik_value(p, "linerad",7,0); } static PPoint lineOffset(Pik *p, PObj *pObj, int cp){ #if 0 /* In legacy PIC, the .center of an unclosed line is half way between ** its .start and .end. */ if( cp==CP_C && !pObj->bClose ){ PPoint out; out.x = 0.5*(pObj->ptEnter.x + pObj->ptExit.x) - pObj->ptAt.x; out.y = 0.5*(pObj->ptEnter.x + pObj->ptExit.y) - pObj->ptAt.y; return out; } #endif return boxOffset(p,pObj,cp); } /* Make a group for the object if: we have a compound, and we didn't make ** a group with .id already. ** Returns a sentinel flag so we know whether to close a <g>: ** - 0: no close (not compound) ** - 1: close (group made here) */ static int pik_append_group(Pik *p, PObj *pObj){ int needsGroup = (int) !(pObj->zName) && (pObj->larrow||pObj->rarrow||pObj->pSublist); if ( needsGroup ) { needsGroup = 1; pik_append_tag_open(p, pObj, "g"); pik_append(p, "\" ", 2); pik_append(p, ">\n", -1); } return needsGroup; } static void lineRender(Pik *p, PObj *pObj){ int enclosed = pik_append_group(p, pObj); if( pObj->sw>0.0 ){ pik_append_tag_open(p, pObj, "path"); if( (pObj->bClose && pObj->fill<0.0) || !pObj->bClose ){ pik_append(p, " _T", 3); } pik_append(p, "\" ", 2); const char *z = "d=\"M"; int i; for(i=0; i<pObj->nPath; i++){ pik_append_xy(p,z,pObj->aPath[i].x,pObj->aPath[i].y); z = "L"; } if( pObj->bClose ){ pik_append(p,"Z",1); }else{ pObj->fill = -1.0; } pik_append(p,"\" ",-1); pik_append_style(p,pObj,pObj->bClose?3:0); pik_append(p," />\n", -1); int n = pObj->nPath; if( pObj->larrow ){ pik_draw_arrowhead(p,&pObj->aPath[1],&pObj->aPath[0],pObj); } if( pObj->rarrow ){ pik_draw_arrowhead(p,&pObj->aPath[n-2],&pObj->aPath[n-1],pObj); } } if( enclosed ){ pik_append(p, "</g>\n", -1); } pik_append_txt(p, pObj, 0); } /* Methods for the "move" class */ static void moveInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "movewid",7,0); pObj->h = pObj->w; pObj->fill = -1.0; pObj->color = -1.0; pObj->sw = -1.0; } static void moveRender(Pik *p, PObj *pObj){ /* No-op */ UNUSED_PARAMETER(p); UNUSED_PARAMETER(pObj); } /* Methods for the "oval" class */ static void ovalInit(Pik *p, PObj *pObj){ pObj->h = pik_value(p, "ovalht",6,0); pObj->w = pik_value(p, "ovalwid",7,0); pObj->rad = 0.5*(pObj->h<pObj->w?pObj->h:pObj->w); } static void ovalNumProp(Pik *p, PObj *pObj, PToken *pId){ UNUSED_PARAMETER(p); UNUSED_PARAMETER(pId); /* Always adjust the radius to be half of the smaller of ** the width and height. */ pObj->rad = 0.5*(pObj->h<pObj->w?pObj->h:pObj->w); } static void ovalFit(Pik *p, PObj *pObj, PNum w, PNum h){ UNUSED_PARAMETER(p); if( w>0 ) pObj->w = w; if( h>0 ) pObj->h = h; if( pObj->w<pObj->h ) pObj->w = pObj->h; pObj->rad = 0.5*(pObj->h<pObj->w?pObj->h:pObj->w); } /* Methods for the "spline" class */ static void splineInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "linewid",7,0); pObj->h = pik_value(p, "lineht",6,0); pObj->rad = 1000; } /* Return a point along the path from "f" to "t" that is r units ** prior to reaching "t", except if the path is less than 2*r total, ** return the midpoint. */ static PPoint radiusMidpoint(PPoint f, PPoint t, PNum r, int *pbMid){ PNum dx = t.x - f.x; PNum dy = t.y - f.y; PNum dist = hypot(dx,dy); PPoint m; if( dist<=0.0 ) return t; dx /= dist; dy /= dist; if( r > 0.5*dist ){ r = 0.5*dist; *pbMid = 1; }else{ *pbMid = 0; } m.x = t.x - r*dx; m.y = t.y - r*dy; return m; } static void radiusPath(Pik *p, PObj *pObj, PNum r){ int i; int n = pObj->nPath; const PPoint *a = pObj->aPath; PPoint m; PPoint an = a[n-1]; int isMid = 0; int iLast = pObj->bClose ? n : n-1; pik_append_tag_open(p, pObj, "path"); if( !pObj->bClose ){ pik_append(p, " _T", 3); } pik_append(p, "\" ", 2); pik_append_xy(p,"d=\"M", a[0].x, a[0].y); m = radiusMidpoint(a[0], a[1], r, &isMid); pik_append_xy(p," L ",m.x,m.y); for(i=1; i<iLast; i++){ an = i<n-1 ? a[i+1] : a[0]; m = radiusMidpoint(an,a[i],r, &isMid); pik_append_xy(p," Q ",a[i].x,a[i].y); pik_append_xy(p," ",m.x,m.y); if( !isMid ){ m = radiusMidpoint(a[i],an,r, &isMid); pik_append_xy(p," L ",m.x,m.y); } } pik_append_xy(p," L ",an.x,an.y); if( pObj->bClose ){ pik_append(p,"Z",1); }else{ pObj->fill = -1.0; } pik_append(p,"\" ",-1); pik_append_style(p,pObj,pObj->bClose?3:0); pik_append(p," />\n", -1); } static void splineRender(Pik *p, PObj *pObj){ if( pObj->sw>0.0 ){ int n = pObj->nPath; PNum r = pObj->rad; if( n<3 || r<=0.0 ){ lineRender(p,pObj); return; } int enclosed = pik_append_group(p, pObj); radiusPath(p,pObj,pObj->rad); if( pObj->larrow ){ pik_draw_arrowhead(p,&pObj->aPath[1],&pObj->aPath[0],pObj); } if( pObj->rarrow ){ pik_draw_arrowhead(p,&pObj->aPath[n-2],&pObj->aPath[n-1],pObj); } if( enclosed ){ pik_append(p, "</g>\n", -1); } } pik_append_txt(p, pObj, 0); } /* Methods for the "text" class */ static void textInit(Pik *p, PObj *pObj){ pik_value(p, "textwid",7,0); pik_value(p, "textht",6,0); pObj->sw = 0.0; } static PPoint textOffset(Pik *p, PObj *pObj, int cp){ /* Automatically slim-down the width and height of text ** statements so that the bounding box tightly encloses the text, ** then get boxOffset() to do the offset computation. */ pik_size_to_fit(p, &pObj->errTok,3); return boxOffset(p, pObj, cp); } static void textRender(Pik *p, PObj *pObj){ pik_append_txt(p, pObj, 0); } /* Methods for the "sublist" class */ static void sublistInit(Pik *p, PObj *pObj){ PList *pList = pObj->pSublist; int i; UNUSED_PARAMETER(p); pik_bbox_init(&pObj->bbox); for(i=0; i<pList->n; i++){ pik_bbox_addbox(&pObj->bbox, &pList->a[i]->bbox); } pObj->w = pObj->bbox.ne.x - pObj->bbox.sw.x; pObj->h = pObj->bbox.ne.y - pObj->bbox.sw.y; pObj->ptAt.x = 0.5*(pObj->bbox.ne.x + pObj->bbox.sw.x); pObj->ptAt.y = 0.5*(pObj->bbox.ne.y + pObj->bbox.sw.y); pObj->mCalc |= A_WIDTH|A_HEIGHT|A_RADIUS; } /* ** The following array holds all the different kinds of objects. ** The special [] object is separate. */ static const PClass aClass[] = { { /* name */ "arc", /* isline */ 1, /* eJust */ 0, /* xInit */ arcInit, /* xNumProp */ 0, /* xCheck */ arcCheck, /* xChop */ 0, /* xOffset */ boxOffset, /* xFit */ 0, /* xRender */ arcRender }, { /* name */ "arrow", /* isline */ 1, /* eJust */ 0, /* xInit */ arrowInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ 0, /* xOffset */ lineOffset, /* xFit */ 0, /* xRender */ splineRender }, { /* name */ "box", /* isline */ 0, /* eJust */ 1, /* xInit */ boxInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ boxChop, /* xOffset */ boxOffset, /* xFit */ boxFit, /* xRender */ boxRender }, { /* name */ "circle", /* isline */ 0, /* eJust */ 0, /* xInit */ circleInit, /* xNumProp */ circleNumProp, /* xCheck */ 0, /* xChop */ circleChop, /* xOffset */ ellipseOffset, /* xFit */ circleFit, /* xRender */ circleRender }, { /* name */ "cylinder", /* isline */ 0, /* eJust */ 1, /* xInit */ cylinderInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ boxChop, /* xOffset */ cylinderOffset, /* xFit */ cylinderFit, /* xRender */ cylinderRender }, { /* name */ "diamond", /* isline */ 0, /* eJust */ 0, /* xInit */ diamondInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ boxChop, /* xOffset */ diamondOffset, /* xFit */ diamondFit, /* xRender */ diamondRender }, { /* name */ "dot", /* isline */ 0, /* eJust */ 0, /* xInit */ dotInit, /* xNumProp */ dotNumProp, /* xCheck */ dotCheck, /* xChop */ circleChop, /* xOffset */ dotOffset, /* xFit */ 0, /* xRender */ dotRender }, { /* name */ "ellipse", /* isline */ 0, /* eJust */ 0, /* xInit */ ellipseInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ ellipseChop, /* xOffset */ ellipseOffset, /* xFit */ boxFit, /* xRender */ ellipseRender }, { /* name */ "file", /* isline */ 0, /* eJust */ 1, /* xInit */ fileInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ boxChop, /* xOffset */ fileOffset, /* xFit */ fileFit, /* xRender */ fileRender }, { /* name */ "line", /* isline */ 1, /* eJust */ 0, /* xInit */ lineInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ 0, /* xOffset */ lineOffset, /* xFit */ 0, /* xRender */ splineRender }, { /* name */ "move", /* isline */ 1, /* eJust */ 0, /* xInit */ moveInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ 0, /* xOffset */ boxOffset, /* xFit */ 0, /* xRender */ moveRender }, { /* name */ "oval", /* isline */ 0, /* eJust */ 1, /* xInit */ ovalInit, /* xNumProp */ ovalNumProp, /* xCheck */ 0, /* xChop */ boxChop, /* xOffset */ boxOffset, /* xFit */ ovalFit, /* xRender */ boxRender }, { /* name */ "spline", /* isline */ 1, /* eJust */ 0, /* xInit */ splineInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ 0, /* xOffset */ lineOffset, /* xFit */ 0, /* xRender */ splineRender }, { /* name */ "text", /* isline */ 0, /* eJust */ 0, /* xInit */ textInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ boxChop, /* xOffset */ textOffset, /* xFit */ boxFit, /* xRender */ textRender }, }; static const PClass sublistClass = { /* name */ "[]", /* isline */ 0, /* eJust */ 0, /* xInit */ sublistInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ 0, /* xOffset */ boxOffset, /* xFit */ 0, /* xRender */ 0 }; static const PClass noopClass = { /* name */ "noop", /* isline */ 0, /* eJust */ 0, /* xInit */ 0, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ 0, /* xOffset */ boxOffset, /* xFit */ 0, /* xRender */ 0 }; /* ** Reduce the length of the line segment by amt (if possible) by ** modifying the location of *t. */ static void pik_chop(PPoint *f, PPoint *t, PNum amt){ PNum dx = t->x - f->x; PNum dy = t->y - f->y; PNum dist = hypot(dx,dy); PNum r; if( dist<=amt ){ *t = *f; return; } r = 1.0 - amt/dist; t->x = f->x + r*dx; t->y = f->y + r*dy; } /* ** Draw an arrowhead on the end of the line segment from pFrom to pTo. ** Also, shorten the line segment (by changing the value of pTo) so that ** the shaft of the arrow does not extend into the arrowhead. */ static void pik_draw_arrowhead(Pik *p, PPoint *f, PPoint *t, PObj *pObj){ PNum dx = t->x - f->x; PNum dy = t->y - f->y; PNum dist = hypot(dx,dy); PNum h = p->hArrow * pObj->sw; PNum w = p->wArrow * pObj->sw; PNum e1, ddx, ddy; PNum bx, by; if( pObj->color<0.0 ) return; if( pObj->sw<=0.0 ) return; if( dist<=0.0 ) return; /* Unable */ dx /= dist; dy /= dist; e1 = dist - h; if( e1<0.0 ){ e1 = 0.0; h = dist; } ddx = -w*dy; ddy = w*dx; bx = f->x + e1*dx; by = f->y + e1*dy; pik_append_tag_open(p, pObj, "polygon"); pik_append(p, "\" ", 2); pik_append_xy(p,"points=\"", t->x, t->y); pik_append_xy(p," ",bx-ddx, by-ddy); pik_append_xy(p," ",bx+ddx, by+ddy); pik_append_clr(p,"\" fill=\"",pObj->color,"\" />\n",0); pik_chop(f,t,h/2); } /* ** Compute the relative offset to an edge location from the reference for a ** an statement. */ static PPoint pik_elem_offset(Pik *p, PObj *pObj, int cp){ return pObj->type->xOffset(p, pObj, cp); } /* ** Append raw text to zOut */ static void pik_append(Pik *p, const char *zText, int n){ if( n<0 ) n = (int)strlen(zText); if( p->nOut+n>=p->nOutAlloc ){ int nNew = (p->nOut+n)*2 + 1; char *z = realloc(p->zOut, nNew); if( z==0 ){ pik_error(p, 0, 0); return; } p->zOut = z; p->nOutAlloc = nNew; } memcpy(p->zOut+p->nOut, zText, n); p->nOut += n; p->zOut[p->nOut] = 0; } /* ** Append a tag, and any classes, to zOut. Leaves the class open ** so elements can add additional class data. */ static void pik_append_tag_open(Pik *p,PObj *pObj,const char *zTag) { pik_append(p, "<", 1); pik_append(p, zTag, -1); pik_append(p, " ", 1); if( pObj==0 ) { pik_append(p, "error=\"missing-pObj\" ", -1); return; } pik_append(p, "class=\"", -1); if( strcmp("[]", pObj->type->zName) ){ pik_append(p, pObj->type->zName, -1); }else{ pik_append(p, "group", -1); } if( pObj->zName ){ pik_append(p, " ", 1); pik_append(p, pObj->zName, -1); } if( pObj->pXmlClass==0 ){ return; }else{ pik_append(p, " ", 1); } for (PXmlClass *pXml= pObj->pXmlClass; pXml; pXml=pXml->pNext ){ pik_append(p, pXml->zClass, -1); if( pXml->pNext ){ pik_append(p, " ", 1); } } } /* ** Given a string and its length, returns true if the string begins ** with a construct which syntactically matches an HTML entity escape ** sequence (without checking for whether it's a known entity). Always ** returns false if zText[0] is false or n<4. Entities match the ** equivalent of the regexes `&#[0-9]{2,};` and ** `&[a-zA-Z][a-zA-Z0-9]+;`. */ static int pik_isentity(char const * zText, int n){ int i = 0; if( n<4 || '&'!=zText[0] ) return 0; n--; zText++; if( '#'==zText[0] ){ zText++; n--; for(i=0; i<n; i++){ if( i>1 && ';'==zText[i] ) return 1; else if( zText[i]<'0' || zText[i]>'9' ) return 0; /* Note that &#nn; values nn<32d are not legal entities. */ } }else{ for(i=0; i<n; i++){ if( i>1 && ';'==zText[i] ) return 1; else if( i>0 && zText[i]>='0' && zText[i]<='9' ){ continue; }else if( zText[i]<'A' || zText[i]>'z' || (zText[i]>'Z' && zText[i]<'a') ) return 0; } } return 0; } /* ** Append text to zOut with HTML characters escaped. ** ** * The space character is changed into non-breaking space (U+00a0) ** if mFlags has the 0x01 bit set. This is needed when outputting ** text to preserve leading and trailing whitespace. Turns out we ** cannot use as that is an HTML-ism and is not valid in XML. ** ** * The "&" character is changed into "&" if mFlags has the ** 0x02 bit set. This is needed when generating error message text. ** ** * The '"' character is changed into """ if mFlags has the ** 0x04 bit set. This is needed when creating XML attribute strings. ** This 'attribute mode' also replaces '\n' with ' '. ** ** * Except for the above, only "<" and ">" are escaped. */ static void pik_append_text(Pik *p, const char *zText, int n, int mFlags){ int i; char c = 0; int bQSpace = mFlags & 1; int bQAmp = mFlags & 2; int bQAttr = mFlags & 4; if( n<0 ) n = (int)strlen(zText); while( n>0 ){ for(i=0; i<n; i++){ c = zText[i]; if( c=='<' || c=='>' ) break; if( c==' ' && bQSpace ) break; if( c=='&' && bQAmp ) break; if( c=='"' && bQAttr ) break; if( c=='\n' && bQAttr ) break; } if( i ) pik_append(p, zText, i); if( i==n ) break; switch( c ){ case '<': { pik_append(p, "<", 4); break; } case '>': { pik_append(p, ">", 4); break; } case ' ': { pik_append(p, "\302\240;", 2); break; } case '\n': { pik_append(p, " ", 1); break; } case '"': { pik_append(p, """, 6); break; } case '&': if( pik_isentity(zText+i, n-i) ){ pik_append(p, "&", 1); } else { pik_append(p, "&", 5); } } i++; n -= i; zText += i; i = 0; } } /* ** Append error message text. This is either a raw append, or an append ** with HTML escapes, depending on whether the PIKCHR_PLAINTEXT_ERRORS flag ** is set. */ static void pik_append_errtxt(Pik *p, const char *zText, int n){ if( p->mFlags & PIKCHR_PLAINTEXT_ERRORS ){ pik_append(p, zText, n); }else{ pik_append_text(p, zText, n, 0); } } /* Append a PNum value */ static void pik_append_num(Pik *p, const char *z,PNum v){ char buf[100]; snprintf(buf, sizeof(buf)-1, "%.10g", (double)v); buf[sizeof(buf)-1] = 0; pik_append(p, z, -1); pik_append(p, buf, -1); } /* Append a PPoint value (Used for debugging only) */ static void pik_append_point(Pik *p, const char *z, PPoint *pPt){ char buf[100]; snprintf(buf, sizeof(buf)-1, "%.10g,%.10g", (double)pPt->x, (double)pPt->y); buf[sizeof(buf)-1] = 0; pik_append(p, z, -1); pik_append(p, buf, -1); } /* ** Invert the RGB color so that it is appropriate for dark mode. ** Variable x hold the initial color. The color is intended for use ** as a background color if isBg is true, and as a foreground color ** if isBg is false. */ static int pik_color_to_dark_mode(int x, int isBg){ int r, g, b; int mn, mx; x = 0xffffff - x; r = (x>>16) & 0xff; g = (x>>8) & 0xff; b = x & 0xff; mx = r; if( g>mx ) mx = g; if( b>mx ) mx = b; mn = r; if( g<mn ) mn = g; if( b<mn ) mn = b; r = mn + (mx-r); g = mn + (mx-g); b = mn + (mx-b); if( isBg ){ if( mx>127 ){ r = (127*r)/mx; g = (127*g)/mx; b = (127*b)/mx; } }else{ if( mn<128 && mx>mn ){ r = 127 + ((r-mn)*128)/(mx-mn); g = 127 + ((g-mn)*128)/(mx-mn); b = 127 + ((b-mn)*128)/(mx-mn); } } return r*0x10000 + g*0x100 + b; } /* Append a PNum value surrounded by text. Do coordinate transformations ** on the value. */ static void pik_append_x(Pik *p, const char *z1, PNum v, const char *z2){ char buf[200]; v -= p->bbox.sw.x; snprintf(buf, sizeof(buf)-1, "%s%g%s", z1, p->rScale*v, z2); buf[sizeof(buf)-1] = 0; pik_append(p, buf, -1); } static void pik_append_y(Pik *p, const char *z1, PNum v, const char *z2){ char buf[200]; v = p->bbox.ne.y - v; snprintf(buf, sizeof(buf)-1, "%s%g%s", z1, p->rScale*v, z2); buf[sizeof(buf)-1] = 0; pik_append(p, buf, -1); } static void pik_append_xy(Pik *p, const char *z1, PNum x, PNum y){ char buf[200]; x = x - p->bbox.sw.x; y = p->bbox.ne.y - y; snprintf(buf, sizeof(buf)-1, "%s%g,%g", z1, p->rScale*x, p->rScale*y); buf[sizeof(buf)-1] = 0; pik_append(p, buf, -1); } static void pik_append_dis(Pik *p, const char *z1, PNum v, const char *z2){ char buf[200]; snprintf(buf, sizeof(buf)-1, "%s%g%s", z1, p->rScale*v, z2); buf[sizeof(buf)-1] = 0; pik_append(p, buf, -1); } /* Append a color specification to the output. ** ** In PIKCHR_DARK_MODE, the color is inverted. The "bg" flags indicates that ** the color is intended for use as a background color if true, or as a ** foreground color if false. The distinction only matters for color ** inversions in PIKCHR_DARK_MODE. */ static void pik_append_clr(Pik *p,const char *z1,PNum v,const char *z2,int bg){ char buf[200]; int x = pik_round(v); int r, g, b; if( x==0 && p->fgcolor>0 && !bg ){ x = p->fgcolor; }else if( bg && x>=0xffffff && p->bgcolor>0 ){ x = p->bgcolor; }else if( p->mFlags & PIKCHR_DARK_MODE ){ x = pik_color_to_dark_mode(x,bg); } r = (x>>16) & 0xff; g = (x>>8) & 0xff; b = x & 0xff; snprintf(buf, sizeof(buf)-1, "%srgb(%d,%d,%d)%s", z1, r, g, b, z2); buf[sizeof(buf)-1] = 0; pik_append(p, buf, -1); } /* Append an SVG path A record: ** ** A r1 r2 0 0 0 x y */ static void pik_append_arc(Pik *p, PNum r1, PNum r2, PNum x, PNum y){ char buf[200]; x = x - p->bbox.sw.x; y = p->bbox.ne.y - y; snprintf(buf, sizeof(buf)-1, "A%g %g 0 0 0 %g %g", p->rScale*r1, p->rScale*r2, p->rScale*x, p->rScale*y); buf[sizeof(buf)-1] = 0; pik_append(p, buf, -1); } /* Append presentation styles to text. ** ** eFill is non-zero to fill in the background, or 0 if no fill should ** occur. Non-zero values of eFill determine the "bg" flag to pik_append_clr() ** for cases when pObj->fill==pObj->color ** ** 1 fill is background, and color is foreground. ** 2 fill and color are both foreground. (Used by "dot" objects) ** 3 fill and color are both background. (Used by most other objs) */ static void pik_append_style(Pik *p, PObj *pObj, int eFill){ int clrIsBg = 0; if( pObj->fill>=0 && eFill ){ int fillIsBg = 1; if( pObj->fill==pObj->color ){ if( eFill==2 ) fillIsBg = 0; if( eFill==3 ) clrIsBg = 1; } pik_append_clr(p, "fill=\"", pObj->fill, "\"", fillIsBg); }else{ #ifndef PIKDEV_NO_ELEMENTS pik_append(p,"fill=\"transparent\"",-1); #endif } if( pObj->sw>=0.0 && pObj->color>=0.0 ){ PNum sw = pObj->sw; pik_append_dis(p, " stroke-width=\"", sw, "\""); if( pObj->nPath>2 && pObj->rad<=pObj->sw ){ pik_append(p, " stroke-linejoin=\"round\"", -1); } pik_append_clr(p, " stroke=\"",pObj->color,"\"",clrIsBg); if( pObj->dotted>0.0 ){ PNum v = pObj->dotted; if( sw<2.1/p->rScale ) sw = 2.1/p->rScale; pik_append_dis(p," stroke-dasharray=\"",sw,""); pik_append_dis(p," ",v,"\""); }else if( pObj->dashed>0.0 ){ PNum v = pObj->dashed; pik_append_dis(p," stroke-dasharray=\"",v,""); pik_append_dis(p," ",v,"\""); } } } /* ** Compute the vertical locations for all text items in the ** object pObj. In other words, set every pObj->aTxt[*].eCode ** value to contain exactly one of: TP_ABOVE2, TP_ABOVE, TP_CENTER, ** TP_BELOW, or TP_BELOW2 is set. */ static void pik_txt_vertical_layout(PObj *pObj){ int n, i; PToken *aTxt; n = pObj->nTxt; if( n==0 ) return; aTxt = pObj->aTxt; if( n==1 ){ if( (aTxt[0].eCode & TP_VMASK)==0 ){ aTxt[0].eCode |= TP_CENTER; } }else{ int allSlots = 0; int aFree[5]; int iSlot; int j, mJust; /* If there is more than one TP_ABOVE, change the first to TP_ABOVE2. */ for(j=mJust=0, i=n-1; i>=0; i--){ if( aTxt[i].eCode & TP_ABOVE ){ if( j==0 ){ j++; mJust = aTxt[i].eCode & TP_JMASK; }else if( j==1 && mJust!=0 && (aTxt[i].eCode & mJust)==0 ){ j++; }else{ aTxt[i].eCode = (aTxt[i].eCode & ~TP_VMASK) | TP_ABOVE2; break; } } } /* If there is more than one TP_BELOW, change the last to TP_BELOW2 */ for(j=mJust=0, i=0; i<n; i++){ if( aTxt[i].eCode & TP_BELOW ){ if( j==0 ){ j++; mJust = aTxt[i].eCode & TP_JMASK; }else if( j==1 && mJust!=0 && (aTxt[i].eCode & mJust)==0 ){ j++; }else{ aTxt[i].eCode = (aTxt[i].eCode & ~TP_VMASK) | TP_BELOW2; break; } } } /* Compute a mask of all slots used */ for(i=0; i<n; i++) allSlots |= aTxt[i].eCode & TP_VMASK; /* Set of an array of available slots */ if( n==2 && ((aTxt[0].eCode|aTxt[1].eCode)&TP_JMASK)==(TP_LJUST|TP_RJUST) ){ /* Special case of two texts that have opposite justification: ** Allow them both to float to center. */ iSlot = 2; aFree[0] = aFree[1] = TP_CENTER; }else{ /* Set up the arrow so that available slots are filled from top to ** bottom */ iSlot = 0; if( n>=4 && (allSlots & TP_ABOVE2)==0 ) aFree[iSlot++] = TP_ABOVE2; if( (allSlots & TP_ABOVE)==0 ) aFree[iSlot++] = TP_ABOVE; if( (n&1)!=0 ) aFree[iSlot++] = TP_CENTER; if( (allSlots & TP_BELOW)==0 ) aFree[iSlot++] = TP_BELOW; if( n>=4 && (allSlots & TP_BELOW2)==0 ) aFree[iSlot++] = TP_BELOW2; } /* Set the VMASK for all unassigned texts */ for(i=iSlot=0; i<n; i++){ if( (aTxt[i].eCode & TP_VMASK)==0 ){ aTxt[i].eCode |= aFree[iSlot++]; } } } } /* Return the font scaling factor associated with the input text attribute. */ static PNum pik_font_scale(PToken *t){ PNum scale = 1.0; if( t->eCode & TP_BIG ) scale *= 1.25; if( t->eCode & TP_SMALL ) scale *= 0.8; if( t->eCode & TP_XTRA ) scale *= scale; return scale; } /* Append a string token as text. */ static void pik_append_str(Pik *p, int nz, const char *z, int mFlags){ while( nz>0 ){ int j; for(j=0; j<nz && z[j]!='\\'; j++){} if( j ) pik_append_text(p, z, j, mFlags); if( j<nz && (j+1==nz || z[j+1]=='\\') ){ pik_append(p, "\", -1); j++; } nz -= j+1; z += j+1; } } /* Append multiple <text> SVG elements for the text fields of the PObj. ** Parameters: ** ** p The Pik object into which we are rendering ** ** pObj Object containing the text to be rendered ** ** pBox If not NULL, do no rendering at all. Instead ** expand the box object so that it will include all ** of the text. */ static void pik_append_txt(Pik *p, PObj *pObj, PBox *pBox){ PNum jw; /* Justification margin relative to center */ PNum ha2 = 0.0; /* Height of the top row of text */ PNum ha1 = 0.0; /* Height of the second "above" row */ PNum hc = 0.0; /* Height of the center row */ PNum hb1 = 0.0; /* Height of the first "below" row of text */ PNum hb2 = 0.0; /* Height of the second "below" row */ PNum yBase = 0.0; PNum sw = pObj->sw>=0.0 ? pObj->sw : 0; int n, i, nz; PNum x, y, orig_y, s; const char *z; PToken *aTxt; unsigned allMask = 0; if( p->nErr ) return; if( pObj->nTxt==0 ) return; aTxt = pObj->aTxt; n = pObj->nTxt; pik_txt_vertical_layout(pObj); x = pObj->ptAt.x; for(i=0; i<n; i++) allMask |= pObj->aTxt[i].eCode; if( pObj->type->isLine ){ hc = sw*1.5; }else if( pObj->rad>0.0 && pObj->type->xInit==cylinderInit ){ yBase = -0.75*pObj->rad; } if( allMask & TP_CENTER ){ for(i=0; i<n; i++){ if( pObj->aTxt[i].eCode & TP_CENTER ){ s = pik_font_scale(pObj->aTxt+i); if( hc<s*p->charHeight ) hc = s*p->charHeight; } } } if( allMask & TP_ABOVE ){ for(i=0; i<n; i++){ if( pObj->aTxt[i].eCode & TP_ABOVE ){ s = pik_font_scale(pObj->aTxt+i)*p->charHeight; if( ha1<s ) ha1 = s; } } if( allMask & TP_ABOVE2 ){ for(i=0; i<n; i++){ if( pObj->aTxt[i].eCode & TP_ABOVE2 ){ s = pik_font_scale(pObj->aTxt+i)*p->charHeight; if( ha2<s ) ha2 = s; } } } } if( allMask & TP_BELOW ){ for(i=0; i<n; i++){ if( pObj->aTxt[i].eCode & TP_BELOW ){ s = pik_font_scale(pObj->aTxt+i)*p->charHeight; if( hb1<s ) hb1 = s; } } if( allMask & TP_BELOW2 ){ for(i=0; i<n; i++){ if( pObj->aTxt[i].eCode & TP_BELOW2 ){ s = pik_font_scale(pObj->aTxt+i)*p->charHeight; if( hb2<s ) hb2 = s; } } } } if( pObj->type->eJust==1 ){ jw = 0.5*(pObj->w - 0.5*(p->charWidth + sw)); }else{ jw = 0.0; } for(i=0; i<n; i++){ PToken *t = &aTxt[i]; PNum xtraFontScale = pik_font_scale(t); PNum nx = 0; orig_y = pObj->ptAt.y; y = yBase; if( t->eCode & TP_ABOVE2 ) y += 0.5*hc + ha1 + 0.5*ha2; if( t->eCode & TP_ABOVE ) y += 0.5*hc + 0.5*ha1; if( t->eCode & TP_BELOW ) y -= 0.5*hc + 0.5*hb1; if( t->eCode & TP_BELOW2 ) y -= 0.5*hc + hb1 + 0.5*hb2; if( t->eCode & TP_LJUST ) nx -= jw; if( t->eCode & TP_RJUST ) nx += jw; if( pBox!=0 ){ /* If pBox is not NULL, do not draw any <text>. Instead, just expand ** pBox to include the text */ PNum cw = pik_text_length(t, t->eCode & TP_MONO)*p->charWidth*xtraFontScale*0.01; PNum ch = p->charHeight*0.5*xtraFontScale; PNum x0, y0, x1, y1; /* Boundary of text relative to pObj->ptAt */ if( (t->eCode & (TP_BOLD|TP_MONO))==TP_BOLD ){ cw *= 1.1; } if( t->eCode & TP_RJUST ){ x0 = nx; y0 = y-ch; x1 = nx-cw; y1 = y+ch; }else if( t->eCode & TP_LJUST ){ x0 = nx; y0 = y-ch; x1 = nx+cw; y1 = y+ch; }else{ x0 = nx+cw/2; y0 = y+ch; x1 = nx-cw/2; y1 = y-ch; } if( (t->eCode & TP_ALIGN)!=0 && pObj->nPath>=2 ){ int nn = pObj->nPath; PNum dx = pObj->aPath[nn-1].x - pObj->aPath[0].x; PNum dy = pObj->aPath[nn-1].y - pObj->aPath[0].y; if( dx!=0 || dy!=0 ){ PNum dist = hypot(dx,dy); PNum tt; dx /= dist; dy /= dist; tt = dx*x0 - dy*y0; y0 = dy*x0 - dx*y0; x0 = tt; tt = dx*x1 - dy*y1; y1 = dy*x1 - dx*y1; x1 = tt; } } pik_bbox_add_xy(pBox, x+x0, orig_y+y0); pik_bbox_add_xy(pBox, x+x1, orig_y+y1); continue; } nx += x; y += orig_y; pik_append_tag_open(p, pObj, "text"); // _D is dominant baseline, every text element has it pik_append(p, " _D", 3); if( t->eCode & TP_RJUST ){ pik_append(p, " _tE", 4); }else if( t->eCode & TP_LJUST ){ pik_append(p, " _tS", 4); }else{ pik_append(p, " _tM", 4); } if( t->eCode & TP_ITALIC ){ pik_append(p, " _Fi", 4); } if( t->eCode & TP_BOLD ){ pik_append(p, " _Fb", 4); } if( t->eCode & TP_MONO ){ pik_append(p, " _Fm", 4); } pik_append(p, "\" ", 2); pik_append_x(p, "x=\"", nx, "\""); pik_append_y(p, " y=\"", y, "\""); #ifndef PIKDEV_NO_ELEMENTS if( t->eCode & TP_RJUST ){ pik_append(p, " text-anchor=\"end\"", -1); }else if( t->eCode & TP_LJUST ){ pik_append(p, " text-anchor=\"start\"", -1); }else{ pik_append(p, " text-anchor=\"middle\"", -1); } if( t->eCode & TP_ITALIC ){ pik_append(p, " font-style=\"italic\"", -1); } if( t->eCode & TP_BOLD ){ pik_append(p, " font-weight=\"bold\"", -1); } if( t->eCode & TP_MONO ){ pik_append(p, " font-family=\"monospace\"", -1); } #endif if( pObj->textcolor>=0.0 ){ pik_append_clr(p, " fill=\"", pObj->textcolor, "\"",0); } xtraFontScale *= p->fontScale; if( xtraFontScale<=0.99 || xtraFontScale>=1.01 ){ pik_append_num(p, " font-size=\"", xtraFontScale*100.0); pik_append(p, "%\"", 2); } if( (t->eCode & TP_ALIGN)!=0 && pObj->nPath>=2 ){ int nn = pObj->nPath; PNum dx = pObj->aPath[nn-1].x - pObj->aPath[0].x; PNum dy = pObj->aPath[nn-1].y - pObj->aPath[0].y; if( dx!=0 || dy!=0 ){ PNum ang = atan2(dy,dx)*-180/M_PI; pik_append_num(p, " transform=\"rotate(", ang); pik_append_xy(p, " ", x, orig_y); pik_append(p,")\"",2); } } #ifndef PIKDEV_NO_ELEMENTS pik_append(p," dominant-baseline=\"central\"",-1); #endif pik_append(p, ">", 1); if( t->n>=2 && t->z[0]=='"' ){ z = t->z+1; nz = t->n-2; }else{ z = t->z; nz = t->n; } pik_append_str(p, nz, z, 0x3); pik_append(p, "</text>\n", -1); } } /* ** Append text (that will go inside of a <pre>...</pre>) that ** shows the context of an error token. */ static void pik_error_context(Pik *p, PToken *pErr, int nContext){ int iErrPt; /* Index of first byte of error from start of input */ int iErrCol; /* Column of the error token on its line */ int iStart; /* Start position of the error context */ int iEnd; /* End position of the error context */ int iLineno; /* Line number of the error */ int iFirstLineno; /* Line number of start of error context */ int i; /* Loop counter */ int iBump = 0; /* Bump the location of the error cursor */ char zLineno[24]; /* Buffer in which to generate line numbers */ iErrPt = (int)(pErr->z - p->sIn.z); if( iErrPt>=(int)p->sIn.n ){ iErrPt = p->sIn.n-1; iBump = 1; }else{ while( iErrPt>0 && (p->sIn.z[iErrPt]=='\n' || p->sIn.z[iErrPt]=='\r') ){ iErrPt--; iBump = 1; } } iLineno = 1; for(i=0; i<iErrPt; i++){ if( p->sIn.z[i]=='\n' ){ iLineno++; } } iStart = 0; iFirstLineno = 1; while( iFirstLineno+nContext<iLineno ){ while( p->sIn.z[iStart]!='\n' ){ iStart++; } iStart++; iFirstLineno++; } for(iEnd=iErrPt; p->sIn.z[iEnd]!=0 && p->sIn.z[iEnd]!='\n'; iEnd++){} i = iStart; while( iFirstLineno<=iLineno ){ snprintf(zLineno,sizeof(zLineno)-1,"/* %4d */ ", iFirstLineno++); zLineno[sizeof(zLineno)-1] = 0; pik_append(p, zLineno, -1); for(i=iStart; p->sIn.z[i]!=0 && p->sIn.z[i]!='\n'; i++){} pik_append_errtxt(p, p->sIn.z+iStart, i-iStart); iStart = i+1; pik_append(p, "\n", 1); } for(iErrCol=0, i=iErrPt; i>0 && p->sIn.z[i]!='\n'; iErrCol++, i--){} for(i=0; i<iErrCol+11+iBump; i++){ pik_append(p, " ", 1); } for(i=0; i<(int)pErr->n; i++) pik_append(p, "^", 1); pik_append(p, "\n", 1); } /* ** Generate an error message for the output. pErr is the token at which ** the error should point. zMsg is the text of the error message. If ** either pErr or zMsg is NULL, generate an out-of-memory error message. ** ** This routine is a no-op if there has already been an error reported. */ static void pik_error(Pik *p, PToken *pErr, const char *zMsg){ int i; if( p==0 ) return; if( p->nErr ) return; p->nErr++; if( zMsg==0 ){ if( p->mFlags & PIKCHR_PLAINTEXT_ERRORS ){ pik_append(p, "\nOut of memory\n", -1); }else{ pik_append(p, "\n<div><p>Out of memory</p></div>\n", -1); } return; } if( pErr==0 ){ pik_append(p, "\n", 1); pik_append_errtxt(p, zMsg, -1); return; } if( (p->mFlags & PIKCHR_PLAINTEXT_ERRORS)==0 ){ pik_append(p, "<div><pre>\n", -1); } pik_error_context(p, pErr, 5); pik_append(p, "ERROR: ", -1); pik_append_errtxt(p, zMsg, -1); pik_append(p, "\n", 1); for(i=p->nCtx-1; i>=0; i--){ pik_append(p, "Called from:\n", -1); pik_error_context(p, &p->aCtx[i], 0); } if( (p->mFlags & PIKCHR_PLAINTEXT_ERRORS)==0 ){ pik_append(p, "</pre></div>\n", -1); } } /* ** Process an "assert( e1 == e2 )" statement. Always return NULL. */ static PObj *pik_assert(Pik *p, PNum e1, PToken *pEq, PNum e2){ char zE1[100], zE2[100], zMsg[300]; /* Convert the numbers to strings using %g for comparison. This ** limits the precision of the comparison to account for rounding error. */ snprintf(zE1, sizeof(zE1), "%g", e1); zE1[sizeof(zE1)-1] = 0; snprintf(zE2, sizeof(zE2), "%g", e2); zE1[sizeof(zE2)-1] = 0; if( strcmp(zE1,zE2)!=0 ){ snprintf(zMsg, sizeof(zMsg), "%.50s != %.50s", zE1, zE2); pik_error(p, pEq, zMsg); } return 0; } /* ** Process an "assert( place1 == place2 )" statement. Always return NULL. */ static PObj *pik_position_assert(Pik *p, PPoint *e1, PToken *pEq, PPoint *e2){ char zE1[100], zE2[100], zMsg[210]; /* Convert the numbers to strings using %g for comparison. This ** limits the precision of the comparison to account for rounding error. */ snprintf(zE1, sizeof(zE1), "(%g,%g)", e1->x, e1->y); zE1[sizeof(zE1)-1] = 0; snprintf(zE2, sizeof(zE2), "(%g,%g)", e2->x, e2->y); zE1[sizeof(zE2)-1] = 0; if( strcmp(zE1,zE2)!=0 ){ snprintf(zMsg, sizeof(zMsg), "%s != %s", zE1, zE2); pik_error(p, pEq, zMsg); } return 0; } /* Free a complete list of objects */ static void pik_elist_free(Pik *p, PList *pList){ int i; if( pList==0 ) return; for(i=0; i<pList->n; i++){ pik_elem_free(p, pList->a[i]); } free(pList->a); free(pList); return; } /* Free a single object, and its substructure */ static void pik_elem_free(Pik *p, PObj *pObj){ if( pObj==0 ) return; free(pObj->zName); pik_xml_class_free(pObj); pik_elist_free(p, pObj->pSublist); free(pObj->aPath); free(pObj); } /* Convert a numeric literal into a number. Return that number. ** There is no error handling because the tokenizer has already ** assured us that the numeric literal is valid. ** ** Allowed number forms: ** ** (1) Floating point literal ** (2) Same as (1) but followed by a unit: "cm", "mm", "in", ** "px", "pt", or "pc". ** (3) Hex integers: 0x000000 ** ** This routine returns the result in inches. If a different unit ** is specified, the conversion happens automatically. */ PNum pik_atof(PToken *num){ char *endptr; PNum ans; if( num->n>=3 && num->z[0]=='0' && (num->z[1]=='x'||num->z[1]=='X') ){ return (PNum)strtol(num->z+2, 0, 16); } ans = strtod(num->z, &endptr); if( (int)(endptr - num->z)==(int)num->n-2 ){ char c1 = endptr[0]; char c2 = endptr[1]; if( c1=='c' && c2=='m' ){ ans /= 2.54; }else if( c1=='m' && c2=='m' ){ ans /= 25.4; }else if( c1=='p' && c2=='x' ){ ans /= 96; }else if( c1=='p' && c2=='t' ){ ans /= 72; }else if( c1=='p' && c2=='c' ){ ans /= 6; } } return ans; } /* ** Compute the distance between two points */ static PNum pik_dist(PPoint *pA, PPoint *pB){ PNum dx, dy; dx = pB->x - pA->x; dy = pB->y - pA->y; return hypot(dx,dy); } /* Return true if a bounding box is empty. */ static int pik_bbox_isempty(PBox *p){ return p->sw.x>p->ne.x; } /* Return true if point pPt is contained within the bounding box pBox */ static int pik_bbox_contains_point(PBox *pBox, PPoint *pPt){ if( pik_bbox_isempty(pBox) ) return 0; if( pPt->x < pBox->sw.x ) return 0; if( pPt->x > pBox->ne.x ) return 0; if( pPt->y < pBox->sw.y ) return 0; if( pPt->y > pBox->ne.y ) return 0; return 1; } /* Initialize a bounding box to an empty container */ static void pik_bbox_init(PBox *p){ p->sw.x = 1.0; p->sw.y = 1.0; p->ne.x = 0.0; p->ne.y = 0.0; } /* Enlarge the PBox of the first argument so that it fully ** covers the second PBox */ static void pik_bbox_addbox(PBox *pA, PBox *pB){ if( pik_bbox_isempty(pA) ){ *pA = *pB; } if( pik_bbox_isempty(pB) ) return; if( pA->sw.x>pB->sw.x ) pA->sw.x = pB->sw.x; if( pA->sw.y>pB->sw.y ) pA->sw.y = pB->sw.y; if( pA->ne.x<pB->ne.x ) pA->ne.x = pB->ne.x; if( pA->ne.y<pB->ne.y ) pA->ne.y = pB->ne.y; } /* Enlarge the PBox of the first argument, if necessary, so that ** it contains the point described by the 2nd and 3rd arguments. */ static void pik_bbox_add_xy(PBox *pA, PNum x, PNum y){ if( pik_bbox_isempty(pA) ){ pA->ne.x = x; pA->ne.y = y; pA->sw.x = x; pA->sw.y = y; return; } if( pA->sw.x>x ) pA->sw.x = x; if( pA->sw.y>y ) pA->sw.y = y; if( pA->ne.x<x ) pA->ne.x = x; if( pA->ne.y<y ) pA->ne.y = y; } /* Enlarge the PBox so that it is able to contain an ellipse ** centered at x,y and with radiuses rx and ry. */ static void pik_bbox_addellipse(PBox *pA, PNum x, PNum y, PNum rx, PNum ry){ if( pik_bbox_isempty(pA) ){ pA->ne.x = x+rx; pA->ne.y = y+ry; pA->sw.x = x-rx; pA->sw.y = y-ry; return; } if( pA->sw.x>x-rx ) pA->sw.x = x-rx; if( pA->sw.y>y-ry ) pA->sw.y = y-ry; if( pA->ne.x<x+rx ) pA->ne.x = x+rx; if( pA->ne.y<y+ry ) pA->ne.y = y+ry; } /* Append a new object onto the end of an object list. The ** object list is created if it does not already exist. Return ** the new object list. */ static PList *pik_elist_append(Pik *p, PList *pList, PObj *pObj){ if( pObj==0 ) return pList; if( pList==0 ){ pList = malloc(sizeof(*pList)); if( pList==0 ){ pik_error(p, 0, 0); pik_elem_free(p, pObj); return 0; } memset(pList, 0, sizeof(*pList)); } if( pList->n>=pList->nAlloc ){ int nNew = (pList->n+5)*2; PObj **pNew = realloc(pList->a, sizeof(PObj*)*nNew); if( pNew==0 ){ pik_error(p, 0, 0); pik_elem_free(p, pObj); return pList; } pList->nAlloc = nNew; pList->a = pNew; } pList->a[pList->n++] = pObj; p->list = pList; return pList; } /* Convert an object class name into a PClass pointer */ static const PClass *pik_find_class(PToken *pId){ int first = 0; int last = count(aClass) - 1; do{ int mid = (first+last)/2; int c = strncmp(aClass[mid].zName, pId->z, pId->n); if( c==0 ){ c = aClass[mid].zName[pId->n]!=0; if( c==0 ) return &aClass[mid]; } if( c<0 ){ first = mid + 1; }else{ last = mid - 1; } }while( first<=last ); return 0; } /* Allocate and return a new PObj object. ** ** If pId!=0 then pId is an identifier that defines the object class. ** If pStr!=0 then it is a STRING literal that defines a text object. ** If pSublist!=0 then this is a [...] object. If all three parameters ** are NULL then this is a no-op object used to define a PLACENAME. */ static PObj *pik_elem_new(Pik *p, PToken *pId, PToken *pStr,PList *pSublist){ PObj *pNew; int miss = 0; if( p->nErr ) return 0; pNew = malloc( sizeof(*pNew) ); if( pNew==0 ){ pik_error(p,0,0); pik_elist_free(p, pSublist); return 0; } memset(pNew, 0, sizeof(*pNew)); p->cur = pNew; p->nTPath = 1; p->thenFlag = 0; if( p->list==0 || p->list->n==0 ){ pNew->ptAt.x = pNew->ptAt.y = 0.0; pNew->eWith = CP_C; }else{ PObj *pPrior = p->list->a[p->list->n-1]; pNew->ptAt = pPrior->ptExit; switch( p->eDir ){ default: pNew->eWith = CP_W; break; case DIR_LEFT: pNew->eWith = CP_E; break; case DIR_UP: pNew->eWith = CP_S; break; case DIR_DOWN: pNew->eWith = CP_N; break; } } p->aTPath[0] = pNew->ptAt; pNew->with = pNew->ptAt; pNew->outDir = pNew->inDir = p->eDir; pNew->iLayer = pik_value_int(p, "layer", 5, &miss); if( miss ) pNew->iLayer = 1000; if( pNew->iLayer<0 ) pNew->iLayer = 0; if( pSublist ){ pNew->type = &sublistClass; pNew->pSublist = pSublist; sublistClass.xInit(p,pNew); return pNew; } if( pStr ){ PToken n; n.z = "text"; n.n = 4; pNew->type = pik_find_class(&n); assert( pNew->type!=0 ); pNew->errTok = *pStr; pNew->type->xInit(p, pNew); pik_add_txt(p, pStr, pStr->eCode); return pNew; } if( pId ){ const PClass *pClass; pNew->errTok = *pId; pClass = pik_find_class(pId); if( pClass ){ pNew->type = pClass; pNew->sw = pik_value(p, "thickness",9,0); pNew->fill = pik_value(p, "fill",4,0); pNew->color = pik_value(p, "color",5,0); pNew->textcolor = pik_value(p, "textcolor",9,0); pClass->xInit(p, pNew); return pNew; } pik_error(p, pId, "unknown object type"); pik_elem_free(p, pNew); return 0; } pNew->type = &noopClass; pNew->ptExit = pNew->ptEnter = pNew->ptAt; return pNew; } /* ** Set a title or label for the Pikchr diagram. */ static void pik_set_title(Pik *p, PToken *pTitle, int bLabel){ if( p->zTitle ){ if( p->bLabel ){ pik_error(p, pTitle, "diagram already has a label"); }else{ pik_error(p, pTitle, "diagram already has a title"); } return; } char *zTitle = malloc(pTitle->n - 1); if( zTitle == 0 ){ pik_error(p,0,0); return; } memcpy(zTitle, pTitle->z + 1, pTitle->n - 2); zTitle[pTitle->n - 2] = 0; p->zTitle = zTitle; p->bLabel = bLabel; } /* ** Set a description for the Pikchr diagram. */ static void pik_set_description(Pik *p, PToken *pDesc){ if ( p->zDesc ){ pik_error(p, pDesc, "diagram already has a description"); } char *zDesc = malloc(pDesc->n - 1); if( zDesc == 0 ){ pik_error(p,0,0); return; } memcpy(zDesc, pDesc->z + 1, pDesc->n - 2); zDesc[pDesc->n - 2] = 0; p->zDesc = zDesc; } /* ** If the ID token in the argument is the name of a macro, return ** the PMacro object for that macro */ static PMacro *pik_find_macro(Pik *p, PToken *pId){ PMacro *pMac; for(pMac = p->pMacros; pMac; pMac=pMac->pNext){ if( pMac->macroName.n==pId->n && strncmp(pMac->macroName.z,pId->z,pId->n)==0 ){ return pMac; } } return 0; } /* Add a new macro */ static void pik_add_macro( Pik *p, /* Current Pikchr diagram */ PToken *pId, /* The ID token that defines the macro name */ PToken *pCode /* Macro body inside of {...} */ ){ PMacro *pNew = pik_find_macro(p, pId); if( pNew==0 ){ pNew = malloc( sizeof(*pNew) ); if( pNew==0 ){ pik_error(p, 0, 0); return; } pNew->pNext = p->pMacros; p->pMacros = pNew; pNew->macroName = *pId; } pNew->macroBody.z = pCode->z+1; pNew->macroBody.n = pCode->n-2; pNew->inUse = 0; } /* ** Set the output direction and exit point for an object */ static void pik_elem_set_exit(PObj *pObj, int eDir){ assert( ValidDir(eDir) ); pObj->outDir = eDir; if( !pObj->type->isLine || pObj->bClose ){ pObj->ptExit = pObj->ptAt; switch( pObj->outDir ){ default: pObj->ptExit.x += pObj->w*0.5; break; case DIR_LEFT: pObj->ptExit.x -= pObj->w*0.5; break; case DIR_UP: pObj->ptExit.y += pObj->h*0.5; break; case DIR_DOWN: pObj->ptExit.y -= pObj->h*0.5; break; } } } /* Change the layout direction. */ static void pik_set_direction(Pik *p, int eDir){ assert( ValidDir(eDir) ); p->eDir = (unsigned char)eDir; /* It seems to make sense to reach back into the last object and ** change its exit point (its ".end") to correspond to the new ** direction. Things just seem to work better this way. However, ** legacy PIC does *not* do this. ** ** The difference can be seen in a script like this: ** ** arrow; circle; down; arrow ** ** You can make pikchr render the above exactly like PIC ** by deleting the following three lines. But I (drh) think ** it works better with those lines in place. */ if( p->list && p->list->n ){ pik_elem_set_exit(p->list->a[p->list->n-1], eDir); } } /* Move all coordinates contained within an object (and within its ** substructure) by dx, dy */ static void pik_elem_move(PObj *pObj, PNum dx, PNum dy){ int i; pObj->ptAt.x += dx; pObj->ptAt.y += dy; pObj->ptEnter.x += dx; pObj->ptEnter.y += dy; pObj->ptExit.x += dx; pObj->ptExit.y += dy; pObj->bbox.ne.x += dx; pObj->bbox.ne.y += dy; pObj->bbox.sw.x += dx; pObj->bbox.sw.y += dy; for(i=0; i<pObj->nPath; i++){ pObj->aPath[i].x += dx; pObj->aPath[i].y += dy; } if( pObj->pSublist ){ pik_elist_move(pObj->pSublist, dx, dy); } } static void pik_elist_move(PList *pList, PNum dx, PNum dy){ int i; for(i=0; i<pList->n; i++){ pik_elem_move(pList->a[i], dx, dy); } } /* ** Check to see if it is ok to set the value of parameter mThis. ** Return 0 if it is ok. If it not ok, generate an appropriate ** error message and return non-zero. ** ** Flags are set in pObj so that the same object or conflicting ** objects may not be set again. ** ** To be ok, bit mThis must be clear and no more than one of ** the bits identified by mBlockers may be set. */ static int pik_param_ok( Pik *p, /* For storing the error message (if any) */ PObj *pObj, /* The object under construction */ PToken *pId, /* Make the error point to this token */ int mThis /* Value we are trying to set */ ){ if( pObj->mProp & mThis ){ pik_error(p, pId, "value is already set"); return 1; } if( pObj->mCalc & mThis ){ pik_error(p, pId, "value already fixed by prior constraints"); return 1; } pObj->mProp |= mThis; return 0; } /* ** Set a numeric property like "width 7" or "radius 200%". ** ** The rAbs term is an absolute value to add in. rRel is ** a relative value by which to change the current value. */ void pik_set_numprop(Pik *p, PToken *pId, PRel *pVal){ PObj *pObj = p->cur; switch( pId->eType ){ case T_HEIGHT: if( pik_param_ok(p, pObj, pId, A_HEIGHT) ) return; pObj->h = pObj->h*pVal->rRel + pVal->rAbs; break; case T_WIDTH: if( pik_param_ok(p, pObj, pId, A_WIDTH) ) return; pObj->w = pObj->w*pVal->rRel + pVal->rAbs; break; case T_RADIUS: if( pik_param_ok(p, pObj, pId, A_RADIUS) ) return; pObj->rad = pObj->rad*pVal->rRel + pVal->rAbs; break; case T_DIAMETER: if( pik_param_ok(p, pObj, pId, A_RADIUS) ) return; pObj->rad = pObj->rad*pVal->rRel + 0.5*pVal->rAbs; /* diam it 2x rad */ break; case T_THICKNESS: if( pik_param_ok(p, pObj, pId, A_THICKNESS) ) return; pObj->sw = pObj->sw*pVal->rRel + pVal->rAbs; break; } if( pObj->type->xNumProp ){ pObj->type->xNumProp(p, pObj, pId); } return; } /* ** Set a color property. The argument is an RGB value. */ void pik_set_clrprop(Pik *p, PToken *pId, PNum rClr){ PObj *pObj = p->cur; switch( pId->eType ){ case T_FILL: if( pik_param_ok(p, pObj, pId, A_FILL) ) return; pObj->fill = rClr; break; case T_COLOR: if( pik_param_ok(p, pObj, pId, A_COLOR) ) return; pObj->color = rClr; // without these lines we would have a breaking change if( !(pObj->mProp & A_TEXTCOLOR)){ pObj->textcolor = rClr; } break; case T_TEXTCOLOR: if( pik_param_ok(p, pObj, pId, A_TEXTCOLOR) ) return; pObj->textcolor = rClr; break; } if( pObj->type->xNumProp ){ pObj->type->xNumProp(p, pObj, pId); } return; } /* ** Set a "dashed" property like "dash 0.05" ** ** Use the value supplied by pVal if available. If pVal==0, use ** a default. */ void pik_set_dashed(Pik *p, PToken *pId, PNum *pVal){ PObj *pObj = p->cur; PNum v; switch( pId->eType ){ case T_DOTTED: { v = pVal==0 ? pik_value(p,"dashwid",7,0) : *pVal; pObj->dotted = v; pObj->dashed = 0.0; break; } case T_DASHED: { v = pVal==0 ? pik_value(p,"dashwid",7,0) : *pVal; pObj->dashed = v; pObj->dotted = 0.0; break; } } } /* ** If the current path information came from a "same" or "same as" ** reset it. */ static void pik_reset_samepath(Pik *p){ if( p->samePath ){ p->samePath = 0; p->nTPath = 1; } } /* Add a new term to the path for a line-oriented object by transferring ** the information in the ptTo field over onto the path and into ptFrom ** resetting the ptTo. */ static void pik_then(Pik *p, PToken *pToken, PObj *pObj){ int n; if( !pObj->type->isLine ){ pik_error(p, pToken, "use with line-oriented objects only"); return; } n = p->nTPath - 1; if( n<1 && (pObj->mProp & A_FROM)==0 ){ pik_error(p, pToken, "no prior path points"); return; } p->thenFlag = 1; } /* Advance to the next entry in p->aTPath. Return its index. */ static int pik_next_rpath(Pik *p, PToken *pErr){ int n = p->nTPath - 1; if( n+1>=(int)count(p->aTPath) ){ pik_error(0, pErr, "too many path elements"); return n; } n++; p->nTPath++; p->aTPath[n] = p->aTPath[n-1]; p->mTPath = 0; return n; } /* Add a direction term to an object. "up 0.5", or "left 3", or "down" ** or "down 50%". */ static void pik_add_direction(Pik *p, PToken *pDir, PRel *pVal){ PObj *pObj = p->cur; int n; int dir; if( !pObj->type->isLine ){ if( pDir ){ pik_error(p, pDir, "use with line-oriented objects only"); }else{ PToken x = pik_next_semantic_token(&pObj->errTok); pik_error(p, &x, "syntax error"); } return; } pik_reset_samepath(p); n = p->nTPath - 1; if( p->thenFlag || p->mTPath==3 || n==0 ){ n = pik_next_rpath(p, pDir); p->thenFlag = 0; } dir = pDir ? pDir->eCode : p->eDir; switch( dir ){ case DIR_UP: if( p->mTPath & 2 ) n = pik_next_rpath(p, pDir); p->aTPath[n].y += pVal->rAbs + pObj->h*pVal->rRel; p->mTPath |= 2; break; case DIR_DOWN: if( p->mTPath & 2 ) n = pik_next_rpath(p, pDir); p->aTPath[n].y -= pVal->rAbs + pObj->h*pVal->rRel; p->mTPath |= 2; break; case DIR_RIGHT: if( p->mTPath & 1 ) n = pik_next_rpath(p, pDir); p->aTPath[n].x += pVal->rAbs + pObj->w*pVal->rRel; p->mTPath |= 1; break; case DIR_LEFT: if( p->mTPath & 1 ) n = pik_next_rpath(p, pDir); p->aTPath[n].x -= pVal->rAbs + pObj->w*pVal->rRel; p->mTPath |= 1; break; } pObj->outDir = dir; } /* Process a movement attribute of one of these forms: ** ** pDist pHdgKW rHdg pEdgept ** GO distance HEADING angle ** GO distance compasspoint */ static void pik_move_hdg( Pik *p, /* The Pikchr context */ PRel *pDist, /* Distance to move */ PToken *pHeading, /* "heading" keyword if present */ PNum rHdg, /* Angle argument to "heading" keyword */ PToken *pEdgept, /* EDGEPT keyword "ne", "sw", etc... */ PToken *pErr /* Token to use for error messages */ ){ PObj *pObj = p->cur; int n; PNum rDist = pDist->rAbs + pik_value(p,"linewid",7,0)*pDist->rRel; if( !pObj->type->isLine ){ pik_error(p, pErr, "use with line-oriented objects only"); return; } pik_reset_samepath(p); do{ n = pik_next_rpath(p, pErr); }while( n<1 ); if( pHeading ){ rHdg = fmod(rHdg,360.0); }else if( pEdgept->eEdge==CP_C ){ pik_error(p, pEdgept, "syntax error"); return; }else{ rHdg = pik_hdg_angle[pEdgept->eEdge]; } if( rHdg<=45.0 ){ pObj->outDir = DIR_UP; }else if( rHdg<=135.0 ){ pObj->outDir = DIR_RIGHT; }else if( rHdg<=225.0 ){ pObj->outDir = DIR_DOWN; }else if( rHdg<=315.0 ){ pObj->outDir = DIR_LEFT; }else{ pObj->outDir = DIR_UP; } rHdg *= 0.017453292519943295769; /* degrees to radians */ p->aTPath[n].x += rDist*sin(rHdg); p->aTPath[n].y += rDist*cos(rHdg); p->mTPath = 2; } /* Process a movement attribute of the form "right until even with ..." ** ** pDir is the first keyword, "right" or "left" or "up" or "down". ** The movement is in that direction until its closest approach to ** the point specified by pPoint. */ static void pik_evenwith(Pik *p, PToken *pDir, PPoint *pPlace){ PObj *pObj = p->cur; int n; if( !pObj->type->isLine ){ pik_error(p, pDir, "use with line-oriented objects only"); return; } pik_reset_samepath(p); n = p->nTPath - 1; if( p->thenFlag || p->mTPath==3 || n==0 ){ n = pik_next_rpath(p, pDir); p->thenFlag = 0; } switch( pDir->eCode ){ case DIR_DOWN: case DIR_UP: if( p->mTPath & 2 ) n = pik_next_rpath(p, pDir); p->aTPath[n].y = pPlace->y; p->mTPath |= 2; break; case DIR_RIGHT: case DIR_LEFT: if( p->mTPath & 1 ) n = pik_next_rpath(p, pDir); p->aTPath[n].x = pPlace->x; p->mTPath |= 1; break; } pObj->outDir = pDir->eCode; } /* If the last referenced object is centered at point pPt then return ** a pointer to that object. If there is no prior object reference, ** or if the points are not the same, return NULL. ** ** This is a side-channel hack used to find the objects at which a ** line begins and ends. For example, in ** ** arrow from OBJ1 to OBJ2 chop ** ** The arrow object is normally just handed the coordinates of the ** centers for OBJ1 and OBJ2. But we also want to know the specific ** object named in case there are multiple objects centered at the ** same point. ** ** See forum post 1d46e3a0bc */ static PObj *pik_last_ref_object(Pik *p, PPoint *pPt){ PObj *pRes = 0; if( p->lastRef==0 ) return 0; if( p->lastRef->ptAt.x==pPt->x && p->lastRef->ptAt.y==pPt->y ){ pRes = p->lastRef; } p->lastRef = 0; return pRes; } /* Set the "from" of an object */ static void pik_set_from(Pik *p, PObj *pObj, PToken *pTk, PPoint *pPt){ if( !pObj->type->isLine ){ pik_error(p, pTk, "use \"at\" to position this object"); return; } if( pObj->mProp & A_FROM ){ pik_error(p, pTk, "line start location already fixed"); return; } if( pObj->bClose ){ pik_error(p, pTk, "polygon is closed"); return; } if( p->nTPath>1 ){ PNum dx = pPt->x - p->aTPath[0].x; PNum dy = pPt->y - p->aTPath[0].y; int i; for(i=1; i<p->nTPath; i++){ p->aTPath[i].x += dx; p->aTPath[i].y += dy; } } p->aTPath[0] = *pPt; p->mTPath = 3; pObj->mProp |= A_FROM; pObj->pFrom = pik_last_ref_object(p, pPt); } /* Set the "to" of an object */ static void pik_add_to(Pik *p, PObj *pObj, PToken *pTk, PPoint *pPt){ int n = p->nTPath-1; if( !pObj->type->isLine ){ pik_error(p, pTk, "use \"at\" to position this object"); return; } if( pObj->bClose ){ pik_error(p, pTk, "polygon is closed"); return; } pik_reset_samepath(p); if( n==0 || p->mTPath==3 || p->thenFlag ){ n = pik_next_rpath(p, pTk); } p->aTPath[n] = *pPt; p->mTPath = 3; pObj->pTo = pik_last_ref_object(p, pPt); } static void pik_close_path(Pik *p, PToken *pErr){ PObj *pObj = p->cur; if( p->nTPath<3 ){ pik_error(p, pErr, "need at least 3 vertexes in order to close the polygon"); return; } if( pObj->bClose ){ pik_error(p, pErr, "polygon already closed"); return; } pObj->bClose = 1; } /* Lower the layer of the current object so that it is behind the ** given object. */ static void pik_behind(Pik *p, PObj *pOther){ PObj *pObj = p->cur; if( p->nErr==0 && pObj->iLayer>=pOther->iLayer ){ pObj->iLayer = pOther->iLayer - 1; } } /* Set the "at" of an object */ static void pik_set_at(Pik *p, PToken *pEdge, PPoint *pAt, PToken *pErrTok){ PObj *pObj; static unsigned char eDirToCp[] = { CP_E, CP_S, CP_W, CP_N }; if( p->nErr ) return; pObj = p->cur; if( pObj->type->isLine ){ pik_error(p, pErrTok, "use \"from\" and \"to\" to position this object"); return; } if( pObj->mProp & A_AT ){ pik_error(p, pErrTok, "location fixed by prior \"at\""); return; } pObj->mProp |= A_AT; pObj->eWith = pEdge ? pEdge->eEdge : CP_C; if( pObj->eWith>=CP_END ){ int dir = pObj->eWith==CP_END ? pObj->outDir : (pObj->inDir+2)%4; pObj->eWith = eDirToCp[dir]; } pObj->with = *pAt; } /* ** Try to add a text attribute to an object */ static void pik_add_txt(Pik *p, PToken *pTxt, int iPos){ PObj *pObj = p->cur; PToken *pT; if( pObj->nTxt >= count(pObj->aTxt) ){ pik_error(p, pTxt, "too many text terms"); return; } p->mAttr |= ATTR_BASELINE; if( iPos & TP_BOLD ) p->mAttr |= ATTR_F_BOLD; if( iPos & TP_ITALIC ) p->mAttr |= ATTR_F_ITALIC; if( iPos & TP_MONO ) p->mAttr |= ATTR_F_MONO; if( iPos & TP_LJUST){ p->mAttr |= ATTR_T_START; }else if( iPos & TP_RJUST){ p->mAttr |= ATTR_T_END; }else{ p->mAttr |= ATTR_T_MIDDLE; } pT = &pObj->aTxt[pObj->nTxt++]; *pT = *pTxt; pT->eCode = (short)iPos; } /* Merge "text-position" flags */ static int pik_text_position(int iPrev, PToken *pFlag){ int iRes = iPrev; switch( pFlag->eType ){ case T_LJUST: iRes = (iRes&~TP_JMASK) | TP_LJUST; break; case T_RJUST: iRes = (iRes&~TP_JMASK) | TP_RJUST; break; case T_ABOVE: iRes = (iRes&~TP_VMASK) | TP_ABOVE; break; case T_CENTER: iRes = (iRes&~TP_VMASK) | TP_CENTER; break; case T_BELOW: iRes = (iRes&~TP_VMASK) | TP_BELOW; break; case T_ITALIC: iRes |= TP_ITALIC; break; case T_BOLD: iRes |= TP_BOLD; break; case T_MONO: iRes |= TP_MONO; break; case T_ALIGNED: iRes |= TP_ALIGN; break; case T_BIG: if( iRes & TP_BIG ) iRes |= TP_XTRA; else iRes = (iRes &~TP_SZMASK)|TP_BIG; break; case T_SMALL: if( iRes & TP_SMALL ) iRes |= TP_XTRA; else iRes = (iRes &~TP_SZMASK)|TP_SMALL; break; } return iRes; } /* ** Table of scale-factor estimates for variable-width characters. ** Actual character widths vary by font. These numbers are only ** guesses. And this table only provides data for ASCII. ** ** 100 means normal width. */ static const unsigned char awChar[] = { /* Skip initial 32 control characters */ /* ' ' */ 45, /* '!' */ 55, /* '"' */ 62, /* '#' */ 115, /* '$' */ 90, /* '%' */ 132, /* '&' */ 125, /* '\''*/ 40, /* '(' */ 55, /* ')' */ 55, /* '*' */ 71, /* '+' */ 115, /* ',' */ 45, /* '-' */ 48, /* '.' */ 45, /* '/' */ 50, /* '0' */ 91, /* '1' */ 91, /* '2' */ 91, /* '3' */ 91, /* '4' */ 91, /* '5' */ 91, /* '6' */ 91, /* '7' */ 91, /* '8' */ 91, /* '9' */ 91, /* ':' */ 50, /* ';' */ 50, /* '<' */ 120, /* '=' */ 120, /* '>' */ 120, /* '?' */ 78, /* '@' */ 142, /* 'A' */ 102, /* 'B' */ 105, /* 'C' */ 110, /* 'D' */ 115, /* 'E' */ 105, /* 'F' */ 98, /* 'G' */ 105, /* 'H' */ 125, /* 'I' */ 58, /* 'J' */ 58, /* 'K' */ 107, /* 'L' */ 95, /* 'M' */ 145, /* 'N' */ 125, /* 'O' */ 115, /* 'P' */ 95, /* 'Q' */ 115, /* 'R' */ 107, /* 'S' */ 95, /* 'T' */ 97, /* 'U' */ 118, /* 'V' */ 102, /* 'W' */ 150, /* 'X' */ 100, /* 'Y' */ 93, /* 'Z' */ 100, /* '[' */ 58, /* '\\'*/ 50, /* ']' */ 58, /* '^' */ 119, /* '_' */ 72, /* '`' */ 72, /* 'a' */ 86, /* 'b' */ 92, /* 'c' */ 80, /* 'd' */ 92, /* 'e' */ 85, /* 'f' */ 52, /* 'g' */ 92, /* 'h' */ 92, /* 'i' */ 47, /* 'j' */ 47, /* 'k' */ 88, /* 'l' */ 48, /* 'm' */ 135, /* 'n' */ 92, /* 'o' */ 86, /* 'p' */ 92, /* 'q' */ 92, /* 'r' */ 69, /* 's' */ 75, /* 't' */ 58, /* 'u' */ 92, /* 'v' */ 80, /* 'w' */ 121, /* 'x' */ 81, /* 'y' */ 80, /* 'z' */ 76, /* '{' */ 91, /* '|'*/ 49, /* '}' */ 91, /* '~' */ 118, }; /* Return an estimate of the width of the displayed characters ** in a character string. The returned value is 100 times the ** average character width. ** ** Omit "\" used to escape characters. And count entities like ** "<" as a single character. Multi-byte UTF8 characters count ** as a single character. ** ** Unless using a monospaced font, attempt to scale the answer by ** the actual characters seen. Wide characters count more than ** narrow characters. But the widths are only guesses. ** */ static int pik_text_length(const PToken *pToken, const int isMonospace){ const int stdAvg=100, monoAvg=82; int n = pToken->n; const char *z = pToken->z; int cnt, j; for(j=1, cnt=0; j<n-1; j++){ char c = z[j]; if( c=='\\' && z[j+1]!='&' ){ c = z[++j]; }else if( c=='&' ){ int k; for(k=j+1; k<j+7 && z[k]!=0 && z[k]!=';'; k++){} if( z[k]==';' ) j = k; cnt += (isMonospace ? monoAvg : stdAvg) * 3 / 2; continue; } if( (c & 0xc0)==0xc0 ){ while( j+1<n-1 && (z[j+1]&0xc0)==0x80 ){ j++; } cnt += isMonospace ? monoAvg : stdAvg; continue; } if( isMonospace ){ cnt += monoAvg; }else if( c >= 0x20 && c <= 0x7e ){ cnt += awChar[c-0x20]; }else{ cnt += stdAvg; } } return cnt; } /* Adjust the width, height, and/or radius of the object so that ** it fits around the text that has been added so far. ** ** (1) Only text specified prior to this attribute is considered. ** (2) The text size is estimated based on the charht and charwid ** variable settings. ** (3) The fitted attributes can be changed again after this ** attribute, for example using "width 110%" if this auto-fit ** underestimates the text size. ** (4) Previously set attributes will not be altered. In other words, ** "width 1in fit" might cause the height to change, but the ** width is now set. ** (5) This only works for attributes that have an xFit method. ** ** The eWhich parameter is: ** ** 1: Fit horizontally only ** 2: Fit vertically only ** 3: Fit both ways */ static void pik_size_to_fit(Pik *p, PToken *pFit, int eWhich){ PObj *pObj; PNum w, h; PBox bbox; if( p->nErr ) return; pObj = p->cur; if( pObj->nTxt==0 ){ pik_error(0, pFit, "no text to fit to"); return; } if( pObj->type->xFit==0 ) return; pik_bbox_init(&bbox); pik_compute_layout_settings(p); pik_append_txt(p, pObj, &bbox); if( (eWhich & 1)!=0 || pObj->bAltAutoFit ){ w = (bbox.ne.x - bbox.sw.x) + p->charWidth; }else{ w = 0; } if( (eWhich & 2)!=0 || pObj->bAltAutoFit ){ PNum h1, h2; h1 = (bbox.ne.y - pObj->ptAt.y); h2 = (pObj->ptAt.y - bbox.sw.y); h = 2.0*( h1<h2 ? h2 : h1 ) + 0.5*p->charHeight; }else{ h = 0; } pObj->type->xFit(p, pObj, w, h); pObj->mProp |= A_FIT; } /* Set a local variable name to "val". ** ** The name might be a built-in variable or a color name. In either case, ** a new application-defined variable is set. Since app-defined variables ** are searched first, this will override any built-in variables. */ static void pik_set_var(Pik *p, PToken *pId, PNum val, PToken *pOp){ PVar *pVar = p->pVar; while( pVar ){ if( pik_token_eq(pId,pVar->zName)==0 ) break; pVar = pVar->pNext; } if( pVar==0 ){ char *z; pVar = malloc( pId->n+1 + sizeof(*pVar) ); if( pVar==0 ){ pik_error(p, 0, 0); return; } pVar->zName = z = (char*)&pVar[1]; memcpy(z, pId->z, pId->n); z[pId->n] = 0; pVar->pNext = p->pVar; pVar->val = pik_value(p, pId->z, pId->n, 0); p->pVar = pVar; } double oldVal = pVar->val; switch( pOp->eCode ){ case T_PLUS: pVar->val += val; break; case T_STAR: pVar->val *= val; break; if( val==0.0 ){ pik_error(p, pOp, "division by zero"); }else{ pVar->val /= val; } break; default: pVar->val = val; break; } // This is an ugly hack, but it keeps diagrams // output-identical. It should be removed to make // textcolor independent of color. if( strcmp(pVar->zName, "color")==0 ) { PVar *pVarT = p->pVar->pNext; while( pVarT ){ if( strcmp(pVarT->zName, "textcolor")==0 ) break; pVarT = pVarT->pNext; } if( pVarT == 0 ){ // "textcolor" + 1 = 10 pVarT = malloc( 10 + sizeof(*pVarT) ); if( pVarT==0 ){ pik_error(p, 0, 0); return; } char *zT; pVarT->zName = zT = (char*)&pVarT[1]; memcpy(zT, "textcolor", 9); zT[9] = 0; pVarT->val = pVar->val; pVarT->pNext = p->pVar; p->pVar = pVarT; }else if( pVarT->val == oldVal ){ pVarT->val = pVar->val; } } p->bLayoutVars = 0; /* Clear the layout setting cache */ } /* ** Round a PNum into the nearest integer */ static int pik_round(PNum v){ if( isnan(v) ) return 0; if( v < -2147483647 ) return (-2147483647-1); if( v >= 2147483647 ) return 2147483647; return (int)v; } /* ** Search for the variable named z[0..n-1] in: ** ** * Application defined variables ** * Built-in variables ** ** Return the value of the variable if found. If not found ** return 0.0. Also if pMiss is not NULL, then set it to 1 ** if not found. ** ** This routine is a subroutine to pik_get_var(). But it is also ** used by object implementations to look up (possibly overwritten) ** values for built-in variables like "boxwid". */ static PNum pik_value(Pik *p, const char *z, int n, int *pMiss){ PVar *pVar; int first, last, mid, c; for(pVar=p->pVar; pVar; pVar=pVar->pNext){ if( strncmp(pVar->zName,z,n)==0 && pVar->zName[n]==0 ){ return pVar->val; } } first = 0; last = count(aBuiltin)-1; while( first<=last ){ mid = (first+last)/2; c = strncmp(z,aBuiltin[mid].zName,n); if( c==0 && aBuiltin[mid].zName[n] ) c = 1; if( c==0 ) return aBuiltin[mid].val; if( c>0 ){ first = mid+1; }else{ last = mid-1; } } // Added in response to [this forum thread](https://pikchr.org/home/forumpost/89e73c4053d154df) // It is otherwise unrelated to the functionality provided by this branch. if( strncmp("nTokens", z, n)==0 ) { return p->nToken; }; if( pMiss ) *pMiss = 1; return 0.0; } static int pik_value_int(Pik *p, const char *z, int n, int *pMiss){ return pik_round(pik_value(p,z,n,pMiss)); } /* ** Look up a color-name. Unlike other names in this program, the ** color-names are not case sensitive. So "DarkBlue" and "darkblue" ** and "DARKBLUE" all find the same value (139). ** ** If not found, return -99.0. Also post an error if p!=NULL. ** ** Special color names "None" and "Off" return -1.0 without causing ** an error. */ static PNum pik_lookup_color(Pik *p, PToken *pId){ int first, last, mid, c = 0; first = 0; last = count(aColor)-1; while( first<=last ){ const char *zClr; int c1, c2; unsigned int i; mid = (first+last)/2; zClr = aColor[mid].zName; for(i=0; i<pId->n; i++){ c1 = zClr[i]&0x7f; if( isupper(c1) ) c1 = tolower(c1); c2 = pId->z[i]&0x7f; if( isupper(c2) ) c2 = tolower(c2); c = c2 - c1; if( c ) break; } if( c==0 && aColor[mid].zName[pId->n] ) c = -1; if( c==0 ) return (double)aColor[mid].val; if( c>0 ){ first = mid+1; }else{ last = mid-1; } } if( p ) pik_error(p, pId, "not a known color name"); return -99.0; } /* Get the value of a variable. ** ** Search in order: ** ** * Application defined variables ** * Built-in variables ** * Color names ** ** If no such variable is found, throw an error. */ static PNum pik_get_var(Pik *p, PToken *pId){ int miss = 0; PNum v = pik_value(p, pId->z, pId->n, &miss); if( miss==0 ) return v; v = pik_lookup_color(0, pId); if( v>-90.0 ) return v; pik_error(p,pId,"no such variable"); return 0.0; } /* Convert a T_NTH token (ex: "2nd", "5th"} into a numeric value and ** return that value. Throw an error if the value is too big. */ static short int pik_nth_value(Pik *p, PToken *pNth){ int i = atoi(pNth->z); if( i>1000 ){ pik_error(p, pNth, "value too big - max '1000th'"); i = 1; } if( i==0 && pik_token_eq(pNth,"first")==0 ) i = 1; return (short int)i; } /* Search for the NTH object. ** ** If pBasis is not NULL then it should be a [] object. Use the ** sublist of that [] object for the search. If pBasis is not a [] ** object, then throw an error. ** ** The pNth token describes the N-th search. The pNth->eCode value ** is one more than the number of items to skip. It is negative ** to search backwards. If pNth->eType==T_ID, then it is the name ** of a class to search for. If pNth->eType==T_LB, then ** search for a [] object. If pNth->eType==T_LAST, then search for ** any type. ** ** Raise an error if the item is not found. */ static PObj *pik_find_nth(Pik *p, PObj *pBasis, PToken *pNth){ PList *pList; int i, n; const PClass *pClass; if( pBasis==0 ){ pList = p->list; }else{ pList = pBasis->pSublist; } if( pList==0 ){ pik_error(p, pNth, "no such object"); return 0; } if( pNth->eType==T_LAST ){ pClass = 0; }else if( pNth->eType==T_LB ){ pClass = &sublistClass; }else{ pClass = pik_find_class(pNth); if( pClass==0 ){ pik_error(0, pNth, "no such object type"); return 0; } } n = pNth->eCode; if( n<0 ){ for(i=pList->n-1; i>=0; i--){ PObj *pObj = pList->a[i]; if( pClass && pObj->type!=pClass ) continue; n++; if( n==0 ){ return pObj; } } }else{ for(i=0; i<pList->n; i++){ PObj *pObj = pList->a[i]; if( pClass && pObj->type!=pClass ) continue; n--; if( n==0 ){ return pObj; } } } pik_error(p, pNth, "no such object"); return 0; } /* Search for an object by name. ** ** Search in pBasis->pSublist if pBasis is not NULL. If pBasis is NULL ** then search in p->list. */ static PObj *pik_find_byname(Pik *p, PObj *pBasis, PToken *pName){ PList *pList; int i, j; if( pBasis==0 ){ pList = p->list; }else{ pList = pBasis->pSublist; } if( pList==0 ){ pik_error(p, pName, "no such object"); return 0; } /* First look explicitly tagged objects */ for(i=pList->n-1; i>=0; i--){ PObj *pObj = pList->a[i]; if( pObj->zName && pik_token_eq(pName,pObj->zName)==0 ){ p->lastRef = pObj; return pObj; } } /* If not found, do a second pass looking for any object containing ** text which exactly matches pName */ for(i=pList->n-1; i>=0; i--){ PObj *pObj = pList->a[i]; for(j=0; j<pObj->nTxt; j++){ if( pObj->aTxt[j].n==pName->n+2 && memcmp(pObj->aTxt[j].z+1,pName->z,pName->n)==0 ){ p->lastRef = pObj; return pObj; } } } pik_error(p, pName, "no such object"); return 0; } /* Change most of the settings for the current object to be the ** same as the pOther object, or the most recent object of the same ** type if pOther is NULL. */ static void pik_same(Pik *p, PObj *pOther, PToken *pErrTok){ PObj *pObj = p->cur; if( p->nErr ) return; if( pOther==0 ){ int i; for(i=(p->list ? p->list->n : 0)-1; i>=0; i--){ pOther = p->list->a[i]; if( pOther->type==pObj->type ) break; } if( i<0 ){ pik_error(p, pErrTok, "no prior objects of the same type"); return; } } if( pOther->nPath && pObj->type->isLine ){ PNum dx, dy; int i; dx = p->aTPath[0].x - pOther->aPath[0].x; dy = p->aTPath[0].y - pOther->aPath[0].y; for(i=1; i<pOther->nPath; i++){ p->aTPath[i].x = pOther->aPath[i].x + dx; p->aTPath[i].y = pOther->aPath[i].y + dy; } p->nTPath = pOther->nPath; p->mTPath = 3; p->samePath = 1; } if( !pObj->type->isLine ){ pObj->w = pOther->w; pObj->h = pOther->h; } pObj->rad = pOther->rad; pObj->sw = pOther->sw; pObj->dashed = pOther->dashed; pObj->dotted = pOther->dotted; pObj->fill = pOther->fill; pObj->color = pOther->color; pObj->textcolor = pOther->textcolor; pObj->cw = pOther->cw; pObj->larrow = pOther->larrow; pObj->rarrow = pOther->rarrow; pObj->bClose = pOther->bClose; pObj->bChop = pOther->bChop; pObj->iLayer = pOther->iLayer; } /* Return a "Place" associated with object pObj. If pEdge is NULL ** return the center of the object. Otherwise, return the corner ** described by pEdge. */ static PPoint pik_place_of_elem(Pik *p, PObj *pObj, PToken *pEdge){ PPoint pt = cZeroPoint; const PClass *pClass; if( pObj==0 ) return pt; if( pEdge==0 ){ return pObj->ptAt; } pClass = pObj->type; if( pEdge->eType==T_EDGEPT || (pEdge->eEdge>0 && pEdge->eEdge<CP_END) ){ pt = pClass->xOffset(p, pObj, pEdge->eEdge); pt.x += pObj->ptAt.x; pt.y += pObj->ptAt.y; return pt; } if( pEdge->eType==T_START ){ return pObj->ptEnter; }else{ return pObj->ptExit; } } /* Do a linear interpolation of two positions. */ static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2){ PPoint out; out.x = p2.x*x + p1.x*(1.0 - x); out.y = p2.y*x + p1.y*(1.0 - x); return out; } /* Compute the position that is dist away from pt at an heading angle of r ** ** The angle is a compass heading in degrees. North is 0 (or 360). ** East is 90. South is 180. West is 270. And so forth. */ static PPoint pik_position_at_angle(PNum dist, PNum r, PPoint pt){ r *= 0.017453292519943295769; /* degrees to radians */ pt.x += dist*sin(r); pt.y += dist*cos(r); return pt; } /* Compute the position that is dist away at a compass point */ static PPoint pik_position_at_hdg(PNum dist, PToken *pD, PPoint pt){ return pik_position_at_angle(dist, pik_hdg_angle[pD->eEdge], pt); } /* Return the coordinates for the n-th vertex of a line. */ static PPoint pik_nth_vertex(Pik *p, PToken *pNth, PToken *pErr, PObj *pObj){ static const PPoint zero = {0, 0}; int n; if( p->nErr || pObj==0 ) return p->aTPath[0]; if( !pObj->type->isLine ){ pik_error(p, pErr, "object is not a line"); return zero; } n = atoi(pNth->z); if( n<1 || n>pObj->nPath ){ pik_error(p, pNth, "no such vertex"); return zero; } return pObj->aPath[n-1]; } /* Return the value of a property of an object. */ static PNum pik_property_of(PObj *pObj, PToken *pProp){ PNum v = 0.0; if( pObj ){ switch( pProp->eType ){ case T_HEIGHT: v = pObj->h; break; case T_WIDTH: v = pObj->w; break; case T_RADIUS: v = pObj->rad; break; case T_DIAMETER: v = pObj->rad*2.0; break; case T_THICKNESS: v = pObj->sw; break; case T_DASHED: v = pObj->dashed; break; case T_DOTTED: v = pObj->dotted; break; case T_FILL: v = pObj->fill; break; case T_COLOR: v = pObj->color; break; case T_TEXTCOLOR: v = pObj->textcolor; break; case T_X: v = pObj->ptAt.x; break; case T_Y: v = pObj->ptAt.y; break; case T_TOP: v = pObj->bbox.ne.y; break; case T_BOTTOM: v = pObj->bbox.sw.y; break; case T_LEFT: v = pObj->bbox.sw.x; break; case T_RIGHT: v = pObj->bbox.ne.x; break; } } return v; } /* Compute one of the built-in functions */ static PNum pik_func(Pik *p, PToken *pFunc, PNum x, PNum y){ PNum v = 0.0; switch( pFunc->eCode ){ case FN_ABS: v = x<0.0 ? -x : x; break; case FN_COS: v = cos(x); break; case FN_INT: v = rint(x); break; case FN_SIN: v = sin(x); break; case FN_SQRT: if( x<0.0 ){ pik_error(p, pFunc, "sqrt of negative value"); v = 0.0; }else{ v = sqrt(x); } break; case FN_MAX: v = x>y ? x : y; break; case FN_MIN: v = x<y ? x : y; break; default: v = 0.0; } return v; } /* Attach a name to an object */ static void pik_elem_setname(Pik *p, PObj *pObj, PToken *pName){ if( pObj==0 ) return; if( pName==0 ) return; free(pObj->zName); pObj->zName = malloc(pName->n+1); if( pObj->zName==0 ){ pik_error(p,0,0); }else{ memcpy(pObj->zName,pName->z,pName->n); pObj->zName[pName->n] = 0; } return; } /* ** Search for object located at *pCenter that has an xChop method and ** that does not enclose point pOther. ** ** Return a pointer to the object, or NULL if not found. */ static PObj *pik_find_chopper(PList *pList, PPoint *pCenter, PPoint *pOther){ int i; if( pList==0 ) return 0; for(i=pList->n-1; i>=0; i--){ PObj *pObj = pList->a[i]; if( pObj->type->xChop!=0 && pObj->ptAt.x==pCenter->x && pObj->ptAt.y==pCenter->y && !pik_bbox_contains_point(&pObj->bbox, pOther) ){ return pObj; }else if( pObj->pSublist ){ pObj = pik_find_chopper(pObj->pSublist,pCenter,pOther); if( pObj ) return pObj; } } return 0; } /* ** There is a line traveling from pFrom to pTo. ** ** If pObj is not null and is a choppable object, then chop at ** the boundary of pObj - where the line crosses the boundary ** of pObj. ** ** If pObj is NULL or has no xChop method, then search for some ** other object centered at pTo that is choppable and use it ** instead. */ static void pik_autochop(Pik *p, PPoint *pFrom, PPoint *pTo, PObj *pObj){ if( pObj==0 || pObj->type->xChop==0 ){ pObj = pik_find_chopper(p->list, pTo, pFrom); } if( pObj ){ *pTo = pObj->type->xChop(p, pObj, pFrom); } } /* This routine runs after all attributes have been received ** on an object. */ static void pik_after_adding_attributes(Pik *p, PObj *pObj){ int i; PPoint ofst; PNum dx, dy; if( p->nErr ) return; /* Check for transparency */ if( pObj->fill<0.0 || (pObj->type->isLine && !pObj->bClose) ){ p->mAttr |= ATTR_FILL_NO; } /* Position block objects */ if( pObj->type->isLine==0 ){ /* A height or width less than or equal to zero means "autofit". ** Change the height or width to be big enough to contain the text, */ if( pObj->h<=0.0 ){ if( pObj->nTxt==0 ){ pObj->h = 0.0; }else if( pObj->w<=0.0 ){ pik_size_to_fit(p, &pObj->errTok, 3); }else{ pik_size_to_fit(p, &pObj->errTok, 2); } } if( pObj->w<=0.0 ){ if( pObj->nTxt==0 ){ pObj->w = 0.0; }else{ pik_size_to_fit(p, &pObj->errTok, 1); } } ofst = pik_elem_offset(p, pObj, pObj->eWith); dx = (pObj->with.x - ofst.x) - pObj->ptAt.x; dy = (pObj->with.y - ofst.y) - pObj->ptAt.y; if( dx!=0 || dy!=0 ){ pik_elem_move(pObj, dx, dy); } } /* For a line object with no movement specified, a single movement ** of the default length in the current direction */ if( pObj->type->isLine && p->nTPath<2 ){ pik_next_rpath(p, 0); assert( p->nTPath==2 ); switch( pObj->inDir ){ default: p->aTPath[1].x += pObj->w; break; case DIR_DOWN: p->aTPath[1].y -= pObj->h; break; case DIR_LEFT: p->aTPath[1].x -= pObj->w; break; case DIR_UP: p->aTPath[1].y += pObj->h; break; } if( pObj->type->xInit==arcInit ){ pObj->outDir = (pObj->inDir + (pObj->cw ? 1 : 3))%4; p->eDir = (unsigned char)pObj->outDir; switch( pObj->outDir ){ default: p->aTPath[1].x += pObj->w; break; case DIR_DOWN: p->aTPath[1].y -= pObj->h; break; case DIR_LEFT: p->aTPath[1].x -= pObj->w; break; case DIR_UP: p->aTPath[1].y += pObj->h; break; } } } /* Initialize the bounding box prior to running xCheck */ pik_bbox_init(&pObj->bbox); /* Run object-specific code */ if( pObj->type->xCheck!=0 ){ pObj->type->xCheck(p,pObj); if( p->nErr ) return; } /* Compute final bounding box, entry and exit points, center ** point (ptAt) and path for the object */ if( pObj->type->isLine ){ pObj->aPath = malloc( sizeof(PPoint)*p->nTPath ); if( pObj->aPath==0 ){ pik_error(p, 0, 0); return; }else{ pObj->nPath = p->nTPath; for(i=0; i<p->nTPath; i++){ pObj->aPath[i] = p->aTPath[i]; } } /* "chop" processing: ** If the line goes to the center of an object with an ** xChop method, then use the xChop method to trim the line. */ if( pObj->bChop && pObj->nPath>=2 ){ int n = pObj->nPath; pik_autochop(p, &pObj->aPath[n-2], &pObj->aPath[n-1], pObj->pTo); pik_autochop(p, &pObj->aPath[1], &pObj->aPath[0], pObj->pFrom); } pObj->ptEnter = pObj->aPath[0]; pObj->ptExit = pObj->aPath[pObj->nPath-1]; /* Compute the center of the line based on the bounding box over ** the vertexes. This is a difference from PIC. In Pikchr, the ** center of a line is the center of its bounding box. In PIC, the ** center of a line is halfway between its .start and .end. For ** straight lines, this is the same point, but for multi-segment ** lines the result is usually diferent */ for(i=0; i<pObj->nPath; i++){ pik_bbox_add_xy(&pObj->bbox, pObj->aPath[i].x, pObj->aPath[i].y); } pObj->ptAt.x = (pObj->bbox.ne.x + pObj->bbox.sw.x)/2.0; pObj->ptAt.y = (pObj->bbox.ne.y + pObj->bbox.sw.y)/2.0; /* Reset the width and height of the object to be the width and height ** of the bounding box over vertexes */ pObj->w = pObj->bbox.ne.x - pObj->bbox.sw.x; pObj->h = pObj->bbox.ne.y - pObj->bbox.sw.y; /* If this is a polygon (if it has the "close" attribute), then ** adjust the exit point */ if( pObj->bClose ){ /* For "closed" lines, the .end is one of the .e, .s, .w, or .n ** points of the bounding box, as with block objects. */ pik_elem_set_exit(pObj, pObj->inDir); } }else{ PNum w2 = pObj->w/2.0; PNum h2 = pObj->h/2.0; pObj->ptEnter = pObj->ptAt; pObj->ptExit = pObj->ptAt; switch( pObj->inDir ){ default: pObj->ptEnter.x -= w2; break; case DIR_LEFT: pObj->ptEnter.x += w2; break; case DIR_UP: pObj->ptEnter.y -= h2; break; case DIR_DOWN: pObj->ptEnter.y += h2; break; } switch( pObj->outDir ){ default: pObj->ptExit.x += w2; break; case DIR_LEFT: pObj->ptExit.x -= w2; break; case DIR_UP: pObj->ptExit.y += h2; break; case DIR_DOWN: pObj->ptExit.y -= h2; break; } pik_bbox_add_xy(&pObj->bbox, pObj->ptAt.x - w2, pObj->ptAt.y - h2); pik_bbox_add_xy(&pObj->bbox, pObj->ptAt.x + w2, pObj->ptAt.y + h2); } p->eDir = (unsigned char)pObj->outDir; } /* Show basic information about each object as a comment in the ** generated HTML. Used for testing and debugging. Activated ** by the (undocumented) "debug = 1;" ** command. */ static void pik_elem_render(Pik *p, PObj *pObj){ char *zDir; if( pObj==0 ) return; pik_append(p,"<!-- ", -1); if( pObj->zName ){ pik_append_text(p, pObj->zName, -1, 0); pik_append(p, ": ", 2); } pik_append_text(p, pObj->type->zName, -1, 0); if( pObj->nTxt ){ pik_append(p, " \"", 2); pik_append_text(p, pObj->aTxt[0].z+1, pObj->aTxt[0].n-2, 1); pik_append(p, "\"", 1); } pik_append_num(p, " w=", pObj->w); pik_append_num(p, " h=", pObj->h); pik_append_point(p, " center=", &pObj->ptAt); pik_append_point(p, " enter=", &pObj->ptEnter); switch( pObj->outDir ){ default: zDir = " right"; break; case DIR_LEFT: zDir = " left"; break; case DIR_UP: zDir = " up"; break; case DIR_DOWN: zDir = " down"; break; } pik_append_point(p, " exit=", &pObj->ptExit); pik_append(p, zDir, -1); pik_append(p, " -->\n", -1); } /* Render a list of objects */ void pik_elist_render(Pik *p, PList *pList){ int i; int iNextLayer = 0; int iThisLayer; int bMoreToDo; int miss = 0; int mDebug = pik_value_int(p, "debug", 5, 0); PNum colorLabel; do{ bMoreToDo = 0; iThisLayer = iNextLayer; iNextLayer = 0x7fffffff; for(i=0; i<pList->n; i++){ PObj *pObj = pList->a[i]; if( pObj->pSublist && pObj->iLayer==iThisLayer ){ pik_append_tag_open(p, pObj, "g"); pik_append(p, "\" >\n", 4); }; void (*xRender)(Pik*,PObj*); if( pObj->iLayer>iThisLayer ){ if( pObj->iLayer<iNextLayer ) iNextLayer = pObj->iLayer; bMoreToDo = 1; continue; /* Defer until another round */ }else if( pObj->iLayer<iThisLayer ){ continue; } if( mDebug & 1 ) pik_elem_render(p, pObj); xRender = pObj->type->xRender; if( xRender ){ xRender(p, pObj); } if( pObj->pSublist ){ pik_elist_render(p, pObj->pSublist); } if( pObj->pSublist && pObj->iLayer==iThisLayer ) { pik_append(p, "</g>\n", -1); } } }while( bMoreToDo ); /* If the color_debug_label value is defined, then go through ** and paint a dot at every label location */ colorLabel = pik_value(p, "debug_label_color", 17, &miss); if( miss==0 && colorLabel>=0.0 ){ PObj dot; memset(&dot, 0, sizeof(dot)); dot.type = &noopClass; dot.rad = 0.015; dot.sw = 0.015; dot.fill = colorLabel; dot.color = colorLabel; dot.textcolor = colorLabel; dot.nTxt = 1; dot.aTxt[0].eCode = TP_ABOVE; for(i=0; i<pList->n; i++){ PObj *pObj = pList->a[i]; if( pObj->zName==0 ) continue; dot.ptAt = pObj->ptAt; dot.aTxt[0].z = pObj->zName; dot.aTxt[0].n = (int)strlen(pObj->zName); dotRender(p, &dot); } } } /* Add all objects of the list pList to the bounding box */ static void pik_bbox_add_elist(Pik *p, PList *pList, PNum wArrow){ int i; for(i=0; i<pList->n; i++){ PObj *pObj = pList->a[i]; if( pObj->sw>=0.0 ) pik_bbox_addbox(&p->bbox, &pObj->bbox); pik_append_txt(p, pObj, &p->bbox); if( pObj->pSublist ) pik_bbox_add_elist(p, pObj->pSublist, wArrow); /* Expand the bounding box to account for arrowheads on lines */ if( pObj->type->isLine && pObj->nPath>0 ){ if( pObj->larrow ){ pik_bbox_addellipse(&p->bbox, pObj->aPath[0].x, pObj->aPath[0].y, wArrow, wArrow); } if( pObj->rarrow ){ int j = pObj->nPath-1; pik_bbox_addellipse(&p->bbox, pObj->aPath[j].x, pObj->aPath[j].y, wArrow, wArrow); } } } } /* Recompute key layout parameters from variables. */ static void pik_compute_layout_settings(Pik *p){ PNum thickness; /* Line thickness */ PNum wArrow; /* Width of arrowheads */ /* Set up rendering parameters */ if( p->bLayoutVars ) return; thickness = pik_value(p,"thickness",9,0); if( thickness<=0.01 ) thickness = 0.01; wArrow = 0.5*pik_value(p,"arrowwid",8,0); p->wArrow = wArrow/thickness; p->hArrow = pik_value(p,"arrowht",7,0)/thickness; p->fontScale = pik_value(p,"fontscale",9,0); if( p->fontScale<=0.0 ) p->fontScale = 1.0; p->rScale = 144.0; p->charWidth = pik_value(p,"charwid",7,0)*p->fontScale; p->charHeight = pik_value(p,"charht",6,0)*p->fontScale; p->bLayoutVars = 1; } /* Render a list of objects. Write the SVG into p->zOut. ** Delete the input object_list before returnning. */ static void pik_render(Pik *p, PList *pList){ if( pList==0 ) return; if( p->nErr==0 ){ PNum thickness; /* Stroke width */ PNum margin; /* Extra bounding box margin */ PNum w, h; /* Drawing width and height */ PNum wArrow; PNum pikScale; /* Value of the "scale" variable */ int miss = 0; /* Set up rendering parameters */ pik_compute_layout_settings(p); thickness = pik_value(p,"thickness",9,0); if( thickness<=0.01 ) thickness = 0.01; margin = pik_value(p,"margin",6,0); margin += thickness; wArrow = p->wArrow*thickness; miss = 0; p->fgcolor = pik_value_int(p,"fgcolor",7,&miss); if( miss ){ PToken t; t.z = "fgcolor"; t.n = 7; p->fgcolor = pik_round(pik_lookup_color(0, &t)); } miss = 0; p->bgcolor = pik_value_int(p,"bgcolor",7,&miss); if( miss ){ PToken t; t.z = "bgcolor"; t.n = 7; p->bgcolor = pik_round(pik_lookup_color(0, &t)); } /* Compute a bounding box over all objects so that we can know ** how big to declare the SVG canvas */ pik_bbox_init(&p->bbox); pik_bbox_add_elist(p, pList, wArrow); /* Expand the bounding box slightly to account for line thickness ** and the optional "margin = EXPR" setting. */ p->bbox.ne.x += margin + pik_value(p,"rightmargin",11,0); p->bbox.ne.y += margin + pik_value(p,"topmargin",9,0); p->bbox.sw.x -= margin + pik_value(p,"leftmargin",10,0); p->bbox.sw.y -= margin + pik_value(p,"bottommargin",12,0); /* Output the SVG */ pik_append(p, "<svg xmlns='http://www.w3.org/2000/svg'" " style='font-size:initial;'",-1); if( p->zClass ){ pik_append(p, " class=\"", -1); pik_append(p, p->zClass, -1); pik_append(p, "\" id=\"", -1); pik_append(p, p->zClass, -1); pik_append(p, p->zID, -1); pik_append(p, "\"", 1); }else{ pik_append(p, " id=\"id-", -1); pik_append(p, p->zID, -1); pik_append(p, "\"", 1); } pik_append(p, " role=\"img\"", -1); int aria_start = 0; if( p->zTitle ){ if( p->bLabel ){ pik_append(p, " aria-label=\"", -1); pik_append_str(p, strlen(p->zTitle), p->zTitle, 0x6); pik_append(p, "\"", 1); }else{ aria_start = 1; pik_append(p, " aria-labelledby=\"title", -1); pik_append(p, p->zID, -1); } } if( p->zDesc ){ if( !aria_start ){ aria_start = 1; pik_append(p, " aria-labelledby=\"", -1); }else{ pik_append(p, " ", 1); } pik_append(p, "desc", -1); pik_append(p, p->zID, -1); } if( aria_start ){ pik_append(p, "\"", 1); } w = p->bbox.ne.x - p->bbox.sw.x; h = p->bbox.ne.y - p->bbox.sw.y; p->wSVG = pik_round(p->rScale*w); p->hSVG = pik_round(p->rScale*h); pikScale = pik_value(p,"scale",5,0); if( pikScale>=0.001 && pikScale<=1000.0 && (pikScale<0.99 || pikScale>1.01) ){ p->wSVG = pik_round(p->wSVG*pikScale); p->hSVG = pik_round(p->hSVG*pikScale); pik_append_num(p, " width=\"", p->wSVG); pik_append_num(p, "\" height=\"", p->hSVG); pik_append(p, "\"", 1); } pik_append_dis(p, " viewBox=\"0 0 ",w,""); pik_append_dis(p, " ",h,"\">\n"); // Closes <svg> // CSS pik_append(p, "<style>\n#", -1); pik_append(p, p->zClass, -1); pik_append(p, p->zID, -1); pik_append(p, " {\n", 3); if( p->mAttr & ATTR_BASELINE ){ pik_append(p, "._D {dominant-baseline:central}\n", -1); } if( p->mAttr & ATTR_FILL_NO ){ pik_append(p, "._T {fill:transparent}\n", -1); } if( p->mAttr & ATTR_T_START ){ pik_append(p, "._tS {text-anchor:start}\n", -1); } if( p->mAttr & ATTR_T_MIDDLE ){ pik_append(p, "._tM {text-anchor:middle}\n", -1); } if( p->mAttr & ATTR_T_END ){ pik_append(p, "._tE {text-anchor:end}\n", -1); } if( p->mAttr & ATTR_F_BOLD ){ pik_append(p, "._Fb {font-weight:bold}\n", -1); } if( p->mAttr & ATTR_F_BOLD ){ pik_append(p, "._Fi {font-style:italic}\n", -1); } if( p->mAttr & ATTR_F_MONO ){ pik_append(p, "._Fm {font-family:monospace}\n", -1); } pik_append(p, "}\n</style>\n", -1); if( p->zTitle && !p->bLabel ){ pik_append(p, "<title id=\"title", -1); pik_append(p, p->zID, -1); pik_append(p, "\">\n", 3); pik_append_str(p, strlen(p->zTitle), p->zTitle, 0x2); pik_append(p, "\n</title>\n", 10); } if( p->zDesc ){ pik_append(p, "<desc id=\"desc", -1); pik_append(p, p->zID, -1); pik_append(p, "\">\n", 3); pik_append_str(p, strlen(p->zDesc), p->zDesc, 0x2); pik_append(p, "\n</desc>\n", 9); } pik_elist_render(p, pList); pik_append(p,"</svg>\n", -1); }else{ p->wSVG = -1; p->hSVG = -1; } pik_elist_free(p, pList); } /* ** An array of this structure defines a list of keywords. */ typedef struct PikWord { char *zWord; /* Text of the keyword */ unsigned char nChar; /* Length of keyword text in bytes */ unsigned char eType; /* Token code */ unsigned char eCode; /* Extra code for the token */ unsigned char eEdge; /* CP_* code for corner/edge keywords */ } PikWord; /* ** Keywords */ static const PikWord pik_keywords[] = { { "above", 5, T_ABOVE, 0, 0 }, { "abs", 3, T_FUNC1, FN_ABS, 0 }, { "aligned", 7, T_ALIGNED, 0, 0 }, { "and", 3, T_AND, 0, 0 }, { "as", 2, T_AS, 0, 0 }, { "assert", 6, T_ASSERT, 0, 0 }, { "at", 2, T_AT, 0, 0 }, { "behind", 6, T_BEHIND, 0, 0 }, { "below", 5, T_BELOW, 0, 0 }, { "between", 7, T_BETWEEN, 0, 0 }, { "big", 3, T_BIG, 0, 0 }, { "bold", 4, T_BOLD, 0, 0 }, { "bot", 3, T_EDGEPT, 0, CP_S }, { "bottom", 6, T_BOTTOM, 0, CP_S }, { "c", 1, T_EDGEPT, 0, CP_C }, { "ccw", 3, T_CCW, 0, 0 }, { "center", 6, T_CENTER, 0, CP_C }, { "chop", 4, T_CHOP, 0, 0 }, { "class", 5, T_CLASS, 0, 0, }, { "close", 5, T_CLOSE, 0, 0 }, { "color", 5, T_COLOR, 0, 0 }, { "cos", 3, T_FUNC1, FN_COS, 0 }, { "cw", 2, T_CW, 0, 0 }, { "dashed", 6, T_DASHED, 0, 0 }, { "define", 6, T_DEFINE, 0, 0 }, { "description", 11, T_DESCRIBE, 0, 0 }, { "diameter", 8, T_DIAMETER, 0, 0 }, { "dist", 4, T_DIST, 0, 0 }, { "dotted", 6, T_DOTTED, 0, 0 }, { "down", 4, T_DOWN, DIR_DOWN, 0 }, { "e", 1, T_EDGEPT, 0, CP_E }, { "east", 4, T_EDGEPT, 0, CP_E }, { "end", 3, T_END, 0, CP_END }, { "even", 4, T_EVEN, 0, 0 }, { "fill", 4, T_FILL, 0, 0 }, { "first", 5, T_NTH, 0, 0 }, { "fit", 3, T_FIT, 0, 0 }, { "from", 4, T_FROM, 0, 0 }, { "go", 2, T_GO, 0, 0 }, { "heading", 7, T_HEADING, 0, 0 }, { "height", 6, T_HEIGHT, 0, 0 }, { "ht", 2, T_HEIGHT, 0, 0 }, { "in", 2, T_IN, 0, 0 }, { "int", 3, T_FUNC1, FN_INT, 0 }, { "invis", 5, T_INVIS, 0, 0 }, { "invisible", 9, T_INVIS, 0, 0 }, { "italic", 6, T_ITALIC, 0, 0 }, { "label", 5, T_LABEL, 0, 0 }, { "last", 4, T_LAST, 0, 0 }, { "left", 4, T_LEFT, DIR_LEFT, CP_W }, { "ljust", 5, T_LJUST, 0, 0 }, { "max", 3, T_FUNC2, FN_MAX, 0 }, { "min", 3, T_FUNC2, FN_MIN, 0 }, { "mono", 4, T_MONO, 0, 0 }, { "monospace", 9, T_MONO, 0, 0 }, { "n", 1, T_EDGEPT, 0, CP_N }, { "ne", 2, T_EDGEPT, 0, CP_NE }, { "north", 5, T_EDGEPT, 0, CP_N }, { "nw", 2, T_EDGEPT, 0, CP_NW }, { "of", 2, T_OF, 0, 0 }, { "previous", 8, T_LAST, 0, 0, }, { "print", 5, T_PRINT, 0, 0 }, { "rad", 3, T_RADIUS, 0, 0 }, { "radius", 6, T_RADIUS, 0, 0 }, { "right", 5, T_RIGHT, DIR_RIGHT, CP_E }, { "rjust", 5, T_RJUST, 0, 0 }, { "s", 1, T_EDGEPT, 0, CP_S }, { "same", 4, T_SAME, 0, 0 }, { "se", 2, T_EDGEPT, 0, CP_SE }, { "sin", 3, T_FUNC1, FN_SIN, 0 }, { "small", 5, T_SMALL, 0, 0 }, { "solid", 5, T_SOLID, 0, 0 }, { "south", 5, T_EDGEPT, 0, CP_S }, { "sqrt", 4, T_FUNC1, FN_SQRT, 0 }, { "start", 5, T_START, 0, CP_START }, { "sw", 2, T_EDGEPT, 0, CP_SW }, { "t", 1, T_TOP, 0, CP_N }, { "textcolor", 9, T_TEXTCOLOR, 0, 0 }, { "the", 3, T_THE, 0, 0 }, { "then", 4, T_THEN, 0, 0 }, { "thick", 5, T_THICK, 0, 0 }, { "thickness", 9, T_THICKNESS, 0, 0 }, { "thin", 4, T_THIN, 0, 0 }, { "this", 4, T_THIS, 0, 0 }, { "title", 5, T_TITLE, 0, 0 }, { "to", 2, T_TO, 0, 0 }, { "top", 3, T_TOP, 0, CP_N }, { "until", 5, T_UNTIL, 0, 0 }, { "up", 2, T_UP, DIR_UP, 0 }, { "vertex", 6, T_VERTEX, 0, 0 }, { "w", 1, T_EDGEPT, 0, CP_W }, { "way", 3, T_WAY, 0, 0 }, { "west", 4, T_EDGEPT, 0, CP_W }, { "wid", 3, T_WIDTH, 0, 0 }, { "width", 5, T_WIDTH, 0, 0 }, { "with", 4, T_WITH, 0, 0 }, { "x", 1, T_X, 0, 0 }, { "y", 1, T_Y, 0, 0 }, }; /* ** Search a PikWordlist for the given keyword. Return a pointer to the ** keyword entry found. Or return 0 if not found. */ static const PikWord *pik_find_word( const char *zIn, /* Word to search for */ int n, /* Length of zIn */ const PikWord *aList, /* List to search */ int nList /* Number of entries in aList */ ){ int first = 0; int last = nList-1; while( first<=last ){ int mid = (first + last)/2; int sz = aList[mid].nChar; int c = strncmp(zIn, aList[mid].zWord, sz<n ? sz : n); if( c==0 ){ c = n - sz; if( c==0 ) return &aList[mid]; } if( c<0 ){ last = mid-1; }else{ first = mid+1; } } return 0; } /* ** Set a symbolic debugger breakpoint on this routine to receive a ** breakpoint when the "#breakpoint" token is parsed. */ static void pik_breakpoint(const unsigned char *z){ /* Prevent C compilers from optimizing out this routine. */ if( z[2]=='X' ) exit(1); } /* ** Return the length of next token. The token starts on ** the pToken->z character. Fill in other fields of the ** pToken object as appropriate. */ static int pik_token_length(PToken *pToken, int bAllowCodeBlock){ const unsigned char *z = (const unsigned char*)pToken->z; int i; unsigned char c, c2; switch( z[0] ){ case '\\': { pToken->eType = T_WHITESPACE; for(i=1; z[i]=='\r' || z[i]==' ' || z[i]=='\t'; i++){} if( z[i]=='\n' ) return i+1; pToken->eType = T_ERROR; return 1; } case ';': case '\n': { pToken->eType = T_EOL; return 1; } case '"': { for(i=1; (c = z[i])!=0; i++){ if( c=='\\' ){ if( z[i+1]==0 ) break; i++; continue; } if( c=='"' ){ pToken->eType = T_STRING; return i+1; } } pToken->eType = T_ERROR; return i; } case ' ': case '\t': case '\f': case '\r': { for(i=1; (c = z[i])==' ' || c=='\t' || c=='\r' || c=='\f'; i++){} pToken->eType = T_WHITESPACE; return i; } case '#': { for(i=1; (c = z[i])!=0 && c!='\n'; i++){} pToken->eType = T_WHITESPACE; /* If the comment is "#breakpoint" then invoke the pik_breakpoint() ** routine. The pik_breakpoint() routie is a no-op that serves as ** a convenient place to set a gdb breakpoint when debugging. */ if( strncmp((const char*)z,"#breakpoint",11)==0 ) pik_breakpoint(z); return i; } case '/': { if( z[1]=='*' ){ for(i=2; z[i]!=0 && (z[i]!='*' || z[i+1]!='/'); i++){} if( z[i]=='*' ){ pToken->eType = T_WHITESPACE; return i+2; }else{ pToken->eType = T_ERROR; return i; } }else if( z[1]=='/' ){ for(i=2; z[i]!=0 && z[i]!='\n'; i++){} pToken->eType = T_WHITESPACE; return i; }else if( z[1]=='=' ){ pToken->eType = T_ASSIGN; pToken->eCode = T_SLASH; return 2; }else{ pToken->eType = T_SLASH; return 1; } } case '+': { if( z[1]=='=' ){ pToken->eType = T_ASSIGN; pToken->eCode = T_PLUS; return 2; } pToken->eType = T_PLUS; return 1; } case '*': { if( z[1]=='=' ){ pToken->eType = T_ASSIGN; pToken->eCode = T_STAR; return 2; } pToken->eType = T_STAR; return 1; } case '%': { pToken->eType = T_PERCENT; return 1; } case '(': { pToken->eType = T_LP; return 1; } case ')': { pToken->eType = T_RP; return 1; } case '[': { pToken->eType = T_LB; return 1; } case ']': { pToken->eType = T_RB; return 1; } case ',': { pToken->eType = T_COMMA; return 1; } case ':': { pToken->eType = T_COLON; return 1; } case '>': { pToken->eType = T_GT; return 1; } case '=': { if( z[1]=='=' ){ pToken->eType = T_EQ; return 2; } pToken->eType = T_ASSIGN; pToken->eCode = T_ASSIGN; return 1; } case '-': { if( z[1]=='>' ){ pToken->eType = T_RARROW; return 2; }else if( z[1]=='=' ){ pToken->eType = T_ASSIGN; pToken->eCode = T_MINUS; return 2; }else{ pToken->eType = T_MINUS; return 1; } } case '<': { if( z[1]=='-' ){ if( z[2]=='>' ){ pToken->eType = T_LRARROW; return 3; }else{ pToken->eType = T_LARROW; return 2; } }else{ pToken->eType = T_LT; return 1; } } case 0xe2: { if( z[1]==0x86 ){ if( z[2]==0x90 ){ pToken->eType = T_LARROW; /* <- */ return 3; } if( z[2]==0x92 ){ pToken->eType = T_RARROW; /* -> */ return 3; } if( z[2]==0x94 ){ pToken->eType = T_LRARROW; /* <-> */ return 3; } } pToken->eType = T_ERROR; return 1; } case '{': { int len, depth; i = 1; if( bAllowCodeBlock ){ depth = 1; while( z[i] && depth>0 ){ PToken x; x.z = (char*)(z+i); len = pik_token_length(&x, 0); if( len==1 ){ if( z[i]=='{' ) depth++; if( z[i]=='}' ) depth--; } i += len; } }else{ depth = 0; } if( depth ){ pToken->eType = T_ERROR; return 1; } pToken->eType = T_CODEBLOCK; return i; } case '&': { static const struct { int nByte; /* Number of bytes in zEntity */ int eCode; /* Corresponding token code */ const char *zEntity; /* Name of the HTML entity */ } aEntity[] = { /* 123456789 1234567 */ { 6, T_RARROW, "→" }, /* Same as -> */ { 12, T_RARROW, "→" }, /* Same as -> */ { 6, T_LARROW, "←" }, /* Same as <- */ { 11, T_LARROW, "←" }, /* Same as <- */ { 16, T_LRARROW, "↔" }, /* Same as <-> */ }; unsigned int i; for(i=0; i<sizeof(aEntity)/sizeof(aEntity[0]); i++){ if( strncmp((const char*)z,aEntity[i].zEntity,aEntity[i].nByte)==0 ){ pToken->eType = aEntity[i].eCode; return aEntity[i].nByte; } } pToken->eType = T_ERROR; return 1; } case '\'': { /* Each token must start with a lower letter */ unsigned int first = 1; for(i=1; (c = z[i])!=0; i++){ if( z[i]==' ' ){ first = 1; continue; }else if( first && islower(z[i]) ){ first = 0; continue; }else if( !first && ( isalnum(z[i]) || z[i] == '_' )){ continue; }else if( z[i] == '\''){ pToken->eType = T_XML_CLASSES; return i+1; }else{ pToken->eType = T_ERROR; return i; } } pToken->eType = T_ERROR; return i; } default: { c = z[0]; if( c=='.' ){ unsigned char c1 = z[1]; if( islower(c1) ){ const PikWord *pFound; for(i=2; (c = z[i])>='a' && c<='z'; i++){} pFound = pik_find_word((const char*)z+1, i-1, pik_keywords, count(pik_keywords)); if( pFound && (pFound->eEdge>0 || pFound->eType==T_EDGEPT || pFound->eType==T_START || pFound->eType==T_END ) ){ /* Dot followed by something that is a 2-D place value */ pToken->eType = T_DOT_E; }else if( pFound && (pFound->eType==T_X || pFound->eType==T_Y) ){ /* Dot followed by "x" or "y" */ pToken->eType = T_DOT_XY; }else{ /* Any other "dot" */ pToken->eType = T_DOT_L; } return 1; }else if( isdigit(c1) ){ i = 0; /* no-op. Fall through to number handling */ }else if( isupper(c1) ){ for(i=2; (c = z[i])!=0 && (isalnum(c) || c=='_'); i++){} pToken->eType = T_DOT_U; return 1; }else{ pToken->eType = T_ERROR; return 1; } } if( (c>='0' && c<='9') || c=='.' ){ int nDigit; int isInt = 1; if( c!='.' ){ nDigit = 1; for(i=1; (c = z[i])>='0' && c<='9'; i++){ nDigit++; } if( i==1 && (c=='x' || c=='X') && z[0] == '0' ){ for(i=2; (c = z[i])!=0 && isxdigit(c); i++){} pToken->eType = T_NUMBER; return i; } }else{ isInt = 0; nDigit = 0; i = 0; } if( c=='.' ){ isInt = 0; for(i++; (c = z[i])>='0' && c<='9'; i++){ nDigit++; } } if( nDigit==0 ){ pToken->eType = T_ERROR; return i; } if( c=='e' || c=='E' ){ int iBefore = i; i++; c2 = z[i]; if( c2=='+' || c2=='-' ){ i++; c2 = z[i]; } if( c2<'0' || c>'9' ){ /* This is not an exp */ i = iBefore; }else{ i++; isInt = 0; while( (c = z[i])>='0' && c<='9' ){ i++; } } } c2 = c ? z[i+1] : 0; if( isInt ){ if( (c=='t' && c2=='h') || (c=='r' && c2=='d') || (c=='n' && c2=='d') || (c=='s' && c2=='t') ){ pToken->eType = T_NTH; return i+2; } } if( (c=='i' && c2=='n') || (c=='c' && c2=='m') || (c=='m' && c2=='m') || (c=='p' && c2=='t') || (c=='p' && c2=='x') || (c=='p' && c2=='c') ){ i += 2; } pToken->eType = T_NUMBER; return i; }else if( islower(c) ){ const PikWord *pFound; for(i=1; (c = z[i])!=0 && (isalnum(c) || c=='_'); i++){} pFound = pik_find_word((const char*)z, i, pik_keywords, count(pik_keywords)); if( pFound ){ pToken->eType = pFound->eType; pToken->eCode = pFound->eCode; pToken->eEdge = pFound->eEdge; return i; } pToken->n = i; if( pik_find_class(pToken)!=0 ){ pToken->eType = T_CLASSNAME; }else{ pToken->eType = T_ID; } return i; }else if( c>='A' && c<='Z' ){ for(i=1; (c = z[i])!=0 && (isalnum(c) || c=='_'); i++){} pToken->eType = T_PLACENAME; return i; }else if( c=='$' && z[1]>='1' && z[1]<='9' && !isdigit(z[2]) ){ pToken->eType = T_PARAMETER; pToken->eCode = z[1] - '1'; return 2; }else if( c=='_' || c=='$' || c=='@' ){ for(i=1; (c = z[i])!=0 && (isalnum(c) || c=='_'); i++){} pToken->eType = T_ID; return i; }else{ pToken->eType = T_ERROR; return 1; } } } } /* ** Return a pointer to the next non-whitespace token after pThis. ** This is used to help form error messages. */ static PToken pik_next_semantic_token(PToken *pThis){ PToken x; int sz; int i = pThis->n; memset(&x, 0, sizeof(x)); x.z = pThis->z; while(1){ x.z = pThis->z + i; sz = pik_token_length(&x, 1); if( x.eType!=T_WHITESPACE ){ x.n = sz; return x; } i += sz; } } /* Parser arguments to a macro invocation ** ** (arg1, arg2, ...) ** ** Arguments are comma-separated, except that commas within string ** literals or with (...), {...}, or [...] do not count. The argument ** list begins and ends with parentheses. There can be at most 9 ** arguments. ** ** Return the number of bytes in the argument list. */ static unsigned int pik_parse_macro_args( Pik *p, const char *z, /* Start of the argument list */ int n, /* Available bytes */ PToken *args, /* Fill in with the arguments */ PToken *pOuter /* Arguments of the next outer context, or NULL */ ){ int nArg = 0; int i, j, sz; int iStart; int depth = 0; PToken x; if( z[0]!='(' ) return 0; args[0].z = z+1; iStart = 1; for(i=1; i<n && z[i]!=')'; i+=sz){ x.z = z+i; sz = pik_token_length(&x, 0); if( sz!=1 ) continue; if( z[i]==',' && depth<=0 ){ args[nArg].n = i - iStart; if( nArg==8 ){ x.z = z; x.n = 1; pik_error(p, &x, "too many macro arguments - max 9"); return 0; } nArg++; args[nArg].z = z+i+1; iStart = i+1; depth = 0; }else if( z[i]=='(' || z[i]=='{' || z[i]=='[' ){ depth++; }else if( z[i]==')' || z[i]=='}' || z[i]==']' ){ depth--; } } if( z[i]==')' ){ args[nArg].n = i - iStart; /* Remove leading and trailing whitespace from each argument. ** If what remains is one of $1, $2, ... $9 then transfer the ** corresponding argument from the outer context */ for(j=0; j<=nArg; j++){ PToken *t = &args[j]; while( t->n>0 && isspace(t->z[0]) ){ t->n--; t->z++; } while( t->n>0 && isspace(t->z[t->n-1]) ){ t->n--; } if( t->n==2 && t->z[0]=='$' && t->z[1]>='1' && t->z[1]<='9' ){ if( pOuter ) *t = pOuter[t->z[1]-'1']; else t->n = 0; } if( t->z ){ hash_step(p->shaDigest, (unsigned char *)t->z, t->n); } } return i+1; } x.z = z; x.n = 1; pik_error(p, &x, "unterminated macro argument list"); return 0; } /* ** Split up the content of a PToken into multiple tokens and ** send each to the parser. */ void pik_tokenize(Pik *p, PToken *pIn, yyParser *pParser, PToken *aParam){ unsigned int i; int sz = 0; PToken token; PMacro *pMac; for(i=0; i<pIn->n && pIn->z[i] && p->nErr==0; i+=sz){ token.eCode = 0; token.eEdge = 0; token.z = pIn->z + i; sz = pik_token_length(&token, 1); if( token.eType==T_WHITESPACE ){ /* no-op */ }else if( sz>50000 ){ token.n = 1; pik_error(p, &token, "token is too long - max length 50000 bytes"); break; }else if( token.eType==T_ERROR ){ token.n = (unsigned short)(sz & 0xffff); pik_error(p, &token, "unrecognized token"); break; }else if( sz+i>pIn->n ){ token.n = pIn->n - i; pik_error(p, &token, "syntax error"); break; }else if( token.eType==T_PARAMETER ){ /* Substitute a parameter into the input stream */ if( aParam==0 || aParam[token.eCode].n==0 ){ continue; } token.n = (unsigned short)(sz & 0xffff); if( p->nCtx>=count(p->aCtx) ){ pik_error(p, &token, "macros nested too deep"); }else{ p->aCtx[p->nCtx++] = token; pik_tokenize(p, &aParam[token.eCode], pParser, 0); p->nCtx--; } }else if( token.eType==T_ID && (token.n = (unsigned short)(sz & 0xffff), (pMac = pik_find_macro(p,&token))!=0) ){ PToken args[9]; unsigned int j = i+sz; if( pMac->inUse ){ pik_error(p, &pMac->macroName, "recursive macro definition"); break; } token.n = (short int)(sz & 0xffff); if( p->nCtx>=count(p->aCtx) ){ pik_error(p, &token, "macros nested too deep"); break; } pMac->inUse = 1; memset(args, 0, sizeof(args)); p->aCtx[p->nCtx++] = token; sz += pik_parse_macro_args(p, pIn->z+j, pIn->n-j, args, aParam); pik_tokenize(p, &pMac->macroBody, pParser, args); p->nCtx--; pMac->inUse = 0; }else{ #if 0 printf("******** Token %s (%d): \"%.*s\" **************\n", yyTokenName[token.eType], token.eType, (int)(isspace(token.z[0]) ? 0 : sz), token.z); #endif token.n = (unsigned short)(sz & 0xffff); if( p->nToken++ > PIKCHR_TOKEN_LIMIT ){ pik_error(p, &token, "script is too complex"); break; } // only hash EOL once + normalize if( token.eType == T_EOL ){ if( !p->bEOL ){ p->bEOL = 1; // treat every EOL as a newline hash_step(p->shaDigest, (unsigned char *)"\n", 1); } }else{ p->bEOL = 0; // Codeblocks (aka macros) we hash if and when they get used, // XML class lists are hashed individually to discount whitespace if( token.eType != T_CODEBLOCK && token.eType != T_XML_CLASSES){ hash_step(p->shaDigest, (unsigned char *)token.z, token.n); } } pik_parser(pParser, token.eType, token); } } } /* ** Parse the PIKCHR script contained in zText[]. Return a rendering. Or ** if an error is encountered, return the error text. The error message ** is HTML formatted. So regardless of what happens, the return text ** is safe to be insertd into an HTML output stream. ** ** If pnWidth and pnHeight are not NULL, then this routine writes the ** width and height of the <SVG> object into the integers that they ** point to. A value of -1 is written if an error is seen. ** ** If zClass is not NULL, then it is a class name to be included in ** the <SVG> markup. ** ** The returned string is contained in memory obtained from malloc() ** and should be released by the caller. */ char *pikchr( const char *zText, /* Input PIKCHR source text. zero-terminated */ const char *zClass, /* Add class="%s" to <svg> markup */ unsigned int mFlags, /* Flags used to influence rendering behavior */ int *pnWidth, /* Write width of <svg> here, if not NULL */ int *pnHeight /* Write height here, if not NULL */ ){ Pik s; yyParser sParse; static int call_count; memset(&s, 0, sizeof(s)); s.sIn.z = zText; s.sIn.n = (unsigned int)strlen(zText); s.eDir = DIR_RIGHT; s.zClass = zClass; s.mFlags = mFlags; s.shaDigest = malloc(sizeof(SHA1Context)); hash_init(s.shaDigest); if( mFlags & PIKCHR_DARK_MODE ) { hash_step(s.shaDigest, (unsigned char *)"darkmode", 8); } if( mFlags & PIKCHR_EXTRA_UNIQUE_ID ){ hash_step(s.shaDigest, (unsigned char *)&call_count, sizeof(int)); } call_count++; s.bEOL = 0; pik_parserInit(&sParse, &s); #if 0 pik_parserTrace(stdout, "parser: "); #endif pik_tokenize(&s, &s.sIn, &sParse, 0); // normalize end of diagram to hash as EOL if( !s.bEOL){ hash_step(s.shaDigest, (unsigned char *)"\n", 1); } char hashOut[41]; hash_finish(s.shaDigest, hashOut); char *zID = malloc(12); zID[0] = '-'; memcpy(&zID[1], &hashOut[0], 10); zID[11] = 0; s.zID = zID; if( s.nErr==0 ){ PToken token; memset(&token,0,sizeof(token)); token.z = zText + (s.sIn.n>0 ? s.sIn.n-1 : 0); token.n = 1; pik_parser(&sParse, 0, token); } pik_parserFinalize(&sParse); if( s.zOut==0 && s.nErr==0 ){ pik_append(&s, "<!-- empty pikchr diagram -->\n", -1); } while( s.pVar ){ PVar *pNext = s.pVar->pNext; free(s.pVar); s.pVar = pNext; } while( s.pMacros ){ PMacro *pNext = s.pMacros->pNext; free(s.pMacros); s.pMacros = pNext; } free(s.shaDigest); free(zID); free(s.zTitle); free(s.zDesc); if( pnWidth ) *pnWidth = s.nErr ? -1 : s.wSVG; if( pnHeight ) *pnHeight = s.nErr ? -1 : s.hSVG; if( s.zOut ){ s.zOut[s.nOut] = 0; s.zOut = realloc(s.zOut, s.nOut+1); } return s.zOut; } #if defined(PIKCHR_FUZZ) #include <stdint.h> int LLVMFuzzerTestOneInput(const uint8_t *aData, size_t nByte){ int w,h; char *zIn, *zOut; unsigned int mFlags = nByte & 3; zIn = malloc( nByte + 1 ); if( zIn==0 ) return 0; memcpy(zIn, aData, nByte); zIn[nByte] = 0; zOut = pikchr(zIn, "pikchr", mFlags, &w, &h); free(zIn); free(zOut); return 0; } #endif /* PIKCHR_FUZZ */ #if defined(PIKCHR_SHELL) /* Print a usage comment for the shell and exit. */ static void usage(const char *argv0){ fprintf(stderr, "usage: %s [OPTIONS] FILE ...\n", argv0); fprintf(stderr, "Convert Pikchr input files into SVG. Filename \"-\" means stdin.\n" "All output goes to stdout.\n" "Options:\n" " --dark-mode Generate \"dark mode\" output\n" " --dont-stop Process all files even if earlier files have errors\n" " --svg-only Emit raw SVG without the HTML wrapper\n" #ifdef PIKDEV_MFLAG_ARGV " --mflags [h] Set mFlags to this (hex) value, overriding --dark-mode\n" #endif ); exit(1); } /* Send text to standard output, but escape HTML markup */ static void print_escape_html(const char *z){ int j; char c; while( z[0]!=0 ){ for(j=0; (c = z[j])!=0 && c!='<' && c!='>' && c!='&'; j++){} if( j ) printf("%.*s", j, z); z += j+1; j = -1; if( c=='<' ){ printf("<"); }else if( c=='>' ){ printf(">"); }else if( c=='&' ){ printf("&"); }else if( c==0 ){ break; } } } /* Read the content of file zFilename into memory obtained from malloc() ** Return the memory. If something goes wrong (ex: the file does not exist ** or cannot be opened) put an error message on stderr and return NULL. ** ** If the filename is "-" read stdin. */ static char *readFile(const char *zFilename){ FILE *in; size_t n; size_t nUsed = 0; size_t nAlloc = 0; char *z = 0, *zNew = 0; in = strcmp(zFilename,"-")==0 ? stdin : fopen(zFilename, "rb"); if( in==0 ){ fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename); return 0; } while(1){ if( nUsed+2>=nAlloc ){ nAlloc = nAlloc*2 + 4000; zNew = realloc(z, nAlloc); } if( zNew==0 ){ free(z); fprintf(stderr, "out of memory trying to allocate %lld bytes\n", (long long int)nAlloc); exit(1); } z = zNew; n = fread(z+nUsed, 1, nAlloc-nUsed-1, in); if( n<=0 ){ break; } nUsed += n; } if( in!=stdin ) fclose(in); z[nUsed] = 0; return z; } /* Testing interface ** ** Generate HTML on standard output that displays both the original ** input text and the rendered SVG for all files named on the command ** line. */ int main(int argc, char **argv){ int i; int bSvgOnly = 0; /* Output SVG only. No HTML wrapper */ int bDontStop = 0; /* Continue in spite of errors */ int exitCode = 0; /* What to return */ int mFlags = 0; /* mFlags argument to pikchr() */ const char *zStyle = ""; /* Extra styling */ const char *zHtmlHdr = "<!DOCTYPE html>\n" "<html lang=\"en-US\">\n" "<head>\n<title>PIKCHR Test</title>\n" "<style>\n" " .hidden {\n" " position: absolute !important;\n" " opacity: 0 !important;\n" " pointer-events: none !important;\n" " display: none !important;\n" " }\n" "</style>\n" "<script>\n" " function toggleHidden(id){\n" " for(var c of document.getElementById(id).children){\n" " c.classList.toggle('hidden');\n" " }\n" " }\n" "</script>\n" "<meta charset=\"utf-8\">\n" "</head>\n" "<body>\n" ; if( argc<2 ) usage(argv[0]); for(i=1; i<argc; i++){ char *zIn; char *zOut; int w, h; if( argv[i][0]=='-' && argv[i][1]!=0 ){ char *z = argv[i]; z++; if( z[0]=='-' ) z++; if( strcmp(z,"dont-stop")==0 ){ bDontStop = 1; }else if( strcmp(z,"dark-mode")==0 ){ zStyle = "color:white;background-color:black;"; mFlags |= PIKCHR_DARK_MODE; }else if( strcmp(z,"svg-only")==0 ){ if( zHtmlHdr==0 ){ fprintf(stderr, "the \"%s\" option must come first\n",argv[i]); exit(1); } bSvgOnly = 1; mFlags |= PIKCHR_PLAINTEXT_ERRORS; }else #ifdef PIKDEV_MFLAG_ARGV if( strcmp(z,"mflags")==0 ){ // This is just for development so we'll roll the dice on // parsing the next argument if( i+1 < argc ){ i++; mFlags = (int)strtol(argv[i], 0, 16); }else{ fprintf(stderr, "missing invalid argument for --mflags"); exit(1); } }else #endif { fprintf(stderr,"unknown option: \"%s\"\n", argv[i]); usage(argv[0]); } continue; } zIn = readFile(argv[i]); if( zIn==0 ) continue; zOut = pikchr(zIn, "pikchr", mFlags, &w, &h); if( w<0 && !bDontStop ) exitCode = 1; if( zOut==0 ){ fprintf(stderr, "pikchr() returns NULL. Out of memory?\n"); if( !bDontStop ) exit(1); }else if( bSvgOnly ){ printf("%s\n", zOut); }else{ if( zHtmlHdr ){ printf("%s", zHtmlHdr); zHtmlHdr = 0; } printf("<h1>File %s</h1>\n", argv[i]); if( w<0 ){ printf("<p>ERROR</p>\n%s\n", zOut); }else{ printf("<div id=\"svg-%d\" onclick=\"toggleHidden('svg-%d')\">\n",i,i); printf("<div style='border:3px solid lightgray;max-width:%dpx;%s'>\n", w,zStyle); printf("%s</div>\n", zOut); printf("<pre class='hidden'>"); print_escape_html(zIn); printf("</pre>\n</div>\n"); } } free(zOut); free(zIn); } if( !bSvgOnly ){ printf("</body></html>\n"); } return exitCode ? EXIT_FAILURE : EXIT_SUCCESS; } #endif /* PIKCHR_SHELL */ #ifdef PIKCHR_TCL #include <tcl.h> /* ** An interface to TCL ** ** TCL command: pikchr $INPUTTEXT ** ** Returns a list of 3 elements which are the output text, the width, and ** the height. ** ** Register the "pikchr" command by invoking Pikchr_Init(Tcl_Interp*). Or ** compile this source file as a shared library and load it using the ** "load" command of Tcl. ** ** Compile this source-code file into a shared library using a command ** similar to this: ** ** gcc -c pikchr.so -DPIKCHR_TCL -shared pikchr.c */ static int pik_tcl_command( ClientData clientData, /* Not Used */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ int w, h; /* Width and height of the pikchr */ const char *zIn; /* Source text input */ char *zOut; /* SVG output text */ Tcl_Obj *pRes; /* The result TCL object */ (void)clientData; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "PIKCHR_SOURCE_TEXT"); return TCL_ERROR; } zIn = Tcl_GetString(objv[1]); w = h = 0; zOut = pikchr(zIn, "pikchr", 0, &w, &h); if( zOut==0 ){ return TCL_ERROR; /* Out of memory */ } pRes = Tcl_NewObj(); Tcl_ListObjAppendElement(0, pRes, Tcl_NewStringObj(zOut, -1)); free(zOut); Tcl_ListObjAppendElement(0, pRes, Tcl_NewIntObj(w)); Tcl_ListObjAppendElement(0, pRes, Tcl_NewIntObj(h)); Tcl_SetObjResult(interp, pRes); return TCL_OK; } #ifndef PACKAGE_NAME # define PACKAGE_NAME "pikchr" #endif #ifndef PACKAGE_VERSION # define PACKAGE_VERSION "1.0" #endif /* Invoke this routine to register the "pikchr" command with the interpreter ** given in the argument */ int Pikchr_Init(Tcl_Interp *interp){ Tcl_CreateObjCommand(interp, "pikchr", pik_tcl_command, 0, 0); Tcl_PkgProvide(interp, PACKAGE_NAME, PACKAGE_VERSION); return TCL_OK; } #endif /* PIKCHR_TCL */ #line 9146 "pikchr.c"