/*
 * Copyright (C) 2012 - Juan Ferrer Toribio
 *
 * 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 3 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. If not, see <http://www.gnu.org/licenses/>.
 */

/* THIS FILE HAS BEEN GENERATED USING THE SOURCE IN parser/scan.rl */ 

#include <string.h>
#include "sql-parser.h"
#include "parser/gram.c"

#define PARSE(token, value)\
G_STMT_START {											\
	state->current = ts;								\
	Parse (parser, SQL_PARSER_##token, value, state);   \
} G_STMT_END

/**
 * SECTION: sql-parser
 * @Short_description: a simple DML query parser
 * @Title: SqlParser
 * 
 * This is the parser used by Hedera to transform a string containing one or 
 * more SQL data management queries into an #SqlObject and thus have an idea
 * of what must happen when a change is made in the affected data.
 **/
static gchar * get_token (const gchar * ts, const gchar * te)
{
	if (!(ts && te)) return NULL;
	gsize len = (gsize) (te-ts);
	gchar * str = g_malloc (len+1);
	g_memmove (str, ts, len);
	str[len] = '\0';
	return str;
}

%%{
	machine sql_scanner;

#/*-----------------------# SYMBOL Definition #-----------------------*/#
# Keywords
	AS		= "AS"i;
	UNION	= "UNION"i;
	ALL		= "ALL"i;
	ANY		= "ANY"i;
	EXCEPT	= "EXCEPT"i;
	INTERSECT = "INTERSECT"i;
	SELECT	= "SELECT"i;
	FROM	= "FROM"i;
	WHERE	= "WHERE"i;
	DISTINCT = "DISTINCT"i;
	JOIN	= "JOIN"i;
	INNER	= "INNER"i;
	LEFT	= ("OUTER"i space+|"") "LEFT"i;
	RIGHT	= ("OUTER"i space+|"") "RIGHT"i;
	ON		= "ON"i;
	USING	= "USING"i;
	NATURAL = "NATURAL"i;
	GROUP	= "GROUP" space+ "BY"i;
	HAVING	= "HAVING"i;
	ORDER	= "ORDER"i space+ "BY"i;
	ASC		= "ASC"i;
	DESC	= "DESC"i;
	LIMIT	= "LIMIT"i;
	OFFSET	= "OFFSET"i;
	UPDATE	= "UPDATE"i;
	SET		= "SET"i;
	INSERT	= "INSERT"i space+ "INTO"i;
	DEFAULT	= "DEFAULT"i;
	VALUES	= "VALUES"i;
	DELETE	= "DELETE"i space+ "FROM"i;
	RETURNING = "RETURNING"i;
# Parenthesis
	LP		= "(";
	RP		= ")";
# Colon
	DOT		= ".";
# Semicolon
	SC		= ";";
# Comma
	CM		= ",";
# Identifier quote
	IQ		= ("\"" | "`");
# Operators
	PLUS	= "+";
	MINUS	= "-";
	STAR	= "*";
	DIV		= "/";
	MOD		= "%";
	EQ		= "=";
	NE		= ("<>" | "!=");
	GT		= ">";
	GE		= ">=";
	LT		= "<";
	LE		= "<=";
# Operator Keywords
	AND		= "AND"i;
	OR		= "OR"i;
	NOT		= ("NOT"i | "!");
	LIKE	= "LIKE"i;
	IS		= "IS"i;
	XOR		= "XOR"i;
# Boolean Keywords
	BOOLEAN	= ("TRUE"i | "FALSE"i);

	Op = (AND | OR | NOT | LIKE | IS | XOR);
	keyword = ( Op | BOOLEAN | AS | UNION | ALL | ANY | EXCEPT | INTERSECT	|
		SELECT | FROM | WHERE | DISTINCT | JOIN | INNER | LEFT| RIGHT | ON	|
		USING | NATURAL | GROUP | HAVING | ORDER | ASC | DESC | LIMIT | OFFSET |
		UPDATE | SET | INSERT | DEFAULT | VALUES | RETURNING | DELETE);

# Identifiers
	Iescape = ("\"\"" | "" );
	IDENTIFIER = ((alpha | "_" | "$") (alnum | "_" | "$")*) - keyword;
	QIDENTIFIER = IQ ((any - IQ)* Iescape (any - IQ)*) IQ;
	PLACEHOLDER = "#" IDENTIFIER;

# Escaped string characters
	escape = ("\\'"| "''" | "");

# Data values
	STRING = "'" (any - "'")* escape (any - "'")* "'";
	INTEGER = digit+;
	FLOAT = digit+ ("." digit+)?;

# Comments
	comment = ("-- " (any - "\n")* "\n"	|
			"/*" (any* -- "*/") "*/");

#/*-----------------------# SCANNER Definition #-----------------------*/#
	main :=
	|*
#// Keywords
		AS		=> { PARSE (AS, 0); };
		UNION	=> { PARSE (UNION, 0); };
		ALL		=> { PARSE (ALL, 0); };
		ANY		=> { PARSE (ANY, 0); };
		EXCEPT	=> { PARSE (EXCEPT, 0); };
		INTERSECT => { PARSE (INTERSECT, 0); };
		SELECT	=> { PARSE (SELECT, 0); };
		DISTINCT => { PARSE (DISTINCT, 0); };
		FROM	=> { PARSE (FROM, 0); };
		WHERE	=> { PARSE (WHERE, 0); };
		JOIN	=> { PARSE (JOIN, 0); };
		INNER	=> { PARSE (INNER, 0); };
		LEFT	=> { PARSE (LEFT, 0); };
		RIGHT	=> { PARSE (RIGHT, 0); };
		ON		=> { PARSE (ON, 0); };
		USING	=> { PARSE (USING, 0); };
#//		NATURAL	=> { PARSE (NATURAL, 0); };
		GROUP	=> { PARSE (GROUP, 0); };
		HAVING	=> { PARSE (HAVING, 0); };
		ORDER	=> { PARSE (ORDER, 0); };
		ASC		=> { PARSE (ASC, 0); };
		DESC	=> { PARSE (DESC, 0); };
		LIMIT	=> { PARSE (LIMIT, 0); };
		OFFSET	=> { PARSE (OFFSET, 0); };
		UPDATE	=> { PARSE (UPDATE, 0); };
		SET		=> { PARSE (SET, 0); };
		INSERT	=> { PARSE (INSERT, 0); };
		DEFAULT	=> { PARSE (DEFAULT, 0); };
		VALUES	=> { PARSE (VALUES, 0); };
		DELETE	=> { PARSE (DELETE, 0); };

#// Punctuation symbols
		LP		=> { PARSE (LP, 0); };
		RP		=> { PARSE (RP, 0); };
		DOT		=> { PARSE (DOT, 0); };
		SC		=> { PARSE (SC, 0); };
		CM		=> { PARSE (CM, 0); };

#// Identifiers
		IDENTIFIER => { PARSE (IDENTIFIER, get_token (ts, te)); };
		QIDENTIFIER => { PARSE (IDENTIFIER, get_token (ts+1, te-1)); };
		PLACEHOLDER => { PARSE (PLACEHOLDER, get_token (ts+1, te)); };

#// Data values
		STRING	=> { PARSE (STRING, get_token (ts+1, te-1)); };
		INTEGER	=> { PARSE (INTEGER, get_token (ts, te)); };
		FLOAT	=> { PARSE (FLOAT, get_token (ts, te)); };
		BOOLEAN	=> { PARSE (BOOLEAN, get_token(ts, te)); };

#// Operators
		PLUS	=> { PARSE (PLUS, 0); };
		MINUS	=> { PARSE (MINUS, 0); };
		STAR	=> { PARSE (STAR, 0); };
		DIV		=> { PARSE (DIV, 0); };
		MOD		=> { PARSE (MOD, 0); };
		EQ		=> { PARSE (EQ, 0); };
		NE		=> { PARSE (NE, 0); };
		GT		=> { PARSE (GT, 0); };
		GE		=> { PARSE (GE, 0); };
		LT		=> { PARSE (LT, 0); };
		LE		=> { PARSE (LE, 0); };

#// Operator Keywords
		AND		=> { PARSE (AND, 0); };
		OR		=> { PARSE (OR, 0); };
		NOT		=> { PARSE (NOT, 0); };
		LIKE	=> { PARSE (LIKE, 0); };
		IS		=> { PARSE (IS, 0); };
		XOR		=> { PARSE (XOR, 0); };

#// Ignored
		space;
		comment;
	*|;
}%%
%% write data;

SqlObject * sql_parser_parse (const gchar * sql, GError ** err)
{
	gint cs, act;
	gchar * p, * pe, * ts, * te;
	gpointer eof, parser;
	ParseState * state;
	SqlObject * object;
	gchar * query;

	if (!sql)
	{
		g_set_error (err, SQL_PARSER_LOG_DOMAIN,
			SQL_PARSER_ERROR_EMPTY_QUERY, "Empty query!\n");
		return NULL;
	}

	query = g_strdup (sql);

	state = g_new (ParseState, 1);
	state->object = NULL;
	p = state->string = state->current = query;
	state->failed = FALSE;
	state->error = err;
	pe = p + strlen (p) + 1;
	eof = pe;

	parser = ParseAlloc (g_malloc);

	%% write init;
	%% write exec;

	Parse (parser, 0, 0, state);
	ParseFree (parser, g_free);

	if (state->failed)
	{
		if (state->object && G_IS_OBJECT (state->object))
			g_object_unref (g_object_ref_sink (state->object));
		object = NULL;
	}
	else
		object = g_object_ref_sink (state->object);

	g_free (state);
	g_free (query);

	return object;
}