Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
BitchX
GitHub Repository: BitchX/BitchX1.3
Path: blob/master/source/expr2.c
1069 views
1
/*
2
* $Id: expr2.c 3 2008-02-25 09:49:14Z keaston $
3
* math.c - mathematical expression evaluation
4
* This file is based on 'math.c', which is part of zsh, the Z shell.
5
*
6
* Copyright (c) 1992-1997 Paul Falstad, All rights reserved.
7
*
8
* Redistribution and use in source and binary forms, with or without
9
* modification, are permitted provided that the following conditions
10
* are met:
11
* 1. Redistributions of source code must retain the copyright notice,
12
* this list of conditions and the two following paragraphs.
13
* 2. Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimers in the
15
* documentation and/or other materials provided with the distribution
16
* 3. The names of the author(s) may not be used to endorse or promote
17
* products derived from this software without specific prior written
18
* permission.
19
*
20
* In no event shall Paul Falstad or the Zsh Development Group be liable
21
* to any party for direct, indirect, special, incidental, or consequential
22
* damages arising out of the use of this software and its documentation,
23
* even if Paul Falstad and the Zsh Development Group have been advised of
24
* the possibility of such damage.
25
*
26
* Paul Falstad and the Zsh Development Group specifically disclaim any
27
* warranties, including, but not limited to, the implied warranties of
28
* merchantability and fitness for a particular purpose. The software
29
* provided hereunder is on an "as is" basis, and Paul Falstad and the
30
* Zsh Development Group have no obligation to provide maintenance,
31
* support, updates, enhancements, or modifications.
32
*
33
*/
34
/*
35
* Significant modifications by Jeremy Nelson
36
* Coypright 1998 EPIC Software Labs, All rights reserved.
37
*
38
* You may distribute this file under the same terms as the above, by
39
* including the parties "Jeremy Nelson" and "EPIC Software Labs" to the
40
* limitations of liability and the express disclaimer of all warranties.
41
* This software is provided "AS IS".
42
*
43
* $Id: expr2.c 3 2008-02-25 09:49:14Z keaston $
44
*/
45
46
#include "irc.h"
47
48
#include <math.h>
49
#include "debug.h"
50
51
/* XXX in expr.c */
52
#if 0
53
static char *alias_special_char(char **, char *, const char *, char *, int *);
54
#endif
55
56
#define STACKSZ 100
57
#define TOKENCOUNT 256
58
#define MAGIC_TOKEN -14
59
60
/*
61
* One thing of note is that while the original did only ints, we really
62
* only do strings. We convert to and from ints as neccesary. Icky,
63
* but given the semantics we require its the only way.
64
*/
65
/*
66
* All the information for each expression is stored in a struct. This
67
* is done so that there are no global variables in use (theyre all collected
68
* making them easier to handle), and makes re-entrancy much easier since
69
* i dont have to ask myself "have i accounted for the old state of all the
70
* global variables?"
71
*/
72
73
/*
74
* When we want to refer symbolically to a token, we just sling around
75
* the integer index to the token table. This serves two purposes: We
76
* dont have to worry about whether something is malloced or not, or who
77
* is resopnsible to free, or anything like that. If you want to keep
78
* something around, you tokenize() it and that returns a "handle" to the
79
* token and then you pass that handle around. So the pair (context,handle)
80
* refers to a unique, usable token.
81
*/
82
typedef int TOKEN;
83
84
/*
85
* This sets up whether we do floating point math or integer math
86
*/
87
#ifdef FLOATING_POINT_MATH /* XXXX This doesnt work yet */
88
typedef double NUMBER;
89
typedef long BooL;
90
# define STON atof
91
# define NTOS ftoa
92
#else
93
typedef unsigned long NUMBER;
94
typedef long BooL;
95
# define STON atol
96
# define NTOS ltoa
97
#endif
98
99
typedef struct
100
{
101
/* POSITION AND STATE INFORMATION */
102
/*
103
* This is the current position in the lexing.
104
*/
105
char *ptr;
106
107
/*
108
* When set, the expression is lexed, but nothing that may have a side
109
* effect (function calls, assignments, etc) are actually performed.
110
* Dummy values are instead substituted.
111
*/
112
int noeval;
113
114
/*
115
* When set, this means the next token may either be a prefix operator
116
* or an operand. When clear, it means the next operator must be a
117
* non-prefix operator.
118
*/
119
int operand;
120
121
122
/* TOKEN TABLE */
123
/*
124
* Each registered 'token' is given a TOKEN id. The idea is
125
* that we want TOKEN to be an opaque type to be used to refer
126
* to a token in a generic way, but in practice its just an integer
127
* offset into a char ** table. We register all tokens sequentially,
128
* so this just gets incremented when we want to register a new token.
129
*/
130
TOKEN token;
131
132
/*
133
* This is the list of operand (string) tokens we have extracted
134
* so far from the expression. Offsets into this array are stored
135
* into the parsing stack.
136
*/
137
char * tokens[TOKENCOUNT + 1];
138
139
140
/* OPERAND STACK */
141
/*
142
* This is the operand shift stack. These are the operands that
143
* are currently awaiting reduction. Note that rather than keeping
144
* track of the lvals and rvals here, we simply keep track of offsets
145
* to the 'tokens' table that actually stores all the relevant data.
146
* Then we can just call the token-class functions to get that data.
147
* This is more efficient because it allows us to recycle tokens
148
* more reasonably without wasteful malloc-copies.
149
*/
150
TOKEN stack[STACKSZ + 1];
151
152
/* Current index to the operand stack */
153
int sp;
154
155
/* This is the last token that was lexed. */
156
TOKEN mtok;
157
158
/* This is set when an error happens */
159
int errflag;
160
161
TOKEN last_token;
162
163
const char *args;
164
int *args_flag;
165
} expr_info;
166
167
__inline static TOKEN tokenize (expr_info *c, const char *t);
168
static char * after_expando_special (expr_info *c);
169
170
void setup_expr_info (expr_info *c)
171
{
172
int i;
173
174
c->ptr = NULL;
175
c->noeval = 0;
176
c->operand = 1;
177
c->token = 0;
178
for (i = 0; i <= TOKENCOUNT; i++)
179
c->tokens[i] = NULL;
180
for (i = 0; i <= STACKSZ; i++)
181
c->stack[i] = 0;
182
c->sp = -1;
183
c->mtok = 0;
184
c->errflag = 0;
185
c->last_token = 0;
186
tokenize(c, empty_string); /* Always token 0 */
187
}
188
189
void destroy_expr_info (expr_info *c)
190
{
191
int i;
192
193
c->ptr = NULL;
194
c->noeval = -1;
195
c->operand = -1;
196
for (i = 0; i < c->token; i++)
197
new_free(&c->tokens[i]);
198
c->token = -1;
199
for (i = 0; i <= STACKSZ; i++)
200
c->stack[i] = -1;
201
c->sp = -1;
202
c->mtok = -1;
203
c->errflag = -1;
204
c->last_token = -1;
205
}
206
207
208
/*
209
* LR = left-to-right associativity
210
* RL = right-to-left associativity
211
* BOO = short-circuiting boolean
212
*/
213
#define LR 0
214
#define RL 1
215
#define BOOL 2
216
217
/*
218
* These are all the token-types. Each of the operators is represented,
219
* as is the generic operand type
220
*/
221
222
enum LEX {
223
M_INPAR,
224
NOT, COMP, PREMINUS, PREPLUS,
225
UPLUS, UMINUS, STRLEN,
226
WORDC, DEREF,
227
POWER,
228
MUL, DIV, MOD,
229
PLUS, MINUS, STRCAT,
230
SHLEFT, SHRIGHT,
231
LES, LEQ, GRE, GEQ,
232
MATCH, NOMATCH,
233
DEQ, NEQ,
234
AND,
235
XOR,
236
OR,
237
DAND,
238
DXOR,
239
DOR,
240
QUEST, COLON,
241
EQ, PLUSEQ, MINUSEQ, MULEQ, DIVEQ,
242
MODEQ, ANDEQ, XOREQ, OREQ,
243
SHLEFTEQ, SHRIGHTEQ, DANDEQ, DOREQ,
244
DXOREQ, POWEREQ, STRCATEQ, STRPREEQ,
245
SWAP,
246
COMMA,
247
POSTMINUS, POSTPLUS,
248
ID,
249
M_OUTPAR,
250
EERROR,
251
EOI,
252
TOKCOUNT
253
};
254
255
256
/*
257
* Precedence table: Operators with a lower precedence VALUE have a higher
258
* precedence. The theory behind infix notation (algebraic notation) is that
259
* you have a sequence of operands seperated by (typically binary) operators.
260
* The problem of precedence is that each operand is surrounded by two
261
* operators, so it is ambiguous which operator the operand "binds" to. This
262
* is resolved by "precedence rules" which state that given two operators,
263
* which one is allowed to "reduce" (operate on) the operand. For a simple
264
* explanation, take the expression (3+4*5). Now the middle operand is a
265
* '4', but we dont know if it should be reduced via the plus, or via the
266
* multiply. If we look up both operators in the prec table, we see that
267
* multiply has the lower value -- therefore the 4 is reduced via the multiply
268
* and then the result of the multiply is reduced by the addition.
269
*/
270
static int prec[TOKCOUNT] =
271
{
272
1,
273
2, 2, 2, 2,
274
2, 2, 2,
275
2, 2,
276
3,
277
4, 4, 4,
278
5, 5, 5,
279
6, 6,
280
7, 7, 7, 7,
281
8, 8,
282
9, 9,
283
10,
284
11,
285
12,
286
13,
287
14,
288
15,
289
16, 16,
290
17, 17, 17, 17, 17,
291
17, 17, 17, 17,
292
17, 17, 17, 17,
293
17, 17, 17, 17,
294
17,
295
18,
296
2, 2,
297
0,
298
137,
299
156,
300
200
301
};
302
#define TOPPREC 21
303
304
305
/*
306
* Associativity table: But precedence isnt enough. What happens when you
307
* have two identical operations to determine between? Well, the easy way
308
* is to say that the first operation is always done first. But some
309
* operators dont work that way (like the assignment operator) and always
310
* reduce the LAST (or rightmost) operation first. For example:
311
* (3+4+5) ((4+3)+5) (7+5) (12)
312
* (v1=v2=3) (v1=(v2=3)) (v1=3) (3)
313
* So for each operator we need to specify how to determine the precedence
314
* of the same operator against itself. This is called "associativity".
315
* Left-to-right associativity means that the operations occur left-to-right,
316
* or first-operator, first-reduced. Right-to-left associativity means
317
* that the operations occur right-to-left, or last-operator, first-reduced.
318
*
319
* We have a special case of associativity called BOOL, which is just a
320
* special type of left-to-right associtivity whereby the right hand side
321
* of the operand is not automatically parsed. (not really, but its the
322
* theory that counts.)
323
*/
324
static int assoc[TOKCOUNT] =
325
{
326
LR,
327
RL, RL, RL, RL,
328
RL, RL, RL,
329
RL, RL,
330
RL,
331
LR, LR, LR,
332
LR, LR, LR,
333
LR, LR,
334
LR, LR, LR, LR,
335
LR, LR,
336
LR, LR,
337
LR,
338
LR,
339
LR,
340
BOOL,
341
BOOL,
342
BOOL,
343
RL, RL,
344
RL, RL, RL, RL, RL,
345
RL, RL, RL, RL,
346
RL, RL, RL, RL,
347
RL, RL, RL, RL,
348
RL,
349
LR,
350
RL, RL,
351
LR,
352
LR,
353
LR,
354
LR
355
};
356
357
358
/* ********************* OPERAND TOKEN REPOSITORY ********************** */
359
/*
360
* This is where we store our lvalues, kind of. What we really store here
361
* are all of the string operands. Actually, we store all of the operands
362
* here. When an operand is parsed, its converted to a string and put in
363
* here and the index into the 'token' table is returned.
364
*/
365
/* THIS FUNCTION MAKES A NEW COPY OF 'T'. YOU MUST DISPOSE OF 'T' YOURSELF */
366
__inline static TOKEN tokenize (expr_info *c, const char *t)
367
{
368
if (c->token >= TOKENCOUNT)
369
{
370
error("Too many tokens for this expression");
371
return -1;
372
}
373
c->tokens[c->token] = m_strdup(t);
374
return c->token++;
375
}
376
377
/* get_token gets the ``lvalue'', or original text, of a token */
378
/* YOU MUST _NOT_ FREE THE RETURN VALUE FROM THIS FUNCTION! */
379
__inline static const char * get_token (expr_info *c, TOKEN v)
380
{
381
if (v == MAGIC_TOKEN) /* Magic token */
382
return c->args;
383
384
if (v < 0 || v >= c->token)
385
{
386
error("Token index [%d] is out of range", v);
387
return get_token(c, 0); /* The empty token */
388
}
389
return c->tokens[v];
390
}
391
392
393
394
/*
395
* These three functions ``getsval'', ``getival'', and ``getbval'' take
396
* as arguments token-indexes, and return the appropriate rvalue for those
397
* tokens. For literal text strings, they are simply expanded and returned.
398
* For function calls, the function is called, and the return value is
399
* returned. For variable references, the value is looked up and returned.
400
* Furthermore, ``getival'' takes this value and converts it into a long
401
* int and returns that. ``getbval'' takes the value and checks it for its
402
* truth or falseness (as determined by check_val()).
403
*/
404
/* YOU MUST FREE THE RETURN VALUE FROM THIS FUNCTION! */
405
static char * getsval2 (expr_info *c, TOKEN s);
406
static char * getsval (expr_info *c, TOKEN s)
407
{
408
char * retval;
409
const char * t;
410
411
t = get_token(c, s);
412
if (x_debug & DEBUG_NEW_MATH_DEBUG)
413
debugyell(">>> Expanding token [%d]: [%s]", s, t);
414
retval = getsval2(c, s);
415
if (x_debug & DEBUG_NEW_MATH_DEBUG)
416
debugyell("<<< Expanded token [%d]: [%s] to: [%s]", s, t, retval);
417
return retval;
418
}
419
420
/* XXX Ick. :-D */
421
static char * getsval2 (expr_info *c, TOKEN s)
422
{
423
const char *t;
424
425
if (c->noeval || s == 0)
426
return m_strdup(get_token(c, 0));
427
428
/* XXXX Bleh */
429
if (s == MAGIC_TOKEN)
430
{
431
*c->args_flag = 1;
432
return m_strdup(c->args);
433
}
434
435
t = get_token(c, s);
436
437
/*
438
* Handle [string] token types.
439
*/
440
if (*t == '[')
441
{
442
t++;
443
444
/*
445
* Attempt to handle $0 and friends here. Also handle
446
* Things like $1-, and also $*.
447
*/
448
if (*t == '$')
449
{
450
if (t[1] == '*' && !t[2])
451
return m_strdup(c->args);
452
else
453
{
454
char * end = NULL;
455
long j = strtol(t + 1, &end, 10);
456
457
/* Handle [$X] */
458
if (end && !*end)
459
{
460
*c->args_flag = 1;
461
462
if (j < 0)
463
return extract2(c->args, SOS, -j);
464
else
465
return extract2(c->args, j, j);
466
}
467
468
/* Gracefully handle [$X-] as well */
469
else if (*end == '-' && !end[1])
470
{
471
*c->args_flag = 1;
472
return extract2(c->args, j, EOS);
473
}
474
475
/* Anything else we dont grok */
476
else
477
return expand_alias(t, c->args,
478
c->args_flag, NULL);
479
}
480
}
481
482
/* Handle [plain text] */
483
else if (!strchr(t, '$') && !strchr(t, '\\'))
484
return m_strdup(t);
485
486
/* Everything else gets expanded per normal */
487
else
488
return expand_alias(t, c->args, c->args_flag, NULL);
489
}
490
491
/* Do this first, its cheap */
492
else if (is_number(t))
493
return m_strdup(t);
494
495
/* Figure out if its a variable reference or a function call */
496
else
497
{
498
char *after,
499
*ptr,
500
*w,
501
saver = 0;
502
int func = 0;
503
504
w = LOCAL_COPY(t);
505
after = after_expando(w, 0, &func);
506
if (after)
507
{
508
saver = *after;
509
*after = 0;
510
}
511
512
if (func)
513
ptr = call_function(w, c->args, c->args_flag);
514
else
515
ptr = get_variable_with_args(w, c->args, c->args_flag);
516
517
if (!ptr)
518
return m_strdup(empty_string);
519
if (after)
520
*after = saver;
521
return ptr;
522
}
523
524
return NULL /* <whatever> */;
525
}
526
527
__inline static NUMBER getnval (expr_info *c, TOKEN s)
528
{
529
char *t;
530
NUMBER retval;
531
532
if (c->noeval)
533
return 0;
534
535
if (!(t = getsval(c, s)))
536
return 0;
537
538
retval = STON(t);
539
new_free(&t);
540
return retval;
541
}
542
543
#ifdef notused
544
__inline static BooL getbval (expr_info *c, TOKEN s)
545
{
546
char *t;
547
long retval;
548
549
if (c->noeval)
550
return 0;
551
552
if (!(t = getsval(c, s)))
553
return 0;
554
555
retval = check_val(t);
556
new_free(&t);
557
return retval;
558
}
559
#endif
560
561
562
/* ******************** ASSIGNING TO VARIABLES ************************** */
563
/*
564
* When you have an lvalue (left hand side of an assignment) that needs to
565
* be assigned to, then you can call these functions to assign to it the
566
* appropriate type. The basic operation is to assign and rvalue token
567
* to an lvalue token. But some times you dont always have a tokenized
568
* rvalue, so you can just pass in a raw value and we will tokenize it for
569
* you and go from there. Note that the "result" of an assignment is the
570
* rvalue token. This is then pushed back onto the stack.
571
*/
572
__inline static TOKEN setvar (expr_info *c, TOKEN l, TOKEN r)
573
{
574
char *t = expand_alias(get_token(c, l), c->args, c->args_flag, NULL);
575
char *u = getsval(c, r);
576
char *s;
577
578
if (!c->noeval)
579
{
580
int old_window_display = window_display;
581
window_display = 0;
582
add_var_alias(t, u);
583
window_display = old_window_display;
584
}
585
586
s = alloca(strlen(u) + 3);
587
s[0] = '[';
588
strcpy(s+1, u);
589
590
new_free(&t);
591
new_free(&u);
592
return tokenize(c, s);
593
}
594
595
__inline static TOKEN setnvar (expr_info *c, TOKEN l, NUMBER v)
596
{
597
return setvar(c, l, tokenize(c, NTOS(v)));
598
}
599
600
__inline static TOKEN setsvar (expr_info *c, TOKEN l, char *v)
601
{
602
char *s;
603
604
s = alloca(strlen(v) + 3);
605
s[0] = '[';
606
strcpy(s+1, v);
607
return setvar(c, l, tokenize(c, s));
608
}
609
610
611
612
613
/* *************************** OPERAND STACK ************************** */
614
615
616
/*
617
* Adding (shifting) and Removing (reducing) operands from the stack is a
618
* fairly straightforward process. The general way to add an token to
619
* the stack is to pass in its TOKEN index. However, there are some times
620
* when you want to shift a value that has not been tokenized. So you call
621
* one of the other functions that will do this for you.
622
*/
623
__inline static TOKEN pusht (expr_info *c, TOKEN t)
624
{
625
if (c->sp == STACKSZ - 1)
626
{
627
error("Expressions may not have more than 99 operands");
628
return -1;
629
}
630
else
631
c->sp++;
632
633
if (x_debug & DEBUG_NEW_MATH_DEBUG)
634
debugyell("Pushing token [%d] [%s]", t, get_token(c, t));
635
return ((c->stack[c->sp] = t));
636
}
637
638
__inline static TOKEN pushn (expr_info *c, NUMBER val)
639
{
640
return pusht(c, tokenize(c, NTOS(val)));
641
}
642
643
__inline static TOKEN pushs (expr_info *c, char *val)
644
{
645
char *blah;
646
blah = alloca(strlen(val) + 2);
647
sprintf(blah, "[%s", val);
648
return pusht(c, tokenize(c, blah));
649
}
650
651
__inline static TOKEN top (expr_info *c)
652
{
653
if (c->sp < 0)
654
{
655
error("No operands.");
656
return -1;
657
}
658
else
659
return c->stack[c->sp];
660
}
661
662
__inline static TOKEN pop (expr_info *c)
663
{
664
if (c->sp < 0)
665
{
666
/*
667
* Attempting to pop more operands than are available
668
* Yeilds empty values. Thats probably the most reasonable
669
* course of action.
670
*/
671
error("Cannot pop operand: no more operands");
672
return 0;
673
}
674
else
675
return c->stack[c->sp--];
676
}
677
678
__inline static double popn (expr_info *c)
679
{
680
char * x = getsval(c, pop(c));
681
NUMBER i = atof(x);
682
683
new_free(&x);
684
return i;
685
}
686
687
/* YOU MUST FREE THE RETURN VALUE FROM THIS FUNCTION */
688
__inline static char * pops (expr_info *c)
689
{
690
return getsval(c, pop(c));
691
}
692
693
__inline static BooL popb (expr_info *c)
694
{
695
char * x = getsval(c, pop(c));
696
BooL i = check_val(x);
697
698
new_free(&x);
699
return i;
700
}
701
702
__inline static void pop2 (expr_info *c, TOKEN *t1, TOKEN *t2)
703
{
704
*t2 = pop(c);
705
*t1 = pop(c);
706
}
707
708
__inline static void pop2n (expr_info *c, NUMBER *a, NUMBER *b)
709
{
710
TOKEN t1, t2;
711
char *x, *y;
712
713
pop2(c, &t1, &t2);
714
x = getsval(c, t1);
715
y = getsval(c, t2);
716
*a = STON(x);
717
*b = STON(y);
718
new_free(&x);
719
new_free(&y);
720
}
721
722
__inline static void pop2s (expr_info *c, char **s, char **t)
723
{
724
TOKEN t1, t2;
725
char *x, *y;
726
727
pop2(c, &t1, &t2);
728
x = getsval(c, t1);
729
y = getsval(c, t2);
730
*s = x;
731
*t = y;
732
}
733
734
__inline static void pop2b (expr_info *c, BooL *a, BooL *b)
735
{
736
TOKEN t1, t2;
737
char *x, *y;
738
739
pop2(c, &t1, &t2);
740
x = getsval(c, t1);
741
y = getsval(c, t2);
742
*a = check_val(x);
743
*b = check_val(y);
744
new_free(&x);
745
new_free(&y);
746
}
747
748
__inline static void pop2n_a (expr_info *c, NUMBER *a, NUMBER *b, TOKEN *v)
749
{
750
TOKEN t1, t2;
751
char *x, *y;
752
753
pop2(c, &t1, &t2);
754
x = getsval(c, t1);
755
y = getsval(c, t2);
756
*a = STON(x);
757
*b = STON(y);
758
*v = t1;
759
new_free(&x);
760
new_free(&y);
761
}
762
763
__inline static void pop2s_a (expr_info *c, char **s, char **t, TOKEN *v)
764
{
765
TOKEN t1, t2;
766
char *x, *y;
767
768
pop2(c, &t1, &t2);
769
x = getsval(c, t1);
770
y = getsval(c, t2);
771
*s = x;
772
*t = y;
773
*v = t1;
774
}
775
776
#if notused
777
__inline static void pop2b_a (expr_info *c, BooL *a, BooL *b, TOKEN *v)
778
{
779
TOKEN t1, t2;
780
char *x, *y;
781
782
pop2(c, &t1, &t2);
783
x = getsval(c, t1);
784
y = getsval(c, t2);
785
*a = check_val(x);
786
*b = check_val(y);
787
*v = t1;
788
new_free(&x);
789
new_free(&y);
790
}
791
#endif
792
793
__inline static void pop3 (expr_info *c, NUMBER *a, TOKEN *v, TOKEN *w)
794
{
795
TOKEN t1, t2, t3;
796
char *x;
797
798
t3 = pop(c);
799
t2 = pop(c);
800
t1 = pop(c);
801
802
x = getsval(c, t1);
803
*a = STON(x);
804
*v = t2;
805
*w = t3;
806
new_free(&x);
807
}
808
809
810
811
/*
812
* This is the reducer. It takes the relevant arguments off the argument
813
* stack and then performs the neccesary operation on them.
814
*/
815
void op (expr_info *cx, int what)
816
{
817
NUMBER a, b;
818
BooL c, d;
819
char *s, *t;
820
TOKEN v, w;
821
822
if (x_debug & DEBUG_NEW_MATH_DEBUG)
823
debugyell("Reducing last operation...");
824
825
if (cx->sp < 0) {
826
error("An operator is missing a required operand");
827
return;
828
}
829
830
if (cx->errflag)
831
return; /* Dont parse on an error */
832
833
#define BINARY(x) \
834
{ \
835
pop2n(cx, &a, &b); \
836
pushn(cx, (x)); \
837
if (x_debug & DEBUG_NEW_MATH_DEBUG) \
838
debugyell("O: %s (%ld %ld) -> %ld", #x, a, b, (long)x); \
839
break; \
840
}
841
#define BINARY_BOOLEAN(x) \
842
{ \
843
pop2b(cx, &c, &d); \
844
pushn(cx, (x)); \
845
if (x_debug & DEBUG_NEW_MATH_DEBUG) \
846
debugyell("O: %s (%ld %ld) -> %ld", #x, c, d, (long)x); \
847
break; \
848
}
849
850
#define BINARY_NOZERO(x) \
851
{ \
852
pop2n(cx, &a, &b); \
853
if (b == 0) { \
854
if (x_debug & DEBUG_NEW_MATH_DEBUG) \
855
debugyell("O: %s (%ld %ld) -> 0", #x, a, b); \
856
error("Division by zero"); \
857
pushn(cx, 0); \
858
} \
859
else \
860
{ \
861
if (x_debug & DEBUG_NEW_MATH_DEBUG) \
862
debugyell("O: %s (%ld %ld) -> %ld", #x, a, b, (long)x); \
863
pushn(cx, (x)); \
864
} \
865
break; \
866
}
867
#define IMPLIED(x) \
868
{ \
869
pop2n_a(cx, &a, &b, &v); \
870
if (x_debug & DEBUG_NEW_MATH_DEBUG) \
871
debugyell("O: %s = %s (%ld %ld) -> %ld", \
872
get_token(cx, v), #x, a, b, x); \
873
pushn(cx, setnvar(cx, v, (x))); \
874
break; \
875
}
876
#define IMPLIED_NOZERO(x) \
877
{ \
878
pop2n_a(cx, &a, &b, &v); \
879
if (b == 0) { \
880
if (x_debug & DEBUG_NEW_MATH_DEBUG) \
881
debugyell("O: %s = %s (%ld %ld) -> 0", \
882
get_token(cx, v), #x, a, b); \
883
error("Division by zero"); \
884
pushn(cx, setnvar(cx, v, 0)); \
885
} \
886
if (x_debug & DEBUG_NEW_MATH_DEBUG) \
887
debugyell("O: %s = %s (%ld %ld) -> %ld", \
888
get_token(cx, v), #x, a, b, x); \
889
pushn(cx, setnvar(cx, v, (x))); \
890
break; \
891
}
892
#define AUTO_UNARY(x, y) \
893
{ \
894
v = pop(cx); \
895
b = getnval(cx, v); \
896
if (x_debug & DEBUG_NEW_MATH_DEBUG) \
897
debugyell("O: %s (%s %ld) -> %ld", \
898
#x, get_token(cx, v), b, (x)); \
899
setnvar(cx, v, (x)); \
900
pushn(cx, (y)); \
901
break; \
902
}
903
904
#define dpushn(x1,x2,y1) \
905
{ \
906
if (x_debug & DEBUG_NEW_MATH_DEBUG) \
907
{ \
908
debugyell("O: COMPARE"); \
909
debugyell("O: %s -> %d", #x2, (x2)); \
910
} \
911
pushn(x1,y1); \
912
}
913
#define COMPARE(x, y) \
914
{ \
915
pop2s(cx, &s, &t); \
916
if ((a = STON(s)) && (b = STON(t))) \
917
{ \
918
if (x_debug & DEBUG_NEW_MATH_DEBUG) \
919
debugyell("O: %s (%ld %ld) -> %d", #x, a, b, (x)); \
920
if ((x)) dpushn(cx, x, 1) \
921
else dpushn(cx, x, 0) \
922
} \
923
else \
924
{ \
925
if (x_debug & DEBUG_NEW_MATH_DEBUG) \
926
debugyell("O: %s (%s %s) -> %d", #x, s, t, (y)); \
927
if ((y)) dpushn(cx, y, 1) \
928
else dpushn(cx, y, 0) \
929
} \
930
new_free(&s); \
931
new_free(&t); \
932
break; \
933
}
934
935
switch (what)
936
{
937
/* Simple unary prefix operators */
938
case NOT:
939
c = popb(cx);
940
if (x_debug & DEBUG_NEW_MATH_DEBUG)
941
debugyell("O: !%ld -> %d", c, !c);
942
pushn(cx, !c);
943
break;
944
case COMP:
945
a = popn(cx);
946
if (x_debug & DEBUG_NEW_MATH_DEBUG)
947
debugyell(": ~%ld -> %ld", a, ~a);
948
pushn(cx, ~a);
949
break;
950
case UPLUS:
951
a = popn(cx);
952
if (x_debug & DEBUG_NEW_MATH_DEBUG)
953
debugyell("O: +%ld -> %ld", a, a);
954
pushn(cx, a);
955
break;
956
case UMINUS:
957
a = popn(cx);
958
if (x_debug & DEBUG_NEW_MATH_DEBUG)
959
debugyell("O: -%ld -> %ld", a, -a);
960
pushn(cx, -a);
961
break;
962
case STRLEN:
963
s = pops(cx);
964
a = strlen(s);
965
if (x_debug & DEBUG_NEW_MATH_DEBUG)
966
debugyell("O: @(%s) -> %ld", s, a);
967
pushn(cx, a);
968
new_free(&s);
969
break;
970
case WORDC:
971
s = pops(cx);
972
a = word_count(s);
973
if (x_debug & DEBUG_NEW_MATH_DEBUG)
974
debugyell("O: #(%s) -> %ld", s, a);
975
pushn(cx, a);
976
new_free(&s);
977
break;
978
case DEREF:
979
{
980
char *buffer = NULL,
981
*tmp;
982
983
if (top(cx) == MAGIC_TOKEN)
984
break; /* Dont do anything */
985
986
s = pops(cx);
987
tmp = expand_alias(s, cx->args, cx->args_flag, NULL);
988
alias_special_char(&buffer, tmp, cx->args,
989
NULL, cx->args_flag);
990
if (buffer == NULL)
991
buffer = m_strdup(empty_string);
992
*cx->args_flag = 1;
993
pushs(cx, buffer);
994
995
new_free(&buffer);
996
new_free(&tmp);
997
break;
998
}
999
1000
/* (pre|post)(in|de)crement operators. */
1001
case PREPLUS: AUTO_UNARY(b + 1, b + 1)
1002
case PREMINUS: AUTO_UNARY(b - 1, b - 1)
1003
case POSTPLUS: AUTO_UNARY(b + 1, b)
1004
case POSTMINUS: AUTO_UNARY(b - 1, b)
1005
1006
/* Simple binary operators */
1007
case AND: BINARY(a & b)
1008
case XOR: BINARY(a ^ b)
1009
case OR: BINARY(a | b)
1010
case PLUS: BINARY(a + b)
1011
case MINUS: BINARY(a - b)
1012
case MUL: BINARY(a * b)
1013
case POWER: BINARY(pow(a, b))
1014
case SHLEFT: BINARY(a << b)
1015
case SHRIGHT: BINARY(a >> b)
1016
case DIV: BINARY_NOZERO(a / b)
1017
case MOD: BINARY_NOZERO(a % b)
1018
case DAND: BINARY_BOOLEAN((long)(c && d))
1019
case DOR: BINARY_BOOLEAN((long)(c || d))
1020
case DXOR: BINARY_BOOLEAN((long)((c && !d) || (!c && d)))
1021
case STRCAT:
1022
pop2s(cx, &s, &t);
1023
if (x_debug & DEBUG_NEW_MATH_DEBUG)
1024
debugyell("O: (%s) ## (%s) -> %s%s", s, t, s, t);
1025
malloc_strcat(&s, t);
1026
pushs(cx, s);
1027
new_free(&s);
1028
new_free(&t);
1029
break;
1030
1031
/* Assignment operators */
1032
case PLUSEQ: IMPLIED(a + b)
1033
case MINUSEQ: IMPLIED(a - b)
1034
case MULEQ: IMPLIED(a * b)
1035
case POWEREQ: IMPLIED((long)pow(a, b))
1036
case DIVEQ: IMPLIED_NOZERO(a / b)
1037
case MODEQ: IMPLIED_NOZERO(a % b)
1038
case ANDEQ: IMPLIED(a & b)
1039
case XOREQ: IMPLIED(a ^ b)
1040
case OREQ: IMPLIED(a | b)
1041
case SHLEFTEQ: IMPLIED(a << b)
1042
case SHRIGHTEQ: IMPLIED(a >> b)
1043
case DANDEQ: IMPLIED((long)(c && d))
1044
case DOREQ: IMPLIED((long)(c || d))
1045
case DXOREQ: IMPLIED((long)((c && !d) || (!c && d)))
1046
case STRCATEQ:
1047
pop2s_a(cx, &s, &t, &v);
1048
if (x_debug & DEBUG_NEW_MATH_DEBUG)
1049
debugyell("O: %s = (%s ## %s) -> %s%s",
1050
get_token(cx, v), s, t, s, t);
1051
malloc_strcat(&s, t);
1052
pusht(cx, setsvar(cx, v, s));
1053
new_free(&s);
1054
new_free(&t);
1055
break;
1056
case STRPREEQ:
1057
pop2s_a(cx, &s, &t, &v);
1058
if (x_debug & DEBUG_NEW_MATH_DEBUG)
1059
debugyell("O: %s = (%s ## %s) -> %s%s",
1060
get_token(cx, v), t, s, t, s);
1061
malloc_strcat(&t, s);
1062
pusht(cx, setsvar(cx, v, t));
1063
new_free(&s);
1064
new_free(&t);
1065
break;
1066
case EQ:
1067
pop2(cx, &v, &w);
1068
if (x_debug & DEBUG_NEW_MATH_DEBUG)
1069
debugyell("O: %s = (%s)",
1070
get_token(cx, v), get_token(cx, w));
1071
pusht(cx, setvar(cx, v, w));
1072
break;
1073
case SWAP:
1074
{
1075
char *vval, *wval;
1076
1077
pop2(cx, &v, &w);
1078
if (x_debug & DEBUG_NEW_MATH_DEBUG)
1079
debugyell("O: %s <=> %s",
1080
get_token(cx, v), get_token(cx, w));
1081
vval = getsval(cx, v); /* lhs variable */
1082
wval = getsval(cx, w); /* rhs variable */
1083
setsvar(cx, w, vval);
1084
pusht(cx, setsvar(cx, v, wval));
1085
new_free(&vval);
1086
new_free(&wval);
1087
break;
1088
}
1089
/* Comparison operators */
1090
case DEQ:
1091
pop2s(cx, &s, &t);
1092
if (x_debug & DEBUG_NEW_MATH_DEBUG)
1093
debugyell("O: %s == %s -> %d", s, t,
1094
!!my_stricmp(s, t));
1095
if (my_stricmp(s, t) == 0) pushn(cx, 1);
1096
else pushn(cx, 0);
1097
new_free(&s);
1098
new_free(&t);
1099
break;
1100
case NEQ:
1101
pop2s(cx, &s, &t);
1102
if (x_debug & DEBUG_NEW_MATH_DEBUG)
1103
debugyell("O: %s != %s -> %d", s, t,
1104
!my_stricmp(s, t));
1105
if (my_stricmp(s, t) != 0) pushn(cx, 1);
1106
else pushn(cx, 0);
1107
new_free(&s);
1108
new_free(&t);
1109
break;
1110
case MATCH:
1111
pop2s(cx, &s, &t);
1112
a = !!wild_match(t, s);
1113
if (x_debug & DEBUG_NEW_MATH_DEBUG)
1114
debugyell("O: %s =~ %s -> %ld", s, t, a);
1115
pushn(cx, a);
1116
new_free(&s);
1117
new_free(&t);
1118
break;
1119
case NOMATCH:
1120
pop2s(cx, &s, &t);
1121
a = !wild_match(t, s);
1122
if (x_debug & DEBUG_NEW_MATH_DEBUG)
1123
debugyell("O: %s !~ %s -> %ld", s, t, a);
1124
pushn(cx, a);
1125
new_free(&s);
1126
new_free(&t);
1127
break;
1128
1129
1130
case LES: COMPARE(a < b, my_stricmp(s, t) < 0)
1131
case LEQ: COMPARE(a <= b, my_stricmp(s, t) <= 0)
1132
case GRE: COMPARE(a > b, my_stricmp(s, t) > 0)
1133
case GEQ: COMPARE(a >= b, my_stricmp(s, t) >= 0)
1134
1135
1136
/* Miscelaneous operators */
1137
case QUEST:
1138
pop3(cx, &a, &v, &w);
1139
if (x_debug & DEBUG_NEW_MATH_DEBUG)
1140
debugyell("O: %ld ? %s : %s -> %s", a,
1141
get_token(cx, v), get_token(cx, w),
1142
(a) ? get_token(cx, v) :
1143
get_token(cx, w));
1144
pusht(cx, (a) ? v : w);
1145
break;
1146
1147
case COLON:
1148
break;
1149
1150
case COMMA:
1151
v = pop(cx);
1152
w = pop(cx);
1153
if (x_debug & DEBUG_NEW_MATH_DEBUG)
1154
debugyell("O: %s , %s -> %s",
1155
get_token(cx, w),
1156
get_token(cx, v),
1157
get_token(cx, v));
1158
pusht(cx, v);
1159
break;
1160
1161
default:
1162
error("Unknown operator or out of operators");
1163
return;
1164
}
1165
}
1166
1167
1168
/***************************************************************************/
1169
/*
1170
* THE LEXER
1171
*/
1172
static int dummy = 1;
1173
1174
int lexerr (expr_info *c, char *format, ...)
1175
{
1176
char buffer[BIG_BUFFER_SIZE + 1];
1177
va_list a;
1178
1179
va_start(a, format);
1180
vsnprintf(buffer, BIG_BUFFER_SIZE, format, a);
1181
va_end(a);
1182
1183
error("%s", buffer);
1184
c->errflag = 1;
1185
return EOI;
1186
}
1187
1188
/*
1189
* 'operand' is state information that tells us about what the next token
1190
* is expected to be. When a binary operator is lexed, then the next token
1191
* is expected to be either a unary operator or an operand. So in this
1192
* case 'operand' is set to 1. When an operand is lexed, then the next token
1193
* is expected to be a binary operator, so 'operand' is set to 0.
1194
*/
1195
__inline int check_implied_arg (expr_info *c)
1196
{
1197
if (c->operand == 2)
1198
{
1199
pusht(c, MAGIC_TOKEN); /* XXXX Bleh */
1200
c->operand = 0;
1201
*c->args_flag = 1;
1202
return 0;
1203
}
1204
1205
return c->operand;
1206
}
1207
1208
__inline TOKEN operator (expr_info *c, char *x, int y, TOKEN z)
1209
{
1210
check_implied_arg(c);
1211
if (c->operand)
1212
return lexerr(c, "A binary operator (%s) was found "
1213
"where an operand was expected", x);
1214
c->ptr += y;
1215
c->operand = 1;
1216
return z;
1217
}
1218
1219
__inline TOKEN unary (expr_info *c, char *x, int y, TOKEN z)
1220
{
1221
if (!c->operand)
1222
return lexerr(c, "An operator (%s) was found where "
1223
"an operand was expected", x);
1224
c->ptr += y;
1225
c->operand = dummy;
1226
return z;
1227
}
1228
1229
1230
/*
1231
* This finds and extracts the next token in the expression
1232
*/
1233
static int zzlex (expr_info *c)
1234
{
1235
char *start = c->ptr;
1236
1237
#define OPERATOR(x, y, z) return operator(c, x, y, z);
1238
#define UNARY(x, y, z) return unary(c, x, y, z);
1239
1240
dummy = 1;
1241
if (x_debug & DEBUG_NEW_MATH_DEBUG)
1242
debugyell("Parsing next token from: [%s]", c->ptr);
1243
1244
for (;;)
1245
{
1246
switch (*(c->ptr++))
1247
{
1248
case '(':
1249
c->operand = 1;
1250
return M_INPAR;
1251
case ')':
1252
/*
1253
* If we get a close paren and the lexer is expecting
1254
* an operand, then obviously thats a syntax error.
1255
* But we gently just insert the empty value as the
1256
* rhs for the last operand and hope it all works out.
1257
*/
1258
if (check_implied_arg(c))
1259
pusht(c, 0);
1260
c->operand = 0;
1261
return M_OUTPAR;
1262
1263
case '+':
1264
{
1265
/*
1266
* Note: In general, any operand that depends on
1267
* whether it is a unary or binary operator based
1268
* upon the context is required to call the func
1269
* 'check_implied_arg' to solidify the context.
1270
* That is because some operators are ambiguous,
1271
* And if you see (# + 4), it can only be determined
1272
* on the fly how to lex that.
1273
*/
1274
check_implied_arg(c);
1275
if (*c->ptr == '+' && (c->operand || !isalnum((unsigned char)*c->ptr)))
1276
{
1277
c->ptr++;
1278
return c->operand ? PREPLUS : POSTPLUS;
1279
}
1280
else if (*c->ptr == '=')
1281
OPERATOR("+=", 1, PLUSEQ)
1282
else if (c->operand)
1283
UNARY("+", 0, UPLUS)
1284
else
1285
OPERATOR("+", 0, PLUS)
1286
}
1287
case '-':
1288
{
1289
check_implied_arg(c);
1290
if (*c->ptr == '-' && (c->operand || !isalnum((unsigned char)*c->ptr)))
1291
{
1292
c->ptr++;
1293
return (c->operand) ? PREMINUS : POSTMINUS;
1294
}
1295
else if (*c->ptr == '=')
1296
OPERATOR("-=", 1, MINUSEQ)
1297
else if (c->operand)
1298
UNARY("-", 0, UMINUS)
1299
else
1300
OPERATOR("-", 0, MINUS)
1301
}
1302
case '*':
1303
{
1304
if (*c->ptr == '*')
1305
{
1306
c->ptr++;
1307
if (*c->ptr == '=')
1308
OPERATOR("**=", 1, POWEREQ)
1309
else
1310
OPERATOR("**", 0, POWER)
1311
}
1312
else if (*c->ptr == '=')
1313
OPERATOR("*=", 1, MULEQ)
1314
else if (c->operand)
1315
{
1316
dummy = 2;
1317
UNARY("*", 0, DEREF)
1318
}
1319
else
1320
OPERATOR("*", 0, MUL)
1321
}
1322
case '/':
1323
{
1324
if (*c->ptr == '=')
1325
OPERATOR("/=", 1, DIVEQ)
1326
else
1327
OPERATOR("/", 0, DIV)
1328
}
1329
case '%':
1330
{
1331
if (*c->ptr == '=')
1332
OPERATOR("%=", 1, MODEQ)
1333
else
1334
OPERATOR("%", 0, MOD)
1335
}
1336
1337
case '!':
1338
{
1339
if (*c->ptr == '=')
1340
OPERATOR("!=", 1, NEQ)
1341
else if (*c->ptr == '~')
1342
OPERATOR("!~", 1, NOMATCH)
1343
else
1344
UNARY("!", 0, NOT)
1345
}
1346
case '~':
1347
UNARY("~", 0, COMP)
1348
1349
case '&':
1350
{
1351
if (*c->ptr == '&')
1352
{
1353
c->ptr++;
1354
if (*c->ptr == '=')
1355
OPERATOR("&&=", 1, DANDEQ)
1356
else
1357
OPERATOR("&&", 0, DAND)
1358
}
1359
else if (*c->ptr == '=')
1360
OPERATOR("&=", 1, ANDEQ)
1361
else
1362
OPERATOR("&", 0, AND)
1363
}
1364
case '|':
1365
{
1366
if (*c->ptr == '|')
1367
{
1368
c->ptr++;
1369
if (*c->ptr == '=')
1370
OPERATOR("||=", 1, DOREQ)
1371
else
1372
OPERATOR("||", 0, DOR)
1373
}
1374
else if (*c->ptr == '=')
1375
OPERATOR("|=", 1, OREQ)
1376
else
1377
OPERATOR("|", 0, OR)
1378
}
1379
case '^':
1380
{
1381
if (*c->ptr == '^')
1382
{
1383
c->ptr++;
1384
if (*c->ptr == '=')
1385
OPERATOR("^^=", 1, DXOREQ)
1386
else
1387
OPERATOR("^^", 0, DXOR)
1388
}
1389
else if (*c->ptr == '=')
1390
OPERATOR("^=", 1, XOREQ)
1391
else
1392
OPERATOR("^", 0, XOR)
1393
}
1394
case '#':
1395
{
1396
check_implied_arg(c);
1397
if (*c->ptr == '#')
1398
{
1399
c->ptr++;
1400
if (*c->ptr == '=')
1401
OPERATOR("##=", 1, STRCATEQ)
1402
else
1403
OPERATOR("##", 0, STRCAT)
1404
}
1405
else if (*c->ptr == '=')
1406
OPERATOR("#=", 1, STRCATEQ)
1407
else if (*c->ptr == '~')
1408
OPERATOR("#~", 1, STRPREEQ)
1409
else if (c->operand)
1410
{
1411
dummy = 2;
1412
UNARY("#", 0, WORDC)
1413
}
1414
else
1415
OPERATOR("#", 0, STRCAT)
1416
}
1417
1418
case '@':
1419
dummy = 2;
1420
UNARY("@", 0, STRLEN)
1421
1422
case '<':
1423
{
1424
if (*c->ptr == '<')
1425
{
1426
c->ptr++;
1427
if (*c->ptr == '=')
1428
OPERATOR("<<=", 1, SHLEFTEQ)
1429
else
1430
OPERATOR("<<", 0, SHLEFT)
1431
}
1432
else if (*c->ptr == '=')
1433
{
1434
c->ptr++;
1435
if (*c->ptr == '>')
1436
OPERATOR("<=>", 1, SWAP)
1437
else
1438
OPERATOR("<=", 0, LEQ)
1439
}
1440
else
1441
OPERATOR("<", 0, LES)
1442
}
1443
case '>':
1444
{
1445
if (*c->ptr == '>')
1446
{
1447
c->ptr++;
1448
if (*c->ptr == '=')
1449
OPERATOR(">>=", 1, SHRIGHTEQ)
1450
else
1451
OPERATOR(">>", 0, SHRIGHT)
1452
}
1453
else if (*c->ptr == '=')
1454
OPERATOR(">=", 1, GEQ)
1455
else
1456
OPERATOR(">", 0, GRE)
1457
}
1458
1459
case '=':
1460
if (*c->ptr == '=')
1461
OPERATOR("==", 1, DEQ)
1462
else if (*c->ptr == '~')
1463
OPERATOR("=~", 1, MATCH)
1464
else
1465
OPERATOR("=", 0, EQ)
1466
1467
case '?':
1468
c->operand = 1;
1469
return QUEST;
1470
case ':':
1471
/*
1472
* I dont want to hear anything from you anti-goto
1473
* bigots out there. ;-) If you can't figure out
1474
* what this does, you ought to give up programming.
1475
* And a big old :p to everyone who insisted that
1476
* i support this horrid hack.
1477
*/
1478
if (c->operand)
1479
goto handle_expando;
1480
1481
c->operand = 1;
1482
return COLON;
1483
1484
case ',':
1485
/* Same song, second verse. */
1486
if (c->operand)
1487
goto handle_expando;
1488
1489
c->operand = 1;
1490
return COMMA;
1491
1492
case '\0':
1493
check_implied_arg(c);
1494
c->operand = 1;
1495
c->ptr--;
1496
return EOI;
1497
1498
case '[':
1499
{
1500
char *p = c->ptr - 1;
1501
char oc = 0;
1502
1503
if (!c->operand)
1504
return lexerr(c, "Misplaced [ token");
1505
1506
if ((c->ptr = MatchingBracket(p + 1, '[', ']')))
1507
{
1508
oc = *c->ptr;
1509
*c->ptr = 0;
1510
}
1511
else
1512
c->ptr = empty_string;
1513
1514
c->last_token = tokenize(c, p);
1515
if (oc)
1516
*c->ptr++ = oc;
1517
c->operand = 0;
1518
return ID;
1519
}
1520
case ' ':
1521
case '\t':
1522
case '\n':
1523
start++;
1524
break;
1525
1526
/*
1527
* Handle literal numbers
1528
*/
1529
case '0': case '1': case '2': case '3': case '4':
1530
case '5': case '6': case '7': case '8': case '9':
1531
{
1532
char *end;
1533
char endc;
1534
1535
c->operand = 0;
1536
c->ptr--;
1537
strtod(c->ptr, &end);
1538
endc = *end;
1539
*end = 0;
1540
c->last_token = tokenize(c, c->ptr);
1541
*end = endc;
1542
c->ptr = end;
1543
return ID;
1544
}
1545
1546
/*
1547
* Handle those weirdo $-values
1548
*/
1549
case '$':
1550
continue;
1551
1552
/*
1553
* Handle generic lvalue operands
1554
*/
1555
default:
1556
handle_expando:
1557
{
1558
char *end;
1559
char endc;
1560
1561
c->operand = 0;
1562
c->ptr--;
1563
if ((end = after_expando_special(c)))
1564
{
1565
endc = *end;
1566
*end = 0;
1567
c->last_token = tokenize(c, start);
1568
*end = endc;
1569
c->ptr = end;
1570
}
1571
else
1572
{
1573
c->last_token = 0; /* Empty token */
1574
c->ptr = empty_string;
1575
}
1576
1577
if (x_debug & DEBUG_NEW_MATH_DEBUG)
1578
debugyell("After token: [%s]", c->ptr);
1579
return ID;
1580
}
1581
}
1582
}
1583
}
1584
1585
/*
1586
* mathparse -- this is the state machine that actually parses the
1587
* expression. The parsing is done through a shift-reduce mechanism,
1588
* and all the precedence levels lower than 'pc' are evaluated.
1589
*/
1590
static void mathparse (expr_info *c, int pc)
1591
{
1592
int otok,
1593
onoeval;
1594
1595
/*
1596
* Drop out of parsing if an error has occured
1597
*/
1598
if (c->errflag)
1599
return;
1600
1601
/*
1602
* Get the next token in the expression
1603
*/
1604
c->mtok = zzlex(c);
1605
1606
/*
1607
* For as long as the next operator indicates a shift operation...
1608
*/
1609
while (prec[c->mtok] <= pc)
1610
{
1611
/* Drop out if an error has occured */
1612
if (c->errflag)
1613
return;
1614
1615
/*
1616
* Figure out what to do with this token that needs
1617
* to be shifted.
1618
*/
1619
switch (c->mtok)
1620
{
1621
case ID:
1622
if (x_debug & DEBUG_NEW_MATH_DEBUG)
1623
debugyell("Parsed identifier token [%s]", get_token(c, c->last_token));
1624
if (c->noeval)
1625
pusht(c, 0);
1626
else
1627
pusht(c, c->last_token);
1628
break;
1629
1630
/*
1631
* An open-parenthesis indicates that we should
1632
* recursively evaluate the inside of the paren-set.
1633
*/
1634
case M_INPAR:
1635
{
1636
if (x_debug & DEBUG_NEW_MATH_DEBUG)
1637
debugyell("Parsed open paren");
1638
mathparse(c, TOPPREC);
1639
1640
/*
1641
* Of course if the expression ends without
1642
* a matching rparen, then we whine about it.
1643
*/
1644
if (c->mtok != M_OUTPAR)
1645
{
1646
if (!c->errflag)
1647
error("')' expected");
1648
return;
1649
}
1650
break;
1651
}
1652
1653
/*
1654
* A question mark requires that we check for short
1655
* circuiting. We check the lhs, and if it is true,
1656
* then we evaluate the lhs of the colon. If it is
1657
* false then we just parse the lhs of the colon and
1658
* evaluate the rhs of the colon.
1659
*/
1660
case QUEST:
1661
{
1662
long u = popb(c);
1663
1664
pushn(c, u);
1665
if (!u)
1666
c->noeval++;
1667
mathparse(c, prec[QUEST] - 1);
1668
if (!u)
1669
c->noeval--;
1670
else
1671
c->noeval++;
1672
mathparse(c, prec[QUEST]);
1673
if (u)
1674
c->noeval--;
1675
op(c, QUEST);
1676
1677
continue;
1678
}
1679
1680
/*
1681
* All other operators handle normally
1682
*/
1683
default:
1684
{
1685
/* Save state */
1686
otok = c->mtok;
1687
onoeval = c->noeval;
1688
1689
/*
1690
* Check for short circuiting.
1691
*/
1692
if (assoc[otok] == BOOL)
1693
{
1694
if (x_debug & DEBUG_NEW_MATH_DEBUG)
1695
debugyell("Parsed short circuit operator");
1696
switch (otok)
1697
{
1698
case DAND:
1699
case DANDEQ:
1700
{
1701
long u = popb(c);
1702
pushn(c, u);
1703
if (!u)
1704
c->noeval++;
1705
break;
1706
}
1707
case DOR:
1708
case DOREQ:
1709
{
1710
long u = popb(c);
1711
pushn(c, u);
1712
if (u)
1713
c->noeval++;
1714
break;
1715
}
1716
}
1717
}
1718
1719
if (x_debug & DEBUG_NEW_MATH_DEBUG)
1720
debugyell("Parsed operator of type [%d]", otok);
1721
1722
/*
1723
* Parse the right hand side through
1724
* recursion if we're doing things R->L.
1725
*/
1726
mathparse(c, prec[otok] - (assoc[otok] != RL));
1727
1728
/*
1729
* Then reduce this operation.
1730
*/
1731
c->noeval = onoeval;
1732
op(c, otok);
1733
continue;
1734
}
1735
}
1736
1737
/*
1738
* Grab the next token
1739
*/
1740
c->mtok = zzlex(c);
1741
}
1742
}
1743
1744
/*
1745
* This is the new math parser. It sets up an execution context, which
1746
* contains sundry information like all the extracted tokens, intermediate
1747
* tokens, shifted tokens, and the like. The expression context is passed
1748
* around from function to function, each function is totaly independant
1749
* of state information stored in global variables. Therefore, this math
1750
* parser is re-entrant safe.
1751
*/
1752
static char * matheval (char *s, const char *args, int *args_flag)
1753
{
1754
expr_info context;
1755
char * ret;
1756
1757
/* Sanity check */
1758
if (!s || !*s)
1759
return m_strdup(empty_string);
1760
1761
/* Create new state */
1762
setup_expr_info(&context);
1763
context.ptr = s;
1764
context.args = args;
1765
context.args_flag = args_flag;
1766
1767
/* Actually do the parsing */
1768
mathparse(&context, TOPPREC);
1769
1770
/* Check for error */
1771
if (context.errflag)
1772
{
1773
ret = m_strdup(empty_string);
1774
goto cleanup;
1775
}
1776
1777
/* Check for leftover operands */
1778
if (context.sp)
1779
error("The expression has too many operands");
1780
1781
if (x_debug & DEBUG_NEW_MATH_DEBUG)
1782
{
1783
int i;
1784
debugyell("Terms left: %d", context.sp);
1785
for (i = 0; i <= context.sp; i++)
1786
debugyell("Term [%d]: [%s]", i,
1787
get_token(&context, context.stack[i]));
1788
}
1789
1790
/* Get the return value */
1791
ret = getsval(&context, pop(&context));
1792
1793
cleanup:
1794
/* Clean up and restore order */
1795
destroy_expr_info(&context);
1796
1797
if (internal_debug & DEBUG_EXPANSIONS && !in_debug_yell)
1798
debugyell("Returning [%s]", ret);
1799
1800
/* Return the result */
1801
return ret;
1802
}
1803
1804
1805
/*
1806
* after_expando_special: This is a special version of after_expando that
1807
* can handle parsing out lvalues in expressions. Due to the eclectic nature
1808
* of lvalues in expressions, this is quite a bit different than the normal
1809
* after_expando, requiring a different function. Ugh.
1810
*
1811
* This replaces some much more complicated logic strewn
1812
* here and there that attempted to figure out just how long an expando
1813
* name was supposed to be. Well, now this changes that. This will slurp
1814
* up everything in 'start' that could possibly be put after a $ that could
1815
* result in a syntactically valid expando. All you need to do is tell it
1816
* if the expando is an rvalue or an lvalue (it *does* make a difference)
1817
*/
1818
static char * after_expando_special (expr_info *c)
1819
{
1820
char *start;
1821
char *rest;
1822
int call;
1823
1824
if (!(start = c->ptr))
1825
return c->ptr;
1826
1827
for (;;)
1828
{
1829
rest = after_expando(start, 0, &call);
1830
if (*rest != '$')
1831
break;
1832
start = rest + 1;
1833
}
1834
1835
/*
1836
* All done!
1837
*/
1838
return rest;
1839
}
1840
1841
1842