changeset 299:856caf91ffaa ccdev

Added token list structure and switched some stuff to use it Swithced to using a token list structure instead of manually fiddling pointers throughout the macro expansion code. Also fixed up some problematic things related to stringification and concatenation.
author William Astle <lost@l-w.ca>
date Sun, 15 Sep 2013 13:06:00 -0600
parents 6112c67728ba
children 8d6c47395653
files lwcc/lex.c lwcc/preproc.c lwcc/symbol.c lwcc/symbol.h lwcc/token.c lwcc/token.h
diffstat 6 files changed, 340 insertions(+), 338 deletions(-) [+]
line wrap: on
line diff
--- a/lwcc/lex.c	Sat Sep 14 22:42:53 2013 -0600
+++ b/lwcc/lex.c	Sun Sep 15 13:06:00 2013 -0600
@@ -596,7 +596,9 @@
 				{
 					if (!pp -> lexstr)
 						preproc_throw_error(pp, "Invalid character constant");
-					break;
+					ttype = TOK_ERROR;
+					strval = strbuf_end(strbuf);
+					goto out;
 				}
 				cl++;
 				strbuf_add(strbuf, c);
@@ -604,10 +606,15 @@
 			}
 			strbuf_add(strbuf, c);
 		}
-		if (cl == 0 && !pp -> lexstr)
-			preproc_throw_error(pp, "Invalid character constant");
 		strval = strbuf_end(strbuf);
-		ttype = TOK_CHR_LIT;
+		if (cl == 0)
+		{
+			ttype = TOK_ERROR;
+			if (!pp -> lexstr)
+				preproc_throw_error(pp, "Invalid character constant");
+		}
+		else
+			ttype = TOK_CHR_LIT;
 		goto out;
 
 	case '"':
@@ -617,7 +624,15 @@
 		for (;;)
 		{
 			c = preproc_lex_fetch_byte(pp);
-			if (c == CPP_EOF || c == CPP_EOL || c == '"')
+			if (c == CPP_EOF || c == CPP_EOL)
+			{
+				ttype = TOK_ERROR;
+				strval = strbuf_end(strbuf);
+				if (!pp -> lexstr)
+					preproc_throw_error(pp, "Invalid string constant");
+				goto out;
+			}
+			if (c == '"')
 				break;
 			if (c == '\\')
 			{
@@ -625,9 +640,11 @@
 				c = preproc_lex_fetch_byte(pp);
 				if (c == CPP_EOF || c == CPP_EOL)
 				{
+					ttype = TOK_ERROR;
 					if (!pp -> lexstr)
 						preproc_throw_error(pp, "Invalid string constant");
-					break;
+					strval = strbuf_end(strbuf);
+					goto out;
 				}
 				cl++;
 				strbuf_add(strbuf, c);
--- a/lwcc/preproc.c	Sat Sep 14 22:42:53 2013 -0600
+++ b/lwcc/preproc.c	Sun Sep 15 13:06:00 2013 -0600
@@ -254,8 +254,7 @@
 
 static void dir_define(struct preproc_info *pp)
 {
-	struct token *tl = NULL;
-	struct token *ttl;
+	struct token_list *tl = NULL;
 	struct token *ct;
 	int nargs = -1;
 	int vargs = 0;
@@ -335,6 +334,7 @@
 baddefine:
 		preproc_throw_error(pp, "bad #define");
 baddefine2:
+		token_list_destroy(tl);
 		skip_eol(pp);
 		lw_free(mname);
 		while (nargs > 0)
@@ -348,12 +348,7 @@
 		ct = preproc_next_token(pp);
 		if (ct -> ttype == TOK_EOL)
 			break;
-		if (!tl)
-			tl = ct;
-		else
-			ttl -> next = ct;
-		ttl = ct;
-		pp -> curtok = NULL;			// tell *_next_token* not to clobber token
+		token_list_append(tl, token_dup(ct));
 	}
 out:
 	if (strcmp(mname, "defined") == 0)
@@ -767,10 +762,11 @@
 /*
 Below here is the logic for expanding a macro
 */
-static char *stringify(struct token *tl)
+static char *stringify(struct token_list *tli)
 {
 	struct strbuf *s;
 	int ws = 0;
+	struct token *tl = tli -> head;
 	
 	s = strbuf_new();
 	strbuf_add(s, '"');
@@ -804,212 +800,141 @@
 	return strbuf_end(s);
 }
 
-/* return list to tokens as a result of ## expansion */
-static struct token *paste_tokens(struct preproc_info *pp, struct symtab_e *s, struct token **arglist, struct token *t1, struct token *t2)
+static int macro_arg(struct symtab_e *s, char *str)
 {
-	struct token *rl = NULL, *rlt;
-	struct token *s1, *s2;
-	struct token *ws;
 	int i;
+	if (strcmp(str, "__VA_ARGS__") == 0)
+		i = s -> nargs;
+	else
+		for (i = 0; i < s -> nargs; i++)
+			if (strcmp(s -> params[i], str) == 0)
+				break;
+	if (i == s -> nargs)
+		if (s -> vargs == 0)
+			return -1;
+	return i;
+}
+
+/* return list to tokens as a result of ## expansion */
+static struct token_list *paste_tokens(struct preproc_info *pp, struct symtab_e *s, struct token_list **arglist, struct token *t1, struct token *t2)
+{
+	struct token_list *left;
+	struct token_list *right;
 	char *tstr;
+	struct token *ttok;
+	int i;
 	
 	if (t1 -> ttype == TOK_IDENT)
 	{
-		if (strcmp(t1 -> strval, "__VA_ARGS__") == 0)
-		{
-			i = s -> nargs;
-		}
-		else
+		i = macro_arg(s, t1 -> strval);
+		if (i == -1)
 		{
-			for (i = 0; i < s -> nargs; i++)
-			{
-				if (strcmp(s -> params[i], t1 -> strval) == 0)
-					break;
-			}
-		}
-		if ((i == s -> nargs) && !(s -> vargs))
-		{
-			s1 = token_dup(t1);
+			left = token_list_create();
+			token_list_append(left, token_dup(t1));
 		}
 		else
 		{
-			/* find last non-whitespace token */
-			ws = NULL;
-			for (t1 = s -> tl; t1; t1 = t1 -> next)
-			{
-				if (t1 -> ttype != TOK_WSPACE)
-					ws = t1;
-			}
-			if (!ws)
-			{
-				s1 = NULL;
-			}
-			else
-			{
-				if (ws != s -> tl)
-				{
-					/* output extra tokens */
-					for (t1 = s -> tl; t1 -> next != ws; t1 = t1 -> next)
-					{
-						if (!rl)
-						{
-							rl = token_dup(t1);
-							rlt = rl;
-						}
-						else
-						{
-							rlt -> next = token_dup(t1);
-							rlt = rlt -> next;
-						}
-					}
-				}
-				s1 = token_dup(ws);
-			}
+			left = token_list_dup(arglist[i]);
 		}
 	}
 	else
 	{
-		s1 = token_dup(t1);
+		left = token_list_create();
+		token_list_append(left, token_dup(t1));
 	}
+	// munch trailing white space
+	while (left -> tail && left -> tail -> ttype == TOK_WSPACE)
+	{
+		token_list_remove(left -> tail);
+	}
+
 	if (t2 -> ttype == TOK_IDENT)
 	{
-		if (strcmp(t1 -> strval, "__VA_ARGS__") == 0)
+		i = macro_arg(s, t2 -> strval);
+		if (i == -1)
 		{
-			i = s -> nargs;
+			right = token_list_create();
+			token_list_append(right, token_dup(t2));
 		}
 		else
 		{
-			for (i = 0; i < s -> nargs; i++)
-			{
-				if (strcmp(s -> params[i], t1 -> strval) == 0)
-					break;
-			}
-		}
-		if ((i == s -> nargs) && !(s -> vargs))
-		{
-			s2 = token_dup(t2);
-			t2 = NULL;
-		}
-		else
-		{
-			/* find last non-whitespace token */
-			ws = NULL;
-			for (t2 = s -> tl; t2; t2 = t2 -> next)
-			{
-				if (t2 -> ttype != TOK_WSPACE)
-				{
-					ws = t2;
-					t2 = t2 -> next;
-					break;
-				}
-			}
-			if (!ws)
-			{
-				s2 = NULL;
-			}
-			else
-			{
-				s2 = token_dup(ws);
-			}
+			right = token_list_dup(arglist[i]);
 		}
 	}
 	else
 	{
-		s2 = token_dup(t2);
+		right = token_list_create();
+		token_list_append(right, token_dup(t2));
+	}
+	// munch leading white space
+	while (right -> head && right -> head -> ttype == TOK_WSPACE)
+	{
+		token_list_remove(right -> head);
+	}
+
+	// nothing to append at all
+	if (left -> head != NULL && right -> head == NULL)
+	{
+		// right arg is empty - use left
+		token_list_destroy(right);
+		return left;
+	}
+	if (left -> head == NULL && right -> head != NULL)
+	{
+		// left arg is empty, use right
+		token_list_destroy(left);
+		return right;
+	}
+	if (left -> head == NULL && right -> head == NULL)
+	{
+		// both empty, use left
+		token_list_destroy(right);
+		return left;
 	}
 	
-	/* here, s1 is NULL if no left operand or a duplicated token for the actual left operand */
-	/* here, s2 is NULL if no right operand or a duplicated token for the actual right operand */
-	/* here, t2 points to a possibly empty list of extra tokens to output after the concatenated tokens */
-	/* here, rl,rlt is a possibly non-empty list of tokens preceding the concatenation */
-	
-	/* tokens combine if the combination exactly matches "combinelist", in which case the string values are
-	   concatenated and the new token type is used to create a new token. If the tokens do not combine,
-	   s1 and s2 are returned in sequence. */
+	// both non-empty - past left tail with right head
+	// then past the right list onto the left
+	tstr = lw_alloc(strlen(left -> tail -> strval) + strlen(right -> head -> strval) + 1);
+	strcpy(tstr, left -> tail -> strval);
+	strcat(tstr, right -> head -> strval);
 	
-	if (!s1 && s2)
-	{
-		if (!rl)
-			rl = s2;
-		else
-			rlt -> next = s2;
-		rlt = s2;
-	}
-	else if (s1 && !s2)
-	{
-		if (!rl)
-			rl = s1;
-		else
-			rlt -> next = s1;
-		rlt = s1;
-	}
-	else if (s1 && s2)
+	pp -> lexstr = tstr;
+	pp -> lexstrloc = 0;
+	
+	ttok = preproc_lex_next_token(pp);
+	if (ttok -> ttype != TOK_ERROR && pp -> lexstr[pp -> lexstrloc] == 0)
 	{
-		tstr = lw_alloc(strlen(s1 -> strval) + strlen(s2 -> strval) + 1);
-		strcpy(tstr, s1 -> strval);
-		strcat(tstr, s2 -> strval);
-		/* now try to lex the string */
-		pp -> lexstr = tstr;
-		pp -> lexstrloc = 0;
-		t1 = preproc_lex_next_token(pp);
-		if (pp -> lexstr[pp -> lexstrloc])
-		{
-			// doesn't make a new token - pass through the original two
-			if (!rl)
-				rl = s1;
-			else
-				rlt -> next = s1;
-			s1 -> next = s2;
-			rlt = s2;
-		}
-		else
-		{
-			// does make a new token
-			t1 -> fn = s1 -> fn;
-			t1 -> column = s1 -> column;
-			t1 -> lineno = s1 -> lineno;
-			if (!rl)
-				rl = t1;
-			else
-				rlt -> next = t1;
-			rlt = t1;
-		}
-		lw_free(tstr);
-		pp -> lexstr = NULL;
+		// we have a new token here
+		token_list_remove(left -> tail);
+		token_list_remove(right -> head);
+		token_list_append(left, token_dup(ttok));
 	}
-	
-	/* add in any extra tokens */
-	while (t2)
+	lw_free(tstr);
+	pp -> lexstr = NULL;
+	pp -> lexstrloc = 0;
+	for (ttok = right -> head; ttok; ttok = ttok -> next)
 	{
-		if (!rl)
-		{
-			rl = token_dup(t2);
-			rlt = rl;
-		}
-		else
-		{
-			rlt -> next = token_dup(t2);
-			rlt = rlt -> next;
-		}
-		t2 = t2 -> next;
+		token_list_append(left, token_dup(ttok));
 	}
-	
-	return rl;
+	token_list_destroy(right);
+	return left;
 }
 
-
 static int expand_macro(struct preproc_info *pp, char *mname)
 {
 	struct symtab_e *s;
 	struct token *t, *t2, *t3;
-	struct token **arglist = NULL;
 	int nargs = 0;
 	struct expand_e *e;
-	struct token **exparglist = NULL;
+	struct token_list **exparglist = NULL;
+	struct token_list **arglist = NULL;
 	int i;
 	int pcount;
 	char *tstr;
-	
+	struct token_list *expand_list;
+	int repl;
+	struct token_list *rtl;
+			
 	s = symtab_find(pp, mname);
 	if (!s)
 		return 0;
@@ -1056,8 +981,8 @@
 	// parse parameters here
 	t = preproc_next_token_nws(pp);
 	nargs = 1;
-	arglist = lw_alloc(sizeof(struct token *));
-	arglist[0] = NULL;
+	arglist = lw_alloc(sizeof(struct token_list *));
+	arglist[0] = token_list_create();
 	t2 = NULL;
 	
 	while (t -> ttype != TOK_CPAREN)
@@ -1079,22 +1004,13 @@
 			if (!(s -> vargs) || (nargs > s -> nargs))
 			{
 				nargs++;
-				arglist = lw_realloc(arglist, sizeof(struct token *) * nargs);
-				arglist[nargs - 1] = NULL;
+				arglist = lw_realloc(arglist, sizeof(struct token_list *) * nargs);
+				arglist[nargs - 1] = token_list_create();
 				t2 = NULL;
 				continue;
 			}
 		}
-		if (t2)
-		{
-			t2 -> next = token_dup(t);
-			t2 = t2 -> next;
-		}
-		else
-		{
-			t2 = token_dup(t);
-			arglist[nargs - 1] = t2;
-		}
+		token_list_append(arglist[nargs - 1], token_dup(t));
 	}
 
 	if (s -> vargs)
@@ -1113,172 +1029,124 @@
 	}
 
 	/* now calculate the pre-expansions of the arguments */
