Advertisement
Guest User

Simple math expression parser

a guest
Dec 13th, 2012
1,901
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 6.13 KB | None | 0 0
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <regex.h>
  5. #include <assert.h>
  6.  
  7. static const char *tokens[] = {
  8.         "[[:blank:][:cntrl:][:space:]]+",
  9.  
  10.         "[\\+\\-]?[0-9]+\\.[0-9]+([eE][\\+\\-]?[0-9]+)?",
  11.         "[\\+\\-]?[0-9]+",
  12.  
  13.         "\\+",
  14.         "\\-",
  15.         "\\*",
  16.         "\\/",
  17.         "\\(",
  18.         "\\)",
  19.  
  20.         NULL
  21. };
  22.  
  23. enum {
  24.         TOK_SPACE,
  25.         TOK_REAL,
  26.         TOK_INT,
  27.         TOK_PLUS,
  28.         TOK_MINUS,
  29.         TOK_MUL,
  30.         TOK_DIV,
  31.         TOK_LPAREN,
  32.         TOK_RPAREN,
  33.         TOK_INVALID
  34. };
  35. typedef unsigned short token_t;
  36.  
  37. const char *tokdesc[] = {
  38.         "TOK_SPACE",
  39.         "TOK_REAL",
  40.         "TOK_INT",
  41.         "TOK_PLUS",
  42.         "TOK_MINUS",
  43.         "TOK_MUL",
  44.         "TOK_DIV",
  45.         "TOK_LPAREN",
  46.         "TOK_RPAREN",
  47.         "TOK_INVALID"
  48. };
  49.  
  50. static int matches_regex(const char *regex, const char *str, regmatch_t *match)
  51. {
  52.         regex_t regex_obj;
  53.         int error;
  54.  
  55.         error = regcomp(&regex_obj, regex, REG_EXTENDED); // TODO: move dis outta loop
  56.         if (error)
  57.                 return 0;
  58.  
  59.         error = regexec(&regex_obj, str, 1, match, 0);
  60.         regfree(&regex_obj);
  61.         return !error;
  62. }
  63.  
  64. static char *__strndup(const char *s, size_t n)
  65. {
  66.         size_t l, i;
  67.         char *r;
  68.  
  69.         l = strlen(s);
  70.         if (l < n)
  71.                 n = l;
  72.  
  73.         r = malloc(n + 1);
  74.         if (r != NULL)
  75.         {
  76.                 i = 0;
  77.                 while(n--)
  78.                 {
  79.                         r[i] = s[i];
  80.                         i++;
  81.                 }
  82.                 r[i] = 0;
  83.         }
  84.  
  85.         return r;
  86. }
  87.  
  88. int __strncnt(const char *haystack, const char *needle, size_t n)
  89. {
  90.         const char *orig;
  91.         const char *next;
  92.         int i;
  93.         size_t len;
  94.  
  95.         orig = haystack;
  96.         i = 0;
  97.         len = strlen(needle);
  98.         while ((next = strstr(haystack, needle)) != NULL && (haystack - orig < n))
  99.         {
  100.                 haystack = next + len;
  101.                 i++;
  102.         }
  103.  
  104.         return i;
  105. }
  106.  
  107. static char *getfile(const char *filename)
  108. {
  109.         ssize_t size;
  110.         FILE *f;
  111.         char *result;
  112.  
  113.         f = fopen(filename, "r");
  114.         if (f == NULL)
  115.         {
  116.                 return NULL;
  117.         }
  118.         fseek(f, 0, SEEK_END);
  119.         size = ftell(f);
  120.         fseek(f, 0, SEEK_SET);
  121.         result = malloc(size + 1);
  122.         if (size != fread(result, sizeof(*result), size, f))
  123.         {
  124.                 free(result);
  125.                 return NULL;
  126.         }
  127.  
  128.         fclose(f);
  129.         result[size] = 0;
  130.         return result;
  131. }
  132.  
  133. /**
  134.  * Helper function for the lexer
  135.  */
  136.  
  137. static char *curr_tok_str = NULL;
  138. static char *stream = NULL;
  139. static char *orig = NULL;
  140. static token_t curr_tok = TOK_INVALID;
  141.  
  142. static token_t get_next_token()
  143. {
  144.         token_t i;
  145.         regmatch_t match;
  146.  
  147.         i = 0;
  148.         while (tokens[i] != NULL)
  149.         {
  150.                 if (matches_regex(tokens[i], stream, &match))
  151.                 {
  152.                         if (match.rm_so == 0)
  153.                         {
  154.                                 free(curr_tok_str);
  155.                                 curr_tok_str = __strndup(stream, match.rm_eo);
  156.                                 stream += match.rm_eo;
  157.                                 return curr_tok = i;
  158.                         }
  159.                 }
  160.                 i++;
  161.         }
  162.        
  163.         free(curr_tok_str);
  164.         curr_tok_str = NULL;
  165.         return curr_tok = TOK_INVALID;
  166. }
  167.  
  168. static token_t lex()
  169. {
  170.         token_t tok;
  171.  
  172.         while ((tok = get_next_token()) == TOK_SPACE)
  173.                 ;
  174.  
  175.         return tok;
  176. }
  177.  
  178. static void init_parser(const char *src)
  179. {
  180.         stream = src;
  181.         orig = stream;
  182. }
  183.  
  184. static void cleanup_parser()
  185. {
  186.         free(orig);
  187. }
  188.  
  189. static int parser_is_at_end()
  190. {
  191.         return !*stream;
  192. }
  193.  
  194. static unsigned int parser_lineno()
  195. {
  196.         return __strncnt(orig, "\n", stream - orig) + 1;
  197. }
  198.  
  199. /********************************/
  200.  
  201. static double expr();
  202.  
  203. static double term()
  204. {
  205.         double d;
  206.  
  207.         if (curr_tok == TOK_REAL) {
  208.                 d = strtod(curr_tok_str, NULL);
  209.                 lex();
  210.                 return d;
  211.         } else if (curr_tok == TOK_INT) {
  212.                 d = strtod(curr_tok_str, NULL);
  213.                 lex();
  214.                 return d;
  215.         } else if (curr_tok == TOK_LPAREN) {
  216.                 lex();
  217.                 d = expr();
  218.                 if (curr_tok != TOK_RPAREN) abort();
  219.                 lex();
  220.                 return d;
  221.         } else {
  222.                 fprintf(stderr, "\nInvalid term: `%s'\n", curr_tok_str);
  223.                 abort();
  224.         }
  225.         return 0.0;
  226. }
  227.  
  228. static double multiplicative_expr()
  229. {
  230.         double a, b;
  231.  
  232.         a = term();
  233.         while (curr_tok == TOK_MUL || curr_tok == TOK_DIV)
  234.         {
  235.                 token_t action = curr_tok;
  236.                 lex();
  237.                 b = term();
  238.                 (action == TOK_MUL) ? (a *= b) : (a /= b);
  239.         }
  240.  
  241.         return a;
  242. }
  243.  
  244. static double additive_expr()
  245. {
  246.         double a, b;
  247.  
  248.         a = multiplicative_expr();
  249.         while (curr_tok == TOK_PLUS || curr_tok == TOK_MINUS)
  250.         {
  251.                 token_t action = curr_tok;
  252.                 lex();
  253.                 b = multiplicative_expr();
  254.                 (action == TOK_PLUS) ? (a += b) : (a -= b);
  255.         }
  256.  
  257.         return a;
  258. }
  259.  
  260. static double expr()
  261. {
  262.         return additive_expr();      
  263. }
  264.  
  265. static double parse()
  266. {
  267.         lex();
  268.         return expr();
  269. }
  270.  
  271. int main(int argc, char **argv)
  272. {
  273.         unsigned lineno;
  274.         token_t tokid;
  275.         char *src;
  276.  
  277.         src = getfile(argv[1]);
  278.         init_parser(src);
  279.         printf("Parsing string...\n");
  280.  
  281.         double val = parse();
  282.        
  283.         lineno = parser_lineno();
  284.  
  285.         if (parser_is_at_end())
  286.                 printf("Parsing succeeded, result: %lf\n", val);
  287.         else
  288.                 fprintf(stderr, "Parsing failed: syntax error on line %u\n", lineno);
  289.  
  290.         cleanup_parser();
  291.  
  292.         return 0;
  293. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement