/*
*  Token definitions for the LibreSignage markup transpiler.
*/

var lexeme = require('./lexeme.js');
var lpost = require('./lexer_post.js');
var err = require('./error.js');

/*
*  Lexer token definitions.
*/
module.exports.tokens = {
	BRACK_OPEN: {
		re: [ /^\[/ ],
		repr: '[',
		context: [ { '-1': '!BACKWARD_SLASH' } ]
	},
	BRACK_CLOSE: {
		re: [ /^\]/ ],
		repr: ']',
		context: [ { '-1': '!BACKWARD_SLASH' } ]
	},
	FORWARD_SLASH: {
		re: [ /^\// ],
		repr: '/',
		context: [ { '-1': '!BACKWARD_SLASH' } ]
	},
	BACKWARD_SLASH: {
		re: [ /^\\/ ],
		repr: '\\',
		context: [ { '-1': '!BACKWARD_SLASH' } ]
	},
	ASSIGN: {
		re: [ /^=/ ],
		repr: '=',
		context: [ { '-1': '!BACKWARD_SLASH' } ]
	},
	WHITESPACE: {
		re: [ /^\s+/ ],
		repr: '(whitespace)'
	},
	DBL_QUOTE: {
		re: [ /^"/ ],
		repr: '"',
		context: [ { '-1': '!BACKWARD_SLASH' } ]
	},
	SNGL_QUOTE: {
		re: [ /^'/ ],
		repr: "'",
		context: [ { '-1': '!BACKWARD_SLASH' } ]
	},
	CHARACTER: {
		re: [ /^.{1}/ ],
		repr: "(character)"
	},

	// Post-processor created tokens.
	TAGNAME: {
		nomatch: true,
		repr: '(tagname)',
	},
	IDENTIFIER: {
		nomatch: true,
		repr: '(identifier)'
	},
	STR_LITERAL: {
		nomatch: true,
		repr: '(string literal)'
	},
	LITERAL: {
		nomatch: true,
		repr: '(literal)'
	}
}

function lexer_post_make_strlit(lexemes, type) {
	let ret = [];
	let tmp = null;
	let i = 0;
	while(i < lexemes.length) {
		if (lexemes[i].type === type) {
			tmp = new lexeme.Lexeme(
				'STR_LITERAL',
				lexemes[i].raw,
				lexemes[i].ln_num,
				lexemes[i].ln_pos
			);

			do {
				if (++i > lexemes.length - 1) {
					throw new err.MarkupSyntaxError(
						'EEOL',
						lexemes[i - 1],
						'Missing closing quote for string literal.'
					);
				}
				tmp.raw += lexemes[i].raw;
			} while(lexemes[i].type !== type);

			ret.push(tmp);
		} else {
			ret.push(lexemes[i]);
		}
		i++;
	}
	return ret;
}

/*
*  Lexer post processor functions.
*
*  Each object in this array contains a type
*  value that defines the type of the lexeme
*  the post-processor function modifies and
*  a post key that contains the function that
*  does all the work. The lexeme array and the
*  type are passed to the post-processor function
*  as arguments.
*/
module.exports.post = [
	{ // Suppress backward slashes.
		type: 'BACKWARD_SLASH',
		post: lpost.lexer_post_suppress
	},
	{ // Concatenate subsequent CHARACTERs.
		type: 'CHARACTER',
		post: lpost.lexer_post_cat
	},
	{ // Convert CHARACTERs into LITERALs.
		type: 'CHARACTER',
		post: function(lexemes, type) {
			let ret = lexemes.slice();
			for (let l of ret) {
				if (l.type === type) { l.ch_type('LITERAL'); }
			}
			return ret;
		}
	},
	{ // Convert all lexemes between DBL_QUOTEs into a STR_LITERAL.
		type: 'DBL_QUOTE',
		post: lexer_post_make_strlit
	},
	{ // Convert all lexemes between SNGL_QUOTEs into a STR_LITERAL.
		type: 'SNGL_QUOTE',
		post: lexer_post_make_strlit
	},
	{ // Convert LITERALs after '[' or '[/' into TAGNAMEs.
		type: 'LITERAL',
		post: function(lexemes, type) {
			let ret = lexemes.slice();
			for (let i = 0; i < ret.length; i++) {
				if (ret[i].type == 'BRACK_OPEN') {
					if (
						ret.length - 1 >= i + 1
						&& ret[i + 1].type == 'LITERAL'
					) {
						ret[i + 1].ch_type('TAGNAME');
					} else if (
						ret.length - 1 >= i + 2
						&& ret[i + 1].type == 'FORWARD_SLASH'
						&& ret[i + 2].type == 'LITERAL'
					) {
						ret[i + 2].ch_type('TAGNAME');
					}
				}
			}
			return ret;
		}
	},
	{ // Convert LITERALs followed by an assignment into IDENTIFIERs.
		type: 'LITERAL',
		post: function(lexemes, type) {
			let ret = lexemes.slice();
			for (let i = 0; i < ret.length; i++) {
				if (
					ret[i].type == type
					&& (
						(
							ret.length - 1 >= i + 1
							&& ret[i + 1].type == 'ASSIGN'
						) || (
							ret.length - 1 >= i + 2
							&& ret[i + 1].type == 'WHITESPACE'
							&& ret[i + 2].type == 'ASSIGN'
						)
					)
				) {
					ret[i].ch_type('IDENTIFIER');
				}
			}
			return ret;
		}
	}
]