-	exparglist = lw_alloc(nargs);
+	exparglist = lw_alloc(nargs * sizeof(struct token_list *));
 	for (i = 0; i < nargs; i++)
 	{
-		t2 = NULL;
-		exparglist[i] = NULL;
+		exparglist[i] = token_list_create();
 		// NOTE: do nothing if empty argument
-		if (arglist[i] == NULL)
+		if (arglist[i] == NULL || arglist[i] -> head == NULL)
 			continue;
-		pp -> sourcelist = arglist[i];
+		pp -> sourcelist = arglist[i]->head;
 		for (;;)
 		{
 			t = preproc_next_processed_token(pp);
 			if (t -> ttype == TOK_EOF)
 				break;
-			if (t2)
-			{
-				t2 -> next = token_dup(t);
-				t2 = t2 -> next;
-			}
-			else
-			{
-				t2 = token_dup(t);
-				exparglist[i] = t2;
-			}
+			token_list_append(exparglist[i], token_dup(t));
 		}
 	}
 
 expandmacro:
-	t2 = NULL;
-	t3 = NULL;
-	
-	for (t = s -> tl; t; t = t -> next)
+	expand_list = token_list_dup(s -> tl);
+
+	// scan for stringification and handle it
+	repl = 0;
+	while (repl == 0)
 	{
-again:
-		if (t -> ttype != TOK_WSPACE && t -> next)
+		for (t = expand_list -> head; t; t = t -> next)
 		{
-			struct token *ct1, *ct2;
-			
-			for (ct1 = t -> next; ct1 && ct1 -> ttype == TOK_WSPACE; ct1 = ct1 -> next)
+			if (t -> ttype == TOK_HASH && t -> next && t -> next -> ttype == TOK_IDENT)
 			{
-				if (ct1 -> ttype == TOK_DBLHASH)
+				i = macro_arg(s, t -> next -> strval);
+				if (i != -1)
 				{
-					// possible concatenation here
-					for (ct2 = ct1 -> next; ct2 && ct2 -> ttype == TOK_WSPACE; ct2 = ct2 -> next)
-						/* do nothing */ ;
-					if (ct2)
-					{
-						// we have concatenation here so we paste str1 and str2 together and see what we get
-						// if we get NULL, the past didn't make a valid token
-						ct1 = paste_tokens(pp, s, arglist, t, ct2);
-						if (ct1)
-						{
-							if (t2)
-							{
-								t2 -> next = ct1;
-							}
-							else
-							{
-								t3 = ct1;
-							}
-							for (t2 = ct1; t2 -> next; t2 = t2 -> next)
-								/* do nothing */ ;
-
-							/* because of the level of control structures, move to next token and restart loop */
-							t = ct2 -> next;
-							goto again;
-						}
-						goto nopaste;
-					}
+					repl = 1;
+					tstr = stringify(arglist[i]);
+					token_list_remove(t -> next);
+					token_list_insert(expand_list, t, token_create(TOK_STRING, tstr, t -> lineno, t -> column, t -> fn));
+					token_list_remove(t);
+					lw_free(tstr);
+					break;
 				}
 			}
 		}
+	}
+
+
+	// scan for concatenation and handle it	
 	
