/***********************************************************************
 *  avra - Assembler for the Atmel AVR microcontroller series
 *  Copyright (C) 1998-1999 Jon Anders Haugum
 *
 *  This program 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 2 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; see the file COPYING.  If not, write to
 *  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *  Boston, MA 02111-1307, USA.
 *
 *
 *  Author of avra can be reached at:
 *     email: jonah@omegav.ntnu.no
 *     www: http://www.omegav.ntnu.no/~jonah/el/avra.html
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "misc.h"
#include "avra.h"

#define IS_UNARY(x) ((x == '!') || (x == '-') || (x == '~'))
#define IS_OPERATOR(x) ((x == '+') || (x == '-') || (x == '*') || (x == '/') || (x == '%') || (x == '<') || (x == '>') || (x == '=') || (x == '!') || (x == '&') || (x == '^') || (x == '|'))
#define IS_2ND_OPERATOR(x) ((x == '<') || (x == '>') || (x == '=') || (x == '&') || (x == '|'))

enum
	{
	OPERATOR_ERROR = 0,
	OPERATOR_MUL,
	OPERATOR_DIV,
	OPERATOR_MOD,
	OPERATOR_ADD,
	OPERATOR_SUB,
	OPERATOR_SHIFT_LEFT,
	OPERATOR_SHIFT_RIGHT,
	OPERATOR_LESS_THAN,
	OPERATOR_LESS_OR_EQUAL,
	OPERATOR_GREATER_THAN,
	OPERATOR_GREATER_OR_EQUAL,
	OPERATOR_EQUAL,
	OPERATOR_NOT_EQUAL,
	OPERATOR_BITWISE_AND,
	OPERATOR_BITWISE_XOR,
	OPERATOR_BITWISE_OR,
	OPERATOR_LOGICAL_AND,
	OPERATOR_LOGICAL_OR
	};

enum
	{
	FUNCTION_LOW = 0,
	FUNCTION_HIGH,
	FUNCTION_BYTE2,
	FUNCTION_BYTE3,
	FUNCTION_BYTE4,
	FUNCTION_LWRD,
	FUNCTION_HWRD,
	FUNCTION_PAGE,
	FUNCTION_EXP2,
	FUNCTION_LOG2,
	FUNCTION_COUNT
	};

struct element
	{
	struct element *next;
	int data;
	};

char *function_list[] =
	{
	"low(",
	"high(",
	"byte2(",
	"byte3(",
	"byte4(",
	"lwrd(",
	"hwrd(",
	"page(",
	"exp2(",
	"log2("
	};

int get_expr(struct prog_info *pi, char *data, int *value)
	{
	int ok = True, end = False, i, count, first_flag, length, function;
	char unary, *label;
	struct element *element, *first_element = NULL, *temp_element;
	struct element **last_element = &first_element;

	for(i = 0, count = 0, first_flag = True, unary = 0; ; i++)
		{
		if(IS_HOR_SPACE(data[i]));
		else if(IS_END(data[i]))
			{
			if((count % 2) != 1)
				print_msg(pi, MSGTYPE_ERROR, "Missing value in expression");
			else
				end = True;
			break;
			}
		else if(first_flag && IS_UNARY(data[i]))
			{
			unary = data[i];
			first_flag = False;
			}
		else if((count % 2) == 1)
			{
			if(!IS_OPERATOR(data[i]))
				{
				print_msg(pi, MSGTYPE_ERROR, "Illegal operator '%c'", data[i]);
				break;
				}
			element = malloc(sizeof(struct element));
			if(!element)
				{
				print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
				ok = False;
				break;
				}
			element->next = NULL;
			element->data = get_operator(&data[i]);
			if(element->data == OPERATOR_ERROR)
				{
				if(IS_2ND_OPERATOR(data[i + 1]))
					print_msg(pi, MSGTYPE_ERROR, "Unknown operator %c%c", data[i], data[i + 1]);
				else
					print_msg(pi, MSGTYPE_ERROR, "Unknown operator %c", data[i]);
				break;
				}
			*last_element = element;
			last_element = &element->next;
			if(IS_2ND_OPERATOR(data[i + 1]))
				i++;
			count++;
			first_flag = True;
			unary = 0;
			}
		else
			{
			element = malloc(sizeof(struct element));
			if(!element)
				{
				print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
				ok = False;
				break;
				}
			element->next = NULL;
			length = 0;
			if(isdigit(data[i]))
				{
				if(tolower(data[i + 1]) == 'x')
					{
					i += 2;
					while(isxdigit(data[i + length])) length++; // TODO: Sjekk overflow
					element->data = atox_n(&data[i], length);
					}
				else if(tolower(data[i + 1]) == 'b')
					{
					i += 2;
					element->data = 0;
					while((data[i + length] == '1') || (data[i + length] == '0'))
						{
						element->data <<= 1;
						element->data |= data[i + length++] - '0'; // TODO: Sjekk overflow
						}
					}
				else
					{
					while(isdigit(data[i + length])) length++;
					element->data = atoi_n(&data[i], length); // TODO: Sjekk overflow
					}
				}
			else if(data[i] == '$')
				{
				i++;
				while(isxdigit(data[i + length])) length++;
				element->data = atox_n(&data[i], length); // TODO: Sjekk overflow
				}
			else if(data[i] == '\'')
				{
				i++;
				element->data = data[i]; // TODO: Sjekk for siste '
				length = 2;
				}
			else if(data[i] == '(')
				{
				i++;
				length = par_length(&data[i]);
				if(length == -1)
					{
					print_msg(pi, MSGTYPE_ERROR, "Missing ')'");
					break;
					}
				data[i + length++] = '\0';
				ok = get_expr(pi, &data[i], &element->data);
				if(!ok)
					break;
				}
			else if(-1 != (function = get_function(&data[i])))
				{
				while(data[i] != '(') i++;
				i++;
				length = par_length(&data[i]);
				if(length == -1)
					{
					print_msg(pi, MSGTYPE_ERROR, "Missing ')'");
					break;
					}
				data[i + length++] = '\0';
				ok = get_expr(pi, &data[i], &element->data);
				if(!ok)
					break;
				element->data = do_function(function, element->data);
				}
			else if(!nocase_strncmp(&data[i], "defined(", 8))
				{
				i += 8;
				length = par_length(&data[i]);
				if(length == -1)
					{
					print_msg(pi, MSGTYPE_ERROR, "Missing ')'");
					break;
					}
				data[i + length++] = '\0';
				if(get_symbol(pi, &data[i], NULL))
					element->data = 1;
				else
					element->data = 0;
				}
			else
				{
				while(IS_LABEL(data[i + length])) length++;
				if((length == 2) && !nocase_strncmp(&data[i], "PC", 2))
					element->data = pi->cseg_addr;
				else
					{
					label = malloc(length + 1);
					if(!label)
						{
						print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
						ok = False;
						break;
						}
					strncpy(label, &data[i], length);
					label[length] = '\0';
					if(get_symbol(pi, label, &element->data))
						free(label);
					else
						{
						print_msg(pi, MSGTYPE_ERROR, "Found no label/variable/constant named %s", label);
						free(label);
						break;
						}
					}
				}
			i += length - 1;
			switch(unary) // TODO: Få den til å takle flere unary på rad.
				{
				case '-':
					element->data = -element->data;
					break;
				case '!':
					element->data = !element->data;
					break;
				case '~':
					element->data = ~element->data;
				}
			*last_element = element;
			last_element = &element->next;
			count++;
			first_flag = False;
			}
		}
	if(end)
		{
		for(i = 13; (i >= 4) && (count != 1); i--)
			{
			for(element = first_element; element->next;)
				{
				if(test_operator_at_precedence(element->next->data, i)) // TODO: Vurder en hi_i for kjapphet
					{
					element->data = calc(pi, element->data, element->next->data, element->next->next->data);
					temp_element = element->next->next->next;
					free(element->next->next);
					free(element->next);
					count -= 2;
					element->next = temp_element;
					}
				else
					element = element->next->next;
				}
			}
		*value = first_element->data;
		}
	for(element = first_element; element;)
		{
		temp_element = element;
		element = element->next;
		free(temp_element);
		}
	return(ok);
	}


int get_operator(char *op)
	{
	switch(op[0])
		{
		case '*':
			return(OPERATOR_MUL);
		case '/':
			return(OPERATOR_DIV);
		case '%':
			return(OPERATOR_MOD);
		case '+':
			return(OPERATOR_ADD);
		case '-':
			return(OPERATOR_SUB);
		case '<':
			switch(op[1])
				{
				case '<':
					return(OPERATOR_SHIFT_LEFT);
				case '=':
					return(OPERATOR_LESS_OR_EQUAL);
				default:
					return(OPERATOR_LESS_THAN);
				}
		case '>':
			switch(op[1])
				{
				case '>':
					return(OPERATOR_SHIFT_RIGHT);
				case '=':
					return(OPERATOR_GREATER_OR_EQUAL);
				default:
					return(OPERATOR_GREATER_THAN);
				}
		case '=':
			if(op[1] == '=')
				return(OPERATOR_EQUAL);
		case '!':
			if(op[1] == '=')
				return(OPERATOR_NOT_EQUAL);
		case '&':
			if(op[1] == '&')
				return(OPERATOR_LOGICAL_AND);
			else
				return(OPERATOR_BITWISE_AND);
		case '^':
			return(OPERATOR_BITWISE_XOR);
		case '|':
			if(op[1] == '|')
				return(OPERATOR_LOGICAL_OR);
			else
				return(OPERATOR_BITWISE_OR);
		}
	return(OPERATOR_ERROR);
	}


int test_operator_at_precedence(int operator, int precedence)
	{
	switch(precedence)
		{
		case 13:
			return((operator == OPERATOR_MUL) || (operator == OPERATOR_DIV)
			       || (operator == OPERATOR_MOD));
		case 12:
			return((operator == OPERATOR_ADD) || (operator == OPERATOR_SUB));
		case 11:
			return((operator == OPERATOR_SHIFT_LEFT) || (operator == OPERATOR_SHIFT_RIGHT));
		case 10:
			return((operator == OPERATOR_LESS_THAN) || (operator == OPERATOR_LESS_OR_EQUAL)
			       || (operator == OPERATOR_GREATER_THAN) || (operator == OPERATOR_GREATER_OR_EQUAL));
		case 9:
			return((operator == OPERATOR_EQUAL) || (operator == OPERATOR_NOT_EQUAL));
		case 8:
			return(operator == OPERATOR_BITWISE_AND);
		case 7:
			return(operator == OPERATOR_BITWISE_XOR);
		case 6:
			return(operator == OPERATOR_BITWISE_OR);
		case 5:
			return(operator == OPERATOR_LOGICAL_AND);
		default: /* Makes the compiler shut up */
		case 4:
			return(operator == OPERATOR_LOGICAL_OR);
		}
	}


