| | 1 | #include "tads.h" |
| | 2 | #include "t3.h" |
| | 3 | #include "dict.h" |
| | 4 | #include "gramprod.h" |
| | 5 | #include "tok.h" |
| | 6 | #include "bignum.h" |
| | 7 | |
| | 8 | enum token tokOp; |
| | 9 | enum token tokFloat; |
| | 10 | |
| | 11 | dictionary gDict; |
| | 12 | dictionary property funcName; |
| | 13 | |
| | 14 | /* |
| | 15 | * Calc globals |
| | 16 | */ |
| | 17 | calcGlobals: object |
| | 18 | /* |
| | 19 | * input precision - for any number entered with smaller precision, |
| | 20 | * we'll increase the precision to this value |
| | 21 | */ |
| | 22 | inputPrecision = 8 |
| | 23 | |
| | 24 | /* guard digits added to input precision */ |
| | 25 | guardPrecision = 3 |
| | 26 | |
| | 27 | /* number of fractional digits to display for each value */ |
| | 28 | dispFracDigits = 8 |
| | 29 | |
| | 30 | /* maximum number of digits to display for each value */ |
| | 31 | dispMaxDigits = 32 |
| | 32 | |
| | 33 | /* display in scientific notation */ |
| | 34 | dispSci = nil |
| | 35 | ; |
| | 36 | |
| | 37 | |
| | 38 | /* |
| | 39 | * Custom tokenizer for arithmetic expressions |
| | 40 | */ |
| | 41 | CalcTokenizer: Tokenizer |
| | 42 | rules_ = |
| | 43 | [ |
| | 44 | /* skip whitespace */ |
| | 45 | ['[ \t]+', nil, tokCvtSkip, nil], |
| | 46 | |
| | 47 | /* numbers */ |
| | 48 | ['(%.[0-9]+|[0-9]+(%.[0-9]*)?)([eE][+-]?[0-9]+)?', |
| | 49 | tokFloat, nil, nil], |
| | 50 | |
| | 51 | /* operators */ |
| | 52 | ['[(+*-/)!^?]', tokOp, nil, nil], |
| | 53 | |
| | 54 | /* words */ |
| | 55 | ['[a-zA-Z]+', tokWord, &tokCvtLower, nil] |
| | 56 | ] |
| | 57 | ; |
| | 58 | |
| | 59 | grammar term: term->val1_ '+' atomic->val2_: object |
| | 60 | eval() { return val1_.eval() + val2_.eval(); } |
| | 61 | ; |
| | 62 | |
| | 63 | grammar term: atomic->val_: object |
| | 64 | eval() { return val_.eval(); } |
| | 65 | ; |
| | 66 | |
| | 67 | grammar atomic: number->num_ : object |
| | 68 | eval() { return num_.eval(); } |
| | 69 | ; |
| | 70 | |
| | 71 | grammar atomic: '(' term->val_ ')' : object |
| | 72 | eval() { return val_.eval(); } |
| | 73 | ; |
| | 74 | |
| | 75 | grammar number: tokFloat->num_ : object |
| | 76 | eval() |
| | 77 | { |
| | 78 | local val; |
| | 79 | |
| | 80 | /* parse the number */ |
| | 81 | val = new BigNumber(num_); |
| | 82 | |
| | 83 | /* if the precision is smaller than the input minimum, increase it */ |
| | 84 | if (val.getPrecision() |
| | 85 | < calcGlobals.inputPrecision + calcGlobals.guardPrecision) |
| | 86 | val = val.setPrecision(calcGlobals.inputPrecision |
| | 87 | + calcGlobals.guardPrecision); |
| | 88 | |
| | 89 | /* return the value */ |
| | 90 | return val; |
| | 91 | } |
| | 92 | ; |
| | 93 | |
| | 94 | main(args) |
| | 95 | { |
| | 96 | "T3 Scientific Calculator\n |
| | 97 | Type ?\ for help, type Q or QUIT to quit.\n"; |
| | 98 | |
| | 99 | for (;;) |
| | 100 | { |
| | 101 | local str, toks; |
| | 102 | local match; |
| | 103 | |
| | 104 | /* read a line */ |
| | 105 | "\b>"; |
| | 106 | str = inputLine(); |
| | 107 | |
| | 108 | /* tokenize the string */ |
| | 109 | try |
| | 110 | { |
| | 111 | toks = CalcTokenizer.tokenize(str); |
| | 112 | } |
| | 113 | catch (TokErrorNoMatch err) |
| | 114 | { |
| | 115 | "Invalid character '<<err.remainingStr_.substr(1, 1)>>'\n"; |
| | 116 | continue; |
| | 117 | } |
| | 118 | |
| | 119 | /* if it's 'quit' or 'q', stop */ |
| | 120 | if (toks.length() == 1 |
| | 121 | && (getTokVal(toks[1]) is in('q', 'quit'))) |
| | 122 | break; |
| | 123 | |
| | 124 | /* if it's '?', show help */ |
| | 125 | if (toks.length() == 1 |
| | 126 | && (getTokVal(toks[1]) == '?')) |
| | 127 | { |
| | 128 | showHelp(); |
| | 129 | continue; |
| | 130 | } |
| | 131 | |
| | 132 | /* parse it */ |
| | 133 | match = term.parseTokens(toks, gDict); |
| | 134 | |
| | 135 | /* if we didn't get anything, say so */ |
| | 136 | if (match.length() == 0) |
| | 137 | { |
| | 138 | "Invalid expression\n"; |
| | 139 | } |
| | 140 | else |
| | 141 | { |
| | 142 | local val; |
| | 143 | local flags; |
| | 144 | |
| | 145 | /* evaluate the expression */ |
| | 146 | val = match[1][2].eval(); |
| | 147 | if (val != nil) |
| | 148 | { |
| | 149 | /* clear the display flags */ |
| | 150 | flags = 0; |
| | 151 | |
| | 152 | /* display in scientific or normal notation as desired */ |
| | 153 | if (calcGlobals.dispSci) |
| | 154 | flags |= BignumExp; |
| | 155 | |
| | 156 | /* display the value */ |
| | 157 | "<<val.formatString(calcGlobals.dispMaxDigits, flags, |
| | 158 | -1, calcGlobals.dispFracDigits)>>\n"; |
| | 159 | } |
| | 160 | } |
| | 161 | } |
| | 162 | } |
| | 163 | |
| | 164 | showHelp() |
| | 165 | { |
| | 166 | "Enter numbers in decimal or scientific notation:\b |
| | 167 | \t3.1415926\n |
| | 168 | \t1.705e-11\b |
| | 169 | Operators, in order of precedence:\b |
| | 170 | \ta ^ b\t\traise a to the power of b\n |
| | 171 | \ta * b\t\tmultiply\n |
| | 172 | \ta / b\t\tdivide\n |
| | 173 | \ta + b\t\tadd\n |
| | 174 | \ta - b\t\tsubtract\n |
| | 175 | \t( a )\t\toverride operator precedence\n |
| | 176 | \b |
| | 177 | Functions:\b |
| | 178 | \tsin(x)\t\ttrigonometric sine\n |
| | 179 | \tcos(x)\t\ttrigonometric cosine\n |
| | 180 | \ttan(x)\t\ttrigonometric tangent\n |
| | 181 | \tln(x)\t\tnatural logarithm\n |
| | 182 | \tlog(x)\t\tcommon (base-10) logarithm\n |
| | 183 | \texp(x)\t\traise e (the base of the natural logarithm) to power\n |
| | 184 | \b |
| | 185 | Constants:\b |
| | 186 | \tpi\t\t3.14159265\n |
| | 187 | \te\t\t2.7182818\n |
| | 188 | \b"; |
| | 189 | "Commands:\b |
| | 190 | \tquit\t\tturn off the calculator\n |
| | 191 | \tsci N\t\tdisplay scientific notation with N digits after the decimal\n |
| | 192 | \tfix N\t\tdisplay fixed notation with N digits after the decimal\n |
| | 193 | \tprec N\t\tset default input precision to N digits\n |
| | 194 | \b"; |
| | 195 | } |
| | 196 | |