-nopaste:
-		if (t -> ttype == TOK_HASH)
+	for (t = expand_list -> head; t; t = t -> next)
+	{
+		if (t -> ttype == TOK_DBLHASH)
 		{
-			if (t -> next && t -> next -> ttype == TOK_IDENT)
+			// have a concatenation operator here
+			for (t2 = t -> prev; t2; t2 = t2 -> prev)
+			{
+				if (t2 -> ttype != TOK_WSPACE)
+					break;
+			}
+			for (t3 = t -> next; t3; t3 = t3 -> next);
 			{
-				if (strcmp(t -> next -> strval, "__VA_ARGS__") == 0)
-				{
-					i = nargs;
-				}
-				else
-				{
-					for (i = 0; i < nargs; i++)
-					{
-						if (strcmp(t -> next -> strval, s -> params[i]) == 0)
-							break;
-					}
-				}
-				if (!((i == s -> nargs) && !(s -> vargs)))
-				{
-					// we have a stringification here
-					t = t -> next;
-					tstr = stringify(arglist[i]);
-					if (t2)
-					{
-						t2 = token_create(TOK_STRING, tstr, t -> lineno, t -> column, t -> fn);
-						t2 = t2 -> next;
-					}
-					else
-					{
-						t3 = token_create(TOK_STRING, tstr, t -> lineno, t -> column, t -> fn);
-						t2 = t3;
-					}
-					lw_free(tstr);
-					continue;
-				}
+				if (t3 -> ttype != TOK_WSPACE)
+					break;
 			}
+			// if no non-whitespace before or after, ignore it
+			if (!t2 || !t3)
+				continue;
+			// eat the whitespace before and after
+			while (t -> prev != t2)
+				token_list_remove(t -> prev);
+			while (t -> next != t3)
+				token_list_remove(t -> next);
+			// now paste t -> prev with t -> next and replace t with the result
+			// continue scanning for ## at t -> next -> next
+			t3 = t -> next -> next;
+			
+			rtl = paste_tokens(pp, s, arglist, t -> prev, t -> next);
+			token_list_remove(t -> next);
+			token_list_remove(t -> prev);
+			t2 = t -> prev;
+			token_list_remove(t);
+			for (t = rtl -> head; t; t = t -> next)
+			{
+				token_list_insert(expand_list, t2, token_dup(t));
+			}
+			t = t3 -> prev;
+			token_list_destroy(rtl);
 		}
+	}
+	
+	// now scan for arguments and expand them
+	for (t = expand_list -> head; t; t = t -> next)
+	{
+	again:
 		if (t -> ttype == TOK_IDENT)
 		{
 			/* identifiers might need expansion to arguments */
-			if (strcmp(t -> strval, "__VA_ARGS__") == 0)
-			{
-				i = s -> nargs;
-			}
-			else
-			{
-				for (i = 0; i < nargs; i++)
-				{
-					if (strcmp(t -> strval, s -> params[i]) == 0)
-						break;
-				}
-			}
-			if ((i == s -> nargs) && !(s -> vargs))
+			i = macro_arg(s, t -> strval);
+			if (i != -1)
 			{
-				struct token *te;
-				// expand argument
-				for (te = exparglist[i]; te; te = te -> next)
-				{
-					if (t2)
-					{
-						t2 -> next = token_dup(te);
-						t2 = t2 -> next;
-					}
-					else
-					{
-						t3 = token_dup(te);
-						t2 = t2;
-					}
-				}
-				continue;
+				t3 = t -> next;
+				for (t2 = exparglist[i] -> tail; t2; t2 = t2 -> prev)
+					token_list_insert(expand_list, t, token_dup(t2));
+				token_list_remove(t);
+				t = t3;
+				goto again;
 			}
 		}
-		if (t2)
-		{
-			t2 -> next = token_dup(t);
-			t2 = t2 -> next;
-		}
-		else
-		{
-			t3 = token_dup(t);
-			t2 = t3;
-		}
 	}
 
 	/* put the new expansion in front of the input, if relevant; if we
 	   expanded to nothing, no need to create an expansion record or
 	   put anything into the input queue */