int calc(struct prog_info *pi, int left, int operator, int right) // TODO: Sjekk litt resultater
	{
	switch(operator)
		{
		case OPERATOR_MUL:
			return(left * right);
		case OPERATOR_DIV:
			if(right == 0)
				{
				print_msg(pi, MSGTYPE_ERROR, "Division by zero");
				return(0);
				}
			return(left / right);
		case OPERATOR_MOD:
			if(right == 0)
				{
				print_msg(pi, MSGTYPE_ERROR, "Division by zero (modulus operator)");
				return(0);
				}
		        return(left % right);
		case OPERATOR_ADD:
			return(left + right);
		case OPERATOR_SUB:
			return(left - right);
		case OPERATOR_SHIFT_LEFT:
			return(left << right);
		case OPERATOR_SHIFT_RIGHT:
			return(left >> right);
		case OPERATOR_LESS_THAN:
			return(left < right);
		case OPERATOR_LESS_OR_EQUAL:
			return(left <= right);
		case OPERATOR_GREATER_THAN:
			return(left > right);
		case OPERATOR_GREATER_OR_EQUAL:
			return(left >= right);
		case OPERATOR_EQUAL:
			return(left == right);
		case OPERATOR_NOT_EQUAL:
			return(left != right);
		case OPERATOR_BITWISE_AND:
			return(left & right);
		case OPERATOR_BITWISE_XOR:
			return(left ^ right);
		case OPERATOR_BITWISE_OR:
			return(left | right);
		case OPERATOR_LOGICAL_AND:
			return(left && right);
		default: /* Make the compiler shut up */
		case OPERATOR_LOGICAL_OR:
			return(left || right);
		}
	}


