github.com/afumu/libc@v0.0.6/musl/src/locale/pleval.c (about)

     1  #include <stdlib.h>
     2  #include <ctype.h>
     3  #include "pleval.h"
     4  
     5  /*
     6  grammar:
     7  
     8  Start = Expr ';'
     9  Expr  = Or | Or '?' Expr ':' Expr
    10  Or    = And | Or '||' And
    11  And   = Eq | And '&&' Eq
    12  Eq    = Rel | Eq '==' Rel | Eq '!=' Rel
    13  Rel   = Add | Rel '<=' Add | Rel '>=' Add | Rel '<' Add | Rel '>' Add
    14  Add   = Mul | Add '+' Mul | Add '-' Mul
    15  Mul   = Prim | Mul '*' Prim | Mul '/' Prim | Mul '%' Prim
    16  Prim  = '(' Expr ')' | '!' Prim | decimal | 'n'
    17  
    18  internals:
    19  
    20  recursive descent expression evaluator with stack depth limit.
    21  for binary operators an operator-precedence parser is used.
    22  eval* functions store the result of the parsed subexpression
    23  and return a pointer to the next non-space character.
    24  */
    25  
    26  struct st {
    27  	unsigned long r;
    28  	unsigned long n;
    29  	int op;
    30  };
    31  
    32  static const char *skipspace(const char *s)
    33  {
    34  	while (isspace(*s)) s++;
    35  	return s;
    36  }
    37  
    38  static const char *evalexpr(struct st *st, const char *s, int d);
    39  
    40  static const char *evalprim(struct st *st, const char *s, int d)
    41  {
    42  	char *e;
    43  	if (--d < 0) return "";
    44  	s = skipspace(s);
    45  	if (isdigit(*s)) {
    46  		st->r = strtoul(s, &e, 10);
    47  		if (e == s || st->r == -1) return "";
    48  		return skipspace(e);
    49  	}
    50  	if (*s == 'n') {
    51  		st->r = st->n;
    52  		return skipspace(s+1);
    53  	}
    54  	if (*s == '(') {
    55  		s = evalexpr(st, s+1, d);
    56  		if (*s != ')') return "";
    57  		return skipspace(s+1);
    58  	}
    59  	if (*s == '!') {
    60  		s = evalprim(st, s+1, d);
    61  		st->r = !st->r;
    62  		return s;
    63  	}
    64  	return "";
    65  }
    66  
    67  static int binop(struct st *st, int op, unsigned long left)
    68  {
    69  	unsigned long a = left, b = st->r;
    70  	switch (op) {
    71  	case 0: st->r = a||b; return 0;
    72  	case 1: st->r = a&&b; return 0;
    73  	case 2: st->r = a==b; return 0;
    74  	case 3: st->r = a!=b; return 0;
    75  	case 4: st->r = a>=b; return 0;
    76  	case 5: st->r = a<=b; return 0;
    77  	case 6: st->r = a>b; return 0;
    78  	case 7: st->r = a<b; return 0;
    79  	case 8: st->r = a+b; return 0;
    80  	case 9: st->r = a-b; return 0;
    81  	case 10: st->r = a*b; return 0;
    82  	case 11: if (b) {st->r = a%b; return 0;} return 1;
    83  	case 12: if (b) {st->r = a/b; return 0;} return 1;
    84  	}
    85  	return 1;
    86  }
    87  
    88  static const char *parseop(struct st *st, const char *s)
    89  {
    90  	static const char opch[11] = "|&=!><+-*%/";
    91  	static const char opch2[6] = "|&====";
    92  	int i;
    93  	for (i=0; i<11; i++)
    94  		if (*s == opch[i]) {
    95  			/* note: >,< are accepted with or without = */
    96  			if (i<6 && s[1] == opch2[i]) {
    97  				st->op = i;
    98  				return s+2;
    99  			}
   100  			if (i>=4) {
   101  				st->op = i+2;
   102  				return s+1;
   103  			}
   104  			break;
   105  		}
   106  	st->op = 13;
   107  	return s;
   108  }
   109  
   110  static const char *evalbinop(struct st *st, const char *s, int minprec, int d)
   111  {
   112  	static const char prec[14] = {1,2,3,3,4,4,4,4,5,5,6,6,6,0};
   113  	unsigned long left;
   114  	int op;
   115  	d--;
   116  	s = evalprim(st, s, d);
   117  	s = parseop(st, s);
   118  	for (;;) {
   119  		/*
   120  		st->r (left hand side value) and st->op are now set,
   121  		get the right hand side or back out if op has low prec,
   122  		if op was missing then prec[op]==0
   123  		*/
   124  		op = st->op;
   125  		if (prec[op] <= minprec)
   126  			return s;
   127  		left = st->r;
   128  		s = evalbinop(st, s, prec[op], d);
   129  		if (binop(st, op, left))
   130  			return "";
   131  	}
   132  }
   133  
   134  static const char *evalexpr(struct st *st, const char *s, int d)
   135  {
   136  	unsigned long a, b;
   137  	if (--d < 0)
   138  		return "";
   139  	s = evalbinop(st, s, 0, d);
   140  	if (*s != '?')
   141  		return s;
   142  	a = st->r;
   143  	s = evalexpr(st, s+1, d);
   144  	if (*s != ':')
   145  		return "";
   146  	b = st->r;
   147  	s = evalexpr(st, s+1, d);
   148  	st->r = a ? b : st->r;
   149  	return s;
   150  }
   151  
   152  unsigned long __pleval(const char *s, unsigned long n)
   153  {
   154  	struct st st;
   155  	st.n = n;
   156  	s = evalexpr(&st, s, 100);
   157  	return *s == ';' ? st.r : -1;
   158  }