From 567786d26b5800263285510e204c08b7be1fd9ce Mon Sep 17 00:00:00 2001 From: Steve Dougherty Date: Fri, 16 Dec 2011 00:00:42 -0500 Subject: [PATCH] Added calc, an integer-based reverse polish notation calculator. --- utils/Makefile | 1 + utils/calc.cpp | 398 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 399 insertions(+) create mode 100644 utils/calc.cpp diff --git a/utils/Makefile b/utils/Makefile index 68f2aa70..45500401 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -5,6 +5,7 @@ include ../crosscompilemakefile.mak INITRDDIR:=../initrd LOCALBINARIES:=\ init \ +calc \ cat \ cp \ echo \ diff --git a/utils/calc.cpp b/utils/calc.cpp new file mode 100644 index 00000000..78ac3a77 --- /dev/null +++ b/utils/calc.cpp @@ -0,0 +1,398 @@ +/****************************************************************************** + + COPYRIGHT(C) STEVEN DOUGHERTY 2011. + COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along + with this program. If not, see . + + calc.cpp + A simple reverse polish calculator. + +******************************************************************************/ + +#include +#include +#include +#include + +//Stack, pending standard library implementation. All in one file due to linking +// annoyances with the makefile. +template +class Stack { + public: + + //Is its own iterator. + /* MODIFIES: this. + * EFFECTS: Self-iterator: sets to first element and returns it. + */ + const T *begin() { + currentIter = head; + return next(); + } + + /* MODIFIES: this. + * EFFECTS: Self-iterator: returns element from the list and advances. + * If all elements have been iterated, returns NULL. Does not + * have invalidation - be careful! + */ + const T *next() { + if (!currentIter) return NULL; + + const T *element = currentIter->o; + currentIter = currentIter->next; + + return element; + } + + /* EFFECTS: Returns true if stack is empty, false otherwise. + */ + bool isEmpty() const { + return !head; + } + + /* MODIFIES: this. + * EFFECTS: Pushes o onto the top of the stack. + */ + void push(T *o) { + head = new node(head, o); + } + + /* MODIFIES this + * EFFECTS Pops and returns the top element. Returns NULL if empty. + */ + T *pop() { + if (isEmpty()) return NULL; + + node *oldHead = head; + T *element = oldHead->o; + head = oldHead->next; + delete oldHead; + + return element; + } + + Stack() : head(NULL) , currentIter(NULL) {} + + Stack(const Stack &s) : head(NULL) , currentIter(NULL) { + s.begin(); + copyAll(s.head); + } + + Stack &operator=(const Stack &s) { + if (this == &s) return; + removeAll(); + copyAll(s.head); + } + + ~Stack() { + removeAll(); + } + + private: + + struct node { + node(node *next, T *o) : next(next), o(o) {} + node *next; + T *o; + }; + + node *head; + node *currentIter; + + /* MODIFIES: this. + * EFFECT: called by copy constructor and operator= to copy elements + * following from the given node into the local instance. + */ + void copyAll(const node *n) { + if (n) { + copyAll(n->next); + push(new T(*(n->o))); + } + } + + void removeAll() { + while (!isEmpty()) delete pop(); + } +}; + +/* MODIFIES: stack. + * EFFECTS: Pushes value onto the stack. + */ +void push(long *value, Stack &stack) { + stack.push(value); +} + +/* MODIFIES: stack. + * EFFECTS: Pushes value onto the stack. + */ +void push(long value, Stack &stack) { + stack.push(new long(value)); +} + +/* MODIFIES: operand, stack, standard output. + * EFFECTS: Attempts to set operand to the top value on the stack. If there + * are no operands on the stack, prints an error message and aborts + * the process with exit code 1. + */ +void pop(long *&operand, Stack &stack) { + if (stack.isEmpty()) { + printf("%s: not enough operands\n", program_invocation_name); + exit(1); + } + + operand = stack.pop(); +} + +/* MODIFIES: operand1, operand2, stack, standard output. + * EFFECTS: Attempts to remove two operands from the top of the stack, setting + * operand1 to the first one removed and operand2 to the second. If + * there are fewer than two enough operands on the stack, prints + * an error message and aborts the process with exit code 1. + */ +void popTwo(long *&operand1, long *&operand2, Stack &stack) { + pop(operand1, stack); + pop(operand2, stack); +} + +/* MODIFIES: stack, standard output. + * EFFECTS: Attempts to replace the top two operands on the stack with the + * result returned by func(first, second). + */ +//Lambda functions would be wonderful for calling this function. +//http://www2.research.att.com/~bs/C++0xFAQ.html#lambda +void apply(long(*func)(long, long), Stack &stack) { + long *operand1, *operand2; + popTwo(operand1, operand2, stack); + push(func(*operand1, *operand2), stack); + delete operand1; + delete operand2; +} + +/* EFFECTS: Returns first + second. + */ +long add(long first, long second) { return first + second; } + +/* EFFECTS: Returns subtracting "the first number from the second" (pg. 5) + */ +long subtract(long first, long second) { return second - first; } + +/* EFFECTS: Returns first * second. + */ +long multiply(long first, long second) { return first * second; } + +/* MODIFIES: stack, standard output. + * EFFECTS: Attempts to replace the top two operands on the stack with the + * result of dividing the second one by the first. + * If the first number is zero, prints an division by zero error and + * exits the process with exit code 1. + */ +void divide(Stack &stack) { + /* Can't use apply for this because it requires a check for the + * denominator being zero. It could have an error-checking function + * pointer, but that would muddy things as no other operators have such + * conditions. + */ + long *first, *second; + + popTwo(first, second, stack); + if (*first == 0) { + printf("%s: division by zero\n", program_invocation_name); + exit(1); + } + + push(*second / *first, stack); + delete first; + delete second; +} + +/* MODIFIES: stack, standard output. + * EFFECTS: Attempts to negate the top element on the stack. + */ +void negate(Stack &stack) { + long *operand; + + pop(operand, stack); + push(-(*operand), stack); + delete operand; +} + +/* MODIFIES: stack, standard output. + * EFFECTS: Attempts to push a copy of the top element on top of the already + * existing one. + */ +void duplicate(Stack &stack) { + long *operand; + + pop(operand, stack); + //Dereferencing creates new instance. + push(*operand, stack); + push(operand, stack); +} + +/* MODIFIES: stack, standard output. + * EFFECTS: Attempts to reverse the first two elements on the stack. + */ +void reverse(Stack &stack) { + long *first, *second; + + popTwo(first, second, stack); + //Push the former first before the former second. + push(first, stack); + push(second, stack); +} + +/* MODIFIES: standard output. + * EFFECTS: Prints the first element on the stack to standard output, followed + * by a newline. + */ +void print(Stack &stack) { + long *operand; + + pop(operand, stack); + printf("%li\n", *operand); + push(operand, stack); +} + +/* MODIFIES: stack. + * EFFECTS: Empties the stack. + */ +void clear(Stack &stack) { + while (!stack.isEmpty()) delete stack.pop(); +} + +/* MODIFIES: standard output. + * EFFECTS: Prints all the elements on the stack, from top to bottom, each + * separated by a single space. No trailing space. Ends with newline. + */ +void printAll(Stack &stack) { + if (!stack.isEmpty()) { + const long *element = stack.begin(); + do { + printf("%li", *element); + } while ((element = stack.next()) && printf(" ")); + } + printf("\n"); +} + +/* MODIFIES: standard output. + * EFFECTS: Prints usage information and returns 0. + */ +int printUsage(const char* argv0) { + printf("Usage: %s [--help] [--usage] COMMAND ...\n", argv0); + printf("%s is a Reverse Polish Notation integer calculator.\n", argv0); + printf("\n"); + printf(" --help, --usage Display this help and exit\n"); + printf("\n"); + printf("%s supports the following commands:\n", argv0); + printf("Any integers supplied are pushed onto the stack and can be given in decimal\n"); + printf("(default), octal with a leading 0, or hexadecimal with a leading 0x. Numbers\n"); + printf("can be prefixed with + or - to specify positive or negative, respectively.\n"); + printf("The operators +, -, *, and / pop the top two elements and push the result of\n"); + printf(" to the stack. The top element is printed on exit\n"); + printf("unless a print command is given or an error occurs. Also,\n"); + printf(" a: prints all elements from first to last.\n"); + printf(" c: clears the stack.\n"); + printf(" d: duplicates the top element.\n"); + printf(" n: negates the top element.\n"); + printf(" p: prints the top element.\n"); + printf(" r: reverses the order of the top two elements.\n"); + printf("\n"); + printf("Examples:\n"); + printf(" %s 2 2 +\n", argv0); + printf(" %s 0xCAFE42 0777 *\n", argv0); + return 0; +} + +int main(int argc, char *argv[]) { + //If no arguments, --usage, or --help. + if (argc == 1 || + (argc > 1 && (!strcmp(argv[1], "--usage") || + !strcmp(argv[1], "--help")))) + return printUsage(argv[0]); + + Stack stack; + bool printedAnything = false; + + //Skip program name in arguments list. + for (int i = 1; i < argc; i++) { + char *endPtr; + //Allow input in decimal, octal, or hex. + long numberInput = strtol(argv[i], &endPtr, 0); + + //Make sure the string is a number and not empty. + if (*argv[i] && !(*endPtr)) { + push(numberInput, stack); + continue; + } + + /* Input was not a valid number, attempt to parse as operator. + * Anything other than one character is not a valid operator. + */ + if (strlen(argv[i]) == 1) { + switch (argv[i][0]) { + case '+': + //Add + apply(add, stack); + continue; + case '-': + //Subtract + apply(subtract, stack); + continue; + case '*': + //Multiply + apply(multiply, stack); + continue; + case '/': + //Divide + divide(stack); + continue; + case 'n': + //Negate + negate(stack); + continue; + case 'd': + //Duplicate + duplicate(stack); + continue; + case 'r': + //Reverse + reverse(stack); + continue; + case 'p': + //Print + print(stack); + printedAnything = true; + continue; + case 'c': + //Clear + clear(stack); + continue; + case 'a': + //Print-all + printAll(stack); + printedAnything = true; + continue; + } + } + + printf("%s: unsupported command: %s\n", program_invocation_name, argv[i]); + exit(1); + } + + if ( !printedAnything && !stack.isEmpty() ) { + print(stack); + } + + return 0; +}