shithub: scc

ref: cffac6768dcdad6bf8fadf8686b89c389a60dc22
dir: /lex.c/

View raw version

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "symbol.h"
#include "tokens.h"


#define NR_KWD_HASH 32

static struct keyword {
	char *str;
	unsigned char tok;
	struct keyword *next;
} keywords [] = {"auto", AUTO, NULL,
		 "break", BREAK, NULL,
		 "_Bool", CHAR, NULL,
		 "case", CASE, NULL,
		 "char", CHAR, NULL,
		 "const", CONST, NULL,
		 "continue", CONTINUE, NULL,
		 "default", DEFAULT, NULL,
		 "do", DO, NULL,
		 "double", DOUBLE, NULL,
		 "else", ELSE, NULL,
		 "enum", ENUM, NULL,
		 "extern", EXTERN, NULL,
		 "float", FLOAT, NULL,
		 "for", FOR, NULL,
		 "goto", GOTO, NULL,
		 "if", IF, NULL,
		 "int", INT, NULL,
		 "long", LONG, NULL,
		 "register", REGISTER, NULL,
		 "restricted", RESTRICTED, NULL,
		 "return", RETURN, NULL,
		 "short", SHORT, NULL,
		 "signed", SIGNED, NULL,
		 "sizeof", SIZEOF, NULL,
		 "static", STATIC, NULL,
		 "struct", STRUCT, NULL,
		 "switch", SWITCH, NULL,
		 "typedef", TYPEDEF, NULL,
		 "union", UNION, NULL,
		 "unsigned", UNSIGNED, NULL,
		 "void", VOID, NULL,
		 "volatile", VOLATILE, NULL,
		 "while", WHILE, NULL,
		 NULL, 0, NULL
};

static struct keyword *khash[NR_KWD_HASH];
static FILE *yyin;

unsigned char yytoken;
unsigned char yyhash;
char yytext[TOKSIZ_MAX + 1];
unsigned linenum;
unsigned columnum;
const char *filename;


static unsigned char hashfun(register const char *s)
{
	register unsigned char h, ch;

	for (h = 0; ch = *s++; h += ch)
		/* nothing */;
	return h;
}

#ifndef NDEBUG
static char *toknames[] = {
	[INT] = "INT",
	[CHAR] = "CHAR",
	[FLOAT] = "FLOAT",
	[LONG] = "LONG",
	[LLONG] = "LLONG",
	[SHORT] = "SHORT",
	[VOID] = "VOID",
	[DOUBLE] = "DOUBLE",
	[LDOUBLE] = "LDOUBLE",
	[STRUCT] = "STRUCT",
	[UNION] = "UNION",
	[ENUM] = "ENUM",
	[UTYPE] = "UTYPE",
	[BOOL] = "BOOL",
	[TYPEDEF] = "TYPEDEF",
	[EXTERN] = "EXTERN",
	[STATIC] = "STATIC",
	[AUTO] = "AUTO",
	[REGISTER] = "REGISTER",
	[VOLATILE] = "VOLATILE",
	[CONST] = "CONST",
	[RESTRICTED] = "RESTRICTED",
	[UNSIGNED] = "UNSIGNED",
	[SIGNED] = "SIGNED",
	[IDENTIFIER] = "IDENTIFIER",
	[CONSTANT] = "CONSTANT",
	[STRING_LITERAL] = "STRING_LITERAL",
	[SIZEOF] = "SIZEOF",
	[PTR_OP] = "PTR_OP",
	[INC_OP] = "INC_OP",
	[DEC_OP] = "DEC_OP",
	[LSHIFT_OP] = "LEFT_OP",
	[RSHIFT_OP] = "RIGHT_OP",
	[LE_OP] = "LE_OP",
	[GE_OP] = "GE_OP",
	[EQ_OP] = "EQ_OP",
	[NE_OP] = "NE_OP",
	[AND_OP] = "AND_OP",
	[OR_OP] = "OR_OP",
	[MUL_ASSIGN] = "MUL_ASSIGN",
	[DIV_ASSIGN] = "DIV_ASSIGN",
	[MOD_ASSIGN] = "MOD_ASSIGN",
	[ADD_ASSIGN] = "ADD_ASSIGN",
	[SUB_ASSIGN] = "SUB_ASSIGN",
	[LSHIFT_ASSIGN] = "LSHIFT_ASSIGN",
	[RSHIFT_ASSIGN] = "RSHIFT_ASSIGN",
	[AND_ASSIGN] = "AND_ASSIGN",
	[XOR_ASSIGN] = "XOR_ASSIGN",
	[OR_ASSIGN] = "OR_ASSIGN",
	[TYPE_NAME] = "TYPE_NAME",
	[ELLIPSIS] = "ELLIPSIS",
	[CASE] = "CASE",
	[DEFAULT] = "DEFAULT",
	[IF] = "IF",
	[ELSE] = "ELSE",
	[SWITCH] = "SWITCH",
	[WHILE] = "WHILE",
	[DO] = "DO",
	[FOR] = "FOR",
	[GOTO] = "GOTO",
	[CONTINUE] = "CONTINUE",
	[BREAK] = "BREAK",
	[RETURN] = "RETURN",
	[EOFTOK] = "EOFTOK"
};

#endif

