calc.c

Denna kod är public domain. Om ni hittar fel eller vill ändra något i koden blir jag jätteglad om ni skickar dessa ändringar till jesper [at] fantasi [punkt] se.


/**
 * En enkel parser för matematiska uttryck. Algoritmen är Recursive
 * descent som är en av de flitigast använda i kompilatorer med mera.
 * Denna version hanterar + - * / och ( ).
 */

#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<string.h>

/* Vi deklarerar funktionen här eftersom vi måste anropa den
 * innan den definieras.
 */
static int readExpression(char** sr);

static int readNumber(char** sr)
{
   int a = 0;
   char next = *sr[0];

   while (isdigit(next))
   {
      (*sr)++;
      a = a * 10 + (int)(next - '0');
      next = *sr[0];
   }

   return a;
}

static int readFactor(char** sr)
{
   char next = *sr[0];

   /* ( expr ) */
   if (next == '(')
   {
      int a;

      (*sr)++;
      a = readExpression(sr);
      next = *sr[0];
      if (next != ')')
      {
         fprintf(stderr, "Syntax Error\n");
         exit(EXIT_FAILURE);
      }
      (*sr)++;

      return a;
   }

   /* - factor */
   if (next == '-')
   {
      (*sr)++;
      return -readFactor(sr);
   }

   /* number */
   if (isdigit(next))
   {
      return readNumber(sr);
   }

   fprintf(stderr, "Syntax Error\n");
   exit(EXIT_FAILURE);
}

static int readTerm(char** sr)
{
   int a = readFactor(sr);
   char next;

   next = *sr[0];
   while (next == '*' || next == '/')
   {
      (*sr)++;
      if (next == '*')
         a *= readFactor(sr);
      else
         a /= readFactor(sr);

      next = *sr[0];
   }

   return a;
}

static int readExpression(char** sr)
{
   int a = readTerm(sr);
   char next;

   next = *sr[0];
   while (next == '+' || next == '-')
   {
      (*sr)++;
      if (next == '+')
         a += readTerm(sr);
      else
         a -= readTerm(sr);

      next = *sr[0];
   }

   return a;
}

static void filter(char* something, char* result)
{
   int i, j;

   for (i = 0, j = 0; something[i] != '\0'; i++)
   {
      switch (something[i])
      {
         case ' ':
         case '\t': break;
         default: result[j++] = tolower(something[i]);
      }
   }
}

int main(int argc, char* argv[])
{
   char* buffer;
   char* filtered;
   int a;

   if (argc < 2)
   {
      printf("Usage: calc <expression>\n");
      return EXIT_FAILURE;
   }

   buffer = filtered = malloc(strlen(argv[1]));
   if (filtered == NULL)
   {
      printf("Out of memory\n");
      return EXIT_FAILURE;
   }
   filter(argv[1], filtered);

   a = readExpression(&filtered);

   if (filtered[0] != '\0')
   {
      fprintf(stderr, "Syntax error\n");
      free(buffer);
      return EXIT_FAILURE;
   }
   else
      printf("%d\n", a);

   free(buffer);
   return EXIT_SUCCESS;
}