/*
 docCopyright("Steve Dekorte; Jonathan Wright (2006)", 2002)
 docLicense("BSD revised")
 */

#include "IoMessage_parser.h"
#include "IoMessage_opShuffle.h"
#include "IoObject.h"
#include "IoSeq.h"
#include "IoMap.h"
#include "IoNumber.h"
#include "IoState.h"
#include "IoLexer.h"
#include <ctype.h>

#define DATA(self) ((IoMessageData *)IoObject_dataPointer(self))

void IoMessage_parseName(IoMessage *self, IoLexer *lexer);
void IoMessage_parseArgs(IoMessage *self, IoLexer *lexer);
void IoMessage_parseAttached(IoMessage *self, IoLexer *lexer);
void IoMessage_parseNext(IoMessage *self, IoLexer *lexer);
IoMessage *IoMessage_newParseNextMessageChain(void *state, IoLexer *lexer);
void IoMessage_ifPossibleCacheToken_(IoMessage *self, IoToken *p);

void IoMessage_ifPossibleCacheToken_(IoMessage *self, IoToken *p) 
{
	IoSymbol *method = DATA(self)->name;
	IoObject *r = NULL;
	
	switch ((int)IoToken_type(p))
	{
		case TRIQUOTE_TOKEN:
			r =  IoState_symbolWithCString_length_(IOSTATE, IoSeq_asCString(method) + 3, IoSeq_rawSize(method) - 6); 
			break;
			
		case MONOQUOTE_TOKEN:
			r =  IoSeq_rawAsUnescapedSymbol(IoSeq_rawAsUnquotedSymbol(method)); 
			break;
			
		case NUMBER_TOKEN:
			r =  IONUMBER(IoSeq_asDouble(method));
			break;
			
		case HEXNUMBER_TOKEN:
			r =  IONUMBER(IoSeq_rawAsDoubleFromHex(method));
			break;      
			
		default:
			if (IoSeq_rawEqualsCString_(method, "nil"))
			{ 
				r = IONIL(self); 
			}
	}
	
	IoMessage_cachedResult_(self, r);
}

IoMessage *IoMessage_newFromText_label_(void *state, const char *text, const char *label)
{
	IoLexer *lexer = IoLexer_new();
	IoMessage *msg;
	
	IoLexer_string_(lexer, text);
	IoLexer_lex(lexer);
	
	
	msg = IoMessage_newParse(state, lexer);
	
	IoMessage_opShuffle_(msg);
	
	{
		IoSymbol *labelSymbol = IoState_symbolWithCString_((IoState *)state, label);
		IoMessage_label_(msg, labelSymbol); 
	}
	
	IoLexer_free(lexer);
	
	return msg;
}

// -------------------------------

IoMessage *IoMessage_newParse(void *state, IoLexer *lexer)
{
	if (IoLexer_errorToken(lexer))
	{ 
		IoMessage *m; 
		IoSymbol *errorString;
		
        // Maybe the nil message could be used here. Or even a NULL.
        IoSymbol *error = IoState_symbolWithCString_(state, "Error");
		m = IoMessage_newWithName_returnsValue_(state, error, error);
		errorString = IoState_symbolWithCString_((IoState *)state, IoLexer_errorDescription(lexer));
		IoState_error_(state, m, "compile error: %s", CSTRING(errorString));
	}
	
	if (IoLexer_topType(lexer) == TERMINATOR_TOKEN)
	{
		IoLexer_pop(lexer);
	}
	
	if (IoTokenType_isValidMessageName(IoLexer_topType(lexer)))
	{
		IoMessage *self = IoMessage_newParseNextMessageChain(state, lexer);
		
		if (IoLexer_topType(lexer) != NO_TOKEN)
		{
			// TODO: Exception as the end was expected
			IoState_error_(state, self, "compile error: %s", "unused tokens");
		}
		
		return self;
	}
	
	return IoMessage_newWithName_returnsValue_(state,
											   IoState_symbolWithCString_((IoState*)state, "nil"),
											   ((IoState*)state)->ioNil
											   );
}

IoMessage *IoMessage_newParseNextMessageChain(void *state, IoLexer *lexer)
{
	IoMessage *self = IoMessage_new(state);
	
	if (IoTokenType_isValidMessageName(IoLexer_topType(lexer)))
	{
		IoMessage_parseName(self, lexer);
	}
	
	if (IoLexer_topType(lexer) == OPENPAREN_TOKEN)
	{
		IoMessage_parseArgs(self, lexer);
	}
	
	if (IoTokenType_isValidMessageName(IoLexer_topType(lexer)))
	{
		IoMessage_parseNext(self, lexer);
	}
	
	while (IoLexer_topType(lexer) == TERMINATOR_TOKEN)
	{
		IoLexer_pop(lexer);
		
		if (IoTokenType_isValidMessageName(IoLexer_topType(lexer)))
		{
			IoMessage *eol = IoMessage_newWithName_(state, ((IoState*)state)->semicolonSymbol);
			IoMessage_rawSetNext(self, eol);
			IoMessage_parseNext(eol, lexer);
		}
	}
	
	return self;
}

void IoMessage_parseName(IoMessage *self, IoLexer *lexer)
{
	IoToken *token = IoLexer_pop(lexer);
	
	DATA(self)->name = IOREF(IOSYMBOL(IoToken_name(token)));
	
	IoMessage_ifPossibleCacheToken_(self, token);
	IoMessage_rawSetLineNumber_(self, IoToken_lineNumber(token));
	IoMessage_rawSetCharNumber_(self, IoToken_charNumber(token));
}

void IoMessage_parseArgs(IoMessage *self, IoLexer *lexer)
{
	IoLexer_pop(lexer);
	
	if (IoTokenType_isValidMessageName(IoLexer_topType(lexer)))
	{
		IoMessage *arg = IoMessage_newParseNextMessageChain(IOSTATE, lexer);
		IoMessage_addArg_(self, arg);
		
		while (IoLexer_topType(lexer) == COMMA_TOKEN)
		{
			IoLexer_pop(lexer);
			
			if (IoTokenType_isValidMessageName(IoLexer_topType(lexer)))
			{
				IoMessage *arg = IoMessage_newParseNextMessageChain(IOSTATE, lexer);
				IoMessage_addArg_(self, arg);
			}
			// Doesn't actually work because the lexer detects this case and reports the error before we can handle it...
			//else if (IoLexer_topType(lexer) == CLOSEPAREN_TOKEN)
			//{
			//	// Allow the last arg to be empty as in, "foo(a,b,c,)".
			//}
			else
			{
				// TODO: Exception, missing message
			}
		}
	}
	
	if (IoLexer_topType(lexer) != CLOSEPAREN_TOKEN)
	{
		// TODO: Exception, missing close paren
	}
	IoLexer_pop(lexer);
}

void IoMessage_parseNext(IoMessage *self, IoLexer *lexer)
{
	IoMessage *next = IoMessage_newParseNextMessageChain(IOSTATE, lexer);
	IoMessage_rawSetNext(self, next);
}



syntax highlighted by Code2HTML, v. 0.9.1