-	if (t3)
+	if (expand_list -> head)
 	{
-		t2 -> next = token_create(TOK_ENDEXPAND, "", -1, -1, "");
-		t2 -> next -> next = pp -> tokqueue;
-		pp -> tokqueue = t3;
-
+		token_list_append(expand_list, token_create(TOK_ENDEXPAND, "", -1, -1, ""));
+		
+		// move the expanded list into the token queue
+		for (t = expand_list -> tail; t; t = t -> prev)
+			preproc_unget_token(pp, token_dup(t));
+		
 		/* set up expansion record */
 		e = lw_alloc(sizeof(struct expand_e));
 		e -> next = pp -> expand_list;
@@ -1287,10 +1155,11 @@
 	}
 	
 	/* now clean up */
+	token_list_destroy(expand_list);
 	for (i = 0; i < nargs; i++)
 	{
-		lw_free(arglist[i]);
-		lw_free(exparglist[i]);
+		token_list_destroy(arglist[i]);
+		token_list_destroy(exparglist[i]);
 	}
 	lw_free(arglist);
 	lw_free(exparglist);
--- a/lwcc/symbol.c	Sat Sep 14 22:42:53 2013 -0600
+++ b/lwcc/symbol.c	Sun Sep 15 13:06:00 2013 -0600
@@ -31,19 +31,13 @@
 void symbol_free(struct symtab_e *s)
 {
 	int i;
-	struct token *t;
-	
+
 	lw_free(s -> name);
 	
 	for (i = 0; i < s -> nargs; i++)
 		lw_free(s -> params[i]);
 	lw_free(s -> params);
-	while (s -> tl)
-	{
-		t = s -> tl;
-		s -> tl = t -> next;
-		token_free(t);
-	}
+	token_list_destroy(s -> tl);
 }
 
 struct symtab_e *symtab_find(struct preproc_info *pp, char *name)