int get_function(char *function)
	{
	int i;

	for(i = 0; i < FUNCTION_COUNT; i++)
		if(!nocase_strncmp(function, function_list[i], strlen(function_list[i]))) return(i);
	return(-1);
	}


int do_function(int function, int value)
	{
	switch(function)
		{
		case FUNCTION_LOW:
			return(value & 0xff);
		case FUNCTION_HIGH:
		case FUNCTION_BYTE2:
			return((value >> 8) & 0xff);
		case FUNCTION_BYTE3:
			return((value >> 16) & 0xff);
		case FUNCTION_BYTE4:
			return((value >> 24) & 0xff);
		case FUNCTION_LWRD:
			return(value & 0xffff);
		case FUNCTION_HWRD:
			return((value >> 16) & 0xffff);
		case FUNCTION_PAGE:
			return((value >> 16) & 0xff);
		case FUNCTION_EXP2:
			return(1 << value);
		case FUNCTION_LOG2:
			return(log2(value));
		default:
			return(0);
		}
	}


int log2(int value)
	{
	int i = 0;

	while(value >>= 1) i++;
	return(i);
	}


int get_symbol(struct prog_info *pi, char *label_name, int *data)
	{
	struct label *label;
	struct macro_call *macro_call;

	for(label = pi->first_constant; label; label = label->next)
		if(!nocase_strcmp(label->name, label_name))
			{
			if(data)
				*data = label->value;
			return(True);
			}

	for(label = pi->first_variable; label; label = label->next)
		if(!nocase_strcmp(label->name, label_name))
			{
			if(data)
				*data = label->value;
			return(True);
			}

	for(macro_call = pi->macro_call; macro_call; macro_call = macro_call->prev_on_stack)
		{
		for(label = pi->macro_call->first_label; label; label = label->next)
			if(!nocase_strcmp(label->name, label_name))
				{
				if(data)
					*data = label->value;
				return(True);
				}
		}

	for(label = pi->first_label; label; label = label->next)
		if(!nocase_strcmp(label->name, label_name))
			{
			if(data)
				*data = label->value;
			return(True);
			}

	return(False);
	}


int par_length(char *data)
	{
	int i = 0, b_count = 1;

	for(;;)
		{
		if(data[i] == ')')
			{
			b_count--;
			if(!b_count)
				return(i);
			}
		else if(data[i] == '(')
			b_count++;
		else if(data[i] == '\0')
			return(-1);
		i++;
		}
	}



