📄 cppexp.c
字号:
/* Parse C expressions for CCCP. Copyright (C) 1987, 1992, 1994, 1995 Free Software Foundation.This program is free software; you can redistribute it and/or modify itunder the terms of the GNU General Public License as published by theFree Software Foundation; either version 2, or (at your option) anylater version.This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program; if not, write to the Free SoftwareFoundation, 59 Temple Place - Suite 330,Boston, MA 02111-1307, USA. In other words, you are welcome to use, share and improve this program. You are forbidden to forbid anyone else to use, share and improve what you give them. Help stamp out software-hoarding!Written by Per Bothner 1994. *//* Parse a C expression from text in a string */ #include "config.h"#include "cpplib.h"extern char *xmalloc PARAMS ((unsigned));extern char *xrealloc PARAMS ((char *, unsigned));#ifdef MULTIBYTE_CHARS#include <stdlib.h>#include <locale.h>#endif#include <stdio.h>/* This is used for communicating lists of keywords with cccp.c. */struct arglist { struct arglist *next; U_CHAR *name; int length; int argno;};/* Define a generic NULL if one hasn't already been defined. */#ifndef NULL#define NULL 0#endif#ifndef GENERIC_PTR#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)#define GENERIC_PTR void *#else#define GENERIC_PTR char *#endif#endif#ifndef NULL_PTR#define NULL_PTR ((GENERIC_PTR)0)#endifextern char *xmalloc ();#ifndef CHAR_TYPE_SIZE#define CHAR_TYPE_SIZE BITS_PER_UNIT#endif#ifndef INT_TYPE_SIZE#define INT_TYPE_SIZE BITS_PER_WORD#endif#ifndef LONG_TYPE_SIZE#define LONG_TYPE_SIZE BITS_PER_WORD#endif#ifndef WCHAR_TYPE_SIZE#define WCHAR_TYPE_SIZE INT_TYPE_SIZE#endif#ifndef MAX_CHAR_TYPE_SIZE#define MAX_CHAR_TYPE_SIZE CHAR_TYPE_SIZE#endif#ifndef MAX_INT_TYPE_SIZE#define MAX_INT_TYPE_SIZE INT_TYPE_SIZE#endif#ifndef MAX_LONG_TYPE_SIZE#define MAX_LONG_TYPE_SIZE LONG_TYPE_SIZE#endif#ifndef MAX_WCHAR_TYPE_SIZE#define MAX_WCHAR_TYPE_SIZE WCHAR_TYPE_SIZE#endif/* Yield nonzero if adding two numbers with A's and B's signs can yield a number with SUM's sign, where A, B, and SUM are all C integers. */#define possible_sum_sign(a, b, sum) ((((a) ^ (b)) | ~ ((a) ^ (sum))) < 0)static void integer_overflow ();static long left_shift ();static long right_shift ();#define ERROR 299#define OROR 300#define ANDAND 301#define EQUAL 302#define NOTEQUAL 303#define LEQ 304#define GEQ 305#define LSH 306#define RSH 307#define NAME 308#define INT 309#define CHAR 310#define LEFT_OPERAND_REQUIRED 1#define RIGHT_OPERAND_REQUIRED 2#define HAVE_VALUE 4/*#define UNSIGNEDP 8*/#ifndef HOST_BITS_PER_WIDE_INT#if HOST_BITS_PER_LONG > HOST_BITS_PER_INT#define HOST_BITS_PER_WIDE_INT HOST_BITS_PER_LONG#define HOST_WIDE_INT long#else#define HOST_BITS_PER_WIDE_INT HOST_BITS_PER_INT#define HOST_WIDE_INT int#endif#endifstruct operation { short op; char rprio; /* Priority of op (relative to it right operand). */ char flags; char unsignedp; /* true if value should be treated as unsigned */ HOST_WIDE_INT value; /* The value logically "right" of op. */};/* Take care of parsing a number (anything that starts with a digit). LEN is the number of characters in it. *//* maybe needs to actually deal with floating point numbers */struct operationparse_number (pfile, start, olen) cpp_reader *pfile; char *start; int olen;{ struct operation op; register char *p = start; register int c; register unsigned long n = 0, nd, ULONG_MAX_over_base; register int base = 10; register int len = olen; register int overflow = 0; register int digit, largest_digit = 0; int spec_long = 0; op.unsignedp = 0; for (c = 0; c < len; c++) if (p[c] == '.') { /* It's a float since it contains a point. */ cpp_error (pfile, "floating point numbers not allowed in #if expressions"); op.op = ERROR; return op; } if (len >= 3 && (!strncmp (p, "0x", 2) || !strncmp (p, "0X", 2))) { p += 2; base = 16; len -= 2; } else if (*p == '0') base = 8; /* Some buggy compilers (e.g. MPW C) seem to need both casts. */ ULONG_MAX_over_base = ((unsigned long) -1) / ((unsigned long) base); for (; len > 0; len--) { c = *p++; if (c >= '0' && c <= '9') digit = c - '0'; else if (base == 16 && c >= 'a' && c <= 'f') digit = c - 'a' + 10; else if (base == 16 && c >= 'A' && c <= 'F') digit = c - 'A' + 10; else { /* `l' means long, and `u' means unsigned. */ while (1) { if (c == 'l' || c == 'L') { if (spec_long) cpp_error (pfile, "two `l's in integer constant"); spec_long = 1; } else if (c == 'u' || c == 'U') { if (op.unsignedp) cpp_error (pfile, "two `u's in integer constant"); op.unsignedp = 1; } else break; if (--len == 0) break; c = *p++; } /* Don't look for any more digits after the suffixes. */ break; } if (largest_digit < digit) largest_digit = digit; nd = n * base + digit; overflow |= ULONG_MAX_over_base < n | nd < n; n = nd; } if (len != 0) { cpp_error (pfile, "Invalid number in #if expression"); op.op = ERROR; return op; } if (base <= largest_digit) cpp_warning (pfile, "integer constant contains digits beyond the radix"); if (overflow) cpp_warning (pfile, "integer constant out of range"); /* If too big to be signed, consider it unsigned. */ if ((long) n < 0 && ! op.unsignedp) { if (base == 10) cpp_warning (pfile, "integer constant is so large that it is unsigned"); op.unsignedp = 1; } op.value = n; op.op = INT; return op;}struct token { char *operator; int token;};static struct token tokentab2[] = { {"&&", ANDAND}, {"||", OROR}, {"<<", LSH}, {">>", RSH}, {"==", EQUAL}, {"!=", NOTEQUAL}, {"<=", LEQ}, {">=", GEQ}, {"++", ERROR}, {"--", ERROR}, {NULL, ERROR}};/* Read one token. */struct operationcpp_lex (pfile)cpp_reader *pfile;{ register int c; register int namelen; register struct token *toktab; enum cpp_token token; struct operation op; U_CHAR *tok_start, *tok_end; int old_written; retry: old_written = CPP_WRITTEN (pfile); cpp_skip_hspace (pfile); c = CPP_BUF_PEEK (CPP_BUFFER (pfile)); if (c == '#') return parse_number (pfile, cpp_read_check_assertion (pfile) ? "1" : "0", 1); if (c == '\n') { op.op = 0; return op; } token = cpp_get_token (pfile); tok_start = pfile->token_buffer + old_written; tok_end = CPP_PWRITTEN (pfile); pfile->limit = tok_start; switch (token) { case CPP_EOF: /* Should not happen ... */ op.op = 0; return op; case CPP_VSPACE: case CPP_POP: if (CPP_BUFFER (pfile)->fname != NULL) { op.op = 0; return op; } goto retry; case CPP_HSPACE: case CPP_COMMENT: goto retry; case CPP_NUMBER: return parse_number (pfile, tok_start, tok_end - tok_start); case CPP_STRING: cpp_error (pfile, "string constants not allowed in #if expressions"); op.op = ERROR; return op; case CPP_CHAR: /* This code for reading a character constant handles multicharacter constants and wide characters. It is mostly copied from c-lex.c. */ { register int result = 0; register num_chars = 0; unsigned width = MAX_CHAR_TYPE_SIZE; int wide_flag = 0; int max_chars; U_CHAR *ptr = tok_start;#ifdef MULTIBYTE_CHARS char token_buffer[MAX_LONG_TYPE_SIZE/MAX_CHAR_TYPE_SIZE + MB_CUR_MAX];#else char token_buffer[MAX_LONG_TYPE_SIZE/MAX_CHAR_TYPE_SIZE + 1];#endif if (*ptr == 'L') { ptr++; wide_flag = 1; width = MAX_WCHAR_TYPE_SIZE;#ifdef MULTIBYTE_CHARS max_chars = MB_CUR_MAX;#else max_chars = 1;#endif } else max_chars = MAX_LONG_TYPE_SIZE / width; ++ptr; while (ptr < tok_end && ((c = *ptr++) != '\'')) { if (c == '\\') { c = cpp_parse_escape (pfile, &ptr); if (width < HOST_BITS_PER_INT && (unsigned) c >= (1 << width)) cpp_pedwarn (pfile, "escape sequence out of range for character"); } num_chars++; /* Merge character into result; ignore excess chars. */ if (num_chars < max_chars + 1) { if (width < HOST_BITS_PER_INT) result = (result << width) | (c & ((1 << width) - 1)); else result = c; token_buffer[num_chars - 1] = c; } } token_buffer[num_chars] = 0; if (c != '\'') cpp_error (pfile, "malformatted character constant"); else if (num_chars == 0) cpp_error (pfile, "empty character constant"); else if (num_chars > max_chars) { num_chars = max_chars; cpp_error (pfile, "character constant too long"); } else if (num_chars != 1 && ! CPP_TRADITIONAL (pfile)) cpp_warning (pfile, "multi-character character constant"); /* If char type is signed, sign-extend the constant. */ if (! wide_flag) { int num_bits = num_chars * width; if (cpp_lookup (pfile, "__CHAR_UNSIGNED__", sizeof ("__CHAR_UNSIGNED__")-1, -1) || ((result >> (num_bits - 1)) & 1) == 0) op.value = result & ((unsigned long) ~0 >> (HOST_BITS_PER_LONG - num_bits)); else op.value = result | ~((unsigned long) ~0 >> (HOST_BITS_PER_LONG - num_bits)); } else {#ifdef MULTIBYTE_CHARS /* Set the initial shift state and convert the next sequence. */ result = 0; /* In all locales L'\0' is zero and mbtowc will return zero, so don't use it. */ if (num_chars > 1 || (num_chars == 1 && token_buffer[0] != '\0')) { wchar_t wc; (void) mbtowc (NULL_PTR, NULL_PTR, 0); if (mbtowc (& wc, token_buffer, num_chars) == num_chars) result = wc; else cpp_warning (pfile,"Ignoring invalid multibyte character"); }#endif op.value = result; } } /* This is always a signed type. */ op.unsignedp = 0; op.op = CHAR; return op; case CPP_NAME: return parse_number (pfile, "0", 0); case CPP_OTHER: /* See if it is a special token of length 2. */ if (tok_start + 2 == tok_end) { for (toktab = tokentab2; toktab->operator != NULL; toktab++) if (tok_start[0] == toktab->operator[0] && tok_start[1] == toktab->operator[1]) break; if (toktab->token == ERROR) { char *buf = (char *) alloca (40); sprintf (buf, "`%s' not allowed in operand of `#if'", tok_start); cpp_error (pfile, buf); } op.op = toktab->token; return op; } /* fall through */ default: op.op = *tok_start; return op; }}/* Parse a C escape sequence. STRING_PTR points to a variable containing a pointer to the string to parse. That pointer is updated past the characters we use. The value of the escape sequence is returned. A negative value means the sequence \ newline was seen, which is supposed to be equivalent to nothing at all. If \ is followed by a null character, we return a negative value and leave the string pointer pointing at the null character. If \ is followed by 000, we return 0 and leave the string pointer after the zeros. A value of 0 does not mean end of string. */intcpp_parse_escape (pfile, string_ptr) cpp_reader *pfile; char **string_ptr;{ register int c = *(*string_ptr)++; switch (c) { case 'a': return TARGET_BELL; case 'b': return TARGET_BS; case 'e': case 'E': if (CPP_PEDANTIC (pfile)) cpp_pedwarn (pfile, "non-ANSI-standard escape sequence, `\\%c'", c); return 033;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -