cfad47cfa3/t3compiler/tads3/test/data/calc.t

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
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 command: expression->expr_ : object
60
    eval() { return expr_.eval(); }
61
;
62
63
class commandWithIntegerOp: object
64
    op_ = nil // this is the grammar element for the operand
65
    desc_ = "desc" // this is the command name
66
    opDesc_ = "operator desc" // description of operator's purpose
67
    eval()
68
    {
69
        try
70
        {
71
            local val = toInteger(op_.eval());
72
            evalWithOp(val);
73
        }
74
        catch (Exception exc)
75
        {
76
            "Invalid argument to '<<desc_>>' - please specify
77
            <<opDesc_>>\n";
78
        }
79
    }
80
;
81
82
grammar command: 'sci' number->op_ : commandWithIntegerOp
83
    desc_ = "sci"
84
    opDesc_ = "the number of digits to display after the decimal point"
85
    evalWithOp(val)
86
    {
87
        /* set scientific notation */
88
        calcGlobals.dispSci = true;
89
90
        /* note the number of digits after the decimal */
91
        calcGlobals.dispFracDigits = val;
92
93
        /* set the maximum digits */
94
        calcGlobals.dispMaxDigits = (val > 32 ? val + 5 : 32);
95
96
        /* explain the change */
97
        "Scientific notation, <<val>> digits after the decimal\n";
98
    }
99
;
100
101
grammar command: [badness 1] 'sci' *: object
102
    eval()
103
    {
104
        "Please enter in the format 'sci N', where N is the number of
105
        digits to display after the decimal point.\n";
106
    }
107
;
108
109
grammar command: 'fix' number->op_ : commandWithIntegerOp
110
    desc_ = "fix"
111
    opDesc_ = "the number of digits to display after the decimal point"
112
    evalWithOp(val)
113
    {
114
        /* set non-scientific notation */
115
        calcGlobals.dispSci = nil;
116
117
        /* note the number of digits after the decimal */
118
        calcGlobals.dispFracDigits = val;
119
120
        /* set the maximum digits */
121
        calcGlobals.dispMaxDigits = (val > 32 ? val + 12 : 32);
122
123
        /* explain the change */
124
        "Fixed notation, <<val>> digits after the decimal\n";
125
    }
126
;
127
128
grammar command: [badness 1] 'fix' *: object
129
    eval()
130
    {
131
        "Please enter in the format 'fix N', where N is the number of
132
        digits to display after the decimal point.\n";
133
    }
134
;
135
136
grammar command: 'prec' number->op_ : commandWithIntegerOp
137
    desc_ = "prec"
138
    opDesc_ = "the default input precision"
139
    evalWithOp(val)
140
    {
141
        /* note the new input precision */
142
        calcGlobals.inputPrecision = val;
143
144
        /* explain the change */
145
        "Input precision is now <<val>> digits\n";
146
    }
147
;
148
149
grammar command: [badness 1] 'prec' *: object
150
    eval()
151
    {
152
        "Please enter in the format 'prec N', where N is the number of
153
        digits to use for the default input precision.\n";
154
    }
155
;
156
157
grammar expression: term->val_: object
158
    eval() { return val_.eval(); }
159
;
160
161
grammar term: term->val1_ '+' factor->val2_: object
162
    eval() { return val1_.eval() + val2_.eval(); }
163
;
164
165
grammar term: term->val1_ '-' factor->val2_: object
166
    eval() { return val1_.eval() - val2_.eval(); }
167
;
168
169
grammar term: factor->val_: object
170
    eval() { return val_.eval(); }
171
;
172
173
grammar factor: factor->val1_ '*' power->val2_: object
174
    eval() { return val1_.eval() * val2_.eval(); }
175
;
176
177
grammar factor: factor->val1_ '/' power->val2_: object
178
    eval() { return val1_.eval() / val2_.eval(); }
179
;
180
181
grammar factor: power->val_: object
182
    eval() { return val_.eval(); }
183
;
184
185
grammar power: power->val1_ '^' prefix->val2_: object
186
    eval() { return val1_.eval().raiseToPower(val2_.eval()); }
187
;
188
189
grammar power: prefix->val_: object
190
    eval() { return val_.eval(); }
