News Uni Dev Res Blag

SEPPLES

2010/08/21 - 19:10

/* calc.cpp - simple weighted calculator
 * Copyright (C) 2010  ed <irc.rizon.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License, version 2, as
 * published by the Free Software Foundation.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, please refer to the following URL:
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 */


/* CHANGELOG:
 * 2010.08.21 11:40 - 2010.08.21 12:18 - parser completed
 * 2010.08.21 12:18 - 2010.08.21 13:37 - calculation completed
 * 2010.08.22 15:56 - 2010.08.22 16:02 - handle division by zero
 * 2010.08.23 06:53 - 2010.08.23 06:55 - minor cleanup
 * 2010.08.26 09:54 - 2010.08.26 09:57 - expression from arguments
 */


/* USER'S MANUAL
 *
 * Compile and run program
 * Type in numbers and operators
 *  - spaces are optional
 * Hit enter
 * Profit
 */


/* ed@neetbook:~$ nano calc.cpp; g++ -Wall -o calc calc.cpp && ./calc
 * expr> 17.68 + 32.14 * 8.1284 / -5.17 * 4.825 - 192.326
 *
 * Parser output:
 *   17.68 + 32.14 * 8.13 / -5.17 * 4.82 - 192.33
 *
 * Calculation output:
 *           8.13  /        -5.17 =        -1.57
 *          32.14  *        -1.57 =       -50.53
 *         -50.53  *         4.82 =      -243.81
 *        -243.81  -       192.33 =      -436.14
 *          17.68  +      -436.14 =      -418.46
 *
 * Final answer: -418.459481
 */


#include <cstdlib>
#include <stdio.h>
#include <math.h>

const int OPC = 6;
const int MAX = 9000;
const char opc[OPC+1] = {
    ' ', '^', '%',
    '/', '*',
    '-', '+' };

// The basic idea:
// 1+2-3*4/5    - expression
// 0 + - * / ND - opc
// 0 1 2 3 4 -1 - o
// 1 2 3 4 5 ND - v
// ND = not defined

// In-depth on opc:
// opc[1..6] operators
// opc[   0] no-op (calculated)
// opc[  -1] EOF

void dump(int max, int *o, double *v) {
    for (int i=0; i<max; i++) {
        if (o[i]<0) fprintf(stderr, "\nSEMANTICAL ERROR\n");
        printf("%c %1.2f ", opc[o[i]], v[i]);
    }
    putc('\n',stdout);
}
bool compute(int max, int *o, double *v, int op, bool verbose) {

    int i=0, j=0;

    // Scan for the operator we're looking for
    for (i=0; i<max; i++) {

        // Is this it?
        if (o[i]==op) {

            // Yup. Look for closest uncomputed number...
            for (j=i-1; j>=0; j--) {

                // Does this number have an operator?
                if (o[j]>0) break;
                // ...if so, we have the correct value of j.
            }

            // Default back to root if no other options
            if (j<0) j=0;

            // NOTE: at this point, opc[o[i]]==opc[op]
            if (verbose) printf(
                "  %12.2f  %c %12.2f = ",
                v[j], opc[op], v[i]);

            // Check for division by zero
            if (v[i] == 0 && (
                opc[op]=='/' ||
                opc[op]=='%'))
                return false;

            // Do the actual calculation
                 if (opc[op]=='+') v[j] += v[i];
            else if (opc[op]=='-') v[j] -= v[i];
            else if (opc[op]=='*') v[j] *= v[i];
            else if (opc[op]=='/') v[j] /= v[i];
            else if (opc[op]=='^') pow(v[j], v[i]);
            else if (opc[op]=='%') v[j] = (int)v[j] % (int)v[i];
            o[i] = 0; //nothing to see here

            if (verbose) printf("%12.2f\n", v[j]);
        }
    }
    return true;
}
int main(int argc, char **argv) {

    char *arg = (char *)"\0";
    if (argc>1) arg = argv[1];

    // Verbose? Default yes; "n" disables
    bool verbose = !(arg[0]=='n');
    if (!verbose) arg++;

    // Expression from argument?
    bool farg = arg[0]!='\0';

    // Expression management
    int max;        //itemcount
    int o[MAX];     //operator
    double v[MAX];  //value
    max = -1;

    // Vars for STDIN parsing
    int i,j;        //indice/offset
    char c;         //stdin buffer
    int frac;       //fraction
    bool neg;       //v*=-1
    i=j=c=frac=0;   //clear
    neg=false;

    if(verbose) printf("expr> ");
    while (i<MAX && max<0) {

        if (farg) {c=arg[0];arg++;}
        else scanf("%c",&c);

        // Parse digits
        if (c >= '0' &&
            c <= '9') {
            c -= '0';

            // Integer
            if (frac<1) {
                v[i] *= 10;
                v[i] += c;

            // Floating point
            } else {
                frac *= 10;
                v[i] += c/1.0/frac;
            }

        // Set floating point
        } else if (c=='.' || c==',') {
            frac = 1;

        // End of input
        } else if (c=='\n' || c=='\0') {
            i++;        //1-based
            max  = i;   //setmax
            o[i] =-1;   //eof

        // Operator?
        } else if (c!=' '){

            // Check if it's a value negation
            if (c=='-' && v[i]==0) {
                neg = true;

            } else {
                if(neg) v[i]*=-1;
                neg=false;
                frac=0;
                i++;

                // Scan for operator
                for (j=1; j<=OPC; j++) {
                    if (c==opc[j]) o[i]=j;
                }

                // Invalid input
                if (o[i]<1) {
                    fprintf(stderr, "\nPARSER ERROR\n");
                    return 1;
                }
            }
        }
    }

    if (verbose) {
        printf("\nParser output:\n");
        dump(max,o,v);
        printf("\nCalculation output:\n");
    }

    // Do calculations in decreasing weight
    for (i=1; i<=OPC; i++) {
        if (!compute(max,o,v,i,verbose)) {
            fflush(stdout);
            fprintf(stderr, "\nILLEGAL ARITHMETIC OPERATION\n");
            return 1;
        }
    }

    if (!verbose) printf("%f\n", v[0]);
    else printf("\nFinal answer: %f\n", v[0]);
    return 0;
}

Copyright © 2010 Ed Edland