// px_plexer.cpp
// `````````````
// Parser / lexer

#define PX_INTERNAL_SOURCE
#include "px_plexer.h"

// 
// GLOBALS
// 

syntax_map_t syntax; // GLOBAL
char*          rightmost_search_pos   = NULL;
enum tok_types rightmost_search_token = TOK_INVALID;

void parse_initGrammar() {
	syntax = getSyntax();
}

//
// GRAMMAR
//

template <typename T> struct vadder : public std::vector<T> {
	vadder(const T& t) { (*this)(t); };
	vadder& operator()(const T& t) { this->push_back(t); return *this; };
};

syntax_map_t getSyntax() {
	syntax_map_t syntax;
	syntax.clear();

	syntax[TOK_BODY]            = vadder<tok_types>(TOK_STATEMENT)(TOK_RULE_OPTIONAL_REMAINING)(TOK_BODY);
	syntax[TOK_BLOCK]           = vadder<tok_types>(TOK_CBRACE_OPEN)(TOK_BODY)(TOK_CBRACE_CLOSE);
	syntax[TOK_STATEMENT]       = vadder<tok_types>(TOK_STATEMENT_INNER)(TOK_OP_SEMICOLON);
	syntax[TOK_STATEMENT_INNER] = vadder<tok_types>(TOK_RULE_ONE_OF_THE_REMAINING)(TOK_BLOCK)(TOK_RETURN_STATEMENT)(TOK_INCLUDE_STATEMENT)(TOK_RWORD_BREAK)(TOK_RWORD_CONTINUE)(TOK_IF_STATEMENT)(TOK_FOR_STATEMENT)(TOK_DIM_VAR)(TOK_FNCALL)(TOK_ASSIGNMENT);

	syntax[TOK_INCLUDE_STATEMENT] = vadder<tok_types>(TOK_RWORD_INCLUDE)(TOK_QUOTED_STRING);

	syntax[TOK_SLICE]			= vadder<tok_types>(TOK_RULE_ONE_OF_THE_REMAINING)(TOK_SLICE_INNER)(TOK_RVALUE);
	syntax[TOK_SLICE_INNER]		= vadder<tok_types>(TOK_RVALUE)(TOK_OP_COLON)(TOK_RVALUE);

	syntax[TOK_VECTOR_LITERAL] = vadder<tok_types>(TOK_SBRACE_OPEN)(TOK_VECTOR_ELEMENT)(TOK_SBRACE_CLOSE);
	syntax[TOK_VECTOR_ELEMENT] = vadder<tok_types>(TOK_RULE_OPTIONAL_REMAINING)(TOK_RVALUE)(TOK_RULE_OPTIONAL_REMAINING)(TOK_OP_COMMA)(TOK_VECTOR_ELEMENT); // like fncall_arglist

	syntax[TOK_DIM_VAR]         = vadder<tok_types>(TOK_TYPE_IDENTIFIER)(TOK_IDENTIFIER)(TOK_RULE_OPTIONAL_REMAINING)(TOK_OP_EQUALS)(TOK_RVALUE);
	syntax[TOK_LVALUE]			= vadder<tok_types>(TOK_IDENTIFIER)(TOK_RULE_OPTIONAL_REMAINING)(TOK_SBRACE_OPEN)(TOK_SLICE)(TOK_SBRACE_CLOSE);
		
	syntax[TOK_FNDEF_ARGLIST]	= vadder<tok_types>(TOK_RULE_OPTIONAL_REMAINING)(TOK_TYPE_IDENTIFIER)(TOK_IDENTIFIER)(TOK_RULE_OPTIONAL_REMAINING)(TOK_OP_COMMA)(TOK_FNDEF_ARGLIST);
	syntax[TOK_FNDEF]           = vadder<tok_types>(TOK_SBRACE_OPEN)(TOK_FNDEF_ARGLIST)(TOK_SBRACE_CLOSE)(TOK_BLOCK);
	syntax[TOK_FNCALL_ARGLIST]	= vadder<tok_types>(TOK_RULE_OPTIONAL_REMAINING)(TOK_RVALUE)(TOK_RULE_OPTIONAL_REMAINING)(TOK_OP_COMMA)(TOK_FNCALL_ARGLIST); // like vector_element
	syntax[TOK_FNCALL]          = vadder<tok_types>(TOK_LVALUE)(TOK_PARENS_OPEN)(TOK_FNCALL_ARGLIST)(TOK_PARENS_CLOSE);
	
	// FIXME: is it possible to have FNCALL use an RVALUE instead of just an LVALUE?
	//   We should be able to call *any* RVALUE - make calling part of the RVALUE_INNER_X chain

	// Rvalue: Order is important;
	//  - the first part of a FNCALL or a NUMERIC_LITERAL etc is also an LVALUE=>IDENTIFIER
	//  - the first part of a VECTOR_LITERAL is also a FNDEF
	syntax[TOK_RVALUE_INNER2]	= vadder<tok_types>(TOK_RULE_ONE_OF_THE_REMAINING)(TOK_FNCALL)(TOK_NUMERIC_LITERAL)(TOK_IMPORT_CLAUSE)(TOK_LVALUE)(TOK_QUOTED_STRING)(TOK_FNDEF)(TOK_VECTOR_LITERAL);
	syntax[TOK_RVALUE_INNER]    = vadder<tok_types>(TOK_RVALUE_INNER2)(TOK_RULE_OPTIONAL_REMAINING)(TOK_OP_PERIOD)(TOK_FNCALL);
	syntax[TOK_RVALUE]          = vadder<tok_types>(TOK_RVALUE_INNER)(TOK_RULE_OPTIONAL_REMAINING)(TOK_BINARY_OPERATOR)(TOK_RVALUE);
	syntax[TOK_ASSIGNMENT]      = vadder<tok_types>(TOK_LVALUE)(TOK_OP_EQUALS)(TOK_RVALUE);

	// Binary expressions
	// TODO: logical and/or/not, binary and/or/complement/xor/lshift/rshift ?
	// TODO: precedence
	syntax[TOK_BINARY_OPERATOR] = vadder<tok_types>(TOK_RULE_ONE_OF_THE_REMAINING)(TOK_OP_EXCLAMATION_EQUALS)(TOK_OP_EQUALS_EQUALS)(TOK_OP_PLUS)(TOK_OP_MINUS)(TOK_OP_STAR)(TOK_OP_FORWARDSLASH)(TOK_OP_GREATERTHAN)(TOK_OP_LESSTHAN);
	
	// Special forms
	syntax[TOK_RETURN_STATEMENT]= vadder<tok_types>(TOK_RWORD_RETURN)(TOK_RVALUE);
	syntax[TOK_IF_STATEMENT]    = vadder<tok_types>(TOK_RWORD_IF)(TOK_PARENS_OPEN)(TOK_RVALUE)(TOK_PARENS_CLOSE)(TOK_BLOCK)(TOK_RULE_OPTIONAL_REMAINING)(TOK_RWORD_ELSE)(TOK_BLOCK);
	syntax[TOK_FOR_STATEMENT]   = vadder<tok_types>(TOK_RWORD_FOR)(TOK_PARENS_OPEN)(TOK_STATEMENT)(TOK_RVALUE)(TOK_OP_SEMICOLON)(TOK_STATEMENT_INNER)(TOK_PARENS_CLOSE)(TOK_BLOCK);
	
	syntax[TOK_IMPORT_CLAUSE]   = vadder<tok_types>(TOK_RWORD_IMPORT)(TOK_TYPE_IDENTIFIER)(TOK_IDENTIFIER);

	// Type expressions
	// Ensure that LITERALs cannot contain < and > characters!
	syntax[TOK_TYPE_IDENTIFIER_LIST] = vadder<tok_types>(TOK_TYPE_IDENTIFIER)(TOK_RULE_OPTIONAL_REMAINING)(TOK_OP_COMMA)(TOK_TYPE_IDENTIFIER_LIST);
	syntax[TOK_TYPE_IDENTIFIER] = vadder<tok_types>(TOK_LITERAL_TYPENAME)(TOK_RULE_OPTIONAL_REMAINING)(TOK_OP_LESSTHAN)(TOK_TYPE_IDENTIFIER_LIST)(TOK_OP_GREATERTHAN);

	// TODO: Function annotations (:pure, :import, :export)
	// Imported functions need some way of specifying the return type
	// var myfn = int [int i] :pure { return }
	// var myfn = [int i] $pure $is<int> { return }
	// var myfn = [pure: int x, int y] { return x + y; }
	// var myfn = [int: int x, int y] { return x + y; }
	// var myfn = pure int [int x, int y] { return x + y; }

	// pure - don't apply lambda lifting, error on unmatched free parameters
	// import<name> - don't include a block, expect the name to be matched by linker   \_ mutually
	// export<name> - mark the function as global, expect it to be used in other TUs   /  exclusive

	// Function definitions need an optional first parameter with the return type
	// It can be inferred by the typechecker for any pexl functions

	return syntax;
}

