rpm  5.4.15
expression.c
Go to the documentation of this file.
1 
14 #include "system.h"
15 
16 #include <rpmio.h>
17 #include <rpmiotypes.h>
18 #include <rpmlog.h>
19 
20 #include <rpmbuild.h>
21 
22 #include "debug.h"
23 
24 /* #define DEBUG_PARSER 1 */
25 
26 #ifdef DEBUG_PARSER
27 #include <stdio.h>
28 #define DEBUG(x) do { x ; } while (0)
29 #else
30 #define DEBUG(x)
31 #endif
32 
36 typedef struct _value {
38  union {
39  const char *s;
40  int i;
41  } data;
42 } *Value;
43 
47  /*@*/
48 {
49  Value v;
50 
51  v = (Value) xmalloc(sizeof(*v));
52  v->type = VALUE_TYPE_INTEGER;
53  v->data.i = i;
54  return v;
55 }
56 
59 static Value valueMakeString(/*@only@*/ const char *s)
60  /*@*/
61 {
62  Value v;
63 
64  v = (Value) xmalloc(sizeof(*v));
65  v->type = VALUE_TYPE_STRING;
66  v->data.s = xstrdup(s);
67  return v;
68 }
69 
72 static void valueFree( /*@only@*/ Value v)
73  /*@modifies v @*/
74 {
75  if (v) {
76  if (v->type == VALUE_TYPE_STRING)
77  v->data.s = _free(v->data.s);
78  v = _free(v);
79  }
80 }
81 
82 #ifdef DEBUG_PARSER
83 static void valueDump(const char *msg, Value v, FILE *fp)
84  /*@*/
85 {
86  if (msg)
87  fprintf(fp, "%s ", msg);
88  if (v) {
89  if (v->type == VALUE_TYPE_INTEGER)
90  fprintf(fp, "INTEGER %d\n", v->data.i);
91  else
92  fprintf(fp, "STRING '%s'\n", v->data.s);
93  } else
94  fprintf(fp, "NULL\n");
95 }
96 #endif
97 
98 #define valueIsInteger(v) ((v)->type == VALUE_TYPE_INTEGER)
99 #define valueIsString(v) ((v)->type == VALUE_TYPE_STRING)
100 #define valueSameType(v1,v2) ((v1)->type == (v2)->type)
101 
102 
106 typedef struct _parseState {
107 /*@owned@*/
108  char *str;
109 /*@dependent@*/
110  char *p;
111  int nextToken;
112 /*@relnull@*/
115 } *ParseState;
116 
117 
122 #define TOK_EOF 1
123 #define TOK_INTEGER 2
124 #define TOK_STRING 3
125 #define TOK_IDENTIFIER 4
126 #define TOK_ADD 5
127 #define TOK_MINUS 6
128 #define TOK_MULTIPLY 7
129 #define TOK_DIVIDE 8
130 #define TOK_OPEN_P 9
131 #define TOK_CLOSE_P 10
132 #define TOK_EQ 11
133 #define TOK_NEQ 12
134 #define TOK_LT 13
135 #define TOK_LE 14
136 #define TOK_GT 15
137 #define TOK_GE 16
138 #define TOK_NOT 17
139 #define TOK_LOGICAL_AND 18
140 #define TOK_LOGICAL_OR 19
141 
143 #define EXPRBUFSIZ BUFSIZ
144 
145 #if defined(DEBUG_PARSER)
146 typedef struct exprTokTableEntry {
147  const char *name;
148  int val;
149 } ETTE_t;
150 
151 ETTE_t exprTokTable[] = {
152  { "EOF", TOK_EOF },
153  { "I", TOK_INTEGER },
154  { "S", TOK_STRING },
155  { "ID", TOK_IDENTIFIER },
156  { "+", TOK_ADD },
157  { "-", TOK_MINUS },
158  { "*", TOK_MULTIPLY },
159  { "/", TOK_DIVIDE },
160  { "( ", TOK_OPEN_P },
161  { " )", TOK_CLOSE_P },
162  { "==", TOK_EQ },
163  { "!=", TOK_NEQ },
164  { "<", TOK_LT },
165  { "<=", TOK_LE },
166  { ">", TOK_GT },
167  { ">=", TOK_GE },
168  { "!", TOK_NOT },
169  { "&&", TOK_LOGICAL_AND },
170  { "||", TOK_LOGICAL_OR },
171  { NULL, 0 }
172 };
173 
174 static const char *prToken(int val)
175  /*@*/
176 {
177  ETTE_t *et;
178 
179  for (et = exprTokTable; et->name != NULL; et++) {
180  if (val == et->val)
181  return et->name;
182  }
183  return "???";
184 }
185 #endif /* DEBUG_PARSER */
186 
190 static int rdToken(ParseState state)
191  /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
192  /*@modifies state->nextToken, state->p, state->tokenValue,
193  rpmGlobalMacroContext, internalState @*/
194 {
195  int token;
196  Value v = NULL;
197  char *p = state->p;
198 
199  /* Skip whitespace before the next token. */
200  while (*p && xisspace(*p)) p++;
201 
202  switch (*p) {
203  case '\0':
204  token = TOK_EOF;
205  p--;
206  break;
207  case '+':
208  token = TOK_ADD;
209  break;
210  case '-':
211  token = TOK_MINUS;
212  break;
213  case '*':
214  token = TOK_MULTIPLY;
215  break;
216  case '/':
217  token = TOK_DIVIDE;
218  break;
219  case '(':
220  token = TOK_OPEN_P;
221  break;
222  case ')':
223  token = TOK_CLOSE_P;
224  break;
225  case '=':
226  if (p[1] == '=') {
227  token = TOK_EQ;
228  p++;
229  } else {
230  rpmlog(RPMLOG_ERR, _("syntax error while parsing ==\n"));
231  return -1;
232  }
233  break;
234  case '!':
235  if (p[1] == '=') {
236  token = TOK_NEQ;
237  p++;
238  } else
239  token = TOK_NOT;
240  break;
241  case '<':
242  if (p[1] == '=') {
243  token = TOK_LE;
244  p++;
245  } else
246  token = TOK_LT;
247  break;
248  case '>':
249  if (p[1] == '=') {
250  token = TOK_GE;
251  p++;
252  } else
253  token = TOK_GT;
254  break;
255  case '&':
256  if (p[1] == '&') {
257  token = TOK_LOGICAL_AND;
258  p++;
259  } else {
260  rpmlog(RPMLOG_ERR, _("syntax error while parsing &&\n"));
261  return -1;
262  }
263  break;
264  case '|':
265  if (p[1] == '|') {
266  token = TOK_LOGICAL_OR;
267  p++;
268  } else {
269  rpmlog(RPMLOG_ERR, _("syntax error while parsing ||\n"));
270  return -1;
271  }
272  break;
273 
274  default:
275  if (xisdigit(*p)) {
276  char temp[EXPRBUFSIZ], *t = temp;
277 
278  temp[0] = '\0';
279  while (*p && xisdigit(*p))
280  *t++ = *p++;
281  *t++ = '\0';
282  p--;
283 
284  token = TOK_INTEGER;
285  v = valueMakeInteger(atoi(temp));
286 
287  } else if (xisalpha(*p)) {
288  char temp[EXPRBUFSIZ], *t = temp;
289 
290  temp[0] = '\0';
291  while (*p && (xisalnum(*p) || *p == '_'))
292  *t++ = *p++;
293  *t++ = '\0';
294  p--;
295 
296  token = TOK_IDENTIFIER;
297  v = valueMakeString( xstrdup(temp) );
298 
299  } else if (*p == '\"') {
300  char temp[EXPRBUFSIZ], *t = temp;
301 
302  temp[0] = '\0';
303  p++;
304  while (*p && *p != '\"')
305  *t++ = *p++;
306  *t++ = '\0';
307 
308  token = TOK_STRING;
309  v = valueMakeString( rpmExpand(temp, NULL) );
310 
311  } else {
312  rpmlog(RPMLOG_ERR, _("parse error in expression\n"));
313  return -1;
314  }
315  }
316 
317  state->p = p + 1;
318  state->nextToken = token;
319  state->tokenValue = v;
320 
321  DEBUG(printf("rdToken: \"%s\" (%d)\n", prToken(token), token));
322  DEBUG(valueDump("rdToken:", state->tokenValue, stdout));
323 
324  return 0;
325 }
326 
327 /*@null@*/
328 static Value doLogical(ParseState state)
329  /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
330  /*@modifies state->nextToken, state->p, state->tokenValue,
331  rpmGlobalMacroContext, internalState @*/;
332 
336 /*@null@*/
338  /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
339  /*@modifies state->nextToken, state->p, state->tokenValue,
340  rpmGlobalMacroContext, internalState @*/
341 {
342  Value v;
343 
344  DEBUG(printf("doPrimary()\n"));
345 
346  switch (state->nextToken) {
347  case TOK_OPEN_P:
348  if (rdToken(state))
349  return NULL;
350  v = doLogical(state);
351  if (state->nextToken != TOK_CLOSE_P) {
352  rpmlog(RPMLOG_ERR, _("unmatched (\n"));
353  return NULL;
354  }
355  if (rdToken(state))
356  return NULL;
357  break;
358 
359  case TOK_INTEGER:
360  case TOK_STRING:
361  v = state->tokenValue;
362  if (rdToken(state))
363  return NULL;
364  break;
365 
366  case TOK_IDENTIFIER: {
367  const char *name = state->tokenValue->data.s;
368  char *t = rpmExpand(name, NULL);
369 
370  v = valueMakeString(t);
371  t = _free(t);
372  if (rdToken(state)) {
373  if (v) valueFree(v);
374  return NULL;
375  }
376  break;
377  }
378 
379  case TOK_MINUS:
380  if (rdToken(state))
381  return NULL;
382 
383  v = doPrimary(state);
384  if (v == NULL)
385  return NULL;
386 
387  if (! valueIsInteger(v)) {
388  rpmlog(RPMLOG_ERR, _("- only on numbers\n"));
389  return NULL;
390  }
391 
392  v = valueMakeInteger(- v->data.i);
393  break;
394 
395  case TOK_NOT:
396  if (rdToken(state))
397  return NULL;
398 
399  v = doPrimary(state);
400  if (v == NULL)
401  return NULL;
402 
403  if (! valueIsInteger(v)) {
404  rpmlog(RPMLOG_ERR, _("! only on numbers\n"));
405  return NULL;
406  }
407 
408  v = valueMakeInteger(! v->data.i);
409  break;
410  default:
411  return NULL;
412  /*@notreached@*/ break;
413  }
414 
415  DEBUG(valueDump("doPrimary:", v, stdout));
416  return v;
417 }
418 
422 /*@null@*/
424  /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
425  /*@modifies state->nextToken, state->p, state->tokenValue,
426  rpmGlobalMacroContext, internalState @*/
427 {
428  Value v1, v2 = NULL;
429 
430  DEBUG(printf("doMultiplyDivide()\n"));
431 
432  v1 = doPrimary(state);
433  if (v1 == NULL)
434  return NULL;
435 
436  while (state->nextToken == TOK_MULTIPLY
437  || state->nextToken == TOK_DIVIDE)
438  {
439  int op = state->nextToken;
440 
441  if (rdToken(state)) {
442  goto errxit;
443  }
444 
445  if (v2) valueFree(v2);
446 
447  v2 = doPrimary(state);
448  if (v2 == NULL) {
449  goto errxit;
450  }
451 
452  if (! valueSameType(v1, v2)) {
453  rpmlog(RPMLOG_ERR, _("types must match\n"));
454  goto errxit;
455  }
456 
457  if (valueIsInteger(v1)) {
458  int i1 = v1->data.i, i2 = v2->data.i;
459 
460  valueFree(v1);
461  if (op == TOK_MULTIPLY)
462  v1 = valueMakeInteger(i1 * i2);
463  else
464  v1 = valueMakeInteger(i1 / i2);
465  } else {
466  rpmlog(RPMLOG_ERR, _("* / not suported for strings\n"));
467  goto errxit;
468  }
469  }
470 
471  if (v2) valueFree(v2);
472  return v1;
473 
474 errxit:
475  if (v2) valueFree(v2);
476  if (v1) valueFree(v1);
477  return NULL;
478 }
479 
483 /*@null@*/
485  /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
486  /*@modifies state->nextToken, state->p, state->tokenValue,
487  rpmGlobalMacroContext, internalState @*/
488 {
489  Value v1, v2 = NULL;
490 
491  DEBUG(printf("doAddSubtract()\n"));
492 
493  v1 = doMultiplyDivide(state);
494  if (v1 == NULL)
495  return NULL;
496 
497  while (state->nextToken == TOK_ADD || state->nextToken == TOK_MINUS) {
498  int op = state->nextToken;
499 
500  if (rdToken(state))
501  goto errxit;
502 
503  if (v2) valueFree(v2);
504 
505  v2 = doMultiplyDivide(state);
506  if (v2 == NULL)
507  goto errxit;
508 
509  if (! valueSameType(v1, v2)) {
510  rpmlog(RPMLOG_ERR, _("types must match\n"));
511  goto errxit;
512  }
513 
514  if (valueIsInteger(v1)) {
515  int i1 = v1->data.i, i2 = v2->data.i;
516 
517  valueFree(v1);
518  if (op == TOK_ADD)
519  v1 = valueMakeInteger(i1 + i2);
520  else
521  v1 = valueMakeInteger(i1 - i2);
522  } else {
523  char *copy;
524 
525  if (op == TOK_MINUS) {
526  rpmlog(RPMLOG_ERR, _("- not suported for strings\n"));
527  goto errxit;
528  }
529 
530  copy = xmalloc(strlen(v1->data.s) + strlen(v2->data.s) + 1);
531  (void) stpcpy( stpcpy(copy, v1->data.s), v2->data.s);
532 
533  valueFree(v1);
534  v1 = valueMakeString(copy);
535  }
536  }
537 
538  if (v2) valueFree(v2);
539  return v1;
540 
541 errxit:
542  if (v1) valueFree(v1);
543  if (v2) valueFree(v2);
544  return NULL;
545 }
546 
550 /*@null@*/
552  /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
553  /*@modifies state->nextToken, state->p, state->tokenValue,
554  rpmGlobalMacroContext, internalState @*/
555 {
556  Value v1 = NULL;
557  Value v2 = NULL;
558 
559  DEBUG(printf("doRelational()\n"));
560 
561  v1 = doAddSubtract(state);
562  if (v1 == NULL)
563  goto errxit;
564 
565  while (state->nextToken >= TOK_EQ && state->nextToken <= TOK_GE) {
566  int op = state->nextToken;
567  int r;
568 
569  if (rdToken(state))
570  goto errxit;
571 
572  if (v2) valueFree(v2);
573 
574  v2 = doAddSubtract(state);
575  if (v2 == NULL)
576  goto errxit;
577 
578  if (! valueSameType(v1, v2)) {
579  rpmlog(RPMLOG_ERR, _("types must match\n"));
580  goto errxit;
581  }
582 
583  r = 0;
584  if (valueIsInteger(v1)) {
585  int i1 = v1->data.i;
586  int i2 = v2->data.i;
587  switch (op) {
588  case TOK_EQ:
589  r = (i1 == i2);
590  /*@switchbreak@*/ break;
591  case TOK_NEQ:
592  r = (i1 != i2);
593  /*@switchbreak@*/ break;
594  case TOK_LT:
595  r = (i1 < i2);
596  /*@switchbreak@*/ break;
597  case TOK_LE:
598  r = (i1 <= i2);
599  /*@switchbreak@*/ break;
600  case TOK_GT:
601  r = (i1 > i2);
602  /*@switchbreak@*/ break;
603  case TOK_GE:
604  r = (i1 >= i2);
605  /*@switchbreak@*/ break;
606  default:
607  /*@switchbreak@*/ break;
608  }
609  } else {
610  const char * s1 = v1->data.s;
611  const char * s2 = v2->data.s;
612  switch (op) {
613  case TOK_EQ:
614  r = (strcmp(s1,s2) == 0);
615  /*@switchbreak@*/ break;
616  case TOK_NEQ:
617  r = (strcmp(s1,s2) != 0);
618  /*@switchbreak@*/ break;
619  case TOK_LT:
620  r = (strcmp(s1,s2) < 0);
621  /*@switchbreak@*/ break;
622  case TOK_LE:
623  r = (strcmp(s1,s2) <= 0);
624  /*@switchbreak@*/ break;
625  case TOK_GT:
626  r = (strcmp(s1,s2) > 0);
627  /*@switchbreak@*/ break;
628  case TOK_GE:
629  r = (strcmp(s1,s2) >= 0);
630  /*@switchbreak@*/ break;
631  default:
632  /*@switchbreak@*/ break;
633  }
634  }
635  valueFree(v1);
636  v1 = valueMakeInteger(r);
637  }
638 
639  if (v2) valueFree(v2);
640  return v1;
641 
642 errxit:
643  if (v1) valueFree(v1);
644  if (v2) valueFree(v2);
645  return NULL;
646 }
647 
652  /*@globals rpmGlobalMacroContext, h_errno @*/
653  /*@modifies state->nextToken, state->p, state->tokenValue,
654  rpmGlobalMacroContext @*/
655 {
656  Value v1 = NULL;
657  Value v2 = NULL;
658 
659  DEBUG(printf("doLogical()\n"));
660 
661  v1 = doRelational(state);
662  if (v1 == NULL)
663  return NULL;
664 
665  while (state->nextToken == TOK_LOGICAL_AND
666  || state->nextToken == TOK_LOGICAL_OR)
667  {
668  int op = state->nextToken;
669 
670  if (rdToken(state))
671  goto errxit;
672 
673  if (v2) valueFree(v2);
674 
675  v2 = doRelational(state);
676  if (v2 == NULL)
677  goto errxit;
678 
679  if (! valueSameType(v1, v2)) {
680  rpmlog(RPMLOG_ERR, _("types must match\n"));
681  goto errxit;
682  }
683 
684  if (valueIsInteger(v1)) {
685  int i1 = v1->data.i, i2 = v2->data.i;
686 
687  valueFree(v1);
688  if (op == TOK_LOGICAL_AND)
689  v1 = valueMakeInteger(i1 && i2);
690  else
691  v1 = valueMakeInteger(i1 || i2);
692  } else {
693  rpmlog(RPMLOG_ERR, _("&& and || not suported for strings\n"));
694  goto errxit;
695  }
696  }
697 
698  if (v2) valueFree(v2);
699  return v1;
700 
701 errxit:
702  if (v1) valueFree(v1);
703  if (v2) valueFree(v2);
704  return NULL;
705 }
706 
707 int parseExpressionBoolean(Spec spec, const char *expr)
708 {
709  struct _parseState state;
710  int result = -1;
711  Value v;
712 
713  DEBUG(printf("parseExprBoolean(?, '%s')\n", expr));
714 
715  /* Initialize the expression parser state. */
716  state.p = state.str = xstrdup(expr);
717  state.spec = spec;
718  state.nextToken = 0;
719  state.tokenValue = NULL;
720  (void) rdToken(&state);
721 
722  /* Parse the expression. */
723  v = doLogical(&state);
724  if (!v) {
725  state.str = _free(state.str);
726  return -1;
727  }
728 
729  /* If the next token is not TOK_EOF, we have a syntax error. */
730  if (state.nextToken != TOK_EOF) {
731  rpmlog(RPMLOG_ERR, _("syntax error in expression\n"));
732  state.str = _free(state.str);
733  return -1;
734  }
735 
736  DEBUG(valueDump("parseExprBoolean:", v, stdout));
737 
738  switch (v->type) {
739  case VALUE_TYPE_INTEGER:
740  result = v->data.i != 0;
741  break;
742  case VALUE_TYPE_STRING:
743  result = v->data.s[0] != '\0';
744  break;
745  default:
746  break;
747  }
748 
749  state.str = _free(state.str);
750  valueFree(v);
751  return result;
752 }
753 
754 char * parseExpressionString(Spec spec, const char *expr)
755 {
756  struct _parseState state;
757  char *result = NULL;
758  Value v;
759 
760  DEBUG(printf("parseExprString(?, '%s')\n", expr));
761 
762  /* Initialize the expression parser state. */
763  state.p = state.str = xstrdup(expr);
764  state.spec = spec;
765  state.nextToken = 0;
766  state.tokenValue = NULL;
767  (void) rdToken(&state);
768 
769  /* Parse the expression. */
770  v = doLogical(&state);
771  if (!v) {
772  state.str = _free(state.str);
773  return NULL;
774  }
775 
776  /* If the next token is not TOK_EOF, we have a syntax error. */
777  if (state.nextToken != TOK_EOF) {
778  rpmlog(RPMLOG_ERR, _("syntax error in expression\n"));
779  state.str = _free(state.str);
780  return NULL;
781  }
782 
783  DEBUG(valueDump("parseExprString:", v, stdout));
784 
785  switch (v->type) {
786  case VALUE_TYPE_INTEGER: {
787  char buf[128];
788  sprintf(buf, "%d", v->data.i);
789  result = xstrdup(buf);
790  } break;
791  case VALUE_TYPE_STRING:
792  result = xstrdup(v->data.s);
793  break;
794  default:
795  break;
796  }
797 
798  state.str = _free(state.str);
799  valueFree(v);
800  return result;
801 }
#define TOK_IDENTIFIER
Definition: expression.c:125
#define TOK_LT
Definition: expression.c:134
Value tokenValue
Definition: expression.c:113
union _value::@1 data
#define TOK_ADD
Definition: expression.c:126
#define TOK_NOT
Definition: expression.c:138
static Value doMultiplyDivide(ParseState state)
Definition: expression.c:423
#define TOK_LOGICAL_AND
Definition: expression.c:139
static int xisalnum(int c)
Definition: rpmiotypes.h:549
char * xstrdup(const char *str)
Definition: rpmmalloc.c:321
static Value doRelational(ParseState state)
Definition: expression.c:551
static Value valueMakeString(const char *s)
Definition: expression.c:59
char * p
Definition: expression.c:110
#define TOK_NEQ
Definition: expression.c:133
static int xisalpha(int c)
Definition: rpmiotypes.h:543
static void rpmlog(int code, const char *fmt,...)
Definition: rpmlog.h:299
#define TOK_EQ
Definition: expression.c:132
#define DEBUG(x)
Definition: expression.c:30
#define TOK_OPEN_P
Definition: expression.c:130
#define TOK_GE
Definition: expression.c:137
#define TOK_MULTIPLY
Definition: expression.c:128
Yet Another syslog(3) API clone.
#define EXPRBUFSIZ
Definition: expression.c:143
#define TOK_LE
Definition: expression.c:135
const char const bson_bool_t v
Definition: bson.h:919
static int rdToken(ParseState state)
Definition: expression.c:190
#define TOK_LOGICAL_OR
Definition: expression.c:140
static Value doLogical(ParseState state)
Definition: expression.c:651
#define TOK_INTEGER
Definition: expression.c:123
static int xisspace(int c)
Definition: rpmiotypes.h:555
static Value valueMakeInteger(int i)
Definition: expression.c:46
#define TOK_MINUS
Definition: expression.c:127
struct _value * Value
Encapsulation of a "value".
The structure used to store values parsed from a spec file.
Definition: rpmspec.h:113
const char const bson const bson * op
Definition: mongo.h:505
char * rpmExpand(const char *arg,...)
Return (malloc'ed) concatenated macro expansion(s).
Definition: macro.c:3250
int parseExpressionBoolean(Spec spec, const char *expr)
Evaluate boolean expression.
Definition: expression.c:707
static Value doAddSubtract(ParseState state)
Definition: expression.c:484
Encapsulation of a "value".
Definition: expression.c:36
char * parseExpressionString(Spec spec, const char *expr)
Evaluate string expression.
Definition: expression.c:754
#define TOK_GT
Definition: expression.c:136
enum _value::@0 type
const char const int i
Definition: bson.h:778
static int xisdigit(int c)
Definition: rpmiotypes.h:546
This is the only module users of librpmbuild should need to include.
#define valueSameType(v1, v2)
Definition: expression.c:100
char * stpcpy(char *dest, const char *src)
static void * _free(const void *p)
Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
Definition: rpmiotypes.h:756
int i
Definition: expression.c:40
#define valueIsInteger(v)
Definition: expression.c:98
#define TOK_STRING
Definition: expression.c:124
Parser state.
Definition: expression.c:106
char * str
Definition: expression.c:108
#define TOK_DIVIDE
Definition: expression.c:129
static const char * name
#define _(Text)
Definition: system.h:29
#define xmalloc
Definition: system.h:32
#define TOK_CLOSE_P
Definition: expression.c:131
static Value doPrimary(ParseState state)
Definition: expression.c:337
const char * s
Definition: expression.c:39
struct _parseState * ParseState
Parser state.
#define TOK_EOF
Definition: expression.c:122
static void valueFree(Value v)
Definition: expression.c:72