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