// ----------------------------------------------------------------------------
// Calculadora que utiliza Notacao Reversa Polonesa (RPN)
// Criado por Wenderson Teixeira
//
// comandos suportados atualmente:
// Drop ou D - retira valor da pilha
// Dup       - duplica ultimo valor da pilha
// Swap ou S - troca os dois ultimos valores da pilha
// Exit ou X - sai do programa
// Abs       - valor absoluto do ultimo numero da pilha
// Sin       - seno
// Cos       - cosseno
// Tan       - tangente
// +         - soma os dois ultimos valores
// -         - subtrai
// *         - multiplica
// /         - divide
// --        - inverte o sinal
//
// Obs.: uma linha de comando pode conter um ou mais comandos, exemplo:
// > 10
// > 10 2 +
// > 4 3.1415269 * 180 /
// 
// Modulo: Calc.cpp      Ultima atualizacao: 19/06/99
// ----------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <alloc.h>
#include <string.h>
#include <conio.h>
#include <math.h>
#include <ctype.h>

// Macro de comparacao de strings
// retorna true se sao iguais e false caso contrario
#define IsStrEqual(a, b) (strcmpi((a), (b)) == 0)

// O tipo bool e as constantes true e false sao novos no C++,
// entao se estiver compilando em C, ou versoes antigas do C++,
// bool deve ser definido
#if (__BORLANDC__ <= 0x460) || !defined(__cplusplus)
typedef enum { false, true } bool;
#endif

// Estrutura da pilha
typedef struct tag_Stack
{
  double value;
  tag_Stack *next;
} TStack;

// Definicao da pilha
TStack *stack = 0;

// Funcao que testa se uma string possui apenas digitos validos p/ 
// um numero, e que esteja no formato [s][[#][.[#]]][#[e[s]#]]
// por ex.:
//  1, +1, -1, 1.1, 1e10, 1e-10, 1.e10, 1.1e10 - Validos
// .1., 1e0.1, -+1, +, -, ., 1+1 - Invalidos
bool IsNumber(char *str)
{
  int c = 0;
  bool valid = false;

  // Verifica sinal
  if(str[c] == '-' || str[c] == '+') c++;
  
  // Verifica ponto decimal
  if(isdigit(str[c]) || str[c] == '.')
  {
    valid = true;

    // Se comecar com ponto decimal, deve haver pelo 
    // menos digito, e nao pode terminar com ponto
    if(str[c] == '.')
    {
      c++;

      // Se nao possui digitos, esta errado
      if(isdigit(str[c]))
      {
        while(isdigit(str[c])) c++;
        
        // Nao pode haver outro ponto
        valid = str[c] != '.';
      }
      else
        valid = false;
    }

    // Se parte inteira esta OK
    if(valid)
    {
      // Verifica digitos antes do ponto decimal
      while(isdigit(str[c])) c++;
      
      // Verifica ponto decimal
      if(str[c] == '.') c++;
      
      // Verificia digitos apos o ponto decimal
      while(isdigit(str[c])) c++;
      
      // Verifica 'e' p/ potencia de 10
      if(tolower(str[c]) == 'e')
      {
        c++;
        
        // Deve existir digitos
        if(str[c] == '\0')
          valid = false;
        else
        {
          // Verifica sinal da pontencia de 10
          if((str[c] == '-') || (str[c] == '+')) c++;
          
          // Deve haver digito apos o sinal
          if(str[c] == '\0')
            valid = false;
          else
          {
            while(isdigit(str[c])) c++;
            valid = str[c] == '\0';
          }
        }
      }
      else
        valid = str[c] == '\0';
    }
  }
  
  return valid;
}


// Salva na pilha
void Push(double value)
{
  TStack *s = (TStack *)malloc(sizeof(TStack));
  
  s->value = value;
  s->next = stack;
  stack = s;
}

// Retira da pilha 
double Pop()
{
  double value = 0;
  
  if(stack)
  {
    TStack *s = stack;
    value = stack->value;
    stack = stack->next;
    free(s);
  }
  
  return value;
}

// Devolve o tamanho da pilha
int Length()
{
  TStack *s = stack;
  int c = 0;
  
  while(s)
  {
    c++;
    s = s->next;
  }
    
  return c;
}

// Destroi a pilha
void Destroy()
{
  while(stack)
    Pop();
}

// Mostra os valores na pilha
void Show(TStack *s)
{
  if(s)
  {
    Show(s->next);
    printf("%10.3lf\n", s->value);
  }
}

// Executa calculadora
void Run()
{
  bool done = false;
  int length;
  char str[256];
  
  while(!done)
  {
    // Exibe lista de comandos
    printf("Stack calculator - Copyright (c) 1999 by Wenderson Teixiera\n\n");
    printf("Commands\n"
           "Drop (D), Dup, Swap (S), Exit (X)\n"
           "Abs, Sin, Cos, Tan, +, -, *, /, --\n\n");
           
    // Mostra a pilha
    Show(stack);

    // Espera ser digitado algum comando
    printf("\n> ");
    scanf("%s", str);
    clrscr();
    
    // Se for numero, poe na pilha
    if(IsNumber(str))
      Push(atof(str));
    else
    {
      // Tentar processar como um comando
      length = Length();
  
      if(IsStrEqual(str, "exit") || IsStrEqual(str, "x"))
        done = true;
      else if(length > 1 && (IsStrEqual(str, "swap") || IsStrEqual(str, "s")))
      {
        double a = Pop(), b = Pop();
        Push(a);
        Push(b);
      }
      else if(length)
      {
        if(IsStrEqual(str, "drop") || IsStrEqual(str, "d"))
          Pop();
        else if(IsStrEqual(str, "dup"))
        {
          double a = Pop();
          Push(a);
          Push(a);
        }
        else if(IsStrEqual(str, "abs"))
          Push(fabs(Pop()));
        else if(IsStrEqual(str, "sin"))
          Push(sin(Pop()));
        else if(IsStrEqual(str, "cos"))
          Push(cos(Pop()));
        else if(IsStrEqual(str, "tan"))
          Push(tan(Pop()));
        else if(IsStrEqual(str, "--"))
          Push(-Pop());
        else if(strlen(str) == 1)
        {
          switch(str[0])
          {
            case '+':
              if(length > 1)
                Push(Pop() + Pop());
            break;
            
            case '-':
              Push(-Pop() + Pop());
            break;
            
            case '*':
              if(length > 1)
                Push(Pop() * Pop());
            break;
            
            case '/':
              if(length > 1)
                Push(1/Pop() * Pop());
            break;
          }
        }
      }
    }
  }
  
  // Destroi a pilha
  Destroy();
}

void main()
{
  Run();
}
1