diff lwasm/macro.c @ 151:427e268e876b

renamed src to lwasm to better reflect its purpose
author lost
date Fri, 30 Jan 2009 04:01:55 +0000
parents src/macro.c@918be0c02239
children bae1e3ecdce1
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/macro.c	Fri Jan 30 04:01:55 2009 +0000
@@ -0,0 +1,340 @@
+/*
+macro.c
+Copyright © 2008 William Astle
+
+This file is part of LWASM.
+
+LWASM is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+
+Contains stuff associated with macro processing
+*/
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include "lwasm.h"
+#include "instab.h"
+#include "util.h"
+
+OPFUNC(pseudo_macro)
+{
+	macrotab_t *m;
+
+	// if skipping a condition, flag in a macro
+	if (as -> skipcond)
+	{
+		as -> skipmacro = 1;
+		return;
+	}
+
+	// actually define a macro
+	if (as -> inmacro)
+	{
+		register_error(as, l, 1, "Attempt to define a macro inside a macro");
+		return;
+	}
+
+	as -> inmacro = 1;
+
+	// don't actually do anything if not pass 1
+	if (as -> passnum != 1)
+		return;
+
+
+	if (!l -> sym)
+	{
+		register_error(as, l, 1, "Macro definition with no effect - no symbol");
+		return;
+	}
+	
+	// search for macro by same name...
+	for (m = as -> macros; m; m = m -> next)
+	{
+		if (!strcmp(m -> name, l -> sym))
+			break;
+	}
+	if (m)
+	{
+		register_error(as, l, 1, "Duplicate macro definition");
+		return;
+	}
+	
+	m = lwasm_alloc(sizeof(macrotab_t));
+	m -> name = lwasm_strdup(l -> sym);
+	m -> next = as -> macros;
+	m -> lines = NULL;
+	m -> numlines = 0;
+	as -> macros = m;
+
+	while (**p && !isspace(**p))
+		(*p)++;
+}
+
+OPFUNC(pseudo_endm)
+{
+	if (as -> skipcond)
+	{
+		as -> skipmacro = 0;
+		return;
+	}
+	
+	if (!as -> inmacro)
+	{
+		register_error(as, l, 1, "ENDM without MACRO");
+		return;
+	}
+	
+	as -> inmacro = 0;
+	
+	// a macro definition counts as a context break for local symbols
+	as -> context = lwasm_next_context(as);
+}
+
+// the current macro will ALWAYS be the first one in the table
+int add_macro_line(asmstate_t *as, char *optr)
+{
+	if (!as -> inmacro)
+		return 0;
+	
+	if (as -> passnum == 2)
+		return 1;
+
+	as -> macros -> lines = lwasm_realloc(as -> macros -> lines, sizeof(char *) * (as -> macros -> numlines + 1));
+	as -> macros -> lines[as -> macros -> numlines] = lwasm_strdup(optr);
+	as -> macros -> numlines += 1;
+	return 1;
+}
+
+void macro_add_to_buff(char **buff, int *loc, int *len, char c)
+{
+	if (*loc == *len)
+	{
+		*buff = lwasm_realloc(*buff, *len + 32);
+		*len += 32;
+	}
+	(*buff)[(*loc)++] = c;
+}
+
+// this is just like a regular operation function
+/*
+macro args are referenced by "\n" where 1 <= n <= 9
+or by "\{n}"; a \ can be included by writing \\
+a comma separates argument but one can be included with "\,"
+whitespace ends argument list but can be included with "\ " or the like
+
+In pass 1, actually add the lines to the system, in pass 2, do not
+In pass 2, track the number of lines to skip because they already will be
+processed by this function - this will be in as -> skiplines
+
+*/
+int expand_macro(asmstate_t *as, lwasm_line_t *l, char **p, char *opc)
+{
+	int lc;
+	lwasm_line_t *cl, *nl;
+	int oldcontext;
+	macrotab_t *m;
+
+	char **args = NULL;		// macro arguments
+	int nargs = 0;			// number of arguments
+
+	char *p2, *p3;
+	
+	int bloc, blen;
+	char *linebuff;
+
+	for (m = as -> macros; m; m = m -> next)
+	{
+		if (!strcmp(opc, m -> name))
+			break;
+	}
+	// signal no macro expansion
+	if (!m)
+		return -1;
+	
+
+	// save current symbol context for after macro expansion
+	oldcontext = as -> context;
+
+	cl = l;
+
+	as -> context = lwasm_next_context(as);
+
+	// step 1: parse arguments (pass 1 only)
+	if (as -> passnum == 1)
+	{
+		while (**p && !isspace(**p) && **p != ',')
+		{
+			p2 = *p;
+			while (*p2 && !isspace(*p2) && *p2 != ',')
+			{
+				if (*p2 == '\\')
+				{
+					if (p2[1])
+						p2++;
+				}
+				p2++;
+			}
+			
+			// have arg here
+			args = lwasm_realloc(args, sizeof(char *) * (nargs + 1));
+			args[nargs] = lwasm_alloc(p2 - *p + 1);
+			args[nargs][p2 - *p] = '\0';
+			memcpy(args[nargs], *p, p2 - *p);
+			*p = p2;
+			
+			// now collapse out "\" characters
+			for (p3 = p2 = args[nargs]; *p2; p2++, p3++)
+			{
+				if (*p2 == '\\' && p2[1])
+				{
+					p2++;
+				}
+				*p3 = *p2;
+			}
+			*p3 = '\0';
+			
+			nargs++;
+			if (**p == ',')
+				(*p)++;
+		}
+	}
+	
+	{
+		int i;
+		for (i = 0; i < nargs; i++)
+		{
+			debug_message(10, "Macro (%s) arg %d: %s", m -> name, i + 1, args[i]);
+		}
+	}
+	
+	// step 2: iterate over the lines
+	if (as -> passnum == 2)
+	{
+		// pass 2 only - parse the lines and count them
+		for (lc = 0; lc < m -> numlines; lc++)
+		{
+			cl = cl -> next;
+			as -> skiplines++;
+			lwasm_parse_line(as, cl);
+		}
+	}
+	else
+	{
+		// pass 1 only - construct the lines and parse them
+		for (lc = 0; lc < m -> numlines; lc++)
+		{
+			nl = lwasm_alloc(sizeof(lwasm_line_t));
+			nl -> lineno = lc + 1;
+			nl -> filename = m -> name;
+			nl -> next = NULL;
+			nl -> prev = as -> linestail;
+			nl -> err = NULL;
+			nl -> fsize = 0;
+			nl -> sym = NULL;
+			nl -> bytes = NULL;
+			nl -> codelen = 0;
+			nl -> codesize = 0;
+			nl -> nocodelen = 0;
+			nl -> addrset = 0;
+			nl -> symaddr = -1;
+			nl -> badop = 0;
+			nl -> relocoff = -1;
+			if (as -> linestail)
+				as -> linestail -> next = nl;
+			as -> linestail = nl;
+			if (!(as -> lineshead))
+				as -> lineshead = nl;
+
+			bloc = blen = 0;
+			linebuff = NULL;
+			for (p2 = m -> lines[lc]; *p2; p2++)
+			{
+				if (*p2 == '\\' && isdigit(p2[1]))
+				{
+					int n;
+					
+					p2++;
+					n = *p2 - '0';
+					if (n == 0)
+					{
+						for (p3 = m -> name; *p3; p3++)
+							macro_add_to_buff(&linebuff, &bloc, &blen, *p3);
+						continue;
+					}
+					if (n < 1 || n > nargs)
+						continue;
+					for (p3 = args[n - 1]; *p3; p3++)
+						macro_add_to_buff(&linebuff, &bloc, &blen, *p3);
+					continue;
+				}
+				else if (*p2 == '{')
+				{
+					int n = 0, n2;
+					p2++;
+					while (*p2 && isdigit(*p2))
+					{
+						n2 = *p2 - '0';
+						if (n2 < 0 || n2 > 9)
+							n2 = 0;
+						n = n * 10 + n2;
+						p2++;
+					}
+					if (*p2 == '}')
+						p2++;
+					 
+					if (n == 0)
+					{
+						for (p3 = m -> name; *p3; p3++)
+							macro_add_to_buff(&linebuff, &bloc, &blen, *p3);
+						continue;
+					}
+					if (n < 1 || n > nargs)
+						continue;
+					for (p3 = args[n - 1]; *p3; p3++)
+						macro_add_to_buff(&linebuff, &bloc, &blen, *p3);
+					continue;
+				}
+				else
+				{
+					macro_add_to_buff(&linebuff, &bloc, &blen, *p2);
+				}
+			}
+
+			macro_add_to_buff(&linebuff, &bloc, &blen, 0);
+			
+			nl -> text = linebuff;
+
+			lwasm_parse_line(as, nl);
+			if (as -> endseen)
+				break;
+
+		}
+	}
+	
+	// restore context from before the macro was called
+	as -> context = oldcontext;
+
+	// clean up
+	if (args)
+	{
+		while (nargs)
+		{
+			lwasm_free(args[--nargs]);
+		}
+		lwasm_free(args);
+	}
+
+	// indicate a macro was expanded
+	return 0;	
+}