-
Notifications
You must be signed in to change notification settings - Fork 16
/
minips.hpp
334 lines (319 loc) · 12.9 KB
/
minips.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
/* minips.hpp -- mini-PostScript parser and structure builder
* by [email protected] at Sat Mar 9 21:33:04 CET 2002
*/
#ifdef __GNUC__
#ifndef __clang__
#pragma interface
#endif
#endif
#ifndef MINIPS_HPP
#define MINIPS_HPP 1
#include "config2.h"
#include "gensi.hpp"
#include "error.hpp"
class MiniPS {
public:
#if SIZEOF_VOID_P <= SIZEOF_INT
typedef signed int ii_t;
#elif SIZEOF_VOID_P <= SIZEOF_LONG
typedef signed long ii_t;
#elif SIZEOF_VOID_P <= SIZEOF_CFG_LONGEST
typedef signed PTS_CFG_LONGEST ii_t;
#else
#error No integral data type to hold a ptr.
#endif
class Real;
/*union*/ struct TokVal {
SimBuffer::B *bb;
ii_t i;
/* double d; */
/*MiniPS::*/Real *r;
};
/** minips tokenizer */
class Tokenizer {
public:
BEGIN_STATIC_ENUM1(int) EOFF=-1, NO_UNGOT=-2 END_STATIC_ENUM()
Tokenizer(GenBuffer::Readable& in_);
/** Reads and returns next token.
* Token types are named for their first character:
* '1': integertype: 31 bits signed (-1073741824 .. 1073741823) is
* guaranteed. VALUED
* '.': realtype (NOT implemented), double guaranteed. VALUED
* 'E': Ename (name without a slash). VALUED
* '/': Sname (name beginning with a slash). VALUED
* '(': stringtype (also with `<hexadecimal>') VALUED
* '[': beginning-of-array (also with `{')
* ']': end-of-array (also with '}')
* '<': beginning-of-dict (`<<')
* '>': end-of-dict (`>>')
* -1: EOF
*/
int yylex();
inline TokVal const& lastTokVal() const { return tv; }
protected:
/** Data for last token read. */
TokVal tv;
SimBuffer::B b;
GenBuffer::Readable& in;
/* NO_UNGOT for nothing, EOFF for EOF, 0..255 otherwise */
int ungot;
};
/* This is somewhat similar to Ruby */
typedef ii_t VALUE;
/** Qundef is the undefined hash key or array elements. It is an _invalid_ VALUE! */
BEGIN_STATIC_ENUM1(VALUE) Qfalse=0, Qtrue=2, Qnull=4, Qundef=6, Qpop=8, Qerror=10, Qmax_=10 END_STATIC_ENUM()
BEGIN_STATIC_ENUM1(unsigned) T_NULL=1, T_BOOLEAN=2, T_INTEGER=3, T_REAL=4,
T_STRING=5, T_ARRAY=6, T_DICT=7, T_SNAME=8, T_ENAME=9, T_VOID=10,
S_SENUM=20, /* must be a dict-member; meta-type used by scanf_dict() */
S_FUNC=21, /* call a function to determine; meta-type used by scanf_dict() */
S_UINTEGER=22, /* non-negative integer; meta-type used by scanf_dict() */
S_ANY=23, /* anything; meta-type used by scanf_dict() */
S_PINTEGER=24, /* positive integer; meta-type used by scanf_dict() */
S_RGBSTR=25, /* an optional 3-byte string, representing an RGB color triplet */
S_NUMBER=26, /* real or integer */
S_PNUMBER=27 /* positive real or integer */
END_STATIC_ENUM()
/** No virtual methods because of special types. MiniPS composite values
* really _contain_ their components; one Value has exactly one reference:
* its container component.
*/
class Value { public:
inline ii_t getLength() const { return len; }
inline ii_t getType() const { return ty; }
inline bool hasPtr() const { return ptr!=NULLP; }
inline bool isDumping() const { return dumping; }
inline char const* getCstr() const { return (char const*)ptr; }
inline char* begin_() const { return (char*)ptr; }
inline char const* operator()() const { return (char const*)ptr; }
protected:
ii_t len;
void *ptr; /* Will be freed by MiniPS::delete0() except for Void. */
unsigned char ty;
bool dumping;
};
/** ptr contains a `void*'. Won't be freed by MiniPS::delete0(). */
class Void: public Value { public:
inline Void(void *ptr_) { ptr=ptr_; ty=T_VOID; }
inline void *getPtr() const { return ptr; }
};
/** Always null-terminated. */
class String: public Value { public:
/** Copies from ptr_ */
String(char const*ptr_, ii_t len_);
/** Replaces (this) with a copy of (a).(b) */
void replace(char const*ap, slen_t alen, char const*bp, slen_t blen);
};
class Sname: public Value { public:
/** ptr_ must begin with '/' */
Sname(char const*ptr_, ii_t len_);
bool equals(Sname const&other);
bool equals(char const*other);
};
class Ename: public Value { public:
Ename(char const*ptr_, ii_t len_);
bool equals(Ename const&other);
bool equals(char const*other);
bool equals(char const*other, slen_t otherlen);
};
class Real: public Value { public:
typedef unsigned char metric_t;
inline Real(double d_): d(d_), metric(0), dumpPS(false) { ty=T_REAL; ptr=(char*)NULLP; }
/** Also supply a string representation of the value (to avoid possible
* loss of precision when converting string -> double -> string).
*/
Real(double d_, char const*ptr_, ii_t len_);
// inline bool isZero() const { return d==0.0; }
inline double getBp() const { return d*me_factor[metric]; }
inline void setDumpPS(bool g) { dumpPS=g; }
inline void setMetric(metric_t metric_) { metric=metric_; }
/** Return true iff the specified null-terminated string is a valid
* dimen.
*/
static bool isDimen(char const *);
void dump(GenBuffer::Writable &out_, bool dumpPS_force=false);
/** @return ME_count on invalid */
static metric_t str2metric(char const str[2]);
BEGIN_STATIC_ENUM1(metric_t)
ME_bp=0, /* 1 bp = 1 bp (big point) */
ME_in=1, /* 1 in = 72 bp (inch) */
ME_pt=2, /* 1 pt = 72/72.27 bp (point) */
ME_pc=3, /* 1 pc = 12*72/72.27 bp (pica) */
ME_dd=4, /* 1 dd = 1238/1157*72/72.27 bp (didot point) [about 1.06601110141206 bp] */
ME_cc=5, /* 1 cc = 12*1238/1157*72/72.27 bp (cicero) */
ME_sp=6, /* 1 sp = 72/72.27/65536 bp (scaled point) */
ME_cm=7, /* 1 cm = 72/2.54 bp (centimeter) */
ME_mm=8, /* 1 mm = 7.2/2.54 bp (millimeter) */
ME_COUNT=9
END_STATIC_ENUM()
protected:
double d;
/* vvv metric added at Sat Sep 7 12:26:08 CEST 2002 */
metric_t metric;
/** Allow PostScript operators such as `div' to appear in the dump */
bool dumpPS;
/** Factor to convert to bp */
static const double me_factor[ME_COUNT];
/** PostScript code to do multiplication by me_factor */
static char const* const me_psfactor[ME_COUNT];
};
class Array: public Value { public:
Array();
void free();
void dump(GenBuffer::Writable &out_, unsigned indent);
void push(VALUE v);
VALUE get(ii_t index);
/** Cannot extend. Calls delete0() when overwriting an element */
void set(ii_t index, VALUE val);
/** val: out */
void getFirst(VALUE *&val);
/** val: in_out */
void getNext(VALUE *&val);
protected:
ii_t alloced;
void extend(ii_t newlen);
};
/** Keys must be Snames here! (i.e no integer keys allowed). The current
* implementation does a linear string search :-(. Dat: might not work
* on extremely long keys if slen_t can hold a larger integer than
* ii_t.
*/
class Dict: public Value { public:
Dict();
void free();
void dump(GenBuffer::Writable &out_, unsigned indent, bool dump_delimiters=true);
/** @return val or Qundef */
VALUE get(char const*key, slen_t keylen);
/** A hack: get and touch. */
VALUE get1(char const*key, slen_t keylen);
void untouch(char const*key, slen_t keylen);
/** Can extend. */
void put(char const*key, VALUE val);
/** Calls delete0() when overwriting an element */
void put(char const*key, slen_t keylen, VALUE val);
/** @return old value for key `key', or Qundef */
VALUE push(char const*key, slen_t keylen, VALUE val);
/** key: out, val: out */
void getFirst(char const*const*& key, slen_t &keylen, VALUE *&val, bool &touched);
/** key: in_out, val: in_out */
void getNext (char const*const*& key, slen_t &keylen, VALUE *&val, bool &touched);
// void getFirst(VALUE *&key, VALUE *&val);
// void getNext(VALUE *&key, VALUE *&val);
protected:
ii_t alloced;
void extend(ii_t newlen);
};
static inline Value* RVALUE(VALUE v) { return static_cast<Value*>((void*)v); }
static inline Void* RVOID(VALUE v) { return static_cast<Void*>((void*)v); }
static inline Array* RARRAY(VALUE v) { return static_cast<Array*>((void*)v); }
static inline String* RSTRING(VALUE v){ return static_cast<String*>((void*)v); }
static inline Dict* RDICT(VALUE v) { return static_cast<Dict*>((void*)v); }
static inline Real* RREAL(VALUE v) { return static_cast<Real*>((void*)v); }
static inline Sname* RSNAME(VALUE v) { return static_cast<Sname*>((void*)v); }
static inline Ename* RENAME(VALUE v) { return static_cast<Ename*>((void*)v); }
static inline bool isDirect(VALUE v) { return (v&1)!=0 || v<=Qmax_; }
/** T_NULL .. T_DICT */
static unsigned getType(VALUE v);
/** "null" .. "dict", "name" (T_SNAME), "ename" (T_ENAME, non-std-PS) */
static char const* getTypeStr(unsigned u);
static void delete0(VALUE v);
/** The current implementation dumps dict keys in order of insertion, but
* this is very likely to change soon to an _arbitrary_ order.
*/
static void dump(GenBuffer::Writable& out_, VALUE v, unsigned indent=0);
static void dump(VALUE v, unsigned indent=0);
static inline VALUE Qinteger(ii_t v) { return (v<<1)+1; }
static inline ii_t int2ii(VALUE v) { return v>>1; }
/** Fortunate coincidence that 2x+1>0 <=> x>0 */
static inline bool isPositive(VALUE v) { return v>0; }
static inline VALUE undef2null(VALUE v) { return v==Qundef ? Qnull : v; }
// static SimBuffer::B scale72(VALUE v, double d);
class Parser {
/** Define this to avoid including <stdio.h> */
typedef class _anon_filet_ {} *FILEP;
public:
BEGIN_STATIC_ENUM1(int) EOF_ALLOWED=Tokenizer::EOFF, EOF_ILLEGAL=-2, EOF_ILLEGAL_POP=-3 END_STATIC_ENUM()
/** Maximum depth of `run' file inclusions. */
BEGIN_STATIC_ENUM1(unsigned) MAX_DEPTH=17 END_STATIC_ENUM()
Parser(char const *filename_);
/** The caller is responsible for closing the FILE* */
Parser(FILEP f_);
Parser(GenBuffer::Readable *rd_);
Parser(Tokenizer *tok_);
~Parser();
/** Allocates and returns. Qundef is returned on EOF
* @param closer: EOF_ILLEGAL, EOF_ALLOWED, '>' or ']'
*/
VALUE parse1(int closer=EOF_ILLEGAL, int sev=Error::EERROR);
void setDepth(unsigned depth_);
/** Sets special filename for the `run' operator. `run' will read that
* special file from param `rd_', and then it will call rd_->vi_rewind().
* Example usage: .addSpecRun("%stdin", new Files::FileR(stdin));
*/
void addSpecRun(char const* filename_, GenBuffer::Readable *rd_);
/*MiniPS::*/Dict *getSpecRuns() const { return specRuns; }
/** Does not copy the dict, but sets the pointer. */
void setSpecRuns(/*MiniPS::*/Dict *);
protected:
Parser(Parser *master_);
// VALUE parse1_real(int closer);
/* 0=nothing, 1=master 2=tok, 3=tok+rd, 4=tok+rd+f */
unsigned free_level;
Parser *master;
Tokenizer *tok;
GenBuffer::Readable *rd;
FILEP f;
int unread;
unsigned depth;
/*MiniPS::*/Dict *specRuns;
/** Should MiniPS::delete0() be called on specRuns upon destruction of
* (this)?
*/
bool specRunsDelete;
};
/** Assumes that param `job' is a dict, extracts its elements into ...,
* emits errors for elements (not found and having default==Qundef), emits
* warnings for elements found in `job', but undescribed in `...' (only
* if show_warnings==true). Example:
*
* MiniPS::scanf_dict(job, true,
* "InputFile", MiniPS::T_STRING, MiniPS::Qundef, &InputFile,
* "OutputFile", MiniPS::T_STRING, MiniPS::Qundef, &OutputFile,
* "Profiles", MiniPS::T_ARRAY, MiniPS::Qundef, &Profiles
* NULLP
* );
*/
static void scanf_dict(VALUE job, bool show_warnings, ...);
static void setDumpPS(VALUE v, bool g);
/** @param v must be T_REAL or T_INTEGER */
static bool isZero(VALUE v);
/** @param v must be T_REAL or T_INTEGER
* @return true iff v==i
*/
static bool isEq(VALUE v, double d);
/** Dumps the human-readable real or integer value of the sum
* (mscale/72)*a+b+c-sub to `out'.
* @param rounding 0: nothing. 1: round the sum _up_ to integers. 2:
* round the sum _up_ to non-negative integers
* @param m must be T_REAL or T_INTEGER. mscale = m/72 if m is positive,
* or 72/m if m is negative.
* @param a must be T_REAL or T_INTEGER
* @param b must be T_REAL or T_INTEGER
* @param c must be T_REAL or T_INTEGER
* @param sub must be T_REAL or T_INTEGER
*/
static void dumpAdd3(GenBuffer::Writable &out, VALUE m, VALUE a, VALUE b, VALUE c, VALUE sub, unsigned rounding=0);
static void dumpScale(GenBuffer::Writable &out, VALUE v);
};
/* Fri Aug 16 17:07:14 CEST 2002 */
#if 0 /* doesn't work because MiniPS::VALUE is really an `int', so there will be an ambiguous overload */
inline GenBuffer::Writable& operator<<(GenBuffer::Writable& out_, MiniPS::VALUE v) {
MiniPS::dump(out_, v);
return out_;
}
#endif
inline GenBuffer::Writable& operator<<(GenBuffer::Writable& out_, MiniPS::Value *v) {
MiniPS::dump(out_, (MiniPS::VALUE)v);
return out_;
}
// GenBuffer::Writable& operator<<(GenBuffer::Writable& out_, MiniPS::Value *v) {
#endif