191
;
192
193
grammar prefix: funcName->func_ '(' expression->expr_ ')': object
194
    eval() { return gDict.findWord(func_, &funcName)[1].eval(expr_); }
195
;
196
197
grammar prefix: funcName->func_ prefix->expr_ : object
198
    eval() { return gDict.findWord(func_, &funcName)[1].eval(expr_); }
199
;
200
201
#define DefFunc(nm, func) \
202
  object funcName = #@nm eval(expr) { return expr.eval().func(); }
203
204
DefFunc(ln, logE);
205
DefFunc(log, log10);
206
DefFunc(exp, expE);
207
DefFunc(sin, sine);
208
DefFunc(cos, cosine);
209
DefFunc(tan, tangent);
210
DefFunc(asin, arcsine);
211
DefFunc(acos, arccosine);
212
DefFunc(atan, arctangent);
213
DefFunc(sqr, sqrt);
214
DefFunc(sqrt, sqrt);
215
216
#if 0
217
lnFunc: object
218
    funcName = 'ln'
219
    eval(expr)
220
    {
221
        return expr.eval().logE();
222
    }
223
;
224
225
logFunc: object
226
    funcName = 'log'
227
    eval(expr)
228
    {
229
        return expr.eval().log10();
230
    }
231
;
232
233
expFunc: object
234
    funcName = 'exp'
235
    eval(expr)
236
    {
237
        return expr.eval().expE();
238
    }
239
;
240
241
sinFunc: object
242
    funcName = 'sin'
243
    eval(expr)
244
    {
245
        return expr.eval().sine();
246
    }
247
;
248
249
cosFunc: object
250
    funcName = 'cos'
251
    eval(expr)
252
    {
253
        return expr.eval().cosine();
254
    }
255
;
256
257
tanFunc: object
258
    funcName = 'tan'
259
    eval(expr)
260
    {
261
        return expr.eval().tangent();
262
    }
263
;
264
265
asinFunc: object
266
    funcName = 'asin'
267
    eval(expr)
268
    {
269
        return expr.eval().arcsine();
270
    }
271
;
272
273
acosFunc: object
274
    funcName = 'acos'
275
    eval(expr)
276
    {
277
        return expr.eval().arccosine();
278
    }
279
;
280
281
atanFunc: object
282
    funcName = 'atan'
283
    eval(expr)
284
    {
285
        return expr.eval().arctangent();
286
    }
287
;
288
289
sqrFunc: object
290
    funcName = 'sqr'
291
    eval(expr)
292
    {
293
        return expr.eval().sqrt();
294
    }
295
;
296
297
sqrtFunc: object
298
    funcName = 'atan'
299
    eval(expr)
300
    {
301
        return expr.eval().sqrt();
302
    }
303
;
304
#endif
305
306
grammar prefix: '-' postfix->val_ : object
307
    eval() { return -val_.eval(); }
308
;
309
310
grammar prefix: '+' postfix->val_: object
311
    eval() { return val_.eval(); }
312
;
313
314
grammar prefix: postfix->val_ : object
315
    eval() { return val_.eval(); }
316
;
317
318
grammar postfix: atomic->val_ '!': object
319
    eval() { return factorial(val_.eval()); }
320
;
321
322
grammar postfix: atomic->val_ : object
323
    eval() { return val_.eval(); }
324
;
325
326
factorial(x)
327
{
328
    local prod;
329
    
330
    /* 
331
     *   do this iteratively rather than recursively, to allow for really
332
     *   big inputs without fear of blowing out the stack 
333
     */
334
    for (prod = 1.0 ; x > 1 ; --x)
335
        prod *= x;
336
337
    /* return the product */
338
    return prod;
339
}
340
341
grammar atomic: '(' expression->val_ ')': object
342
    eval() { return val_.eval(); }
343
;
344
345
grammar atomic: number->num_ : object
346
    eval() { return num_.eval(); }
347
;
348
349
grammar number: tokFloat->num_ : object
350
    eval()
351
    {
352
        local val;
353
354
        /* parse the number */
355
        val = new BigNumber(num_);
356
357
        /* if the precision is smaller than the input minimum, increase it */
358
        if (val.getPrecision()
359
            < calcGlobals.inputPrecision + calcGlobals.guardPrecision)
360
            val = val.setPrecision(calcGlobals.inputPrecision
361
                                   + calcGlobals.guardPrecision);
362
363
        /* return the value */
364
        return val;
365
    }
