|
Professional Programmer
Join Date: Nov 2004
Posts: 250
Rep Power: 5 
|
That's a lot of bulk for not much functionality. For example, would you really want to fire this program up whenever you needed a calculator? Probably not, because it only works with two numbers and takes too much of the user's effort to handle even that. Of course, writing a parser that allows one to type expressions isn't a trivial task, which is why the Python program will be simplicity itself (just call eval on the input string).
If I might offer a suggestion in C:
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define UNARY 0 /* Offset into actions for unary words */
#define BINARY 6 /* Offset into actions for binary words */
#define ACLEN 17 /* Total number of actions */
#define UNLEN 6 /* Total number of unary words */
#define BILEN 11 /* Total number of binary words */
static const char *actions[] = {
".", "sqrt", "sq", "not", "abs", "log",
"+", "-", "*", "/", "mod", "pow",
"<", "=", ">", "and", "or",
};
static long int stack[1024];
static int top = 0;
static void eval ( void );
static void gather ( void );
static void process ( const char *action );
static void do_unary ( const char *action );
static void do_binary ( const char *action );
static void die ( const char *msg );
static int is_valid ( const char *action, size_t offset, size_t limit );
int main ( void )
{
while ( 1 )
eval();
}
void eval ( void )
{
gather();
puts ( " ok" );
}
void gather ( void )
{
char buffer[BUFSIZ];
char *action;
char *end;
if ( fgets ( buffer, sizeof buffer, stdin ) == NULL )
die ( "Input error" );
for ( action = strtok ( buffer, " \t\n" );
action != NULL; action = strtok ( NULL, " \t\n" ) )
{
long int number;
/* Process a number or an action */
number = strtol ( action, &end, 0 );
if ( end > action )
stack[top++] = number;
else if ( is_valid ( action, 0, ACLEN ) )
process ( action );
else if ( strcmp ( action, "bye" ) == 0 )
exit ( EXIT_SUCCESS );
else
die ( "Input error" );
}
}
void process ( const char *action )
{
if ( is_valid ( action, UNARY, UNLEN ) )
do_unary ( action );
else if ( is_valid ( action, BINARY, BILEN ) )
do_binary ( action );
}
void do_unary ( const char *action )
{
long int result = 0;
long int a;
if ( top == 0 )
die ( "Stack underflow" );
a = stack[--top];
if ( strcmp ( action, "." ) == 0 ) {
printf ( "%ld ", a );
fflush ( stdout );
return;
}
else if ( strcmp ( action, "not" ) == 0 )
result = !a;
else if ( strcmp ( action, "sqrt" ) == 0 )
result = (long int)sqrt ( a );
else if ( strcmp ( action, "sq" ) == 0 )
result = a * a;
else if ( strcmp ( action, "abs" ) == 0 )
result = abs ( a );
else if ( strcmp ( action, "log" ) == 0 )
result = (long int)log ( a );
else
die ( "Invalid format" );
stack[top++] = result;
}
void do_binary ( const char *action )
{
long int result = 0;
long int a, b;
if ( top < 2 )
die ( "Stack underflow" );
b = stack[--top];
a = stack[--top];
if ( action[0] == '+' )
result = a + b;
else if ( action[0] == '-' )
result = a - b;
else if ( action[0] == '*' )
result = a * b;
else if ( action[0] == '/' ) {
if ( b == 0 )
die ( "Division by zero" );
result = a / b;
}
else if ( strcmp ( action, "mod" ) == 0 ) {
if ( b == 0 )
die ( "Division by zero" );
result = a % b;
}
else if ( strcmp ( action, "pow" ) == 0 )
result = (long int)pow ( a, b );
else if ( action[0] == '<' )
result = a < b;
else if ( action[0] == '=' )
result = a == b;
else if ( action[0] == '>' )
result = a > b;
else if ( strcmp ( action, "and" ) == 0 )
result = a && b;
else if ( strcmp ( action, "or" ) == 0 )
result = a || b;
else
die ( "Invalid format" );
stack[top++] = result;
}
int is_valid ( const char *action, size_t offset, size_t limit )
{
size_t i;
for ( i = offset; i < limit + offset; i++ ) {
if ( strcmp ( action, actions[i] ) == 0 )
return 1;
}
return 0;
}
void die ( const char *msg )
{
fprintf ( stderr, "%s\n", msg );
exit ( EXIT_FAILURE );
} A stack based calculator is considerably shorter and much more powerful than any naive parsing calculator. It also has the benefit of being simpler and easier to get right. The input consists of only two particles: numbers and actions. When a valid number is found, it's pushed into the stack. When a valid action is found, it pops however many numbers it needs off of the stack, and manipulates them, pushing its result back onto the stack for later use.
This gives rise to a powerful, if somewhat cryptic (at first), notation. For example, to calculate :
You would use the following expression in the stack calculator:
Operands come first, then operators. Operands are ordered the same in relation to infix expressions (a op b becomes a b op). The nicest benefit to this scheme is that you save yourself the horror of trying to parse order of evaluation for complex expressions using parentheses. Postfix notation, as it is usually called, uses positioning to remove the need for parentheses:
Becomes:
Notice that the . action prints the current top of the stack, and the program tells you when it's ok to enter another expression. Don't be afraid to save results and chain multiple expressions together. The results will be saved on the stack if they aren't printed:
This solution is easily transferred to C++, Java, Python, and any other language you care for.
|