1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
26 * Evaluate an #if expression.
29 static char* opname
[] = { /* For debug and error messages */
30 "end of expression", "val", "id",
31 "+", "-", "*", "/", "%",
32 "<<", ">>", "&", "|", "^",
33 "==", "!=", "<", "<=", ">=", ">",
34 "&&", "||", "?", ":", ",",
35 "unary +", "unary -", "~", "!", "(", ")", "(none)",
39 * opdope[] has the operator precedence:
41 * 7 Unused (so the value is always positive)
42 * 6-2 Precedence (000x .. 017x)
43 * 1-0 Binary op. flags:
44 * 01 The binop flag should be set/cleared when this op is seen.
45 * 10 The new value of the binop flag.
46 * Note: Expected, New binop
47 * constant 0 1 Binop, end, or ) should follow constants
48 * End of line 1 0 End may not be preceded by an operator
49 * binary 1 0 Binary op follows a value, value follows.
50 * unary 0 0 Unary op doesn't follow a value, value follows
51 * ( 0 0 Doesn't follow value, value or unop follows
52 * ) 1 1 Follows value. Op follows.
55 static char opdope
[OP_MAX
] = {
56 0001, /* End of expression */
58 0000, /* Letter (identifier) */
59 0141, 0141, 0151, 0151, 0151, /* ADD, SUB, MUL, DIV, MOD */
60 0131, 0131, 0101, 0071, 0071, /* ASL, ASR, AND, OR, XOR */
61 0111, 0111, 0121, 0121, 0121, 0121, /* EQ, NE, LT, LE, GE, GT */
62 0061, 0051, 0041, 0041, 0031, /* ANA, ORO, QUE, COL, CMA */
66 0160, 0160, 0160, 0160, /* NEG, PLU, COM, NOT */
67 0170, 0013, 0023, /* LPA, RPA, END */
70 * OP_QUE and OP_RPA have alternate precedences:
72 #define OP_RPA_PREC 0013
73 #define OP_QUE_PREC 0034
76 * S_ANDOR and S_QUEST signal "short-circuit" boolean evaluation, so that
77 * #if FOO != 0 && 10 / FOO ...
78 * doesn't generate an error message. They are stored in optab.skip.
85 char op
; /* Operator */
86 char prec
; /* Its precedence */
87 char skip
; /* Short-circuit: TRUE to skip */
89 static int evalue
; /* Current value from evallex() */
91 #define isbinary(op) (op >= FIRST_BINOP && op <= LAST_BINOP)
94 * The following definitions are used to specify basic variable sizes.
98 #define S_CHAR (sizeof (char))
101 #define S_SINT (sizeof (short int))
104 #define S_INT (sizeof (int))
107 #define S_LINT (sizeof (long int))
110 #define S_FLOAT (sizeof (float))
113 #define S_DOUBLE (sizeof (double))
116 #define S_PCHAR (sizeof (char *))
119 #define S_PSINT (sizeof (short int *))
122 #define S_PINT (sizeof (int *))
125 #define S_PLINT (sizeof (long int *))
128 #define S_PFLOAT (sizeof (float *))
131 #define S_PDOUBLE (sizeof (double *))
134 #define S_PFPTR (sizeof (int (*)(void)))
139 short type
; /* This is the bit if */
140 char *name
; /* this is the token word */
143 static TYPES basic_types
[] = {
146 { T_FLOAT
, "float", },
147 { T_DOUBLE
, "double", },
148 { T_SHORT
, "short", },
150 { T_SIGNED
, "signed", },
151 { T_UNSIGNED
, "unsigned", },
152 { 0, NULL
, }, /* Signal end */
156 * Test_table[] is used to test for illegal combinations.
158 static short test_table
[] = {
159 T_FLOAT
| T_DOUBLE
| T_LONG
| T_SHORT
,
160 T_FLOAT
| T_DOUBLE
| T_CHAR
| T_INT
,
161 T_FLOAT
| T_DOUBLE
| T_SIGNED
| T_UNSIGNED
,
162 T_LONG
| T_SHORT
| T_CHAR
,
167 * The order of this table is important -- it is also referenced by
168 * the command line processor to allow run-time overriding of the
169 * built-in size values. The order must not be changed:
170 * char, short, int, long, float, double (func pointer)
172 SIZES size_table
[] = {
173 { T_CHAR
, S_CHAR
, S_PCHAR
}, /* char */
174 { T_SHORT
, S_SINT
, S_PSINT
}, /* short int */
175 { T_INT
, S_INT
, S_PINT
}, /* int */
176 { T_LONG
, S_LINT
, S_PLINT
}, /* long */
177 { T_FLOAT
, S_FLOAT
, S_PFLOAT
}, /* float */
178 { T_DOUBLE
, S_DOUBLE
, S_PDOUBLE
}, /* double */
179 { T_FPTR
, 0, S_PFPTR
}, /* int (*()) */
180 { 0, 0, 0 }, /* End of table */
184 * Evaluate an expression. Straight-forward operator precedence.
185 * This is called from control() on encountering an #if statement.
186 * It calls the following routines:
187 * evallex Lexical analyser -- returns the type and value of
188 * the next input token.
189 * evaleval Evaluate the current operator, given the values on
190 * the value stack. Returns a pointer to the (new)
192 * For compatibility with older cpp's, this return returns 1 (TRUE)
193 * if a syntax error is detected.
197 int op
; /* Current operator */
198 int* valp
; /* -> value vector */
199 OPTAB
* opp
; /* Operator stack */
200 int prec
; /* Op precedence */
201 int binop
; /* Set if binary op. needed */
202 int op1
; /* Operand from stack */
203 int skip
; /* For short-circuit testing */
204 int value
[NEXP
]; /* Value stack */
205 OPTAB opstack
[NEXP
]; /* Operand stack */
209 opp
->op
= OP_END
; /* Mark bottom of stack */
210 opp
->prec
= opdope
[OP_END
]; /* And its precedence */
211 opp
->skip
= 0; /* Not skipping now */
216 fprintf( pCppOut
, "In #if at again: skip = %d, binop = %d, line is: %s",
217 opp
->skip
, binop
, infile
->bptr
);
219 if ((op
= evallex(opp
->skip
)) == OP_SUB
&& binop
== 0)
220 op
= OP_NEG
; /* Unary minus */
221 else if (op
== OP_ADD
&& binop
== 0)
222 op
= OP_PLU
; /* Unary plus */
223 else if (op
== OP_FAIL
)
224 return 1; /* Error in evallex */
226 fprintf( pCppOut
, "op = %s, opdope = %03o, binop = %d, skip = %d\n",
227 opname
[op
], opdope
[op
], binop
, opp
->skip
);
229 if (op
== DIG
) /* Value? */
233 cerror("misplaced constant in #if", NULLST
);
236 else if (valp
>= &value
[NEXP
-1])
238 cerror("#if value stack overflow", NULLST
);
244 fprintf( pCppOut
, "pushing %d onto value stack[%d]\n",
245 evalue
, valp
- value
);
252 else if (op
> OP_END
)
254 cerror("Illegal #if line", NULLST
);
258 if (binop
!= (prec
& 1))
260 cerror("Operator %s in incorrect context", opname
[op
]);
263 binop
= (prec
& 2) >> 1;
267 fprintf( pCppOut
, "op %s, prec %d., stacked op %s, prec %d, skip %d\n",
268 opname
[op
], prec
, opname
[opp
->op
], opp
->prec
, opp
->skip
);
270 if (prec
> opp
->prec
)
274 else if (op
== OP_QUE
)
276 op1
= opp
->skip
; /* Save skip for test */
278 * Push operator onto op. stack.
281 if (opp
>= &opstack
[NEXP
])
283 cerror("expression stack overflow at op \"%s\"",
288 opp
->prec
= (char)prec
;
290 * Do the short-circuit stuff here. Short-circuiting
291 * stops automagically when operators are evaluated.
293 if ((op
== OP_ANA
&& valp
[-1] == 0) ||
294 (op
== OP_ORO
&& valp
[-1] != 0))
296 opp
->skip
= S_ANDOR
; /* And/or skip starts */
298 else if (op
== OP_QUE
) /* Start of ?: operator */
299 opp
->skip
= (char)((op1
& S_ANDOR
) | ((valp
[-1] == 0) ? S_QUEST
: 0));
300 else if (op
== OP_COL
) /* : inverts S_QUEST */
302 opp
->skip
= (char)((op1
& S_ANDOR
)
303 | (((op1
& S_QUEST
) != 0) ? 0 : S_QUEST
));
305 else /* Other ops leave */
307 opp
->skip
= (char)op1
; /* skipping unchanged. */
310 fprintf( pCppOut
, "stacking %s, valp[-1] == %d at %s",
311 opname
[op
], valp
[-1], infile
->bptr
);
312 dumpstack(opstack
, opp
, value
, valp
);
317 * Pop operator from op. stack and evaluate it.
318 * End of stack and '(' are specials.
320 skip
= opp
->skip
; /* Remember skip value */
321 switch ((op1
= opp
->op
)) /* Look at stacked op */
323 case OP_END
: /* Stack end marker */
325 return valp
[-1]; /* Finished ok. */
326 goto again
; /* Read another op. */
328 case OP_LPA
: /* ( on stack */
329 if (op
!= OP_RPA
) /* Matches ) on input */
331 cerror("unbalanced paren's, op is \"%s\"", opname
[op
]);
334 opp
--; /* Unstack it */
335 /* goto again; -- Fall through */
338 goto again
; /* Evaluate true expr. */
340 case OP_COL
: /* : on stack. */
341 opp
--; /* Unstack : */
342 if (opp
->op
!= OP_QUE
) /* Matches ? on stack? */
344 cerror("Misplaced '?' or ':', previous operator is %s",
345 opname
[(int)opp
->op
]);
351 default: /* Others: */
352 opp
--; /* Unstack the operator */
354 fprintf( pCppOut
, "Stack before evaluation of %s\n", opname
[op1
]);
355 dumpstack(opstack
, opp
, value
, valp
);
357 valp
= evaleval(valp
, op1
, skip
);
359 fprintf( pCppOut
, "Stack after evaluation\n");
360 dumpstack(opstack
, opp
, value
, valp
);
362 } /* op1 switch end */
363 } /* Stack unwind loop */
367 * Return next eval operator or value. Called from eval(). It
368 * calls a special-purpose routines for 'char' strings and
370 * evalchar called to evaluate 'x'
371 * evalnum called to evaluate numbers.
373 FILE_LOCAL
int evallex(int skip
)
380 do /* Collect the token */
383 if ((c
= macroid(c
)) == EOF_CHAR
|| c
== '\n')
386 return OP_EOE
; /* End of expression */
389 while ((t
= type
[c
]) == LET
&& catenate());
390 if (t
== INV
) /* Total nonsense */
394 if (isascii(c
) && isprint(c
))
395 cierror("illegal character '%c' in #if", c
);
397 cierror("illegal character (%d decimal) in #if", c
);
401 else if (t
== QUO
) /* ' or " */
403 if (c
== '\'') /* Character constant */
405 evalue
= evalchar(skip
); /* Somewhat messy */
407 fprintf( pCppOut
, "evalchar returns %d.\n", evalue
);
409 return DIG
; /* Return a value */
411 cerror("Can't use a string in an #if", NULLST
);
414 else if (t
== LET
) /* ID must be a macro */
416 if (streq(token
, "defined")) /* Or defined name */
419 if (c
== '(') /* Allow defined(name) */
423 evalue
= (lookid(c
) != NULL
);
424 if (c1
!= '(' || /* Need to balance */
425 skipws() == ')') /* Did we balance? */
427 return DIG
; /* Parsed ok */
430 cerror("Bad #if ... defined() syntax", NULLST
);
433 else if (streq(token
, "sizeof")) /* New sizeof hackery */
434 return dosizeof(); /* Gets own routine */
436 * The Draft ANSI C Standard says that an undefined symbol
437 * in an #if has the value zero. We are a bit pickier,
438 * warning except where the programmer was careful to write
439 * #if defined(foo) ? foo : 0
443 cwarn("undefined symbol \"%s\" in #if, 0 used", token
);
448 else if (t
== DIG
) /* Numbers are harder */
452 fprintf( pCppOut
, "evalnum returns %d.\n", evalue
);
455 else if (strchr("!=<>&|\\", c
) != NULL
)
458 * Process a possible multi-byte lexeme.
460 c1
= cget(); /* Peek at next char */
469 if (c1
!= '=') /* Can't say a=b in #if */
472 cerror("= not allowed in #if", NULLST
);
480 return ((c
== '<') ? OP_ASL
: OP_ASR
);
482 return ((c
== '<') ? OP_LE
: OP_GE
);
488 return ((c
== '|') ? OP_ORO
: OP_ANA
);
492 if (c1
== '\n') /* Multi-line if */
494 cerror("Unexpected \\ in #if", NULLST
);
503 * Process the sizeof (basic type) operation in an #if string.
504 * Sets evalue to the size and returns
506 * OP_FAIL bad parse or something.
508 FILE_LOCAL
int dosizeof()
516 if ((c
= skipws()) != '(')
519 * Scan off the tokens.
522 while (0 != (c
= skipws()))
524 if ((c
= macroid(c
)) == EOF_CHAR
|| c
== '\n')
525 goto nogood
; /* End of line is a bug */
526 else if (c
== '(') /* thing (*)() func ptr */
528 if (skipws() == '*' && skipws() == ')')
530 if (skipws() != '(') /* Let () be optional */
532 else if (skipws() != ')')
534 typecode
|= T_FPTR
; /* Function pointer */
536 else /* Junk is a bug */
539 else if (type
[c
] != LET
) /* Exit if not a type */
541 else if (!catenate()) /* Maybe combine tokens */
544 * Look for this unexpandable token in basic_types.
545 * The code accepts "int long" as well as "long int"
546 * which is a minor bug as bugs go (and one shared with
547 * a lot of C compilers).
549 for (tp
= basic_types
; tp
->name
!= NULLST
; tp
++)
551 if (streq(token
, tp
->name
))
554 if (tp
->name
== NULLST
)
556 cerror("#if sizeof, unknown type \"%s\"", token
);
559 typecode
|= tp
->type
; /* Or in the type bit */
563 * We are at the end of the type scan. Chew off '*' if necessary.
570 if (c
== ')') /* Last syntax check */
572 for (testp
= test_table
; *testp
!= 0; testp
++)
574 if (!bittest(typecode
& *testp
))
576 cerror("#if ... sizeof: illegal type combination", NULLST
);
581 * We assume that all function pointers are the same size:
582 * sizeof (int (*)()) == sizeof (float (*)())
583 * We assume that signed and unsigned don't change the size:
584 * sizeof (signed int) == (sizeof unsigned int)
586 if ((typecode
& T_FPTR
) != 0) /* Function pointer */
587 typecode
= T_FPTR
| T_PTR
;
588 else /* Var or var * datum */
590 typecode
&= ~(T_SIGNED
| T_UNSIGNED
);
591 if ((typecode
& (T_SHORT
| T_LONG
)) != 0)
594 if ((typecode
& ~T_PTR
) == 0)
596 cerror("#if sizeof() error, no type specified", NULLST
);
600 * Exactly one bit (and possibly T_PTR) may be set.
602 for (sizp
= size_table
; sizp
->bits
!= 0; sizp
++)
604 if ((typecode
& ~T_PTR
) == sizp
->bits
)
606 evalue
= ((typecode
& T_PTR
) != 0)
607 ? sizp
->psize
: sizp
->size
;
610 } /* We shouldn't fail */
611 cierror("#if ... sizeof: bug, unknown type code 0x%x", typecode
);
617 cerror("#if ... sizeof() syntax error", NULLST
);
622 * TRUE if value is zero or exactly one bit is set in value.
624 FILE_LOCAL
int bittest(int value
)
626 /* whoaa!! really worried about non 2's complement machines...
627 * but not at all about cross-compiling ?
629 #if (4096 & ~(-4096)) == 0
630 return ((value
& ~(-value
)) == 0);
633 * Do it the hard way (for non 2's complement machines)
635 return (value
== 0 || value
^ (value
- 1) == (value
* 2 - 1));
640 * Expand number for #if lexical analysis. Note: evalnum recognizes
641 * the unsigned suffix, but only returns a signed int value.
643 FILE_LOCAL
int evalnum(int c
)
651 else if ((c
= cget()) == 'x' || c
== 'X')
661 if (isascii(c
) && isupper(c1
))
667 if (c1
< 0 || c1
>= base
)
673 if (c
== 'u' || c
== 'U') /* Unsigned nonsense */
680 * Get a character constant
682 FILE_LOCAL
int evalchar(int skip
)
689 if ((c
= cget()) == '\\')
691 switch ((c
= cget()))
693 case 'a': /* New in Standard */
694 #if ('a' == '\a' || '\a' == ALERT)
695 value
= ALERT
; /* Use predefined value */
697 value
= '\a'; /* Use compiler's value */
721 case 'v': /* New in Standard */
722 #if ('v' == '\v' || '\v' == VT)
723 value
= VT
; /* Use predefined value */
725 value
= '\v'; /* Use compiler's value */
729 case 'x': /* '\xFF' */
732 while ((((c
= get()) >= '0' && c
<= '9') ||
733 (c
>= 'a' && c
<= 'f') ||
734 (c
>= 'A' && c
<= 'F')) &&
744 if (c
>= '0' && c
<= '7')
748 while (c
>= '0' && c
<= '7' && --count
>= 0)
765 * We warn on multi-byte constants and try to hack
766 * (big|little)endian machines.
768 while ((c
= get()) != '\'' && c
!= EOF_CHAR
&& c
!= '\n')
771 ciwarn("multi-byte constant '%c' isn't portable", c
);
780 * Apply the argument operator to the data on the value stack.
781 * One or two values are popped from the value stack and the result
782 * is pushed onto the value stack.
784 * OP_COL is a special case.
786 * evaleval() returns the new pointer to the top of the value stack.
788 FILE_LOCAL
int * evaleval(int* valp
, int op
, int skip
)
797 fprintf( pCppOut
, "%s op %s", (isbinary(op
)) ? "binary" : "unary",
800 fprintf( pCppOut
, ", v2 = %d.", v2
);
801 fprintf( pCppOut
, ", v1 = %d.\n", v1
);
826 cwarn("%s by zero in #if, zero result assumed",
827 (op
== OP_DIV
) ? "divide" : "mod");
831 else if (op
== OP_DIV
)
891 * v1 has the "true" value, v2 the "false" value.
892 * The top of the value stack has the test.
894 v1
= (*--valp
) ? v1
: v2
;
913 cierror("#if bug, operand = %d.", op
);
921 dumpstack(opstack
, opp
, value
, valp
)
922 OPTAB opstack
[NEXP
]; /* Operand stack */
923 OPTAB
*opp
; /* Operator stack */
924 int value
[NEXP
]; /* Value stack */
925 int *valp
; /* -> value vector */
927 fprintf( pCppOut
, "index op prec skip name -- op stack at %s", infile
->bptr
);
928 while (opp
> opstack
)
930 fprintf( pCppOut
, " [%2d] %2d %03o %d %s\n", opp
- opstack
,
931 opp
->op
, opp
->prec
, opp
->skip
, opname
[opp
->op
]);
934 while (--valp
>= value
)
936 fprintf( pCppOut
, "value[%d] = %d\n", (valp
- value
), *valp
);
941 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */