p2  0.0
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
potion.c
Go to the documentation of this file.
1 //
2 // potion.c
3 // the Potion!
4 //
5 // (c) 2008 why the lucky stiff, the freelance professor
6 // (c) 2013-2014 by perl11 org
7 //
8 #include <stdio.h>
9 #include <sys/stat.h>
10 #include <sys/stat.h>
11 #include <fcntl.h>
12 #include <unistd.h>
13 
14 #include "potion.h"
15 #include "internal.h"
16 #include "opcodes.h"
17 #include "khash.h"
18 #include "table.h"
19 
20 const char potion_banner[] = "potion " POTION_VERSION
21  " (date='" POTION_DATE "', commit='" POTION_COMMIT
22  "', platform='" POTION_PLATFORM "', jit=%d)\n";
24 
25 static void potion_cmd_usage(Potion *P) {
26  printf("usage: potion [options] [filename] [arguments]\n"
27  " -e code execute code instead of filename\n"
28  " -B, --bytecode run with bytecode VM (slower, but cross-platform)\n"
29  " -X, --x86 run with x86 JIT VM (faster, x86 and x86-64)\n"
30  " -I, --inspect print only the return value\n"
31  " -V, --verbose show bytecode and ast info\n"
32  " -Ldirectory add library search path\n"
33  " -Mmodule[=args] load module\n"
34  " -d[module[=args]] debug script\n"
35  " --debug[=module[,args]]\n"
36  " -c, --compile compile the script to bytecode\n"
37  " --compile={target[,args]} compile to target: c or exe\n" //TODO: js,jvm,.net
38  " --check check the script syntax and exit\n"
39  " -h, --help show this helpful stuff\n"
40 #ifdef DEBUG
41  " -D[itPpcvGJ?] debugging flags, try -D?\n"
42 #endif
43  " -v, --version show version\n"
44  "(default: %s)\n",
45 #if POTION_JIT == 1
46  "-X"
47 #else
48  "-B"
49 #endif
50  );
51 }
52 
53 static void potion_cmd_stats(Potion *P) {
54  printf("sizeof(PN=%d, PNObject=%d, PNTuple=%d, PNTuple+1=%d, PNTable=%d)\n",
55  (int)sizeof(PN), (int)sizeof(struct PNObject), (int)sizeof(struct PNTuple),
56  (int)(sizeof(PN) + sizeof(struct PNTuple)), (int)sizeof(struct PNTable));
57  printf("GC (fixed=%ld, actual=%ld, reserved=%ld)\n",
58  PN_INT(potion_gc_fixed(P, 0, 0)), PN_INT(potion_gc_actual(P, 0, 0)),
59  PN_INT(potion_gc_reserved(P, 0, 0)));
60 }
61 
62 static void potion_cmd_version(Potion *P) {
63  printf(potion_banner, POTION_JIT);
64 }
65 
66 #define DBG_Pv(c) \
67  if (P->flags & DEBUG_VERBOSE) \
68  potion_p(P, c)
69 //v OR i!
70 #define DBG_Pvi(c) \
71  if (P->flags & (DEBUG_INSPECT|DEBUG_VERBOSE)) \
72  potion_p(P, c)
73 
74 static PN potion_cmd_exec(Potion *P, PN buf, char *filename, char *compile, char *addcode) {
75  exec_mode_t exec = (exec_mode_t)(P->flags & ((1<<EXEC_BITS)-1));
76  int fd = -1;
77  PN code = PN_NIL;
78 
79  if (!buf && filename) {
80  struct stat stats;
81  if (stat(filename, &stats) == -1) {
82  fprintf(stderr, "** %s does not exist.", filename);
83  goto done;
84  }
85  fd = open(filename, O_RDONLY | O_BINARY);
86  if (fd == -1) {
87  fprintf(stderr, "** could not open %s. check permissions.", filename);
88  goto done;
89  }
90  long size = stats.st_size;
91  char *bufptr;
92  if (addcode) {
93  int len = strlen(addcode);
94  size += len;
95  buf = potion_bytes(P, size);
96  bufptr = PN_STR_PTR(buf);
97  PN_MEMCPY_N(bufptr, addcode, char, len);
98  bufptr += len;
99  } else {
100  buf = potion_bytes(P, size);
101  bufptr = PN_STR_PTR(buf);
102  }
103  // TODO: mmap instead of read all
104  if (read(fd, bufptr, stats.st_size) == stats.st_size) {
105  PN_STR_PTR(buf)[size] = '\0';
106  } else {
107  fprintf(stderr, "** could not read entire file.");
108  goto done;
109  }
110  }
111  else if (!filename) filename = "-e";
112 
113  code = potion_source_load(P, PN_NIL, buf);
114  if (PN_IS_PROTO(code)) {
115  DBG_v("\n-- loaded --\n");
116  } else {
117  code = potion_parse(P, buf, filename);
118  if (!code || PN_TYPE(code) == PN_TERROR) {
119  potion_p(P, code);
120  return code;
121  }
122  DBG_v("\n-- parsed --\n");
123  DBG_Pv(code);
124  code = potion_send(code, PN_compile, potion_str(P, filename),
125  compile ? potion_str(P, compile): PN_NIL);
126  DBG_v("\n-- compiled --\n");
127  }
128  DBG_Pv(code);
129  if (exec == EXEC_VM || exec == EXEC_DEBUG) {
130  code = potion_vm(P, code, P->lobby, PN_NIL, 0, NULL);
131  DBG_v("\n-- vm returned %p (fixed=%ld, actual=%ld, reserved=%ld, time=%0.6gms %dx/%dm/%di) --\n",
132  (void *)code,
133  PN_INT(potion_gc_fixed(P, 0, 0)), PN_INT(potion_gc_actual(P, 0, 0)),
134  PN_INT(potion_gc_reserved(P, 0, 0)), P->mem->time * 1000, P->mem->pass,
135  P->mem->majors, P->mem->minors);
136  DBG_Pvi(code);
137  } else if (exec == EXEC_JIT) {
138 #ifdef POTION_JIT_TARGET
139  PN val;
140  PN cl = potion_closure_new(P, (PN_F)potion_jit_proto(P, code), PN_NIL, 1);
141  PN_CLOSURE(cl)->data[0] = code;
142  val = PN_PROTO(code)->jit(P, cl, P->lobby);
143  DBG_v("\n-- jit returned %p (fixed=%ld, actual=%ld, reserved=%ld, time=%0.6gms %dx/%dm/%di) --\n", PN_PROTO(code)->jit,
144  PN_INT(potion_gc_fixed(P, 0, 0)), PN_INT(potion_gc_actual(P, 0, 0)),
145  PN_INT(potion_gc_reserved(P, 0, 0)), P->mem->time * 1000, P->mem->pass,
146  P->mem->majors, P->mem->minors);
147  DBG_Pvi(val);
148 #else
149  fprintf(stderr, "** potion built without JIT support\n");
150 #endif
151  }
152  else if (exec == EXEC_CHECK) {
153  DBG_v("\n-- check --\n");
154  }
155 
156  if (!code || PN_TYPE(code) == PN_TERROR)
157  goto done;
158 
159  if (exec >= MAX_EXEC)
160  potion_fatal("fatal: stack corruption (exec > MAX_EXEC)\n");
161 
162  if (exec == EXEC_COMPILE) { // needs an inputfile. TODO: -e"" -ofile
163  char outpath[255];
164  FILE *pnb;
165  PN opts;
166  char *c_opts = NULL;
167  PN_SIZE written = 0;
168  if (compile) { // --compile=c[,OPTS]
169  if ((c_opts = strchr(compile,','))) {
170  c_opts[0] = '\0';
171  c_opts++;
172  //TODO check -o for outpath
173  }
174  }
175  if (!compile || !strcmp(compile, "bc"))
176  sprintf(outpath, "%sb", filename); // .pnb
177  else if (!strcmp(compile, "c"))
178  sprintf(outpath, "%s.c", filename); // .pn.c
179  else if (!strcmp(compile, "exe"))
180  sprintf(outpath, "%s.out", filename); // TODO: strip ext
181  opts = c_opts
182  ? pn_printf(P, potion_bytes(P,0), "%s,-o%s", c_opts, outpath)
183  : potion_strcat(P, "-o", outpath);
184 
185  pnb = fopen(outpath, "wb");
186  if (!pnb) {
187  fprintf(stderr, "** could not open %s for writing. check permissions.\n", outpath);
188  goto done;
189  }
190  if (!compile)
191  code = potion_source_dumpbc(P, 0, code, PN_NIL);
192  else
193  code = potion_source_dump(P, 0, code,
194  potion_str(P, compile),
195  opts ? opts : PN_NIL);
196  if (code &&
197  (written = fwrite(PN_STR_PTR(code), 1, PN_STR_LEN(code), pnb) == PN_STR_LEN(code))) {
198  printf("** compiled code saved to %s\n", outpath);
199  fclose(pnb);
200 
201  if (!compile || !strcmp(compile, "bc"))
202  printf("** run it with: potion %s\n", outpath);
203  // TODO: let the compilers write its own hints (,-ooutfile)
204  else if (!strcmp(compile, "c"))
205  printf("** compile it with: %s %s %s\n", POTION_CC, POTION_CFLAGS, outpath);
206  else if (!strcmp(compile, "exe"))
207  printf("** run it with: ./%s\n", outpath);
208  } else {
209  fprintf(stderr, "** could not write all %s compiled code (%u/%u) to %s\n",
210  compile?compile:"bytecode", written, code?PN_STR_LEN(code):0, outpath);
211  }
212  }
213 
214 #if defined(DEBUG)
215  if (P->flags & DEBUG_GC) { // GC sanity check
216  fprintf(stderr, "\n-- gc check --\n");
217  void *scanptr = (void *)((char *)P->mem->old_lo + (sizeof(PN) * 2));
218  while ((PN)scanptr < (PN)P->mem->old_cur) {
219  fprintf(stderr, "%p.vt = %x (%u)\n",
220  scanptr, ((struct PNObject *)scanptr)->vt,
221  potion_type_size(P, scanptr));
222  if (((struct PNFwd *)scanptr)->fwd != POTION_FWD
223  && ((struct PNFwd *)scanptr)->fwd != POTION_COPIED) {
224  PNType vt = ((struct PNObject *)scanptr)->vt;
225  if ((signed int)vt < 0 || vt > PN_TUSER) {
226  fprintf(stderr, "** wrong type for allocated object: %p.vt = %x\n",
227  scanptr, vt);
228  break;
229  }
230  }
231  scanptr = (void *)((char *)scanptr + potion_type_size(P, scanptr));
232  if ((PN)scanptr > (PN)P->mem->old_cur) {
233  fprintf(stderr, "** allocated object goes beyond GC pointer\n");
234  break;
235  }
236  }
237  }
238 #endif
239 
240 done:
241  if (fd != -1)
242  close(fd);
243 
244  return code;
245 }
246 
247 char * addmodule(Potion *P, char *result, char *prefix, char *name) {
248  char *args = strchr(name, '=');
249  PN out = potion_bytes(P, 0);
250  if (args) *args++ = '\0';
251  if (prefix) {
252  pn_printf(P, out, "load \"%s/%s\"\n", prefix, name);
253  } else {
254  pn_printf(P, out, "load \"%s\"\n", name);
255  }
256  if (args)
257  pn_printf(P, out, "%s(%s)\n", name, args);
258  else
259  pn_printf(P, out, "%s()\n", name);
260  return PN_STR_PTR(out);
261 }
262 
263 int main(int argc, char *argv[]) {
264  POTION_INIT_STACK(sp);
265  Potion *P = potion_create(sp);
266  int i;
268  PN buf = PN_NIL;
269  char *compile = NULL;
270  char *fn = NULL;
271  char *addmodules = NULL;
272 
273 #if defined(STATIC) || defined(SANDBOX)
275  Potion_Init_aio(P);
276 #ifndef SANDBOX
278 #endif
279 #endif
280 
281  for (i = 1; i < argc; i++) {
282  if (!strcmp(argv[i], "--")) { i++; break; }
283  if (!strcmp(argv[i], "-I") || !strcmp(argv[i], "--inspect")) {
284  P->flags |= DEBUG_INSPECT; continue; }
285  if (!strcmp(argv[i], "-L")) {
286 #ifdef SANDBOX
287  potion_fatal("-L disabled in SANDBOX");
288 #else
289  char *extra_path = &argv[i][2]; // todo: flexible
290  if (*extra_path)
291  potion_loader_add(P, potion_str(P, extra_path));
292  else {
293  if (i == argc) {
294  fprintf(stderr, "-L missing directory\n");
295  goto END;
296  }
297  potion_loader_add(P, potion_str(P, argv[i+1]));
298  i++;
299  }
300  continue;
301 #endif /* SANDBOX */
302  }
303  if (!strcmp(argv[i], "-V") || !strcmp(argv[i], "--verbose")) {
304  P->flags |= DEBUG_VERBOSE; continue; }
305  if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
306  potion_cmd_version(P); goto END; }
307  if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
308  potion_cmd_usage(P); goto END; }
309  if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--stats")) {
310  potion_cmd_stats(P); goto END; }
311  if (!strcmp(argv[i], "--check")) { exec = EXEC_CHECK; continue; }
312  if (!strncmp(argv[i], "--compile=", 10)) {
313  exec = EXEC_COMPILE; compile = &argv[i][10]; continue; }
314  if (!strcmp(argv[i], "--compile") || !strcmp(argv[i], "-c")) {
315  exec = EXEC_COMPILE; compile = NULL; continue; }
316  if (!strcmp(argv[i], "-B") || !strcmp(argv[i], "--bytecode")) {
317  exec = EXEC_VM; continue; }
318  if (!strcmp(argv[i], "-X") || !strcmp(argv[i], "--x86")) {
319  exec = EXEC_JIT; continue; }
320  if (!strcmp(argv[i], "--debug") || !strcmp(argv[i], "-d")) {
321  addmodules = addmodule(P, addmodules, NULL, "debug");
322  exec = EXEC_DEBUG; continue; }
323  if (!strcmp(argv[i], "--debug=")) {
324  addmodules = addmodule(P, addmodules, "debug", &argv[i][9]);
325  exec = EXEC_DEBUG; continue; }
326  if (argv[i][0] == '-' && argv[i][1] == 'd') {
327  if (argv[i][2] == '=')
328  addmodules = addmodule(P, addmodules, NULL,
329  PN_STR_PTR(PN_STRCAT("debug=", &argv[i][3])));
330  else
331  addmodules = addmodule(P, addmodules, "debug", &argv[i][2]);
332  exec = EXEC_DEBUG; continue; }
333  if (argv[i][0] == '-' && argv[i][1] == 'M') {
334  addmodules = addmodule(P, addmodules, NULL, &argv[i][2]);
335  continue; }
336 #ifdef DEBUG
337  if (argv[i][0] == '-' && argv[i][1] == 'D') {
338  if (strchr(&argv[i][2], '?')) {
339  printf("-D debugging flags:\n");
340  printf(" i inspect\n");
341  printf(" v verbose\n");
342  printf(" t trace\n");
343  printf(" p parse\n");
344  printf(" P parse verbose\n");
345  printf(" c compile\n");
346  printf(" J disassemble Jit code\n");
347  printf(" G GC (use w/ or wo/ -Dv\n");
348  goto END;
349  }
350  if (strchr(&argv[i][2], 'i')) P->flags |= DEBUG_INSPECT;
351  if (strchr(&argv[i][2], 'v')) P->flags |= DEBUG_VERBOSE;
352  if (strchr(&argv[i][2], 't')) { P->flags |= DEBUG_TRACE;
353  exec = exec==EXEC_JIT ? EXEC_VM : exec; }
354  if (strchr(&argv[i][2], 'p')) P->flags |= DEBUG_PARSE;
355  if (strchr(&argv[i][2], 'P')) P->flags |= DEBUG_PARSE_VERBOSE;
356  if (strchr(&argv[i][2], 'c')) P->flags |= DEBUG_COMPILE;
357  if (strchr(&argv[i][2], 'J')) P->flags |= DEBUG_JIT;
358  if (strchr(&argv[i][2], 'G')) P->flags |= DEBUG_GC;
359  continue;
360  }
361 #endif
362  if (argv[i][0] == '-') {
363  if (argv[i][1] == 'e') {
364  char *arg;
365  if (strlen(argv[i]) == 2) {
366  arg = argv[i+1];
367  } else if (i <= argc) {
368  arg = argv[i]+2;
369  } else { // or go into interactive mode?
370  potion_fatal("Missing argument for -e");
371  goto END;
372  }
373  if (addmodules)
374  buf = potion_strcat(P, addmodules, arg);
375  else
376  buf = potion_str(P, arg);
377  continue;
378  } else {
379  fprintf(stderr, "** Unrecognized option: %s\n", argv[i]);
380  }
381  }
382  else {
383  break;
384  }
385  }
386  P->flags = P->flags + exec;
387 
388  if (buf || i < argc) {
389  PN args = PN_TUP0();
390  if (buf == PN_NIL) { fn = argv[i++]; PN_PUSH(args, PN_STR(fn)); }
391  else { PN_PUSH(args, PN_STR("-e")); }
392  for (; i < argc; i++) PN_PUSH(args, PN_STR(argv[i]));
393  potion_define_global(P, PN_STR("argv"), args);
394  if (buf != PN_NIL) {
395  potion_cmd_exec(P, buf, NULL, compile, "");
396  } else {
397  potion_cmd_exec(P, buf, fn, compile, addmodules);
398  }
399  } else {
400  if (!exec || P->flags & DEBUG_INSPECT) potion_fatal("no filename given");
401  PN args = PN_TUP0();
402  PN_PUSH(args, PN_STR(""));
403  potion_define_global(P, PN_STR("argv"), args);
405  "load 'readline'\n" \
406  "loop:\n" \
407  " code = readline('>> ')\n" \
408  " if (not code): \"\\n\" print, break.\n" \
409  " if (code != ''):\n" \
410  " obj = code eval\n" \
411  " if (obj kind == Error):\n" \
412  " obj string print." \
413  " else: ('=> ', obj, \"\\n\") join print.\n" \
414  " .\n"
415  "."));
416  }
417  END:
418 #if !defined(POTION_JIT_TARGET) || defined(DEBUG)
419  if (P != NULL)
420  potion_destroy(P);
421 #endif
422  return 0;
423 }
volatile void * old_lo
the old region (TODO: consider making the old region common to all threads)
Definition: potion.h:682
PN PN_compile
Definition: internal.c:14
PN potion_vm(Potion *, PN, PN, PN, PN_SIZE, PN *volatile)
the bytecode run-loop
Definition: vm.c:594
PN potion_bytes(Potion *, size_t)
Definition: string.c:342
PN potion_closure_new(Potion *P, PN_F meth, PN sig, PN_SIZE extra)
Definition: objmodel.c:17
forwarding pointer (in case of reallocation)
Definition: potion.h:313
bytecode (switch or cgoto)
Definition: potion.h:615
void potion_destroy(Potion *P)
Definition: internal.c:137
PN potion_str(Potion *, const char *)
Definition: string.c:33
void Potion_Init_readline(Potion *P)
Definition: readline.c:22
PN potion_gc_actual(Potion *P, PN cl, PN self)
Definition: gc.c:714
klib hash table library based on double hashing http://en.wikipedia.org/wiki/Double_hashing ...
PN_F potion_jit_proto(Potion *, PN)
Definition: vm.c:221
exec_mode_t
the interpreter (one per thread, houses its own garbage collector)
Definition: potion.h:614
a tuple is an array of PNs.
Definition: potion.h:477
const char potion_banner[]
Definition: potion.c:20
#define POTION_DATE
Definition: config.h:5
#define PN_CLOSURE(x)
Definition: potion.h:225
Potion_Flags flags
vm flags: execution model and debug flags
Definition: potion.h:659
#define PN_TUSER
Definition: potion.h:130
static void potion_cmd_usage(Potion *P)
Definition: potion.c:25
PN PN potion_byte_str(Potion *, const char *)
Definition: string.c:331
PN potion_source_dumpbc(Potion *P, PN cl, PN proto, PN options)
Definition: compile.c:1360
int main(int argc, char *argv[])
Definition: potion.c:263
unsigned int PNType
Definition: potion.h:79
#define PN_PUSH(T, X)
Definition: potion.h:272
#define PN_STR_LEN(x)
Definition: potion.h:223
struct PNMemory * mem
allocator/gc
Definition: potion.h:660
#define POTION_PLATFORM
Definition: config.h:23
PN potion_source_load(Potion *P, PN cl, PN buf)
Definition: compile.c:1287
const char potion_version[]
Definition: potion.c:23
#define POTION_CC
Definition: config.h:8
#define POTION_JIT
Definition: config.h:17
the Potion VM instruction set (heavily based on Lua's)
#define PN_STR(x)
Definition: potion.h:219
#define PN_INT(x)
Definition: potion.h:214
PN potion_source_dump(Potion *P, PN cl, PN self, PN backend, PN options)
Definition: compile.c:1381
unsigned int PN_SIZE
Definition: potion.h:79
standard objects act like C structs the fields are defined by the type and it's a fixed size...
Definition: potion.h:304
#define POTION_INIT_STACK(x)
Definition: potion.h:692
PN lobby
root namespace
Definition: potion.h:657
#define EXEC_BITS
0-4
Definition: potion.h:625
DLLEXPORT void Potion_Init_buffile(Potion *P)
Definition: buffile.c:296
the table class, based on khash
Definition: table.h:40
void potion_define_global(Potion *P, PN name, PN val)
Definition: objmodel.c:628
PN potion_parse(Potion *, PN, char *)
#define POTION_CFLAGS
Definition: config.h:9
char * addmodule(Potion *P, char *result, char *prefix, char *name)
Definition: potion.c:247
#define PN_MEMCPY_N(X, Y, T, N)
Definition: internal.h:22
PN potion_gc_fixed(Potion *P, PN cl, PN self)
Definition: gc.c:724
PN potion_eval(Potion *P, PN bytes)
Definition: compile.c:1415
static void potion_cmd_version(Potion *P)
Definition: potion.c:62
JIT if detected at config-time (x86, ppc)
Definition: potion.h:616
-c stop after compilation
Definition: potion.h:618
#define POTION_COMMIT
Definition: config.h:6
#define DBG_v(...)
Definition: potion.h:263
non-API internal parts
#define PN_STRCAT(a, b)
Definition: potion.h:221
volatile int pass
Definition: potion.h:684
#define DBG_Pvi(c)
Definition: potion.c:70
DLLEXPORT void Potion_Init_aio(Potion *P)
Definition: aio.c:1809
PN_SIZE potion_type_size(Potion *P, const struct PNObject *ptr)
Definition: gc.c:306
volatile void * old_cur
Definition: potion.h:682
Potion * potion_create(void *sp)
the potion API
Definition: internal.c:124
sanity-check (possible stack overwrites by callcc)
Definition: potion.h:620
#define PN_IS_PROTO(v)
Definition: potion.h:178
-d: instrumented bytecode (line stepping) or just slow runloop?
Definition: potion.h:617
the global interpreter state P. currently singleton (not threads yet)
Definition: potion.h:653
The potion API.
volatile int majors
Definition: potion.h:684
PN_SIZE pn_printf(Potion *, PN, const char *,...) __attribute__((format(printf
static void potion_cmd_stats(Potion *P)
Definition: potion.c:53
#define PN_TYPE(x)
Definition: potion.h:133
PN potion_strcat(Potion *P, char *str, char *str2)
Definition: string.c:64
static PN potion_cmd_exec(Potion *P, PN buf, char *filename, char *compile, char *addcode)
Definition: potion.c:74
#define POTION_COPIED
Definition: potion.h:147
the central table type, based on core/khash.h
_PN(* PN_F)(Potion *, PN, PN,...)
Definition: potion.h:216
#define O_BINARY
Definition: internal.h:128
#define potion_send(RCV, MSG, ARGS...)
method caches (more great stuff from ian piumarta)
Definition: potion.h:781
const char * name
Definition: compile.c:30
void potion_p(Potion *P, PN x)
Definition: internal.c:310
PN potion_gc_reserved(Potion *P, PN cl, PN self)
Definition: gc.c:732
volatile _PN PN
Definition: potion.h:81
#define PN_NIL
Definition: potion.h:139
void potion_loader_add(Potion *P, PN path)
Definition: load.c:221
#define POTION_VERSION
Definition: config.h:4
#define POTION_FWD
Definition: potion.h:146
#define PN_TERROR
Definition: potion.h:128
to bytecode (dumpbc)
Definition: potion.h:619
void potion_fatal(char *message)
Definition: internal.c:286
const u8 args
Definition: compile.c:31
volatile int minors
Definition: potion.h:684
#define PN_STR_PTR(x)
Definition: potion.h:222
#define PN_TUP0()
Definition: potion.h:270
#define DBG_Pv(c)
Definition: potion.c:66
#define PN_PROTO(x)
Definition: potion.h:227