text: /*
  *   $Id: asm.c 536 2007-06-02 06:09:00Z elliotth $
  *
  *   Copyright (c) 2000-2003, Darren Hiebert
  *
  *   This source code is released for free distribution under the terms of the
  *   GNU General Public License.
  *
  *   This module contains functions for generating tags for assembly language
  *   files.
  */
  
  /*
  *   INCLUDE FILES
  */
  #include "general.h"  /* must always come first */
  
  #include <string.h>
  
  #include "debug.h"
  #include "keyword.h"
  #include "parse.h"
  #include "read.h"
  #include "routines.h"
  #include "vstring.h"
  
  /*
  *   DATA DECLARATIONS
  */
  typedef enum {
  	K_NONE = -1, K_DEFINE, K_LABEL, K_MACRO, K_TYPE
  } AsmKind;
  
  typedef enum {
  	OP_UNDEFINED = -1,
  	OP_ALIGN,
  	OP_COLON_EQUAL,
  	OP_END,
  	OP_ENDM,
  	OP_ENDMACRO,
  	OP_ENDP,
  	OP_ENDS,
  	OP_EQU,
  	OP_EQUAL,
  	OP_LABEL,
  	OP_MACRO,
  	OP_PROC,
  	OP_RECORD,
  	OP_SECTIONS,
  	OP_SET,
  	OP_STRUCT,
  	OP_LAST
  } opKeyword;
  
  typedef struct {
  	const char *operator;
  	opKeyword keyword;
  } asmKeyword;
  
  typedef struct {
  	opKeyword keyword;
  	AsmKind kind;
  } opKind;
  
  /*
  *   DATA DEFINITIONS
  */
  static langType Lang_asm;
  
  static kindOption AsmKinds [] = {
  	{ TRUE, 'd', "define", "defines" },
  	{ TRUE, 'l', "label",  "labels"  },
  	{ TRUE, 'm', "macro",  "macros"  },
  	{ TRUE, 't', "type",   "types (structs and records)"   }
  };
  
  static const asmKeyword AsmKeywords [] = {
  	{ "align",    OP_ALIGN       },
  	{ "endmacro", OP_ENDMACRO    },
  	{ "endm",     OP_ENDM        },
  	{ "end",      OP_END         },
  	{ "endp",     OP_ENDP        },
  	{ "ends",     OP_ENDS        },
  	{ "equ",      OP_EQU         },
  	{ "label",    OP_LABEL       },
  	{ "macro",    OP_MACRO       },
  	{ ":=",       OP_COLON_EQUAL },
  	{ "=",        OP_EQUAL       },
  	{ "proc",     OP_PROC        },
  	{ "record",   OP_RECORD      },
  	{ "sections", OP_SECTIONS    },
  	{ "set",      OP_SET         },
  	{ "struct",   OP_STRUCT      }
  };
  
  static const opKind OpKinds [] = {
  	/* must be ordered same as opKeyword enumeration */
  	{ OP_ALIGN,       K_NONE   },
  	{ OP_COLON_EQUAL, K_DEFINE },
  	{ OP_END,         K_NONE   },
  	{ OP_ENDM,        K_NONE   },
  	{ OP_ENDMACRO,    K_NONE   },
  	{ OP_ENDP,        K_NONE   },
  	{ OP_ENDS,        K_NONE   },
  	{ OP_EQU,         K_DEFINE },
  	{ OP_EQUAL,       K_DEFINE },
  	{ OP_LABEL,       K_LABEL  },
  	{ OP_MACRO,       K_MACRO  },
  	{ OP_PROC,        K_LABEL  },
  	{ OP_RECORD,      K_TYPE   },
  	{ OP_SECTIONS,    K_NONE   },
  	{ OP_SET,         K_DEFINE },
  	{ OP_STRUCT,      K_TYPE   }
  };
  
  /*
  *   FUNCTION DEFINITIONS
  */
  static void buildAsmKeywordHash (void)
  {
  	const size_t count = sizeof (AsmKeywords) / sizeof (AsmKeywords [0]);
  	size_t i;
  	for (i = 0  ;  i < count  ;  ++i)
  	{
  		const asmKeyword* const p = AsmKeywords + i;
  		addKeyword (p->operator, Lang_asm, (int) p->keyword);
  	}
  }
  
  static opKeyword analyzeOperator (const vString *const op)
  {
  	vString *keyword = vStringNew ();
  	opKeyword result;
  
  	vStringCopyToLower (keyword, op);
  	result = (opKeyword) lookupKeyword (vStringValue (keyword), Lang_asm);
  	vStringDelete (keyword);
  	return result;
  }
  
  static boolean isInitialSymbolCharacter (int c)
  {
  	return (boolean) (c != '\0' && (isalpha (c) || strchr ("_$", c) != NULL));
  }
  
  static boolean isSymbolCharacter (int c)
  {
  	/* '?' character is allowed in AMD 29K family */
  	return (boolean) (c != '\0' && (isalnum (c) || strchr ("_$?", c) != NULL));
  }
  
  static boolean readPreProc (const unsigned char *const line)
  {
  	boolean result;
  	const unsigned char *cp = line;
  	vString *name = vStringNew ();
  	while (isSymbolCharacter ((int) *cp))
  	{
  		vStringPut (name, *cp);
  		++cp;
  	}
  	vStringTerminate (name);
  	result = (boolean) (strcmp (vStringValue (name), "define") == 0);
  	if (result)
  	{
  		while (isspace ((int) *cp))
  			++cp;
  		vStringClear (name);
  		while (isSymbolCharacter ((int) *cp))
  		{
  			vStringPut (name, *cp);
  			++cp;
  		}
  		vStringTerminate (name);
  		makeSimpleTag (name, AsmKinds, K_DEFINE);
  	}
  	vStringDelete (name);
  	return result;
  }
  
  static AsmKind operatorKind (
  		const vString *const operator,
  		boolean *const found)
  {
  	AsmKind result = K_NONE;
  	const opKeyword kw = analyzeOperator (operator);
  	*found = (boolean) (kw != OP_UNDEFINED);
  	if (*found)
  	{
  		result = OpKinds [kw].kind;
  		Assert (OpKinds [kw].keyword == kw);
  	}
  	return result;
  }
  
  /*  We must check for "DB", "DB.L", "DCB.W" (68000)
   */
  static boolean isDefineOperator (const vString *const operator)
  {
  	const unsigned char *const op =
  		(unsigned char*) vStringValue (operator); 
  	const size_t length = vStringLength (operator);
  	const boolean result = (boolean) (length > 0  &&
  		toupper ((int) *op) == 'D'  &&
  		(length == 2 ||
  		 (length == 4  &&  (int) op [2] == '.') ||
  		 (length == 5  &&  (int) op [3] == '.')));
  	return result;
  }
  
  static void makeAsmTag (
  		const vString *const name,
  		const vString *const operator,
  		const boolean labelCandidate,
  		const boolean nameFollows)
  {
  	if (vStringLength (name) > 0)
  	{
  		boolean found;
  		const AsmKind kind = operatorKind (operator, &found);
  		if (found)
  		{
  			if (kind != K_NONE)
  				makeSimpleTag (name, AsmKinds, kind);
  		}
  		else if (isDefineOperator (operator))
  		{
  			if (! nameFollows)
  				makeSimpleTag (name, AsmKinds, K_DEFINE);
  		}
  		else if (labelCandidate)
  		{
  			operatorKind (name, &found);
  			if (! found)
  				makeSimpleTag (name, AsmKinds, K_LABEL);
  		}
  	}
  }
  
  static const unsigned char *readSymbol (
  		const unsigned char *const start,
  		vString *const sym)
  {
  	const unsigned char *cp = start;
  	vStringClear (sym);
  	if (isInitialSymbolCharacter ((int) *cp))
  	{
  		while (isSymbolCharacter ((int) *cp))
  		{
  			vStringPut (sym, *cp);
  			++cp;
  		}
  		vStringTerminate (sym);
  	}
  	return cp;
  }
  
  static const unsigned char *readOperator (
  		const unsigned char *const start,
  		vString *const operator)
  {
  	const unsigned char *cp = start;
  	vStringClear (operator);
  	while (*cp != '\0'  &&  ! isspace ((int) *cp))
  	{
  		vStringPut (operator, *cp);
  		++cp;
  	}
  	vStringTerminate (operator);
  	return cp;
  }
  
  static void findAsmTags (void)
  {
  	vString *name = vStringNew ();
  	vString *operator = vStringNew ();
  	const unsigned char *line;
  	boolean inCComment = FALSE;
  
  	while ((line = fileReadLine ()) != NULL)
  	{
  		const unsigned char *cp = line;
  		boolean labelCandidate = (boolean) (! isspace ((int) *cp));
  		boolean nameFollows = FALSE;
  		const boolean isComment = (boolean)
  				(*cp != '\0' && strchr (";*@", *cp) != NULL);
  
  		/* skip comments */
  		if (strncmp ((const char*) cp, "/*", (size_t) 2) == 0)
  		{
  			inCComment = TRUE;
  			cp += 2;
  		}
  		if (inCComment)
  		{
  			do
  			{
  				if (strncmp ((const char*) cp, "*/", (size_t) 2) == 0)
  				{
  					inCComment = FALSE;
  					cp += 2;
  					break;
  				}
  				++cp;
  			} while (*cp != '\0');
  		}
  		if (isComment || inCComment)
  			continue;
  
  		/* read preprocessor defines */
  		if (*cp == '#')
  		{
  			++cp;
  			readPreProc (cp);
  			continue;
  		}
  
  		/* skip white space */
  		while (isspace ((int) *cp))
  			++cp;
  
  		/* read symbol */
  		cp = readSymbol (cp, name);
  		if (vStringLength (name) > 0  &&  *cp == ':')
  		{
  			labelCandidate = TRUE;
  			++cp;
  		}
  
  		if (! isspace ((int) *cp)  &&  *cp != '\0')
  			continue;
  
  		/* skip white space */
  		while (isspace ((int) *cp))
  			++cp;
  
  		/* skip leading dot */
  #if 0
  		if (*cp == '.')
  			++cp;
  #endif
  
  		cp = readOperator (cp, operator);
  
  		/* attempt second read of symbol */
  		if (vStringLength (name) == 0)
  		{
  			while (isspace ((int) *cp))
  				++cp;
  			cp = readSymbol (cp, name);
  			nameFollows = TRUE;
  		}
  		makeAsmTag (name, operator, labelCandidate, nameFollows);
  	}
  	vStringDelete (name);
  	vStringDelete (operator);
  }
  
  static void initialize (const langType language)
  {
  	Lang_asm = language;
  	buildAsmKeywordHash ();
  }
  
  extern parserDefinition* AsmParser (void)
  {
  	static const char *const extensions [] = {
  		"asm", "ASM", "s", "S", NULL
  	};
  	static const char *const patterns [] = {
  		"*.A51",
  		"*.29[kK]",
  		"*.[68][68][kKsSxX]",
  		"*.[xX][68][68]",
  		NULL
  	};
  	parserDefinition* def = parserNew ("Asm");
  	def->kinds      = AsmKinds;
  	def->kindCount  = KIND_COUNT (AsmKinds);
  	def->extensions = extensions;
  	def->patterns   = patterns;
  	def->parser     = findAsmTags;
  	def->initialize = initialize;
  	return def;
  }
  
  /* vi:set tabstop=4 shiftwidth=4: */
  