//
// PARSER
//


bool parse_is_reserved_word(enum tok_types rword, std::string& strword) {
	switch(rword) {
		case TOK_RWORD_DO:
			return (strword == "do");
		case TOK_RWORD_FOR:
			return (strword == "for");
		case TOK_RWORD_IF:
			return (strword == "if");
		case TOK_RWORD_ELSE:
			return (strword == "else");
		case TOK_RWORD_RETURN:
			return (strword == "return");
		case TOK_RWORD_WHILE:
			return (strword == "while");
		case TOK_RWORD_IMPORT:
			return (strword == "import");
		case TOK_RWORD_BREAK:
			return (strword == "break");
		case TOK_RWORD_CONTINUE:
			return (strword == "continue");
		case TOK_RWORD_INCLUDE:
			return (strword == "include");
		default:
			return false; // Unknown reserved word. Should never happen
	}
}

int parse_is_alpha_char(char c) {
	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}

int parse_is_numeric_char(char c) {
	return (c >= '0' && c <= '9');
}

int parse_is_identifier_char(char c) {
	return parse_is_alpha_char(c) || parse_is_numeric_char(c) || c == '_';
}

int parse_is_typename_char(char c) {
	// Don't include <>
	return parse_is_alpha_char(c) || parse_is_numeric_char(c) || c == '_';
}