void init_lex(void)
{
	register struct keyword *bp;
	static unsigned char h;

	for (bp = keywords; bp->str; bp++) {
		register struct keyword *aux, *ant;
		h = hashfun(bp->str) & (NR_KWD_HASH - 1);
		if (!(aux = khash[h]) || strcmp(bp->str, aux->str) < 0) {
			khash[h] = bp;
			bp->next = aux;
			continue;
		}
		for (ant = aux; aux; ant = aux, aux = aux->next) {
			if (strcmp(bp->str, aux->str) < 0)
				break;
		}
		ant->next = bp;
		bp->next = aux;
	}
}

static char number(void)
{
	register char *bp;
	register char ch;

	for (bp = yytext; bp < yytext + TOKSIZ_MAX; *bp++ = ch) {
		if (!isdigit(ch = getc(yyin)))
			break;
	}
	if (bp == yytext + TOKSIZ_MAX)
		error("identifier too long %s", yytext);
	ungetc(ch, yyin);
	*bp = '\0';
	return CONSTANT;
}

static unsigned char iden(void)
{
	register struct keyword *kwp;
	register char ch;
	register char *bp = yytext;

	for (yyhash = 0; bp < yytext + TOKSIZ_MAX; *bp++ = ch) {
		if (!isalnum(ch = getc(yyin)) && ch != '_')
			break;
		yyhash += ch;
	}
	if (bp == yytext + TOKSIZ_MAX)
		error("identifier too long %s", yytext);
	ungetc(ch, yyin);
	*bp = '\0';
	yyhash &= NR_KWD_HASH - 1;
	for (kwp = khash[yyhash]; kwp; kwp = kwp->next) {
		if (!strcmp(kwp->str, yytext))
			return kwp->tok;
	}
	return IDENTIFIER;
}



unsigned char next(void)
{
	static unsigned int c;
	register unsigned char ch;
	extern char parser_out_home;

	while (isspace(c = getc(yyin)))
		/* nothing */;
	if (c == EOF) {
		if (parser_out_home)
			error("Find EOF while parsing");
		ch = EOFTOK;
		memcpy(yytext, "EOF", sizeof("EOF"));
		goto return_token;
	}
	ch = c;
	if (isalpha(ch) || ch == '_') {
		ungetc(ch, yyin);
		ch = iden();
	} else if (isdigit(ch)) {
		ungetc(ch, yyin);
		ch = number();
	} else {
		register unsigned char aux;;
		aux = getc(yyin);
		yytext[0] = ch;
		yytext[1] = aux;
		yytext[2] = '\0';

		switch (ch) {
		case '&':
			switch (aux) {
			case '&': ch = AND_OP; break;
			case '=': ch = AND_ASSIGN; break;
			default:  goto no_doble_character;
			}
			break;
		case '|':
			switch (aux) {
			case '|': ch = OR_OP; break;
			case '=': ch = OR_ASSIGN; break;
			default: goto no_doble_character;
			}
			break;
		case '<':
			switch (aux) {
			case '<':  ch = LSHIFT_OP; break;
			case '=':  ch = LSHIFT_ASSIGN; break;
			default: goto no_doble_character;
			}
			break;
		case '>':
			switch (aux) {
			case '<':  ch = RSHIFT_OP; break;
			case '=':  ch = RSHIFT_ASSIGN; break;
			default: goto no_doble_character;
			}
			break;
		case '-':
			switch (aux) {
			case '-':  ch = DEC_OP; break;
			case '>':  ch = PTR_OP; break;
			case '=':  ch = ADD_ASSIGN; break;
			default: goto no_doble_character;
			}
			break;
		case '=':
			if (aux == '=') ch = EQ_OP;
			else goto no_doble_character;
			break;
		case '^':
			if (aux == '=') ch = XOR_ASSIGN;
			else goto no_doble_character;
			break;
		case '*':
			if (aux == '=') ch = LSHIFT_ASSIGN;
			else goto no_doble_character;
			break;
		case '+':
			if (aux == '+')  ch = INC_OP;
			else if (aux == '=') ch = ADD_ASSIGN;
			else goto no_doble_character;
			break;
		case '!':
			if (aux == '=') {
				ch = EQ_OP;
				break;
			}
		no_doble_character:
		case '/': case ';': case '{': case '}':
		case '(': case ')': case '~': case ',':
		case '?': case '[': case ']': case ':':
			ungetc(aux, yyin);
			yytext[1] = '\0';
			break;
		default:
			error("Incorrect character '%02x", c);
		}
	}

return_token:
#ifndef NDEBUG
	printf(!toknames[ch] ?
	       "Token = (%u, '%s')\n" :
	       "Token = (%u, '%s' %s)\n",
	       (unsigned) ch, yytext, toknames[ch]);
#endif
	return yytoken = ch;
}

char accept(unsigned char tok)
{
	if (yytoken == tok) {
		next();
		return 1;
	}
	return 0;
}

void expect(unsigned char tok)
{
	if (yytoken != tok)
		error("unexpected %s", yytext);
	next();
}

void open_file(const char *file)
{
	if (yyin != NULL)
		fclose(yyin);
	if (file == NULL) {
		yyin = stdin;
		filename = "(stdin)";
		return;
	}
	if ((yyin = fopen(file, "r")) == NULL)
		die("file '%s' not found", file);
	filename = file;
}