All pastes #818183 Raw Edit

k4st

public c v1 · immutable
·published 2007-12-16 03:45 UTC
rendered paste body
#include <stdlib.h>#include <string.h>#include <ctype.h>#include "../types/tokens.h"/* Return a single token from the buffer.      Note: the buffer and the current character position need to be initialized   and maintained *outside* of this function. To work this function, use a   while/for loop checking that this function doesn't return NULL. At the end    of loop increment 'pos'!! */SwToken *get_token(char *buffer, unsigned int *pos) {		unsigned int i;	char ch, nch;	TOKEN tk;			/* Ignore all white space and control characters. */	for(i = *pos; (buffer[i] != '\0') && (iscntrl(buffer[i]) || \		isspace(buffer[i])); i++);	*pos = i;		/* Make sure we're not at the end of the string */	if(buffer[*pos] == '\0')		return NULL;		/* Get the current and next characters */	ch = buffer[*pos];	nch = peek_next_token(buffer, pos);		/* We will do cases for every one-character token, then the default will	   be looked at as a possible identifier. The one-character tokens are	   used so that we can peek ahead to the next character(s) and see if we	   should collect more characters for the token on stay with the original	   single-character token.	 */	switch(ch) {				/* The following tokens have only a single character and no other 		   tokens start with the same character, so we can handle these in the		   same way.		 */		case ':': tk = SW_TOKEN_COLON;          goto SwTokenSimpleCreate;		case ';': tk = SW_TOKEN_SEMICOLON;      goto SwTokenSimpleCreate;		case '.': tk = SW_TOKEN_PERIOD;         goto SwTokenSimpleCreate;		case ',': tk = SW_TOKEN_COMMA;          goto SwTokenSimpleCreate;		case '_': tk = SW_TOKEN_JOIN;           goto SwTokenSimpleCreate;		case '/': tk = SW_TOKEN_DIVIDE;         goto SwTokenSimpleCreate;		case '+': tk = SW_TOKEN_ADD;            goto SwTokenSimpleCreate;		case '(': tk = SW_TOKEN_PAREN_OPEN;     goto SwTokenSimpleCreate;		case ')': tk = SW_TOKEN_PAREN_CLOSE;    goto SwTokenSimpleCreate;		case '[': tk = SW_TOKEN_BRACKET_OPEN;   goto SwTokenSimpleCreate;		case ']': tk = SW_TOKEN_BRACKET_CLOSE;  goto SwTokenSimpleCreate;		case '{': tk = SW_TOKEN_BRACE_OPEN;     goto SwTokenSimpleCreate;		case '}': tk = SW_TOKEN_BRACE_CLOSE;    goto SwTokenSimpleCreate;		case '~': tk = SW_TOKEN_BIT_COMPLEMENT; goto SwTokenSimpleCreate;		case '%': tk = SW_TOKEN_SUBSTITUTE;     goto SwTokenSimpleCreate;						SwTokenSimpleCreate:             /* goto's go here */						return make_token(tk, NULL);				/* Make tokens given that the next character. The choice for these is		   between two possible tokens where there are only two tokens that		   start with the same character.		 */		case '!':			return make_token_cond(nc != '=', SW_TOKEN_NOT, \								  (*pos++, SW_TOKEN_NOT_EQUALS));		case '-':			return make_token_cond(nc != '>', SW_TOKEN_SUBTRACT, \								  (*pos++, SW_TOKEN_RELATE));		case '*':			return make_token_cond(c != nc, SW_TOKEN_MULTIPLY, \								  (*pos++, SW_TOKEN_EXPONENT));		case '|':			return make_token_cond(c != nc, SW_TOKEN_BIT_OR, \								  (*pos++, SW_TOKEN_LOGICAL_OR));		case '&':			return make_token_cond(c != nc, SW_TOKEN_BIT_AND, \								  (*pos++, SW_TOKEN_LOGICAL_AND));		case '^':			return make_token_cond(c != nc, SW_TOKEN_BIT_XOR, \								  (*pos++, SW_TOKEN_LOCIAL_XOR));				/* Make tokens given the next character, where there are three 		   possible tokens that start with the same character.		 */		case '=':			return nc == '>' ? make_token((*pos++, SW_TOKEN_PROCEDURE), NULL):				   make_token_cond(c != nc, SW_TOKEN_SET, \								  (*pos++, SW_TOKEN_EQUALS));		case '<':			return nc == '=' ? make_token((*pos++, SW_TOKEN_LT_EQUAL), NULL) :				   make_token_cond(c != nc, SW_TOKEN_LT, \								  (*pos++, SW_TOKEN_BIT_LSHIFT));		case '>':			return nc == '=' ? make_token((*pos++, SW_TOKEN_GT_EQUAL), NULL) :				   make_token_cond(c != nc, SW_TOKEN_GT, \								  (*pos++, SW_TOKEN_BIT_RSHIFT));				/* Special case for the ellipsis signifying more arguments. */		case '.':			if(nc != '.')				return make_token(SW_TOKEN_PERIOD, NULL);			else				if(nc == '.' && buffer[*pos+2] == '.') {					*pos += 2;										return make_token(SW_TOKEN_MORE_ARGS, NULL);				} else {					return make_token(SW_TOKEN_RANGE, NULL);				}						/* Special cases, when we find a ' or " then we will try to find the		   string literal and we will consider that to be a token instead of		   tokenizing the actual ' or ".		 */		case '\'':		case '"':			return make_token_string_literal(ch, buffer, pos);				/* We will accept all other characters from here. We can assume that		   they will somehow be identifiers. In the parsing stage, if they are		   misplaced then the program will report an error.		 */		default:			return make_token_identifier(buffer, pos);	}}/* Allocate and return a token. If no value is needed for the token, as is the   case for most tokens, then NULL will be supplied. The 'val' is useful for   numbers, string literals, and identifiers. */SwToken *make_token(TOKEN token, char *val) {	}/* This is for when we find a character and are looking to see if the next   character is the same. If the next character is the same, then we have   found the 'xx' token, otherwise we stick with the original guess of having   found the 'x' token. */SwToken *make_token_cond(int cond, TOKEN x, TOKEN xx) {	return make_token((cond ? x : xx), NULL);}/* Make a token for a string literal. This will work for single/double quotes   and Python-like triple quoting. */SwToken *make_token_string_literal(char q, char *buffer, unsigned int *pos) {		unsigned int i, started_at = 0, ended_at = 0;	int is_triple = 0;		/* We could be dealing with a triple-quotes literal or an empty string. */	if(q == peek_next_token(buffer, pos)) {				*pos++;				/* yes, this is triple-quotes */		if(q == peek_next_token(buffer, pos)) {			is_triple = 1;			*pos++;					/* no, this is an empty string */		} else {			ended_at = *pos+1;						goto SwTokenMakeLiteral;		}	}		/* start from the character *after* this quotation mark */	started_at = *pos+1;		/* Go over the characters in the buffer and try to find the end of this	   string literal.	 */	for(i = started_at; (buffer[i] != '\0'); i++) {		if(buffer[i] == q) {						/* ignore this quote because the previous character negated it			   with a backslash			 */			if(buffer[i-1] == '\\')				continue;						/* If this is a triple, than this might be an everyday quote			   inside it which is allowed, otherwise it could be the ending			   one. Let's make sure.			 */			else {								/* The closing of the triple-quote. We don't need to worry				   about going out-of-bounds given the assumption that buffer				   is a properly formatted C string.				 */				if(is_triple && (buffer[i+1] == q) && (buffer[i+2] == q)) {										ended_at = i;					*pos += i + 2;										break;									/* Not a triple, we have found the end of the end of the				   string. */				} else if(!is_triple) {										ended_at = i;					*pos += i;										break;				}			}		}	}		/* We now have the start and end position of the string. Let's allocate	   space for it and then make the token.	 */	SwTokenMakeLiteral:		return make_token(SW_TOKEN_STRING_LITERAL, make_token_value(buffer, \					  started_at, ended_at));}char *make_token_value(char *buffer, int started_at, int ended_at) {	char *literal;	size_t len;		len = ended_at - started_at;		if( (literal = (char *)malloc(sizeof(char) * (len + 1))) == NULL)		FATAL_ERROR("Could not allocate memory for string literal.");		/* Make the string. Copies the memory at the right start location by	   using pointer arithmetic.	 */	literal = (char *)memcpy(literal, buffer + started_at, len);	literal[len] = '\0';		return literal;}/* Create a token for a text-only identifier. The identifier can have a lot in   it but there are restrictions.      This can find ints/floats, or normal identifiers. Note: we don't worry   about numbers being negative. The parser can figure this out :) */SwToken *make_token_identifier(char *buffer, unsigned int *pos) {	unsigned int i = *pos, started_at = i;	char *val;	TOKEN tk;		/* number, could be a float, integer, etc. */	if(buffer[i] >= '0' || buffer[i] <= '9') {		/* identifier of some sort, could be a built-in construct */		} else if(char_allowed_in_identifier(buffer[i])) {				for(i = *pos; char_allowed_in_identifier(buffer[i]); i++);				val = make_token_value(buffer, started_at, i);				switch(val) {			case "for":			case "do":			case "while":			case "map":			case "reduce":			case "yield":			case "resume":			case "transfer":			case "return":			case "continue":			case "break":						case "is": tk = SW_TOKEN_IS;            goto SwTokenMakeFreeValue;			case "in": tk = SW_TOKEN_IN;            goto SwTokenMakeFreeValue;			case "true": tk = SW_TOKEN_BOOL_TRUE;   goto SwTokenMakeFreeValue;			case "false": tk = SW_TOKEN_BOOL_FALSE; goto SwTokenMakeFreeValue;			default: tk = SW_TOKEN_IDENTIFIER;     goto SwTokenMakeIdentifier; 		}				SwTokenMakeFreeValue:		val = (free(val), NULL);				SwTokenMakeIdentifier: /* goto comes here, skip the freeing of the val		                          if this isn't something that we recognize.		                        */		return make_token(SW_TOKEN_STRING_LITERAL, val);		/* unknown character, todo: error or act as comment? */				} else		PARSE_ERROR(sprintf("Unknown character [%c] found.", buffer[i]));}int char_allowed_in_identifier(char ch) {	return ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || \		    (ch >= '0' && ch <= '9') || ch == '_' || ch == '$');}/* Peek at the next character in the buffer */char peek_next_token(char *buffer, unsigned int *pos) {	return buffer[*pos+1];}