void parse_skip_whitespace(char** pos) {
	while (**pos == ' ' || **pos == '\n' || **pos == '\t' || **pos == '\r') {
		*pos += 1;
	}

	// Skip over comments
	if (**pos == '/' && *(*pos + 1) == '*') {
		while( ! (**pos == '*' && *(*pos + 1) == '/')) {
			*pos += 1;
		}
		*pos += 2;
		parse_skip_whitespace(pos);
	}
}

#define PARSE_SINGLE_CHAR(parse_tok_alias, parse_tok_char) \
	case parse_tok_alias : {                               \
		if (**pos == parse_tok_char) {                     \
			(*ret)->clear( parse_tok_alias );              \
			*pos += 1;                                     \
			return 1;                                      \
		} else return 0;                                   \
	} break;

#define PARSE_DOUBLE_CHAR(parse_tok_alias, parse_tok_c1, parse_tok_c2) \
	case parse_tok_alias: {                                            \
		if (**pos == parse_tok_c1 && *(*pos + 1) == parse_tok_c2) {    \
			(*ret)->clear( parse_tok_alias );                          \
			*pos += 2;                                                 \
			return 1;                                                  \
		} else return 0;                                               \
	} break;

int parse(enum tok_types tok_type, char** pos, token** ret) {
	
	if (**pos == '\0') return 0;
	
	// Skip over any whitespace
	parse_skip_whitespace(pos);

	// Better parser errors
	if (*pos > rightmost_search_pos) {
		rightmost_search_pos = *pos;
		rightmost_search_token = tok_type;
	}

	// Determine if we can parse this token at this position

	switch (tok_type) {
		case TOK_INVALID: {
			printf("Tried to test for TOK_INVALID!\n");
			return 0;
		} break;

		PARSE_SINGLE_CHAR( TOK_CBRACE_OPEN,     '{' );
		PARSE_SINGLE_CHAR( TOK_CBRACE_CLOSE,    '}' );
		PARSE_SINGLE_CHAR( TOK_SBRACE_OPEN,     '[' );
		PARSE_SINGLE_CHAR( TOK_SBRACE_CLOSE,    ']' );
		PARSE_SINGLE_CHAR( TOK_PARENS_OPEN,     '(' );
		PARSE_SINGLE_CHAR( TOK_PARENS_CLOSE,    ')' );
		PARSE_SINGLE_CHAR( TOK_OP_QUOTE,        '"' );
		PARSE_SINGLE_CHAR( TOK_OP_COMMA,        ',' );
		PARSE_SINGLE_CHAR( TOK_OP_COLON,        ':' );
		PARSE_SINGLE_CHAR( TOK_OP_SEMICOLON,    ';' );
		PARSE_SINGLE_CHAR( TOK_OP_EQUALS,       '=' );
		PARSE_SINGLE_CHAR( TOK_OP_PLUS,         '+' );
		PARSE_SINGLE_CHAR( TOK_OP_MINUS,        '-' );
		PARSE_SINGLE_CHAR( TOK_OP_STAR,         '*' );
		PARSE_SINGLE_CHAR( TOK_OP_FORWARDSLASH, '/' );
		PARSE_SINGLE_CHAR( TOK_OP_BACKSLASH,    '\\');
		PARSE_SINGLE_CHAR( TOK_OP_PERCENT,      '%' );
		PARSE_SINGLE_CHAR( TOK_OP_LESSTHAN,     '<' );
		PARSE_SINGLE_CHAR( TOK_OP_GREATERTHAN,  '>' );
		PARSE_SINGLE_CHAR( TOK_OP_PERIOD,       '.' );

		PARSE_DOUBLE_CHAR( TOK_OP_EQUALS_EQUALS,      '=', '=' );
		PARSE_DOUBLE_CHAR( TOK_OP_EXCLAMATION_EQUALS, '!', '=' );

		case TOK_RWORD_DO:
		case TOK_RWORD_FOR:
		case TOK_RWORD_IF:
		case TOK_RWORD_ELSE:
		case TOK_RWORD_RETURN:
		case TOK_RWORD_WHILE:
		case TOK_RWORD_IMPORT:
		case TOK_RWORD_BREAK:
		case TOK_RWORD_CONTINUE:
		case TOK_RWORD_INCLUDE:
		{
			char* stash = *pos;
			if (parse_is_alpha_char(**pos)) {
				// Read word
				std::string word;
				while (parse_is_alpha_char(**pos)) {
					word += (**pos);
					*pos += 1;
				}
				// See if reserved word matches
				if (parse_is_reserved_word(tok_type, word)) {
					*ret = new token( tok_type );
					// No need to store the actual word
					return 1;
				}
			}
			// Didn't match, restore
			*pos = stash;
			return 0;
		} break;

		case TOK_IDENTIFIER: {
			if (parse_is_identifier_char(**pos)) {
				*ret = new token( TOK_IDENTIFIER );
				while (parse_is_identifier_char(**pos)) {
					(**ret).data += (**pos);
					*pos += 1;
				}
				return 1;
			} else return 0;
		} break;

		case TOK_NUMERIC_LITERAL: {
			if (parse_is_numeric_char(**pos)) {
				*ret = new token( TOK_NUMERIC_LITERAL );
				(**ret).data_int = 0;
				while (parse_is_numeric_char(**pos)) {
					(**ret).data_int *= 10;
					(**ret).data_int += ((**pos) - '0');
					*pos += 1;
				}
				return 1;
			} else return 0;
		} break;

		case TOK_LITERAL_TYPENAME: {
			std::string word;
			while (parse_is_typename_char(**pos)) { //for(;;) {
				word += (**pos);
				*pos += 1;
				//if (**pos == ' ') break;
			}
			if (word == "var" || word == "int" || word=="function" || word=="string" || word=="vector" ) { //list" || word=="array" || word=="map") {
				*ret = new token( TOK_LITERAL_TYPENAME );
				(**ret).data = word;
				return 1;
			} else {
				return 0;
			}
		} break;

		case TOK_QUOTED_STRING: {
			if (**pos == '"') {
				*ret = new token( TOK_QUOTED_STRING );
				*pos += 1; // opening quote
				while (**pos != '"') {
					if (**pos == '\\') {
						*pos += 1;
						switch (**pos) {
							case 'n': {
								(**ret).data += '\n';
							} break;
							case '"': {
								(**ret).data += '"';
							} break;
							case '\\': {
								(**ret).data += '\\';
							} break;
								//
							default: {
								// Incorrect escape sequence
								printf("%%fatal \"Malformed escape sequence in string literal near character %d\"\n", pos);
								return 0;
							} break;
						}

					} else {
						(**ret).data += (**pos);
					}

					*pos += 1;
				}
				*pos += 1; // closing quote
				return 1;
			} else return 0;
		} break;

		// Not a generic parseable, check for existence of a composite
		//   syntax rule
		default: {
			syntax_map_t::iterator sp = syntax.find(tok_type);
			if (sp != syntax.end()) {
				
				char* test_pos( *pos );
				enum tok_types last_rule( TOK_RULE_ALL_IN_ORDER );
				std::vector<token*> elements;

				for (int i = 0, e = sp->second.size(); i < e; i++) {
					enum tok_types tok = sp->second[i];

					// Was it a rule?
					if (tok > TOK_RULES_START && tok < TOK_RULES_END) {
						last_rule = tok;
						continue;
					}

					// Attempt to parse

					token *child = new token(TOK_INVALID);
					int parsed = parse(tok, &test_pos, &child);

					// What does the rule say we should do now?

					if (last_rule == TOK_RULE_ALL_IN_ORDER) {

						
							// If it parsed, store it; otherwise, fail
							if (parsed) {
								child->parent = *ret;
								elements.push_back( child );
								// Continue
							} else {
								// FIXME: child NOT FREED
								return 0;
							}
						

					} else if (last_rule == TOK_RULE_OPTIONAL_REMAINING) {

							// The remaining elements must either all occur, or none of them.
							if (parsed) {
								// All the remaining elements are required.
								last_rule = TOK_RULE_ALL_IN_ORDER;
								// Store
								child->parent = *ret;
								elements.push_back( child );
							} else {
								break; // Success
							}

					} else if (last_rule == TOK_RULE_ONE_OF_THE_REMAINING) {

						
							// If it parsed, store it and succeed; otherwise, continue
							// If there are no remaining tokens and this is the condition,
							//    then fail.
							if (parsed) {
								last_rule = TOK_RULE_ALL_IN_ORDER; // Don't hit the failure condition later on
								child->parent = *ret;
								elements.push_back( child );
								break; // Success
							} else {
								// Reset test position
								*test_pos = **pos;
								// Continue
							}

					} else {
							// Unhandled rule exception
							// FIXME: child NOT FREED
							return 0;
						
					} // switch^W if-cascade


				} // for

				// Check conditions for success. Most rules should have
				//   exited by now
				if (last_rule == TOK_RULE_ONE_OF_THE_REMAINING) {
					// We didn't find one of the remaining.
					return 0;
				} else {
					// Made it through ALL_IN_ORDER or OPTIONAL_REMAINING successfully
					(*ret)->clear(tok_type);
					(*ret)->children = elements;
					*pos = test_pos;
					return 1;
				}

			} else {
				return 0;
			}

		} break;
	}
}

size_t char_pos_to_line_number(std::string& str, size_t pos) {
	size_t idx = 0, line = 1;
	for(;;) {
		idx = str.find('\n', idx);
		if (idx == str.npos) break;
		if (idx > pos) break;

		idx++; // skip \n character
		line++;
	}
	return line;
}

token* parse_easy(enum tok_types type, std::string& code) {

	// Clear global error state
	rightmost_search_pos   = NULL; // nullptr
	rightmost_search_token = TOK_INVALID;

	// Parse
	token *t  = new token(TOK_INVALID);
	char *pos = &(code[0]);
	if (! parse(type, &pos, &t)) {
		t->clear(TOK_INVALID);
	}
	
	// Detect if parsing stopped before the end of the string
	parse_skip_whitespace(&pos);
	size_t cpos = (pos - code.c_str());
	if (cpos != code.length()) {

		// Parsed $cpos characters, syntax error continuing past character $xpos
		size_t xpos = (rightmost_search_pos - code.c_str());
		printf(
			"%%fatal \"Parse error: expected %s on line %d\"\n",
			pexl_pprint_typename( rightmost_search_token ),
			char_pos_to_line_number(code, xpos)
			//, cpos
		);
	}

	return t;
}