~ubuntu-branches/ubuntu/wily/gargoyle-free/wily-proposed

« back to all changes in this revision

Viewing changes to tads/tads3/test/data/calc.t

  • Committer: Bazaar Package Importer
  • Author(s): Sylvain Beucler
  • Date: 2009-09-11 20:09:43 UTC
  • Revision ID: james.westby@ubuntu.com-20090911200943-idgzoyupq6650zpn
Tags: upstream-2009-08-25
ImportĀ upstreamĀ versionĀ 2009-08-25

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
}