/* scanner.re
 * This file is part of the Phalcon Framework.
 *
 * (c) Phalcon Team <team@phalcon.io>
 *
 * For the full copyright and license information, please view the
 * LICENSE.txt file that was distributed with this source code.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_phalcon.h"

#include "scanner.h"

#define YYCTYPE unsigned char
#define YYCURSOR (s->start)
#define YYLIMIT (s->end)
#define YYMARKER marker

int phql_get_token(phql_scanner_state *s, phql_scanner_token *token) {

	char *q = YYCURSOR;
	char *marker = YYCURSOR;
	int status = PHQL_SCANNER_RETCODE_IMPOSSIBLE;

	/*!re2c
	ESCQQ           = [\\]["];
	ESCQ            = [\\]['];
	ESCSEQ          = [\\][^\n\x00];
	ANYNOEOF        = [\001-\377];
	PRINTABLE       = [^\x00-\x1F\x7F];

	DSTRING = (["] (ESCQQ|ESCSEQ|ANYNOEOF\[\\"])* ["]);
	SSTRING = (['] (ESCQ|ESCSEQ|ANYNOEOF\[\\'])* [']);
	STRING  = DSTRING|SSTRING;
	*/
	while (PHQL_SCANNER_RETCODE_IMPOSSIBLE == status) {

		/*!re2c
		re2c:indent:top = 2;
		re2c:yyfill:enable = 0;

		INTEGER = [0-9]+;
		INTEGER {
			token->opcode = PHQL_T_INTEGER;
			token->value = estrndup(q, YYCURSOR - q);
			token->len = YYCURSOR - q;
			q = YYCURSOR;
			return 0;
		}

		HINTEGER = [x0-9A-Fa-f]+;
		HINTEGER {
			token->value = estrndup(q, YYCURSOR - q);
			token->len = YYCURSOR - q;
			if (token->len > 2 && !memcmp(token->value, "0x", 2)) {
				token->opcode = PHQL_T_HINTEGER;
			} else {
				int i, alpha = 0;
				for (i = 0; i < token->len; i++) {
					unsigned char ch = token->value[i];
					if (!((ch >= '0') && (ch <= '9'))) {
						alpha = 1;
						break;
					}
				}
				if (alpha) {
					token->opcode = PHQL_T_IDENTIFIER;
				} else {
					token->opcode = PHQL_T_INTEGER;
				}
			}
			q = YYCURSOR;
			return 0;
		}

		DOUBLE = ([0-9]*[.][0-9]+)|([0-9]+[.][0-9]*);
		DOUBLE {
			token->opcode = PHQL_T_DOUBLE;
			token->value = estrndup(q, YYCURSOR - q);
			token->len = YYCURSOR - q;
			q = YYCURSOR;
			return 0;
		}

		NPLACEHOLDER = "?"[0-9]+;
		NPLACEHOLDER {
			token->opcode = PHQL_T_NPLACEHOLDER;
			token->value = estrndup(q, YYCURSOR - q);
			token->len = YYCURSOR - q;
			q = YYCURSOR;
			return 0;
		}

		SPLACEHOLDER = ":"[a-zA-Z0-9_\-]+":";
		SPLACEHOLDER {
			token->opcode = PHQL_T_SPLACEHOLDER;
			token->value = estrndup(q + 1, YYCURSOR - q - 2);
			token->len = YYCURSOR - q - 2;
			q = YYCURSOR;
			return 0;
		}

		BPLACEHOLDER = "{"[a-zA-Z0-9_:\-]+"}";
		BPLACEHOLDER {
			token->opcode = PHQL_T_BPLACEHOLDER;
			token->value = estrndup(q + 1, YYCURSOR - q - 2);
			token->len = YYCURSOR - q - 2;
			q = YYCURSOR;
			return 0;
		}

		'UPDATE' {
			token->opcode = PHQL_T_UPDATE;
			return 0;
		}

		'SET' {
			token->opcode = PHQL_T_SET;
			return 0;
		}

		'DELETE' {
			token->opcode = PHQL_T_DELETE;
			return 0;
		}

		'FROM' {
			token->opcode = PHQL_T_FROM;
			return 0;
		}

		'AS' {
			token->opcode = PHQL_T_AS;
			return 0;
		}

		'INSERT' {
			token->opcode = PHQL_T_INSERT;
			return 0;
		}

		'INTO' {
			token->opcode = PHQL_T_INTO;
			return 0;
		}

		'VALUES' {
			token->opcode = PHQL_T_VALUES;
			return 0;
		}

		'WHERE' {
			token->opcode = PHQL_T_WHERE;
			return 0;
		}

		'SELECT' {
			token->opcode = PHQL_T_SELECT;
			return 0;
		}

		'ORDER' {
			token->opcode = PHQL_T_ORDER;
			return 0;
		}

		'BY' {
			token->opcode = PHQL_T_BY;
			return 0;
		}

		'ASC' {
			token->opcode = PHQL_T_ASC;
			return 0;
		}

		'DESC' {
			token->opcode = PHQL_T_DESC;
			return 0;
		}

		'GROUP' {
			token->opcode = PHQL_T_GROUP;
			return 0;
		}

		'HAVING' {
			token->opcode = PHQL_T_HAVING;
			return 0;
		}

		'LIMIT' {
			token->opcode = PHQL_T_LIMIT;
			return 0;
		}

		'OFFSET' {
			token->opcode = PHQL_T_OFFSET;
			return 0;
		}

		'NOT' {
			token->opcode = PHQL_T_NOT;
			return 0;
		}

		'AND' {
			token->opcode = PHQL_T_AND;
			return 0;
		}

		'OR' {
			token->opcode = PHQL_T_OR;
			return 0;
		}

		'LIKE' {
			token->opcode = PHQL_T_LIKE;
			return 0;
		}

		'ILIKE' {
			token->opcode = PHQL_T_ILIKE;
			return 0;
		}

		'AGAINST' {
			token->opcode = PHQL_T_AGAINST;
			return 0;
		}

		'IN' {
			token->opcode = PHQL_T_IN;
			return 0;
		}

		'ON' {
			token->opcode = PHQL_T_ON;
			return 0;
		}

		'JOIN' {
			token->opcode = PHQL_T_JOIN;
			return 0;
		}

		'INNER' {
			token->opcode = PHQL_T_INNER;
			return 0;
		}

		'LEFT' {
			token->opcode = PHQL_T_LEFT;
			return 0;
		}

		'RIGHT' {
			token->opcode = PHQL_T_RIGHT;
			return 0;
		}

		'CROSS' {
			token->opcode = PHQL_T_CROSS;
			return 0;
		}

		'OUTER' {
			token->opcode = PHQL_T_OUTER;
			return 0;
		}

		'FULL' {
			token->opcode = PHQL_T_FULL;
			return 0;
		}

		'IS' {
			token->opcode = PHQL_T_IS;
			return 0;
		}

		'NULL' {
			token->opcode = PHQL_T_NULL;
			return 0;
		}

		'DISTINCT' {
			token->opcode = PHQL_T_DISTINCT;
			return 0;
		}

		'ALL' {
			token->opcode = PHQL_T_ALL;
			return 0;
		}

		'NOT BETWEEN' {
			token->opcode = PHQL_T_BETWEEN_NOT;
			return 0;
		}

		'BETWEEN' {
			token->opcode = PHQL_T_BETWEEN;
			return 0;
		}

		'CAST' {
			token->opcode = PHQL_T_CAST;
			return 0;
		}

		'CONVERT' {
			token->opcode = PHQL_T_CONVERT;
			return 0;
		}

		'USING' {
			token->opcode = PHQL_T_USING;
			return 0;
		}

		'EXISTS' {
			token->opcode = PHQL_T_EXISTS;
			return 0;
		}

		'TRUE' {
			token->opcode = PHQL_T_TRUE;
			return 0;
		}

		'FALSE' {
			token->opcode = PHQL_T_FALSE;
			return 0;
		}

		'CASE' {
			token->opcode = PHQL_T_CASE;
			return 0;
		}

		'WHEN' {
			token->opcode = PHQL_T_WHEN;
			return 0;
		}

		'THEN' {
			token->opcode = PHQL_T_THEN;
			return 0;
		}

		'ELSE' {
			token->opcode = PHQL_T_ELSE;
			return 0;
		}

		'END' {
			token->opcode = PHQL_T_END;
			return 0;
		}

		'FOR' {
			token->opcode = PHQL_T_FOR;
			return 0;
		}

		'WITH' {
			token->opcode = PHQL_T_WITH;
			return 0;
		}

		STRING {
			token->opcode = PHQL_T_STRING;
			token->value = estrndup(q + 1, YYCURSOR - q - 2);
			token->len = YYCURSOR - q - 2;
			q = YYCURSOR;
			return 0;
		}

		IDENTIFIER = [\\]?[a-zA-Z_][a-zA-Z0-9_\\:]*;
		IDENTIFIER {
			token->opcode = PHQL_T_IDENTIFIER;
			if ((YYCURSOR - q) > 1) {
				if (q[0] == '\\') {
					token->value = estrndup(q + 1, YYCURSOR - q - 1);
					token->len = YYCURSOR - q - 1;
				} else {
					token->value = estrndup(q, YYCURSOR - q);
					token->len = YYCURSOR - q;
				}
			} else {
				token->value = estrndup(q, YYCURSOR - q);
				token->len = YYCURSOR - q;
			}
			q = YYCURSOR;
			return 0;
		}

		EIDENTIFIER = '[' ([\\][\[]|[\\][\]]|PRINTABLE\[\[\]])* ']';
		EIDENTIFIER {
			token->opcode = PHQL_T_IDENTIFIER;
			token->value = estrndup(q + 1, YYCURSOR - q - 2);
			token->len = YYCURSOR - q - 2;
			q = YYCURSOR;
			return 0;
		}

		"+" {
			token->opcode = PHQL_T_ADD;
			return 0;
		}

		"-" {
			token->opcode = PHQL_T_SUB;
			return 0;
		}

		"*" {
			token->opcode = PHQL_T_MUL;
			return 0;
		}

		"/" {
			token->opcode = PHQL_T_DIV;
			return 0;
		}

		"%" {
			token->opcode = PHQL_T_MOD;
			return 0;
		}

		"." {
			token->opcode = PHQL_T_DOT;
			return 0;
		}

		":" {
			token->opcode = PHQL_T_COLON;
			return 0;
		}

		"," {
			token->opcode = PHQL_T_COMMA;
			return 0;
		}

		"(" {
			token->opcode = PHQL_T_PARENTHESES_OPEN;
			return 0;
		}

		")" {
			token->opcode = PHQL_T_PARENTHESES_CLOSE;
			return 0;
		}

		"!=" {
			token->opcode = PHQL_T_NOTEQUALS;
			return 0;
		}

		"<>" {
			token->opcode = PHQL_T_NOTEQUALS;
			return 0;
		}

		"<=" {
			token->opcode = PHQL_T_LESSEQUAL;
			return 0;
		}

		">=" {
			token->opcode = PHQL_T_GREATEREQUAL;
			return 0;
		}

		"<" {
			token->opcode = PHQL_T_LESS;
			return 0;
		}

		">" {
			token->opcode = PHQL_T_GREATER;
			return 0;
		}

		"=" {
			token->opcode = PHQL_T_EQUALS;
			return 0;
		}

		"->>" {
			token->opcode = PHQL_T_OP_JSON_GET_TEXT;
			return 0;
		}

		"->" {
			token->opcode = PHQL_T_OP_JSON_GET;
			return 0;
		}

		"#>>" {
			token->opcode = PHQL_T_OP_JSON_PATH_TEXT;
			return 0;
		}

		"#>" {
			token->opcode = PHQL_T_OP_JSON_PATH;
			return 0;
		}

		"@@" {
			token->opcode = PHQL_T_OP_MATCHES;
			return 0;
		}

		"@>" {
			token->opcode = PHQL_T_OP_CONTAINS;
			return 0;
		}

		"<@" {
			token->opcode = PHQL_T_OP_CONTAINED;
			return 0;
		}

		"&&" {
			token->opcode = PHQL_T_OP_OVERLAPS;
			return 0;
		}

		"||" {
			token->opcode = PHQL_T_OP_CONCAT;
			return 0;
		}

		"!" {
			token->opcode = PHQL_T_NOT;
			return 0;
		}

		"~" {
			token->opcode = PHQL_T_BITWISE_NOT;
			return 0;
		}

		"^" {
			token->opcode = PHQL_T_BITWISE_XOR;
			return 0;
		}

		"&" {
			token->opcode = PHQL_T_BITWISE_AND;
			return 0;
		}

		"|" {
			token->opcode = PHQL_T_BITWISE_OR;
			return 0;
		}

		[ \t\n\r]+ {
			token->opcode = PHQL_T_IGNORE;
			return 0;
		}

		"\000" {
			status = PHQL_SCANNER_RETCODE_EOF;
			break;
		}

		[^] {
			status = PHQL_SCANNER_RETCODE_ERR;
			break;
		}

		*/
	}

	return status;
}
