1 // script binding functionality
2
3 enum { VAL_NULL = 0, VAL_INT, VAL_FLOAT, VAL_STR, VAL_ANY, VAL_CODE, VAL_MACRO, VAL_IDENT, VAL_CSTR, VAL_CANY, VAL_WORD, VAL_POP, VAL_COND };
4
5 enum
6 {
7 CODE_START = 0,
8 CODE_OFFSET,
9 CODE_NULL, CODE_TRUE, CODE_FALSE, CODE_NOT,
10 CODE_POP,
11 CODE_ENTER, CODE_ENTER_RESULT,
12 CODE_EXIT, CODE_RESULT_ARG,
13 CODE_VAL, CODE_VALI,
14 CODE_DUP,
15 CODE_MACRO,
16 CODE_BOOL,
17 CODE_BLOCK, CODE_EMPTY,
18 CODE_COMPILE, CODE_COND,
19 CODE_FORCE,
20 CODE_RESULT,
21 CODE_IDENT, CODE_IDENTU, CODE_IDENTARG,
22 CODE_COM, CODE_COMD, CODE_COMC, CODE_COMV,
23 CODE_CONC, CODE_CONCW, CODE_CONCM, CODE_DOWN,
24 CODE_SVAR, CODE_SVARM, CODE_SVAR1,
25 CODE_IVAR, CODE_IVAR1, CODE_IVAR2, CODE_IVAR3,
26 CODE_FVAR, CODE_FVAR1,
27 CODE_LOOKUP, CODE_LOOKUPU, CODE_LOOKUPARG,
28 CODE_LOOKUPM, CODE_LOOKUPMU, CODE_LOOKUPMARG,
29 CODE_ALIAS, CODE_ALIASU, CODE_ALIASARG, CODE_CALL, CODE_CALLU, CODE_CALLARG,
30 CODE_PRINT,
31 CODE_LOCAL,
32 CODE_DO, CODE_DOARGS,
33 CODE_JUMP, CODE_JUMP_TRUE, CODE_JUMP_FALSE, CODE_JUMP_RESULT_TRUE, CODE_JUMP_RESULT_FALSE,
34
35 CODE_OP_MASK = 0x3F,
36 CODE_RET = 6,
37 CODE_RET_MASK = 0xC0,
38
39 /* return type flags */
40 RET_NULL = VAL_NULL<<CODE_RET,
41 RET_STR = VAL_STR<<CODE_RET,
42 RET_INT = VAL_INT<<CODE_RET,
43 RET_FLOAT = VAL_FLOAT<<CODE_RET,
44 };
45
46 enum { ID_VAR, ID_FVAR, ID_SVAR, ID_COMMAND, ID_ALIAS, ID_LOCAL, ID_DO, ID_DOARGS, ID_IF, ID_RESULT, ID_NOT, ID_AND, ID_OR };
47
48 enum { IDF_PERSIST = 1<<0, IDF_OVERRIDE = 1<<1, IDF_HEX = 1<<2, IDF_READONLY = 1<<3, IDF_OVERRIDDEN = 1<<4, IDF_UNKNOWN = 1<<5, IDF_ARG = 1<<6 };
49
50 struct ident;
51
52 struct identval
53 {
54 union
55 {
56 int i; // ID_VAR, VAL_INT
57 float f; // ID_FVAR, VAL_FLOAT
58 char *s; // ID_SVAR, VAL_STR
59 const uint *code; // VAL_CODE
60 ident *id; // VAL_IDENT
61 const char *cstr; // VAL_CSTR
62 };
63 };
64
65 struct tagval : identval
66 {
67 int type;
68
setinttagval69 void setint(int val) { type = VAL_INT; i = val; }
setfloattagval70 void setfloat(float val) { type = VAL_FLOAT; f = val; }
setnumbertagval71 void setnumber(double val) { i = int(val); if(val == i) type = VAL_INT; else { type = VAL_FLOAT; f = val; } }
setstrtagval72 void setstr(char *val) { type = VAL_STR; s = val; }
setnulltagval73 void setnull() { type = VAL_NULL; i = 0; }
setcodetagval74 void setcode(const uint *val) { type = VAL_CODE; code = val; }
setmacrotagval75 void setmacro(const uint *val) { type = VAL_MACRO; code = val; }
setcstrtagval76 void setcstr(const char *val) { type = VAL_CSTR; cstr = val; }
setidenttagval77 void setident(ident *val) { type = VAL_IDENT; id = val; }
78
79 const char *getstr() const;
80 int getint() const;
81 float getfloat() const;
82 double getnumber() const;
83 bool getbool() const;
84 void getval(tagval &r) const;
85
86 void cleanup();
87 };
88
89 struct identstack
90 {
91 identval val;
92 int valtype;
93 identstack *next;
94 };
95
96 union identvalptr
97 {
98 void *p; // ID_*VAR
99 int *i; // ID_VAR
100 float *f; // ID_FVAR
101 char **s; // ID_SVAR
102 };
103
104 typedef void (__cdecl *identfun)(ident *id);
105
106 struct ident
107 {
108 int type; // one of ID_* above
109 const char *name;
110 union
111 {
112 int minval; // ID_VAR
113 float minvalf; // ID_FVAR
114 int valtype; // ID_ALIAS
115 int numargs; // ID_COMMAND
116 };
117 union
118 {
119 int maxval; // ID_VAR
120 float maxvalf; // ID_FVAR
121 uint *code; // ID_ALIAS
122 };
123 union
124 {
125 const char *args; // ID_COMMAND
126 identval val; // ID_ALIAS
127 identvalptr storage; // ID_VAR, ID_FVAR, ID_SVAR
128 };
129 union
130 {
131 identval overrideval; // ID_VAR, ID_FVAR, ID_SVAR
132 identstack *stack; // ID_ALIAS
133 uint argmask; // ID_COMMAND
134 };
135 identfun fun; // ID_VAR, ID_FVAR, ID_SVAR, ID_COMMAND
136 int flags, index;
137
identident138 ident() {}
139 // ID_VAR
140 ident(int t, const char *n, int m, int x, int *s, void *f = NULL, int flags = 0)
typeident141 : type(t), name(n), minval(m), maxval(x), fun((identfun)f), flags(flags | (m > x ? IDF_READONLY : 0))
142 { storage.i = s; }
143 // ID_FVAR
144 ident(int t, const char *n, float m, float x, float *s, void *f = NULL, int flags = 0)
typeident145 : type(t), name(n), minvalf(m), maxvalf(x), fun((identfun)f), flags(flags | (m > x ? IDF_READONLY : 0))
146 { storage.f = s; }
147 // ID_SVAR
148 ident(int t, const char *n, char **s, void *f = NULL, int flags = 0)
typeident149 : type(t), name(n), fun((identfun)f), flags(flags)
150 { storage.s = s; }
151 // ID_ALIAS
identident152 ident(int t, const char *n, char *a, int flags)
153 : type(t), name(n), valtype(VAL_STR), code(NULL), stack(NULL), flags(flags)
154 { val.s = a; }
identident155 ident(int t, const char *n, int a, int flags)
156 : type(t), name(n), valtype(VAL_INT), code(NULL), stack(NULL), flags(flags)
157 { val.i = a; }
identident158 ident(int t, const char *n, float a, int flags)
159 : type(t), name(n), valtype(VAL_FLOAT), code(NULL), stack(NULL), flags(flags)
160 { val.f = a; }
identident161 ident(int t, const char *n, int flags)
162 : type(t), name(n), valtype(VAL_NULL), code(NULL), stack(NULL), flags(flags)
163 {}
identident164 ident(int t, const char *n, const tagval &v, int flags)
165 : type(t), name(n), valtype(v.type), code(NULL), stack(NULL), flags(flags)
166 { val = v; }
167 // ID_COMMAND
168 ident(int t, const char *n, const char *args, uint argmask, int numargs, void *f = NULL, int flags = 0)
typeident169 : type(t), name(n), numargs(numargs), args(args), argmask(argmask), fun((identfun)f), flags(flags)
170 {}
171
changedident172 void changed() { if(fun) fun(this); }
173
setvalident174 void setval(const tagval &v)
175 {
176 valtype = v.type;
177 val = v;
178 }
179
setvalident180 void setval(const identstack &v)
181 {
182 valtype = v.valtype;
183 val = v.val;
184 }
185
forcenullident186 void forcenull()
187 {
188 if(valtype==VAL_STR) delete[] val.s;
189 valtype = VAL_NULL;
190 }
191
192 float getfloat() const;
193 int getint() const;
194 double getnumber() const;
195 const char *getstr() const;
196 void getval(tagval &r) const;
197 void getcstr(tagval &v) const;
198 void getcval(tagval &v) const;
199 };
200
201 extern void addident(ident *id);
202
203 extern tagval *commandret;
204 extern const char *intstr(int v);
205 extern void intret(int v);
206 extern const char *floatstr(float v);
207 extern void floatret(float v);
208 extern const char *numberstr(double v);
209 extern void numberret(double v);
210 extern void stringret(char *s);
211 extern void result(tagval &v);
212 extern void result(const char *s);
213
parseint(const char * s)214 static inline int parseint(const char *s)
215 {
216 return int(strtoul(s, NULL, 0));
217 }
218
219 #define PARSEFLOAT(name, type) \
220 static inline type parse##name(const char *s) \
221 { \
222 /* not all platforms (windows) can parse hexadecimal integers via strtod */ \
223 char *end; \
224 double val = strtod(s, &end); \
225 return val || end==s || (*end!='x' && *end!='X') ? type(val) : type(parseint(s)); \
226 }
PARSEFLOAT(float,float)227 PARSEFLOAT(float, float)
228 PARSEFLOAT(number, double)
229
230 static inline void intformat(char *buf, int v, int len = 20) { nformatstring(buf, len, "%d", v); }
231 static inline void floatformat(char *buf, float v, int len = 20) { nformatstring(buf, len, v==int(v) ? "%.1f" : "%.7g", v); }
232 static inline void numberformat(char *buf, double v, int len = 20)
233 {
234 int i = int(v);
235 if(v == i) nformatstring(buf, len, "%d", i);
236 else nformatstring(buf, len, "%.7g", v);
237 }
238
getstr(const identval & v,int type)239 static inline const char *getstr(const identval &v, int type)
240 {
241 switch(type)
242 {
243 case VAL_STR: case VAL_MACRO: case VAL_CSTR: return v.s;
244 case VAL_INT: return intstr(v.i);
245 case VAL_FLOAT: return floatstr(v.f);
246 default: return "";
247 }
248 }
getstr()249 inline const char *tagval::getstr() const { return ::getstr(*this, type); }
getstr()250 inline const char *ident::getstr() const { return ::getstr(val, valtype); }
251
252 #define GETNUMBER(name, ret) \
253 static inline ret get##name(const identval &v, int type) \
254 { \
255 switch(type) \
256 { \
257 case VAL_FLOAT: return ret(v.f); \
258 case VAL_INT: return ret(v.i); \
259 case VAL_STR: case VAL_MACRO: case VAL_CSTR: return parse##name(v.s); \
260 default: return ret(0); \
261 } \
262 } \
263 inline ret tagval::get##name() const { return ::get##name(*this, type); } \
264 inline ret ident::get##name() const { return ::get##name(val, valtype); }
GETNUMBER(int,int)265 GETNUMBER(int, int)
266 GETNUMBER(float, float)
267 GETNUMBER(number, double)
268
269 static inline void getval(const identval &v, int type, tagval &r)
270 {
271 switch(type)
272 {
273 case VAL_STR: case VAL_MACRO: case VAL_CSTR: r.setstr(newstring(v.s)); break;
274 case VAL_INT: r.setint(v.i); break;
275 case VAL_FLOAT: r.setfloat(v.f); break;
276 default: r.setnull(); break;
277 }
278 }
279
getval(tagval & r)280 inline void tagval::getval(tagval &r) const { ::getval(*this, type, r); }
getval(tagval & r)281 inline void ident::getval(tagval &r) const { ::getval(val, valtype, r); }
282
getcstr(tagval & v)283 inline void ident::getcstr(tagval &v) const
284 {
285 switch(valtype)
286 {
287 case VAL_MACRO: v.setmacro(val.code); break;
288 case VAL_STR: case VAL_CSTR: v.setcstr(val.s); break;
289 case VAL_INT: v.setstr(newstring(intstr(val.i))); break;
290 case VAL_FLOAT: v.setstr(newstring(floatstr(val.f))); break;
291 default: v.setcstr(""); break;
292 }
293 }
294
getcval(tagval & v)295 inline void ident::getcval(tagval &v) const
296 {
297 switch(valtype)
298 {
299 case VAL_MACRO: v.setmacro(val.code); break;
300 case VAL_STR: case VAL_CSTR: v.setcstr(val.s); break;
301 case VAL_INT: v.setint(val.i); break;
302 case VAL_FLOAT: v.setfloat(val.f); break;
303 default: v.setnull(); break;
304 }
305 }
306
307 // nasty macros for registering script functions, abuses globals to avoid excessive infrastructure
308 #define KEYWORD(name, type) UNUSED static bool __dummy_##type = addcommand(#name, (identfun)NULL, NULL, type)
309 #define COMMANDKN(name, type, fun, nargs) UNUSED static bool __dummy_##fun = addcommand(#name, (identfun)fun, nargs, type)
310 #define COMMANDK(name, type, nargs) COMMANDKN(name, type, name, nargs)
311 #define COMMANDN(name, fun, nargs) COMMANDKN(name, ID_COMMAND, fun, nargs)
312 #define COMMAND(name, nargs) COMMANDN(name, name, nargs)
313
314 #define _VAR(name, global, min, cur, max, persist) int global = variable(#name, min, cur, max, &global, NULL, persist)
315 #define VARN(name, global, min, cur, max) _VAR(name, global, min, cur, max, 0)
316 #define VARNP(name, global, min, cur, max) _VAR(name, global, min, cur, max, IDF_PERSIST)
317 #define VARNR(name, global, min, cur, max) _VAR(name, global, min, cur, max, IDF_OVERRIDE)
318 #define VAR(name, min, cur, max) _VAR(name, name, min, cur, max, 0)
319 #define VARP(name, min, cur, max) _VAR(name, name, min, cur, max, IDF_PERSIST)
320 #define VARR(name, min, cur, max) _VAR(name, name, min, cur, max, IDF_OVERRIDE)
321 #define _VARF(name, global, min, cur, max, body, persist) void var_##name(ident *id); int global = variable(#name, min, cur, max, &global, var_##name, persist); void var_##name(ident *id) { body; }
322 #define VARFN(name, global, min, cur, max, body) _VARF(name, global, min, cur, max, body, 0)
323 #define VARF(name, min, cur, max, body) _VARF(name, name, min, cur, max, body, 0)
324 #define VARFP(name, min, cur, max, body) _VARF(name, name, min, cur, max, body, IDF_PERSIST)
325 #define VARFR(name, min, cur, max, body) _VARF(name, name, min, cur, max, body, IDF_OVERRIDE)
326 #define VARFNP(name, global, min, cur, max, body) _VARF(name, global, min, cur, max, body, IDF_PERSIST)
327 #define VARFNR(name, global, min, cur, max, body) _VARF(name, global, min, cur, max, body, IDF_OVERRIDE)
328 #define _VARM(name, min, cur, max, scale, persist) int name = cur * scale; _VARF(name, _##name, min, cur, max, { name = _##name * scale; }, persist)
329 #define VARMP(name, min, cur, max, scale) _VARM(name, min, cur, max, scale, IDF_PERSIST)
330 #define VARMR(name, min, cur, max, scale) _VARM(name, min, cur, max, scale, IDF_OVERRIDE)
331
332 #define _HVAR(name, global, min, cur, max, persist) int global = variable(#name, min, cur, max, &global, NULL, persist | IDF_HEX)
333 #define HVARN(name, global, min, cur, max) _HVAR(name, global, min, cur, max, 0)
334 #define HVARNP(name, global, min, cur, max) _HVAR(name, global, min, cur, max, IDF_PERSIST)
335 #define HVARNR(name, global, min, cur, max) _HVAR(name, global, min, cur, max, IDF_OVERRIDE)
336 #define HVAR(name, min, cur, max) _HVAR(name, name, min, cur, max, 0)
337 #define HVARP(name, min, cur, max) _HVAR(name, name, min, cur, max, IDF_PERSIST)
338 #define HVARR(name, min, cur, max) _HVAR(name, name, min, cur, max, IDF_OVERRIDE)
339 #define _HVARF(name, global, min, cur, max, body, persist) void var_##name(ident *id); int global = variable(#name, min, cur, max, &global, var_##name, persist | IDF_HEX); void var_##name(ident *id) { body; }
340 #define HVARFN(name, global, min, cur, max, body) _HVARF(name, global, min, cur, max, body, 0)
341 #define HVARF(name, min, cur, max, body) _HVARF(name, name, min, cur, max, body, 0)
342 #define HVARFP(name, min, cur, max, body) _HVARF(name, name, min, cur, max, body, IDF_PERSIST)
343 #define HVARFR(name, min, cur, max, body) _HVARF(name, name, min, cur, max, body, IDF_OVERRIDE)
344 #define HVARFNP(name, global, min, cur, max, body) _HVARF(name, global, min, cur, max, body, IDF_PERSIST)
345 #define HVARFNR(name, global, min, cur, max, body) _HVARF(name, global, min, cur, max, body, IDF_OVERRIDE)
346
347 #define _CVAR(name, cur, init, body, persist) bvec name = bvec::hexcolor(cur); _HVARF(name, _##name, 0, cur, 0xFFFFFF, { init; name = bvec::hexcolor(_##name); body; }, persist)
348 #define CVARP(name, cur) _CVAR(name, cur, , , IDF_PERSIST)
349 #define CVARR(name, cur) _CVAR(name, cur, , , IDF_OVERRIDE)
350 #define CVARFP(name, cur, body) _CVAR(name, cur, , body, IDF_PERSIST)
351 #define CVARFR(name, cur, body) _CVAR(name, cur, , body, IDF_OVERRIDE)
352 #define _CVAR0(name, cur, body, persist) _CVAR(name, cur, { if(!_##name) _##name = cur; }, body, persist)
353 #define CVAR0P(name, cur) _CVAR0(name, cur, , IDF_PERSIST)
354 #define CVAR0R(name, cur) _CVAR0(name, cur, , IDF_OVERRIDE)
355 #define CVAR0FP(name, cur, body) _CVAR0(name, cur, body, IDF_PERSIST)
356 #define CVAR0FR(name, cur, body) _CVAR0(name, cur, body, IDF_OVERRIDE)
357 #define _CVAR1(name, cur, body, persist) _CVAR(name, cur, { if(_##name <= 255) _##name |= (_##name<<8) | (_##name<<16); }, body, persist)
358 #define CVAR1P(name, cur) _CVAR1(name, cur, , IDF_PERSIST)
359 #define CVAR1R(name, cur) _CVAR1(name, cur, , IDF_OVERRIDE)
360 #define CVAR1FP(name, cur, body) _CVAR1(name, cur, body, IDF_PERSIST)
361 #define CVAR1FR(name, cur, body) _CVAR1(name, cur, body, IDF_OVERRIDE)
362
363 #define _FVAR(name, global, min, cur, max, persist) float global = fvariable(#name, min, cur, max, &global, NULL, persist)
364 #define FVARN(name, global, min, cur, max) _FVAR(name, global, min, cur, max, 0)
365 #define FVARNP(name, global, min, cur, max) _FVAR(name, global, min, cur, max, IDF_PERSIST)
366 #define FVARNR(name, global, min, cur, max) _FVAR(name, global, min, cur, max, IDF_OVERRIDE)
367 #define FVAR(name, min, cur, max) _FVAR(name, name, min, cur, max, 0)
368 #define FVARP(name, min, cur, max) _FVAR(name, name, min, cur, max, IDF_PERSIST)
369 #define FVARR(name, min, cur, max) _FVAR(name, name, min, cur, max, IDF_OVERRIDE)
370 #define _FVARF(name, global, min, cur, max, body, persist) void var_##name(ident *id); float global = fvariable(#name, min, cur, max, &global, var_##name, persist); void var_##name(ident *id) { body; }
371 #define FVARFN(name, global, min, cur, max, body) _FVARF(name, global, min, cur, max, body, 0)
372 #define FVARF(name, min, cur, max, body) _FVARF(name, name, min, cur, max, body, 0)
373 #define FVARFP(name, min, cur, max, body) _FVARF(name, name, min, cur, max, body, IDF_PERSIST)
374 #define FVARFR(name, min, cur, max, body) _FVARF(name, name, min, cur, max, body, IDF_OVERRIDE)
375 #define FVARFNP(name, global, min, cur, max, body) _FVARF(name, global, min, cur, max, body, IDF_PERSIST)
376 #define FVARFNR(name, global, min, cur, max, body) _FVARF(name, global, min, cur, max, body, IDF_OVERRIDE)
377
378 #define _SVAR(name, global, cur, persist) char *global = svariable(#name, cur, &global, NULL, persist)
379 #define SVARN(name, global, cur) _SVAR(name, global, cur, 0)
380 #define SVARNP(name, global, cur) _SVAR(name, global, cur, IDF_PERSIST)
381 #define SVARNR(name, global, cur) _SVAR(name, global, cur, IDF_OVERRIDE)
382 #define SVAR(name, cur) _SVAR(name, name, cur, 0)
383 #define SVARP(name, cur) _SVAR(name, name, cur, IDF_PERSIST)
384 #define SVARR(name, cur) _SVAR(name, name, cur, IDF_OVERRIDE)
385 #define _SVARF(name, global, cur, body, persist) void var_##name(ident *id); char *global = svariable(#name, cur, &global, var_##name, persist); void var_##name(ident *id) { body; }
386 #define SVARFN(name, global, cur, body) _SVARF(name, global, cur, body, 0)
387 #define SVARF(name, cur, body) _SVARF(name, name, cur, body, 0)
388 #define SVARFP(name, cur, body) _SVARF(name, name, cur, body, IDF_PERSIST)
389 #define SVARFR(name, cur, body) _SVARF(name, name, cur, body, IDF_OVERRIDE)
390 #define SVARFNP(name, global, cur, body) _SVARF(name, global, cur, body, IDF_PERSIST)
391 #define SVARFNR(name, global, cur, body) _SVARF(name, global, cur, body, IDF_OVERRIDE)
392
393 // anonymous inline commands, uses nasty template trick with line numbers to keep names unique
394 #define ICOMMANDNAME(name) _icmd_##name
395 #define ICOMMANDSNAME _icmds_
396 #define ICOMMANDKNS(name, type, cmdname, nargs, proto, b) template<int N> struct cmdname; template<> struct cmdname<__LINE__> { static bool init; static void run proto; }; bool cmdname<__LINE__>::init = addcommand(name, (identfun)cmdname<__LINE__>::run, nargs, type); void cmdname<__LINE__>::run proto \
397 { b; }
398 #define ICOMMANDKN(name, type, cmdname, nargs, proto, b) ICOMMANDKNS(#name, type, cmdname, nargs, proto, b)
399 #define ICOMMANDK(name, type, nargs, proto, b) ICOMMANDKN(name, type, ICOMMANDNAME(name), nargs, proto, b)
400 #define ICOMMANDKS(name, type, nargs, proto, b) ICOMMANDKNS(name, type, ICOMMANDSNAME, nargs, proto, b)
401 #define ICOMMANDNS(name, cmdname, nargs, proto, b) ICOMMANDKNS(name, ID_COMMAND, cmdname, nargs, proto, b)
402 #define ICOMMANDN(name, cmdname, nargs, proto, b) ICOMMANDNS(#name, cmdname, nargs, proto, b)
403 #define ICOMMAND(name, nargs, proto, b) ICOMMANDN(name, ICOMMANDNAME(name), nargs, proto, b)
404 #define ICOMMANDS(name, nargs, proto, b) ICOMMANDNS(name, ICOMMANDSNAME, nargs, proto, b)
405
406