Goto sanos source index

#include <os.h>
#include <math.h>
#include <float.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

double eval_expr(char **expr);

void skip_space(char **expr) {
  while (**expr == ' ') (*expr)++;
}

void next(char **expr) {
  if (**expr) (*expr)++;
  while (**expr == ' ') (*expr)++;
}

double error(char **expr, char *msg) {
  printf("%s: %s\n", msg, *expr);
  while (**expr) (*expr)++;
  return 0.0;
}

double eval_factor(char **expr) {
  double result;

  if (**expr == '(') {
    next(expr);
    result = eval_expr(expr);
    if (**expr != ')') return error(expr, "')' missing");
    next(expr);
  } else if (**expr == '+') {
    next(expr);
    result = eval_factor(expr);
  } else if (**expr == '-') {
    next(expr);
    result = -eval_factor(expr);
  } else if (**expr == '.' || isdigit(**expr)) {
    char *newpos;
    result = strtod(*expr, &newpos);
    if (!newpos || *expr == newpos) return error(expr, "error in number");
    *expr = newpos;
    skip_space(expr);
  } else if (isalpha(**expr)) {
    double arg1 = 0.0;
    double arg2 = 0.0;
    int n;
    char *func = *expr;
    while (isalnum(**expr)) (*expr)++;
    n = *expr - func;

    if (strncmp(func, "inf", n) == 0) return INFINITY;
    if (strncmp(func, "nan", n) == 0) return NAN;

    skip_space(expr);
    if (**expr != '(') return error(expr, "'(' missing");
    next(expr);
    arg1 = eval_expr(expr);
    if (**expr == ',') {
      next(expr);
      arg2 = eval_expr(expr);
    }
    if (**expr != ')') return error(expr, "')' missing");
    next(expr);

    if (strncmp(func, "asin", n) == 0) {
      result = asin(arg1);
    } else if (strncmp(func, "acos", n) == 0) {
      result = acos(arg1);
    } else if (strncmp(func, "atan", n) == 0) {
      result = atan(arg1);
    } else if (strncmp(func, "atan2", n) == 0) {
      result = atan2(arg1, arg2);
    } else if (strncmp(func, "ceil", n) == 0) {
      result = ceil(arg1);
    } else if (strncmp(func, "cos", n) == 0) {
      result = cos(arg1);
    } else if (strncmp(func, "cosh", n) == 0) {
      result = cosh(arg1);
    } else if (strncmp(func, "exp", n) == 0) {
      result = exp(arg1);
    } else if (strncmp(func, "fabs", n) == 0) {
      result = fabs(arg1);
    } else if (strncmp(func, "floor", n) == 0) {
      result = floor(arg1);
    } else if (strncmp(func, "fmod", n) == 0) {
      result = fmod(arg1, arg2);
    } else if (strncmp(func, "mant", n) == 0) {
      int expo;
      result = frexp(arg1, &expo);
    } else if (strncmp(func, "expo", n) == 0) {
      int expo;
      frexp(arg1, &expo);
      result = expo;
    } else if (strncmp(func, "ldexp", n) == 0) {
      result = fmod(arg1, (int) arg2);
    } else if (strncmp(func, "log", n) == 0) {
      result = log(arg1);
    } else if (strncmp(func, "log10", n) == 0) {
      result = log10(arg1);
    } else if (strncmp(func, "frac", n) == 0) {
      double intg;
      result = modf(arg1, &intg);
    } else if (strncmp(func, "int", n) == 0) {
      modf(arg1, &result);
    } else if (strncmp(func, "pow", n) == 0) {
      result = pow(arg1, arg2);
    } else if (strncmp(func, "sin", n) == 0) {
      result = sin(arg1);
    } else if (strncmp(func, "sinh", n) == 0) {
      result = sinh(arg1);
    } else if (strncmp(func, "sqrt", n) == 0) {
      result = sqrt(arg1);
    } else if (strncmp(func, "tan", n) == 0) {
      result = tan(arg1);
    } else if (strncmp(func, "tanh", n) == 0) {
      result = tanh(arg1);
    } else {
      *expr = func;
      return error(expr, "unknown function");
    }
  } else {
    return error(expr, "syntax error");
  }

  return result;
}

double eval_term(char **expr) {
  double result;

  result = eval_factor(expr);
  while (**expr == '*' || **expr == '/') {
    if (**expr == '*') {
      next(expr);
      result *= eval_factor(expr);
    } else {
      next(expr);
      result /= eval_factor(expr);
    }
  }

  return result;
}

double eval_expr(char **expr) {
  double result;

  result = eval_term(expr);
  while (**expr == '+' || **expr == '-') {
    if (**expr == '+') {
      next(expr);
      result += eval_term(expr);
    } else {
      next(expr);
      result -= eval_term(expr);
    }
  }

  return result;
}

double eval(char **expr) {
  skip_space(expr);
  return eval_expr(expr);
}

int main(int argc, char *argv[]) {
  int i;
  double result;
  char *expr;
  for (i = 1; i < argc; i++) {
    expr = argv[i];
    result = eval(&expr);
    if (*expr) {
      printf("syntax error\n");
    } else {
      printf("%g\n", result);
    }
  }

  return 0;
}