@@ -77,7 +71,7 @@
 	}
 }
 
-void symtab_define(struct preproc_info *pp, char *name, struct token *def, int nargs, char **params, int vargs)
+void symtab_define(struct preproc_info *pp, char *name, struct token_list *def, int nargs, char **params, int vargs)
 {
 	struct symtab_e *s;
 	
--- a/lwcc/symbol.h	Sat Sep 14 22:42:53 2013 -0600
+++ b/lwcc/symbol.h	Sun Sep 15 13:06:00 2013 -0600
@@ -28,7 +28,7 @@
 struct symtab_e
 {
 	char *name;				// symbol name
-	struct token *tl;		// token list the name is defined as, NULL for none
+	struct token_list *tl;	// token list the name is defined as, NULL for none
 	int nargs;				// number named of arguments - -1 for object like macro
 	int vargs;				// set if macro has varargs style
 	char **params;			// the names of the parameters
@@ -37,6 +37,6 @@
 
 struct symtab_e *symtab_find(struct preproc_info *, char *);
 void symtab_undef(struct preproc_info *, char *);
-void symtab_define(struct preproc_info *, char *, struct token *, int, char **, int);
+void symtab_define(struct preproc_info *, char *, struct token_list *, int, char **, int);
 
 #endif // symbol_h_seen___
--- a/lwcc/token.c	Sat Sep 14 22:42:53 2013 -0600
+++ b/lwcc/token.c	Sun Sep 15 13:06:00 2013 -0600
@@ -132,3 +132,112 @@
 	if (t -> strval)
 		fprintf(f, "%s", t -> strval);
 }
+
+/* token list management */
+struct token_list *token_list_create(void)
+{
+	struct token_list *tl;
+	tl = lw_alloc(sizeof(struct token_list));
+	tl -> head = NULL;
+	tl -> tail = NULL;
+	return tl;
+}
+
+void token_list_destroy(struct token_list *tl)
+{
+	if (tl == NULL)
+		return;
+	while (tl -> head)
+	{
+		tl -> tail = tl -> head;
+		tl -> head = tl -> head -> next;
+		token_free(tl -> tail);
+		lw_free(tl -> tail);
+	}
+	lw_free(tl);
+}
+
+void token_list_append(struct token_list *tl, struct token *tok)
+{
+	tok -> list = tl;
+	if (tl -> head == NULL)
+	{
+		tl -> head = tl -> tail = tok;
+		tok -> next = tok -> prev = NULL;
+		return;
+	}
+	tl -> tail -> next = tok;
+	tok -> prev = tl -> tail;
+	tl -> tail = tok;
+	tok -> next = NULL;
+	return;
+}
+
+void token_list_remove(struct token *tok)
+{
+	if (tok -> list == NULL)
+		return;
+
+	if (tok -> prev)
+		tok -> prev -> next = tok -> next;
+	if (tok -> next)
+		tok -> next -> prev = tok -> prev;
+	if (tok == tok -> list -> head)
+		tok -> list -> head = tok -> next;
+	if (tok == tok -> list -> tail)
+		tok -> list -> tail = tok -> prev;
+	tok -> list = NULL;
+}
+
+void token_list_prepend(struct token_list *tl, struct token *tok)
+{
+	tok -> list = tl;
+	if (tl -> head == NULL)
+	{
+		tl -> head = tl -> tail = tok;
+		tok -> next = tok -> prev = NULL;
+	}
+	tl -> head -> prev = tok;
+	tok -> next = tl -> head;
+	tl -> head = tok;
+	tok -> prev = NULL;
+}
+
+void token_list_insert(struct token_list *tl, struct token *after, struct token *newt)
+{
+	struct token *t;
+	
+	if (after == NULL || tl -> head == NULL)
+	{
+		token_list_prepend(tl, newt);
+		return;
+	}
+	
+	for (t = tl -> head; t && t != after; t = t -> next)
+		/* do nothing */ ;
+	if (!t)
+	{
+		token_list_append(tl, newt);
+		return;
+	}
+	newt -> prev = t;
+	newt -> next = t -> next;
+	if (t -> next)
+		t -> next -> prev = newt;
+	else
+		tl -> tail = newt;
+	t -> next = newt;
+}
+
+struct token_list *token_list_dup(struct token_list *tl)
+{
+	struct token_list *nl;
+	struct token *t;
+	
+	nl = token_list_create();
+	for (t = tl -> head; t; t = t -> next)
+	{
+		token_list_append(nl, token_dup(t));
+	}
+	return nl;
+}
--- a/lwcc/token.h	Sat Sep 14 22:42:53 2013 -0600
+++ b/lwcc/token.h	Sun Sep 15 13:06:00 2013 -0600
@@ -93,6 +93,7 @@
 	TOK_ARROW,
 	TOK_ENDEXPAND,
 	TOK_STARTEXPAND,
+	TOK_ERROR,
 	TOK_MAX
 };
 
