/*
* Copyright (c) 2003 - 2007, Nils R. Weller
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Helper functions for parsing string and character constants,
* trigraphs, operators and constants. Also contains global token
* list and functions to add to this list. Used by lexical
* analyzer.
*/
#include "token.h"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <ctype.h>
#include "backend.h"
#include "error.h"
#include "defs.h"
#include "numlimits.h"
#include "decl.h"
#include "misc.h"
#include "type.h"
#include "cc1_main.h"
#include "typemap.h"
#include "debug.h"
#include "n_libc.h"
struct token *toklist;
size_t lex_chars_read;
char *lex_line_ptr;
char *lex_tok_ptr;
char *lex_file_map;
char *lex_file_map_end;
struct ty_string *str_const = NULL;
static struct ty_string *str_const_tail = NULL;
struct ty_float *float_const = NULL;
static struct ty_float *float_const_tail = NULL;
struct ty_llong *llong_const = NULL;
static struct ty_llong *llong_const_tail = NULL;
struct token *
alloc_token(void) {
struct token *ret = n_xmalloc(sizeof *ret);
static struct token nulltok;
*ret = nulltok;
return ret;
}
void
free_tokens(struct token *from, struct token *to, int type) {
do {
struct token *next = from->next;
if (type == FREE_DECL) {
if (from->type == TOK_PAREN_OPEN
|| from->type == TOK_PAREN_CLOSE
|| from->type == TOK_ARRAY_OPEN
|| from->type == TOK_ARRAY_CLOSE
|| from->type == TOK_SEMICOLON
|| IS_KEYWORD(from->type)) {
free(from);
} else if (from->type == TOK_OPERATOR
&& *(int *)from->data == TOK_OP_ASSIGN) {
break;
}
} else {
abort();
}
from = next;
} while (from != to);
}
static void
append_ty_string(
struct ty_string **head,
struct ty_string **tail,
struct ty_string **str) {
struct ty_string *tmp;
for (tmp = str_const; tmp != NULL; tmp = tmp->next) {
if (tmp->size == (*str)->size) {
if (strcmp(tmp->str, (*str)->str) == 0) {
*str = tmp;
return;
}
}
}
if (*head == NULL) {
*head = *tail = *str;
} else {
(*tail)->next = *str;
*tail = (*tail)->next;
}
(*str)->count = ty_string_count();
(*tail)->next = NULL;
}
#if 0
static int
get_next_char(FILE *fd) {
int ch = getc(fd);
if (ch == '\n') {
lex_line_ptr = lex_file_map + lex_chars_read;
err_setlineptr(lex_line_ptr);
}
return ch;
}
#endif
static int store_char_index = 0;
/*
* Routine used by several functions in this module - stores
* character ch in *p, resizing it if necessary. Must be called
* with a first argument of NULL before it can be used in order
* to initialize static data. Exits if no memory is available.
*
* XXX Not reentrant
* XXX Should use nwstr library instead, which may also speed up
* detecing keywords in the source
*/
int
store_char(char **p, int ch) {
static int chunksiz = 32;
static int alloc = 0;
if (p == NULL) {
/* Initialize static data */
chunksiz = 8;
alloc = 0;
store_char_index= 0;
return 0;
}
if (store_char_index >= alloc) {
alloc += chunksiz;
chunksiz *= 2;
if ((*p = n_xrealloc(*p, alloc)) == NULL) {
perror("realloc");
exit(EXIT_FAILURE);
}
}
(*p)[(unsigned)store_char_index] = (unsigned char)ch;
return ++store_char_index;
}
int
store_string(char **p, char *str) {
int rc = 0;
for (; *str != 0; ++str) {
rc = store_char(p, *str);
}
store_char(p, 0);
return rc;
}
/*
* Function parses the escape sequence pointed to by s and returns
* the result. The type argument indicates whether the function is
* called on a string or character constant. -1 is returned on
* failure (bad format.)
*
* XXX this is not aware of cross-compilation... So in the event that
* we're going to support a system with 16- or 32-bit bytes (not
* infinitely unlikely), it will have to be adapted as necessary.
* This should also affect the preprocessor as far as character
* constants are concerned
*/
static int
get_escape_sequence(const char *s, int type) {
int rc = -1;
if (isdigit((unsigned char)*s)) {
/* Octal constant */
if (s[0] == '0' && isdigit((unsigned char)s[1]) == 0) {
/* Null constant '\0' */
return 0;
}
if (s[0] == '0') ++s;
if (sscanf(s, type == TOK_STRING_LITERAL ? "%3o" : "%o",
(unsigned *)&rc) != 1) {
lexerror("Invalid octal constant.");
return -1;
}
} else if (tolower((unsigned char)*s) == 'x') {
/* Is hexadecimal constant */
if (sscanf(s+1, type == TOK_STRING_LITERAL ? "%2x" : "%x",
(unsigned *)&rc) != 1) {
lexerror("Invalid hexadecimal constant.");
return -1;
}
} else {
/* Must be other escape sequence */
static struct {
char ch;
char seq;
} esc[] = {
{ 'n', '\n' },
{ 'r', '\r' },
{ 't', '\t' },
{ 'v', '\v' },
{ 'b', '\b' },
{ 'a', '\a' },
{ 'f', '\f' },
{ '\\', '\\' },
{ '\'', '\''},
{ '\"', '\"'},
{ '\?', '\?'},
{ 0, 0 }
};
int i;
for (i = 0; esc[i].ch != 0; ++i) {
if (s[0] == esc[i].ch) {
rc = esc[i].seq;
if (s[1] != 0 && type == TOK_CHAR_LITERAL) {
if (s[1] != '\'') {
lexwarning("Multiple characters "
"in character constant"
" - ignoring excess"
"characters.");
}
}
break;
}
}
if (esc[i].ch == 0) {
lexerror("Invalid escape sequence.");
}
}
if (type == TOK_CHAR_LITERAL
&& (rc > UCHAR_MAX || rc < CHAR_MIN)) {
lexwarning("Literal out of character range.");
}
rc = (char)rc;
return rc;
}
/*
* Reads trigraph from stream f, if present, parses it and returns the
* result. This function must be called AFTER a ``?'' character has
* been read from f. The function has three possible types of a return
* value:
* 1) -1 - This means a non-question-mark character was read. f remains
* the same as it was upon entry of the function
* 2) 0 - This means a question mark character was read, but the
* next character did not complete a valid trigraph. f points to
* that non-question-mark character
* 3) everything else - This is the character to which the trigraph maps
* f points after the trigraph
*
* XXX we shouldn't disappoint the millions of digraphs users either lol
*/
int
get_trigraph(FILE *f) {
static struct {
char sigch; /* significant character */
char trans; /* translation result of trigraph */
} trig_map[] = {
{ '=', '#' },
{ ')', ']' },
{ '!', '|' },
{ '(', '[' },
{ '\'', '^' },
{ '>', '}' },
{ '/', '\\' },
{ '<', '{' },
{ '-', '~' },
{ 0, 0 }
};
int ch;
int i;
if ((ch = FGETC(f)) == EOF) {
lexerror("Unexpected end of file");
return 0;
}
if (ch != '?') {
/* This has nothing to do with trigraphs at all... */
UNGETC(ch, f);
return -1;
}
/* If the next char matches, we have a trigraph */
if ((ch = FGETC(f)) == EOF) {
lexerror("Unexpected end of file");
return 0;
}
for (i = 0; trig_map[i].sigch != 0; ++i) {
if (trig_map[i].sigch == ch) {
/* Is a trigraph! */
lexwarning("Interpreting `??%c' as trigraph",
trig_map[i].sigch);
return trig_map[i].trans;
}
}
/* Not a trigraph */
UNGETC(ch, f);
return 0;
}
/*
* Reads character literal from stream f, parses it and returns
* the result. This function must be called AFTER the opening '
* has already been read from f. -1 is returned on failure (bad
* format / no closing ')
*/
int
get_char_literal(FILE *f, int *err) {
int ch;
int trig;
int last = 0;
int rc;
char *p = NULL;
*err = 0;
/* Initialize store_char()'s private data */
store_char(NULL, 0);
while ((ch = FGETC(f)) != EOF) {
/*
* Loop until closing ' encountered. It's important to check
* that no \' escape sequence is considered to be a closing '.
*
* UPDATE: checking for last = \ did not suffice and hid a
* really obscure bug. If a literal reads '\\', then obviously
* last should not make the loop continue
*/
if (ch == '?') {
/* Might be trigraph */
if ((trig = get_trigraph(f)) == -1) {
/*
* This is an illegal multi-char literal only if
* the current byte at f is not a ' char!
*/
store_char(&p, ch);
last = ch;
} else if (trig == 0) {
/* Illegal, but let code later on catch it */
store_char(&p, ch);
store_char(&p, '?');
last = '?';
} else {
/* Legal trigraph */
store_char(&p, trig);
last = trig;
}
continue;
} else if (ch != '\'') {
store_char(&p, ch);
last = ch;
continue;
} else if (last == '\\') {
/*
* 06/30/07: Seems p[1] was being read uninitialized!
* Hence the iterations check
*/
if (store_char_index < 2
|| !(p[0] == '\\' && p[1] == '\\')) {
store_char(&p, ch);
last = ch;
continue;
}
}
/* Literal ends here, parse and return it now */
if (p == NULL) {
lexerror("Empty character literal");
*err = 1;
return -1;
}
store_char(&p, 0);
if (p[0] == '\\') {
/* Is escape sequence */
rc = get_escape_sequence(p + 1, TOK_CHAR_LITERAL);
#ifdef DEBUG
printf("Read character literal %d\n", rc);
#endif
return rc;
} else {
/* Ordinary character */
if (p[1] != 0) {
/*
* Multi-character character literals are
* uncommon but legal in C (and illegal in
* C++.) Since their values are
* implementation-defined, it is OK to
* truncate them
*/
lexwarning("Multiple characters in character "
"constant - ignoring excess "
"characters.");
}
#ifdef DEBUG
printf("Read character literal ``%c''\n", p[0]);
#endif
return p[0];
}
}
lexerror("Unexpected end of file - expected ' to close "
"character constant.");
*err = 1;
return -1;
}
/*
* Returns 1 if ``code'' is part of the numeric system specified by
* ``type'' ('x' indicates hexadecimal, everything else octal) in
* ascii, else 0
*/
static int
is_part_of_constant(int code, int type) {
if (isdigit((unsigned char)code)) {
if (type != 'x') {
/* Must be octal */
if (code > '7') {
return 0;
}
}
return 1;
}
if (type == 'x') {
/* Hexadecimal */
code = toupper((unsigned char)code);
if (code != 0 && strchr("ABCDEF", code) != NULL) {
return 1;
}
}
return 0;
}
/*
* Reads string literal from stream f and returns a dynamically
* allocated string as result. Must be called AFTER the opening
* " has been read from f. A null pointer is returned on failure
* (no closing ")
*/
struct ty_string *
get_string_literal(FILE *f) {
char *p = NULL;
int ch;
int trig;
/* Initialize store_char()'s private data */
store_char(NULL, 0);
while ((ch = FGETC(f)) != EOF) {
if (ch == '?') {
/* Might be trigraph */
if ((trig = get_trigraph(f)) == -1) {
store_char(&p, ch);
continue;
} else if (trig == 0) {
store_char(&p, ch);
store_char(&p, '?');
continue;
} else {
/* Is trigraph! Let code below handle it */
ch = trig;
}
}
if (ch == '"') {
/* Literal ends here, nul-terminate and return it */
int size = store_char(&p, 0);
struct ty_string *ret;
#ifdef DEBUG
printf("Read string literal `%s'\n", p);
#endif
ret = alloc_ty_string();
ret->size = size;
ret->str = p;
return ret;
}
if (ch == '\n' || ch == '\r') {
lexerror("Newline in string literal - use '\\n' instead");
} else if (ch == '\\') {
/* Escape sequence */
char buf[4];
/*
* Unfortunately, some escape sequence parsing must be
* done here already, because it wouldn't otherwise be
* known how many characters can be read (octal /
* hexadecimal constants take two, newlines, formfeeds,
* etc only one)
*/
if ((ch = FGETC(f)) == EOF) {
break;
}
buf[0] = (unsigned char)ch;
if (tolower((unsigned char)buf[0]) != 'x'
&& !isdigit(buf[0])) {
buf[1] = 0;
ch = get_escape_sequence(buf,
TOK_STRING_LITERAL);
} else {
/*
* Read numeric components of escape sequence.
* We must be careful to exclude non-digit
* characters from processing
*/
if ((ch = FGETC(f)) == EOF) {
break;
}
if (is_part_of_constant(ch, buf[0])) {
buf[1] = (unsigned char)ch;
if ((ch = FGETC(f)) == EOF) {
break;
}
if (is_part_of_constant(ch, buf[0])) {
buf[2] = (unsigned char)ch;
buf[3] = 0;
} else {
UNGETC(ch, f);
buf[2] = 0;
}
} else {
UNGETC(ch, f);
if (buf[0] == 'x') {
lexwarning("Empty hexadecimal "
" escape sequence");
continue;
} else {
buf[1] = 0;
}
}
ch = get_escape_sequence(buf,
TOK_STRING_LITERAL);
}
if (ch == -1) {
store_char(&p, '\x1');
} else {
store_char(&p, ch);
}
continue;
}
store_char(&p, ch);
}
lexerror("Unexpected end of file - expected \" to close string literal.");
if (p) free(p);
return NULL;
}
/*
* Returns the numeric code corresponding to ascii operator ``op''
* on success, -1 if the operator is unknown
*/
static int
do_get_operator(char *op, char **ascii) {
int i;
int c = *op;
if ((i = LOOKUP_OP(c)) == 0) {
return -1;
}
for (; operators[i].name[0] == c; ++i) {
if (strcmp(operators[i].name, op) == 0) {
if (operators[i].is_ambig != 0) {
*ascii = operators[i].name;
return operators[i].is_ambig;
} else {
*ascii = operators[i].name;
return operators[i].value;
}
}
}
return -1;
}
/*
* Read operator, starting from ch, from stream f. The result is
* merely an approximation to the real semantics of the operator
* in cases where multiple interpretations are possible in
* different contexts (e.g. ``&'' as address-of operator vs. ``&''
* as bitwise and.) The reported value for one of those ambiguous
* operators must be adjusted at the hierarchial source analysis.
* In particular, ambiguities occur with these operators:
*
* + - unary plus | addition
* - - unary minus | substraction
* * - multiply | dereference (pointer declaration)
* & - bitwise and | address-of
* ++ - pre-increment | post-increment
* -- - pre-decrement | post-decrement
* : - cond. oper. (bitfield member | label)
*/
int
get_operator(int ch, FILE *f, char **ascii) {
char buf[8];
int i = 1;
int tmp;
int latest_valid = -1;
buf[0] = ch;
buf[1] = 0;
latest_valid = do_get_operator(buf, ascii);
/*
* The strategy is to read new characters until the resulting
* string is NOT a valid operator anymore (or an identifier /
* whitespace is encountered.) The effect is that the previous
* operator (latest_valid) must have been valid, because we
* otherwise would have never reached this point. This is very
* handy for operators that, e.g., turn from ``<'' to ``<<''
* to ``<<=''.
*/
while ((ch = FGETC(f)) != EOF) {
if (isalnum((unsigned char)ch)
|| (ch != 0 && strchr("_$ \t\n", ch) != NULL)) {
/* Identifier or whitespace reached */
UNGETC(ch, f);
#ifdef DEBUG
buf[i] = 0;
printf("Read operator %s\n", buf);
#endif
return latest_valid;
}
buf[i] = ch;
buf[i + 1] = 0;
if ((tmp = do_get_operator(buf, ascii)) == -1) {
/* Not valid anymore - return last index */
UNGETC(ch, f);
#ifdef DEBUG
buf[i] = 0;
printf("Read operator %s\n", buf);
#endif
return latest_valid;
}
latest_valid = tmp;
++i;
}
lexerror("Unexpected end of file.");
return -1;
}
/*
* Debugging function to print the value pointed to by ``ptr'',
* interpreting it based on the type code ``type''. If the
* ``verbose'' argument is nonzero, it will also print exactly
* what type is being dealt with
*/
void
rv_setrc_print(void *ptr, int type, int verbose) {
(void)ptr; (void)type; (void)verbose;
#ifdef DEBUG
if (type == TY_INT) {
printf("%s %d\n",
verbose?"Read integer":"", *(int *)ptr);
} else if (type == TY_UINT) {
printf("%s %u\n",
verbose?"Read unsigned integer":"", *(unsigned *)ptr);
} else if (type == TY_LONG) {
printf("%s %ld\n",
verbose?"Read long":"", *(long *)ptr);
} else if (type == TY_ULONG) {
printf("%s %lu\n",
verbose?"Read unsigned long":"", *(unsigned long *)ptr);
} else if (type == TY_FLOAT) {
printf("%s %f\n",
verbose?"Read float":"", *(float *)ptr);
} else if (type == TY_DOUBLE) {
printf("%s %f\n",
verbose?"Read double":"", *(double *)ptr);
} else if (type == TY_LDOUBLE) {
printf("%s %Lf\n",
verbose?"Read long double":"", *(long double *)ptr);
}
#endif
}
/*
* XXX this is a quick kludge for 64bit PowerPC long/long long constants.
* Whenever we encounter a long constant that is not part of a constant
* expression, we create the constant in the data segment and use a
* displacement with the TOC
*/
void
put_ppc_llong(struct num *n) {
struct ty_llong *tll;
static unsigned long llcount;
tll = alloc_ty_llong();
tll->count = llcount++;
tll->num = n;
tll->next = llong_const;
llong_const = tll;
}
static int
hexdigit_to_val(int ch) {
int digit;
if (isdigit(ch)) {
digit = ch - '0';
} else {
static const char *hexdigits = "abcdef";
/* Must be a - f */
digit = 10 + (strchr(hexdigits, tolower(ch))
- hexdigits);
}
return digit;
}
static int
parse_hexfloat_const(struct num *ret, char *textval, char *bin_exp) {
char *p;
char *after;
unsigned before_val = 0;
int factor = 1;
int expval;
int negative_exp = 0;
int digit;
int divisor;
void *res;
long double ld;
textval += 2; /* skip 0x part */
if ((p = strchr(textval, '.')) == NULL) {
return -1;
}
*p = 0;
after = p+1;
for (--p;; --p) {
if (p == textval - 1) {
break;
}
digit = hexdigit_to_val(*p);
before_val += digit * factor;
factor <<= 4;
}
ld = before_val;
divisor = 16;
for (p = after; *p != 0; ++p) {
digit = hexdigit_to_val(*p);
ld += (double)digit / divisor;
divisor <<= 4;
}
++bin_exp; /* skip p */
if (*bin_exp == '-') {
negative_exp = 1;
++bin_exp;
} else if (*bin_exp == '+') {
++bin_exp;
}
expval = atoi(bin_exp);
if (expval) {
if (negative_exp) {
while (expval--) {
ld /= 2;
}
} else {
while (expval--) {
ld *= 2;
}
}
}
/*
* Now we have the result as a HOST long double. This requires
* us to convert it to the requested target type (by default
* double, with f postfix float, and with L postfix long double).
* This may require us to use double as source format because
* there is no mapping between host long double and target type.
*/
res = n_xmalloc(16); /* XXX */
/*if (ret->type == TY_LDOUBLE) {
unimpl();
} else*/ {
double d = (double)ld;
/* XXX again this ignores endianness, this stuff is
* completely hopeless :-( */
memcpy(res, &d, sizeof d);
cross_conv_host_to_target(res, ret->type,
TY_DOUBLE);
}
ret->value = res;
return 0;
}
/*
* Reads a numeric literal from stream f and returns a pointer to
* a dynamically allocated ``struct num'' containing its value and
* type. The first character that has already been read from the
* literal (and which actually indicated that we are dealing with
* one) is passed as ``firstch''. On failure (bad format or memory
* allocation problems) a null pointer is returned.
* This stuff is believed to be able to handle all types of numeric
* constants that exist in C
*/
struct num *
get_num_literal(int firstch, FILE *f) {
int ch;
int real_type = 0;
int octal_flag = 0;
int hexa_flag = 0;
int long_flag = 0;
int unsigned_flag = 0;
int float_flag = 0;
int fp_flag = 0;
int digits_read = 0;
int last_dig = 0;
int hex_float = 0;
char *p = NULL;
struct num *rc;
char hex_fract_const[128];
char bin_exp[128];
int hex_fract_idx = 0;
int bin_exp_idx = 0;
/* Initialize store_char() */
store_char(NULL, 0);
store_char(&p, firstch);
if (firstch == '0') {
if ((ch = FGETC(f)) != EOF) {
if (tolower((unsigned char)ch) == 'x') {
/* hexadecimal (0x1) */
hexa_flag = 1;
store_char(&p, ch);
} else if (ch == '.') {
/* floating point (0.1) */
fp_flag = 1;
store_char(&p, ch);
} else if (isdigit((unsigned char)ch)) {
/* octal (01) */
octal_flag = 1;
store_char(&p, ch);
if (ch != '0') { /* 0 is insignificant */
++digits_read;
} else {
last_dig = ch;
}
} else {
/* unknown */
UNGETC(ch, f);
++digits_read;
}
}
} else if (firstch == '.') {
/* Number like .5 (equivalent to 0.5) */
fp_flag = 1;
} else {
/* Is significant digit */
++digits_read;
last_dig = firstch;
}
while ((ch = FGETC(f)) != EOF) {
switch (ch) {
case 'l':
case 'L':
++long_flag;
if (long_flag > 2) {
lexerror("Unknown constant (was expecting "
"long long)");
return NULL;
}
break;
case 'u':
case 'U':
++unsigned_flag;
if (unsigned_flag > 1) {
lexwarning("Multiple unsigned designators "
"in constant");
}
break;
case 'f':
case 'F':
if (hexa_flag && (!hex_float || bin_exp_idx == 0)) {
store_char(&p, 'f');
++digits_read;
} else {
float_flag = ++fp_flag;
if (float_flag > 2) {
lexwarning(
"Multiple floating point "
"designators in constant");
}
}
break;
case '.':
fp_flag = 1;
if (hexa_flag){
hex_float = 1;
}
store_char(&p, '.');
break;
default:
if (isdigit((unsigned char)ch)) {
/*
* Verify the numeric part has not yet been
* termianted
*/
if (long_flag || unsigned_flag || float_flag) {
lexerror("Unexpected digit.");
if (p) free(p);
return NULL;
}
if (hex_float && bin_exp_idx > 0) {
bin_exp[bin_exp_idx++] = ch;
} else {
store_char(&p, ch);
++digits_read;
if (last_dig == 0 && ch != '0') {
last_dig = ch;
}
}
continue;
}
/* Not digit - Does the constant end here? */
if (tolower((unsigned char)ch) == 'e') {
if (hexa_flag == 0) {
/*
* Must be scientific notation
*/
if (fp_flag == 0) {
#if 0
error("Using scientific "
"notation on "
"integral value.");
if (p) free(p);
return NULL;
#endif
fp_flag = 1;
}
store_char(&p, ch);
if ((ch = FGETC(f)) == '+'
|| ch == '-') {
store_char(&p, ch);
} else {
UNGETC(ch, f);
}
continue;
} else {
++digits_read;
}
store_char(&p, ch);
continue;
}
/* Hexadecimal digit? */
if (hexa_flag == 1) {
int t;
t = tolower((unsigned char)ch);
if (strchr("abcd", t) != NULL
&& bin_exp_idx == 0) {
/* e & f are covered by other cases */
store_char(&p, t);
++digits_read;
continue;
/*break; ??? */
} else if (hex_float) {
if (t == 'p') {
if (bin_exp_idx > 0) {
lexerror("Constant already "
"has a binary "
"exponent");
if (p) free(p);
return NULL;
} else {
bin_exp[bin_exp_idx++]
= t;
}
continue;
} else if (t == '-' || t == '+') {
if (t == '-') {
bin_exp[bin_exp_idx++]
= '-';
}
continue;
}
}
}
/* Ok, done with it */
store_char(&p, 0);
UNGETC(ch, f);
if (digits_read == 0 && !octal_flag && !fp_flag) {
lexerror("Constant with no digits");
} else if (fp_flag == 0) {
/*
* Perform range check, adjust type of
* constant as needed
*/
real_type = range_check(p, hexa_flag,
octal_flag, unsigned_flag, long_flag,
digits_read);
if (real_type == -1) {
free(p);
return NULL;
} else if (real_type != 0) {
/* 0 means unchanged */
if (IS_LLONG(real_type)
&& long_flag != 2) {
/*
* The number wasn't requested
* to be of type ``long long'',
* but has to be! This may not
* be intended
*/
lexwarning("Number `%s' is too "
"large for `unsigned "
"long'", p);
}
if (real_type == TY_ULONG
|| real_type == TY_ULLONG
|| real_type == TY_UINT) {
unsigned_flag = 1;
}
if (real_type == TY_LONG
|| real_type == TY_ULONG) {
long_flag = 1;
} else if (real_type == TY_LLONG
|| real_type == TY_ULLONG) {
long_flag = 2;
}
} else {
/*
* The user-specified type is
* sufficient
*/
if (unsigned_flag) {
if (long_flag == 1) {
real_type = TY_ULONG;
} else if (long_flag == 2) {
real_type = TY_ULLONG;
} else {
real_type = TY_UINT;
}
} else {
/* signed */
if (long_flag == 1) {
real_type = TY_LONG;
} else if (long_flag == 2) {
real_type = TY_LLONG;
} else {
real_type = TY_INT;
}
}
}
} else {
/* Floating point */
if (float_flag) {
real_type = TY_FLOAT;
} else if (long_flag) {
real_type = TY_LDOUBLE;
} else {
real_type = TY_DOUBLE;
}
}
if (backend->arch == ARCH_MIPS
|| backend->arch == ARCH_POWER) {
/*
* XXX MIPS/PPC kludge. This means there
* is also no correct typechecking for
* double vs long double!
*/
if (fp_flag && long_flag) {
/* long double becomes double */
real_type = TY_DOUBLE;
}
}
if (hex_float) {
if (bin_exp_idx == 0) {
lexerror("Hexadecimal floating point "
"constant misses exponent");
return NULL;
}
rc = n_xmalloc(sizeof *rc);
rc->value = n_xmalloc(backend->get_sizeof_type(
make_basic_type(real_type), NULL));
hex_fract_const[hex_fract_idx] = 0;
bin_exp[bin_exp_idx] = 0;
rc->type = real_type;
if (parse_hexfloat_const(rc, p, bin_exp) == 0) {
put_float_const_list(rc);
}
} else {
rc = cross_scan_value(p, real_type,
hexa_flag, octal_flag, fp_flag);
}
#ifdef DEBUG
printf("(%d digits)\n", digits_read);
#endif
if (fp_flag /*&& backend->need_floatconst*/) {
#if 0
struct ty_float *fc;
static unsigned long count;
fc = n_xmalloc(sizeof *fc);
fc->count = count++;
fc->num = rc;
fc->next = float_const;
float_const = fc;
#endif
} else if (long_flag
&& backend->abi == ABI_POWER64) {
/* 64bit native long long/long! */
put_ppc_llong(rc);
}
return rc;
}
}
lexerror("Unexpected end of file - expected end of numeric constant.");
if (p) free(p);
return NULL;
}
/*
* Reads an identifier from stream f and returns a pointer to a
* dynamically allocated string containing the result. It will
* append all characters until a character is encountered that
* is neither alpha-numeric, nor ``_'', nor ``$''. On failure
* (end of file), a null pointer is returned
*/
char *
get_identifier(int ch, FILE *f) {
char *p = NULL;
/* Initialize static store_char() data */
store_char(NULL, 0);
store_char(&p, ch);
while ((ch = FGETC(f)) != EOF) {
if (isalnum((unsigned char)ch) || ch == '_' || ch == '$') {
store_char(&p, ch);
if (ch == '$') {
lexerror("`$' characters are not allowed in "
"identifiers");
}
} else {
/*
* Operator, white space or other token encountered -
* return
*/
store_char(&p, 0);
UNGETC(ch, f);
#ifdef DEBUG
printf("Read identifier ``%s''\n", p);
#endif
return p;
}
}
lexerror("Unexpected end of file - expected termination of identifier.");
return NULL;
}
static char *curfile;
static int curfileid;
/*
* Set path of file currently processed. This will be used by
* store_token(), which saves in each token which file it is
* contained by
* XXX This is sorta bogus??
*/
void
token_setfile(char *file) {
curfile = file;
}
void
token_setfileid(int id) {
curfileid = id;
}
static char *
tok_to_ascii(int type, void *data) {
char *ret = "???";
if (type == TOK_IDENTIFIER) {
if (data != NULL) ret = data;
else ret = "identifier";
} else if (type == TOK_STRING_LITERAL) {
if (data != NULL) {
struct ty_string *str;
str = data;
ret = str->str;
} else {
ret = "string constant";
}
} else if (type == TOK_OPERATOR) {
if (data != NULL) {
ret = lookup_operator(*(int *)data);
if (ret == NULL) {
(void) fprintf(stderr,
"FATAL - unknown operator - %d\n",
*(int *)data);
abort();
}
} else {
ret = "operator";
}
} else if (type == TOK_PAREN_OPEN) {
ret = "(";
} else if (type == TOK_PAREN_CLOSE) {
ret = ")";
} else if (type == TOK_COMP_OPEN) {
ret = "{";
} else if (type == TOK_COMP_CLOSE) {
ret = "}";
} else if (type == TOK_ARRAY_OPEN) {
ret = "[";
} else if (type == TOK_ARRAY_CLOSE) {
ret = "]";
} else if (type == TOK_SEMICOLON) {
ret = ";";
} else if (IS_CONSTANT(type)) {
ret = "(constant)";
} else if (IS_OPERATOR(type)) {
/* This will only be used by expect_token() */
} else {
#ifdef DEBUG
char lame[256];
sprintf(lame, "<unknown>(code = %d)", type);
ret = strdup(lame);
#else
ret = "???";
#endif
}
return ret;
}
static void
check_ident(struct token *t, const char *ident) {
(void) t; (void) ident;
#if 0
char *p = NULL;
if (cur_inc != NULL
&& cur_inc_is_std) {
/*
* Identifier occurs in standard
* include - part of implementation!
*/
return;
}
if (strncmp(ident, "str", 3) == 0) {
p = "begins with `str'";
} else if (strncmp(ident, "mem", 3) == 0) {
p = "begins with `mem'";
} else if (strncmp(ident, "is", 2) == 0
&& isalpha(ident[3])) {
p = "begins with `is' followed by letter";
} else if (ident[0] == '_') {
if (isupper((unsigned char)ident[1])
|| ident[1] == '_') {
p = "begins with underscore followed "
"by uppercase letter or underscore";
} else {
if (curscope == &global_scope) {
p = "begins with underscore at "
"global/file scope or in the tag namespace";
}
}
}
if (p != NULL) {
warningfl(t, "Identifier `%s' invades "
"implementation namespace (%s)", ident, p);
}
#endif
}
/*
* Appends the token specified by the data-type-linenum triple to
* the token list pointed to by ``dest''. Exits with an error
* message if no memory can be allocated. ``data'' MUST point to
* dynamically allocated memory if you intend use destroy_toklist()
* token_setfile() must be called to validate the file state data
* before store_token() can be used
* Adjacent string literals are concatenated
*/
void
store_token(struct token **dest, void *data, int type, int linenum, char *ascii) {
struct token *t;
static struct token *cur;
if (*dest == NULL) {
#if 0
*dest = n_xmalloc(sizeof **dest);
#endif
*dest = alloc_token();
t = *dest;
t->prev = NULL;
t->next = NULL;
cur = t;
} else {
t = cur;
if (type == TOK_STRING_LITERAL) {
/*
* Let's check whether we have to concatenate adjacent
* string literals
*/
struct type *ty;
struct ty_string *newstr = data;
if (t && t->type == TOK_STRING_LITERAL) {
/* Yes! */
/*
* XXX this will make "foo" "bar"
* appear as "foobar" in error message
* output as soon as we display the
* line containing the error. This may
* be worth investigating
*/
struct ty_string *oldstr = t->data;
size_t newsiz =
oldstr->size + newstr->size - 1;
oldstr->str = n_xrealloc(oldstr->str, newsiz);
memcpy(oldstr->str + oldstr->size - 1,
newstr->str,
newstr->size);
oldstr->size = newsiz;
t->ascii = oldstr->str;
oldstr->ty->tlist->arrarg_const = oldstr->size;
/* Okay, we are done here already */
return;
}
ty = make_array_type(newstr->size);
newstr->ty = ty;
} else if (cur && cur->type == TOK_STRING_LITERAL) {
/*
* Only now can we store the string constant
* because there are no adjacent constants left
* to merge ...
*/
struct ty_string *ts = cur->data;
append_ty_string(&str_const, &str_const_tail, &ts);
cur->data = ts;
}
#if 0
t->next = n_xmalloc(sizeof *t->next);
#endif
t->next = alloc_token();
t->next->prev = t;
t = t->next;
t->next = NULL;
cur = t;
}
/* Save token in ascii */
if (ascii != NULL) {
/* Caller supplies text */
t->ascii = ascii;
} else {
/* We have to make stuff up ourselves */
t->ascii = tok_to_ascii(type, data);
}
t->file = curfile;
t->fileid = curfileid;
if (data == NULL) {
/* We're done - terminate list */
t->type = 0;
t->data = NULL;
t->line = linenum;
return;
}
t->type = type;
t->line = linenum;
if (float_const != NULL &&
(type == TY_FLOAT
|| type == TY_DOUBLE
|| type == TY_LDOUBLE)) {
/* XXX ugly way to pass the data :( */
t->data = float_const;
} else if (llong_const != NULL
&& (IS_LLONG(type) || IS_LONG(type))) {
/*t->data = llong_const;*/
t->data = data;
t->data2 = llong_const;
} else {
t->data = data;
}
t->line_ptr = lex_line_ptr;
t->tok_ptr = lex_tok_ptr;
if (type == TOK_IDENTIFIER) {
/* Let's try and see whether this identifier is a keyword */
struct keyword *kw;
#if 0
if ((i = lookup_key(data)) != -1) {
#endif
if ((kw = lookup_key(data)) != NULL) {
#if 0
t->type = keywords[i].value;
t->data = t->ascii = keywords[i].name;
free(data);
#endif
#ifdef DEBUG
printf("Identifier is keyword?!)\n");
#endif
if ( /*keywords[i].*/ kw->value == TOK_KEY_EXTENSION) {
/*
* Keyword used to decorate GNU C extensions -
* just ignore it
*/
cur = t->prev;
cur->next = NULL;
free(data);
free(t);
} else if (standard == C89
&& /*keywords[i].*/ kw->std != C89
&& ((char *)data)[0] != '_') {
lexwarning("`%s' is a C99 keyword", data);
t->type = /*keywords[i].*/ kw->value;
t->data = t->ascii = /*keywords[i].*/ kw->name;
free(data);
} else {
t->type = /*keywords[i].*/ kw->value;
t->data = t->ascii = /*keywords[i].*/ kw->name;
free(data);
}
} else {
/*
* Check whether ident invades the implementation
* namespace
*/
if (/*strictansi*/1) {
check_ident(t, data);
}
}
}
}
#ifdef TEST_STORE_TOKEN
/*
* Inserts token specified by data-type-lineno triple before token
* list member pointed to by dest. ``data'' MUST point to dynamically
* allocated memory if you intend to use destroy_toklist()
* XXX this is not used anywhere
*/
void
insert_token(struct token *dest, void *data, int type, int lineno) {
struct token *t = n_xmalloc(sizeof *t);
t->data = data;
t->type = type;
t->line = lineno;
t->next = dest;
t->prev = dest->prev;
dest->prev->next = t;
}
#endif
/*
* Advances token list pointed to by tok to next node if possible.
* Returns 1 if the end of the list was reached unexpectedly, else 0
*/
int
next_token(struct token **tok) {
struct token *t = *tok;
if (t == NULL || t->next == NULL) {
errorfl(t, "Unexpected end of file!");
return 1;
}
*tok = t->next;
return 0;
}
/*
* May not be called to read something that may be the last token
* in a translation unit!
* expect_token(&t, TOK_SEMICOLON) = BAD!! because expect_token()
* fails if there's no token after the token we're looking for
*/
int
expect_token(struct token **tok, int value, int skiptok) {
struct token *t;
int notfound = 0;
if ((t = *tok) == NULL) return -1;
if (next_token(&t) != 0) return -1;
if (t->type == TOK_OPERATOR) {
if (*(int *)t->data != value) {
notfound = 1;
}
} else if (t->type != value) {
notfound = 1;
}
if (notfound) {
char *ascii = tok_to_ascii(value, NULL);
errorfl(t, "Unexpected token `%s' - expected `%s'",
t->ascii, ascii);
}
if (skiptok) {
if (next_token(&t) != 0) return -1;
}
*tok = t;
return 0;
}
#ifdef TEST_STORE_TOKEN
/*
* Deletes all nodes of the token list pointed to by ``tok'', as well
* as their data members, if they're nonzero. (It can't be stressed
* enough that for this very reason only dynamically allocated data
* should be put into the token list nodes.)
* XXX this is not needed anymore
*/
void
destroy_toklist(struct token **tok) {
struct token *t;
struct token *tmp;
for (t = *tok; t != NULL;) {
if (t->data != NULL) free(t->data);
tmp = t;
t = t->next;
free(tmp);
}
*tok = NULL;
}
#endif
#ifdef TEST_STORE_TOKEN
/*
* - Construct list with nodes ``one'', ``two'', ``three'', ``four''
* - Traverse forwards, print all nodes
* - Traverse backwards, print all nodes
* - Insert ``hehe'' as second node in list
*/
int
main(void) {
int i;
struct token *tmp;
struct token *list = NULL;
char *data[] = {
"one", "two", "three", "four", NULL
};
for (i = 0; i < 5; ++i) {
store_token(&list, data[i]?strdup(data[i]):NULL,
TOK_COMP_OPEN, i, NULL);
}
for (tmp = list; tmp->next != NULL; tmp = tmp->next) {
printf("%s\n", (char *)tmp->data);
}
puts("----");
/* Skip terminator */
tmp = tmp->prev;
for (; tmp != NULL; tmp = tmp->prev) {
printf("%s\n", (char *)tmp->data);
}
insert_token(list->next, strdup("hehehe"), TOK_COMP_OPEN, 0);
puts("----");
for (tmp = list; tmp->next != NULL; tmp = tmp->next) {
printf("%s\n", (char *)tmp->data);
}
destroy_toklist(&list);
return 0;
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1