366
;
367
368
grammar atomic: 'e' : object
369
    eval()
370
    {
371
        return BigNumber.getE(calcGlobals.inputPrecision
372
                              + calcGlobals.guardPrecision);
373
    }
374
;
375
376
grammar atomic: 'pi': object
377
    eval()
378
    {
379
        return BigNumber.getPi(calcGlobals.inputPrecision
380
                               + calcGlobals.guardPrecision);
381
    }
382
;
383
384
main(args)
385
{
386
    "T3 Scientific Calculator\n
387
    Type ?\ for help, type Q or QUIT to quit.\n";
388
    
389
    for (;;)
390
    {
391
        local str, toks;
392
        local match;
393
394
        /* read a line */
395
        "\b>";
396
        str = inputLine();
397
398
        /* tokenize the string */
399
        try
400
        {
401
            toks = CalcTokenizer.tokenize(str);
402
        }
403
        catch (TokErrorNoMatch err)
404
        {
405
            "Invalid character '<<err.remainingStr_.substr(1, 1)>>'\n";
406
            continue;
407
        }
408
409
        /* if it's 'quit' or 'q', stop */
410
        if (toks.length() == 1
411
            && (getTokVal(toks[1]) is in ('q', 'quit')))
412
            break;
413
414
        /* if it's '?', show help */
415
        if (toks.length() == 1
416
            && (getTokVal(toks[1]) == '?'))
417
        {
418
            showHelp();
419
            continue;
420
        }
421
422
        /* parse it */
423
        match = command.parseTokens(toks, gDict);
424
425
        /* if we didn't get anything, say so */
426
        if (match.length() == 0)
427
        {
428
            "Invalid expression\n";
429
        }
430
        else
431
        {
432
            local val;
433
            local flags;
434
            
435
            try
436
            {
437
                /* evaluate the expression */
438
                val = match[1].eval();
439
440
                /* if we got a valid, display it */
441
                if (val != nil)
442
                {
443
                    /* clear the display flags */
444
                    flags = 0;
445
                    
446
                    /* display in scientific or normal notation as desired */
447
                    if (calcGlobals.dispSci)
448
                        flags |= BignumExp;
449
                    
450
                    /* display the value */                
451
                    "<<val.formatString(calcGlobals.dispMaxDigits, flags,
452
                    -1, calcGlobals.dispFracDigits)>>\n";
453
                }
454
            }
455
            catch (Exception exc)
456
            {
457
                "Evaluation error: <<exc.displayException()>>\n";
458
            }
459
        }
460
    }
461
}
462
463
showHelp()
464
{
465
    "Enter numbers in decimal or scientific notation:\b
466
    \t3.1415926\n
467
    \t1.705e-11\b
468
    Operators, in order of precedence:\b
469
    \ta ^ b\t\traise a to the power of b\n
470
    \ta * b\t\tmultiply\n
471
    \ta / b\t\tdivide\n
472
    \ta + b\t\tadd\n
473
    \ta - b\t\tsubtract\n
474
    \t( a )\t\toverride operator precedence\n
475
    \b
476
    Functions:\b
477
    \tsin(x)\t\ttrigonometric sine\n
478
    \tcos(x)\t\ttrigonometric cosine\n
479
    \ttan(x)\t\ttrigonometric tangent\n
480
    \tasin(x)\t\tarcsine\n
481
    \tacos(x)\t\tarccosine\n
482
    \tatan(x)\t\tarctangent\n
483
    \tln(x)\t\tnatural logarithm\n
484
    \tlog(x)\t\tcommon (base-10) logarithm\n
485
    \texp(x)\t\traise e (the base of the natural logarithm) to power\n
486
    \tsqr(x)\t\tsquare root (sqrt(x) is equivalent)\n
487
    \b
488
    Constants:\b
489
    \tpi\t\t3.14159265\n
490
    \te\t\t2.7182818\n
491
    \b";
492
    "Commands:\b
493
    \tquit\t\tturn off the calculator\n
494
    \tsci N\t\tdisplay scientific notation with N digits after the decimal\n
495
    \tfix N\t\tdisplay fixed notation with N digits after the decimal\n
496
    \tprec N\t\tset default input precision to N digits\n
497
    \b";
498
}