@@ -102,23 +103,35 @@
 	char *strval;			// the token value if relevant
 	struct token *prev;		// previous token in a list
 	struct token *next;		// next token in a list
+	struct token_list *list;// pointer to head of list descriptor this token is on
 	int lineno;				// line number token came from
 	int column;				// character column token came from
 	const char *fn;			// file name token came from
 };
 
+struct token_list
+{
+	struct token *head;		// the head of the list
+	struct token *tail;		// the tail of the list
+};
+
 extern void token_free(struct token *);
 extern struct token *token_create(int, char *strval, int, int, const char *);
 extern struct token *token_dup(struct token *);
 /* add a token to the end of a list */
-extern void token_append(struct token *, struct token *);
+extern void token_list_append(struct token_list *, struct token *);
 /* add a token to the start of a list */
-extern void token_prepend(struct token *, struct token *);
+extern void token_list_prepend(struct token_list *, struct token *);
 /* remove individual token from whatever list it is on */
-extern void token_remove(struct token *);
+extern void token_list_remove(struct token *);
 /* replace token with list of tokens specified */
-extern void token_replace(struct token *, struct token *);
+extern void token_list_insert(struct token_list *, struct token *, struct token *);
+/* duplicate a list to a new list pointer */
+extern struct token_list *token_list_dup(struct token_list *);
 /* print a token out */
+extern struct token_list *token_list_create(void);
+extern void token_list_destroy(struct token_list *);
+
 extern void token_print(struct token *, FILE *);
 
 #endif // token_h_seen___