/********************************************************************************
*
* File: sql.y
* Title: The sql parser; it generates Expr terms
* Programmer: Leonidas Fegaras, UTA
* Date: 2/21/97
*
********************************************************************************/
%{
#include <stream.h>
#include <string.h>
#include "external.h"
#include "evaluation.h"

static int lineno = 1;
#include "sql.yy.c"


static list<Expr>* table_constraints = Nil;

static Expr current_attribute;


yyerror (char *s) {
  cout << s << " (line: " << lineno << ")" << " Token: " << yytext << "\n";
};

%}
%union {
	String		Tstring;
	int		Tint;
	Expr		Texp;
	list<Expr>*	Texps;
}
%type <Texp> stmt table attribute column ddl domain constraint dml query projection
%type <Texp> predicate expr fullconstraint itable opr function
%type <Texps> stmts attributes columns coldef coldefs constraints constrs
%type <Texps> assignments exprs ordering selection grouping having tables projections
%type <Tstring> name compr
%type <Tint> num
%token NUM ID CSTRING REAL CREATE TABLE ALTER VIEW ADD DROP INTT DECIMAL CHAR VARCHAR
       SNULL PRIMARY KEY UNIQUE REFERENCES FOREIGN UPDATE SET WHERE INTO VALUES INDEX
       DELETE FROM SELECT GROUP BY HAVING ORDER IS GE LE NE ON
       ALL SOME ANY IN COLUMN INSERT COUNT MIN MAX AVG SUM
%left ';'
%left ','
%left UNION
%left INTERSECT
%left EXISTS
%nonassoc AS
%left OR
%left AND
%left NOT
%left GE LE NE '=' '>' '<'
%left '-' '+'
%left '*' '/'
%left NEG
%left '.'
%%
stmts:		  stmt ';'			{ $$ = Nil->cons($1); }
		| stmts stmt ';'		{ $$ = $1->cons($2); }
		;
stmt:		  ddl				{ do_DDL($1,table_constraints); $$ = $1; }
		| dml				{ do_DML($1); $$ = $1; }
		;
name:		  ID				{ $$ = new string(yytext); }
		;
num:		  NUM				{ $$ = atoi(yytext); }
		;
table:		  name				{ $$ = variable($1); }
		;
attribute:	  name				{ $$ = variable($1); }
		;
attributes:	  attribute			{ $$ = Nil->cons($1); }
		| attributes ',' attribute	{ $$ = $1->cons($3); }
		;
column:		  attribute			{ $$ = function(variable("project"),
							Cons(variable("UNKNOWN"),Cons($1,Nil))); }
		| table '.' attribute		{ $$ = function(variable("project"),
								Cons($1,Cons($3,Nil))); }
		;
columns:	  column			{ $$ = Nil->cons($1); }
		| columns ',' column		{ $$ = $1->cons($3); }
		;
ddl:		  CREATE TABLE			{ table_constraints = Nil; }
		  table '(' coldefs ')'		{ $$ = function(variable("create_table"),
								Cons($4,$6)); }
		| DROP TABLE table		{ $$ = function(variable("drop_table"),
								Cons($3,Nil)); }
		| ALTER TABLE table		{ table_constraints = Nil; }
		  ADD COLUMN coldef
			{ if ($7->consp())
			     $$ = function(variable("alter_table_col"),Cons($3,$7));
			  else $$ = function(variable("alter_table_constr"),
					     Cons($3,table_constraints)); }
		| ALTER TABLE table DROP COLUMN attribute
			{ $$ = function(variable("alter_table_drop"),Cons($3,Cons($6,Nil))); }
		| CREATE VIEW table AS query
			{ $$ = function(variable("create_view"),Cons($3,Cons($5,Nil))); }
		| CREATE INDEX name ON table '(' attributes ')'
			{ $$ = function(variable("create_index"),Cons(variable($3),
							Cons($5,$7->reverse()))); }
		| DROP INDEX name		{ $$ = function(variable("drop_index"),
								Cons(variable($3),Nil)); }
		;
coldef:		  attribute			{ current_attribute = $1; }
		  domain constraints
			{ int b = 1;
			  list<Expr>* c = Nil;
			  for(list<Expr>* r = $4; r->consp(); r=r->tl)
			     if (r->hd->variablep())
				if (r->hd->name()->eq("null")) b=0;
				else if (r->hd->name()->eq("notnull")) b=1;
				     else c = c->cons(r->hd);
			     else c = c->cons(r->hd);
			  table_constraints = table_constraints->append(c);
			  $$ = Nil->cons(function(variable("attribute"),
						  Cons($1,Cons($3,Cons(integer(b),Nil))))); }
		| fullconstraint		{ table_constraints = table_constraints->cons($1);
						  $$ = Nil; }
		;
coldefs:	  coldef			{ $$ = Nil->append($1); }
		| coldefs ',' coldef		{ $$ = $1->append($3); }
		;
domain:		  INTT				{ $$ = variable("int"); }
		| DECIMAL			{ $$ = variable("decimal"); }
		| CHAR				{ $$ = function(variable("char"),Cons(integer(1),Nil)); }
		| CHAR '(' num ')'		{ $$ = function(variable("char"),Cons(integer($3),Nil)); }
		| VARCHAR '(' num ')'		{ $$ = function(variable("varchar"),
								Cons(integer($3),Nil)); }
		;
constraint:	  SNULL				{ $$ = variable("null"); }
		| NOT SNULL			{ $$ = variable("notnull"); }
		| PRIMARY KEY			{ $$ = function(variable("key"),
								Cons(current_attribute,Nil)); }
		| UNIQUE			{ $$ = function(variable("unique"),
								Cons(current_attribute,Nil)); }
		| REFERENCES table '(' attributes ')'
			{ $$ = function(variable("foreign"),
					Cons(function(variable("from"),Cons(current_attribute,Nil)),
					Cons(function(variable("to"),Cons($2,$4->reverse())),Nil))); }
		;
constrs:	  constraint			{ $$ = Nil->cons($1); }
		| constrs constraint		{ $$ = $1->cons($2); }
		;
constraints:	  /* empty */			{ $$ = Nil; }
		| constrs			{ $$ = $1; }
		;
fullconstraint:   PRIMARY KEY '(' attributes ')' { $$ = function(variable("key"),$4->reverse()); }
		| UNIQUE '(' attributes ')'	 { $$ = function(variable("unique"),$3->reverse()); }
		| FOREIGN KEY '(' attributes ')' REFERENCES table '(' attributes ')'
			{ $$ = function(variable("foreign"),Cons(function(variable("from"),$4->reverse()),
					Cons(function(variable("to"),Cons($7,$9->reverse())),Nil))); }
		;
dml:		  query				{ $$ = $1; }
		| UPDATE table SET assignments
			{ $$ = function(variable("update"),Cons($2,Cons(function(variable("assignments"),
					$4),Cons(variable("true"),Nil)))); }
		| UPDATE table SET assignments WHERE predicate
			{ $$ = function(variable("update"),Cons($2,Cons(function(variable("assignments"),
					$4),Cons($6,Nil)))); }
		| INSERT INTO table VALUES '(' exprs ')'
			{ $$ = function(variable("insert"),Cons($3,Cons(variable("ALL"),
					Cons(function(variable("tuple"),$6->reverse()),Nil)))); }
		| INSERT INTO table '(' attributes ')' VALUES '(' exprs ')'
			{ $$ = function(variable("insert"),Cons($3,Cons(function(variable("attributes"),
							$5->reverse()),
					Cons(function(variable("tuple"),$9->reverse()),Nil)))); }
		| DELETE FROM table	{ $$ = function(variable("delete"),
							Cons($3,Cons(variable("true"),Nil))); }
		| DELETE FROM table WHERE predicate
					{ $$ = function(variable("delete"),Cons($3,Cons($5,Nil))); }
		;
assignments:	  attribute '=' expr	{ $$ = Nil->cons(function(variable("assign"),
								  Cons($1,Cons($3,Nil)))); }
		| assignments ',' attribute '=' expr
					{ $$ = $1->cons(function(variable("assign"),
							Cons($3,Cons($5,Nil)))); }
		;
query:		  SELECT '*' FROM tables selection
			{ $$ = function(variable("sql_select"),Cons(variable("ALL"),
					Cons(function(variable("from"),$4->reverse()),$5))); }
		| SELECT projections FROM tables selection
			{ $$ = function(variable("sql_select"),Cons(function(variable("project"),
									$2->reverse()),
				Cons(function(variable("from"),$4->reverse()),$5))); }
		| query UNION query	{ $$ = function(variable("union"),Cons($1,Cons($3,Nil))); }
		| query INTERSECT query	{ $$ = function(variable("intersect"),Cons($1,Cons($3,Nil))); }
		| '(' query ')'		{ $$ = $2; }
		;
projection:	  expr			{ $$ = function(variable("as"),Cons($1,Cons(variable("NONE"),Nil))); }
		| expr AS attribute	{ $$ = function(variable("as"),Cons($1,Cons($3,Nil))); }
		| expr attribute	{ $$ = function(variable("as"),Cons($1,Cons($2,Nil))); }
		| table '.' '*'		{ $$ = function(variable("as"),Cons(function(variable("project"),
						 Cons($1,Cons(variable("ALL"),Nil))),
						 Cons(variable("NONE"),Nil))); }
		;
projections:	  projection			{ $$ = Nil->cons($1); }
		| projections ',' projection	{ $$ = $1->cons($3); }
		;
itable:		  table				{ $$ = function(variable("as"),Cons($1,Cons($1,Nil))); }
		| table AS table		{ $$ = function(variable("as"),Cons($1,Cons($3,Nil))); }
		| table table			{ $$ = function(variable("as"),Cons($1,Cons($2,Nil))); }
		| '(' query ')' AS table	{ $$ = function(variable("as"),Cons($2,Cons($5,Nil))); }
		;
tables:		  itable			{ $$ = Nil->cons($1); }
		| tables ',' itable		{ $$ = $1->cons($3); }
		;
selection:	  grouping			{ $$ = $1->cons(variable("true")); }
		| WHERE predicate grouping	{ $$ = $3->cons($2); }
		;
grouping:	  ordering		{ $$ = $1->cons(variable("true"))->cons(variable("NONE")); }
		| GROUP BY columns having	{ $$ = $4->cons(function(variable("columns"),
								$3->reverse())); }
		;
having:		  ordering			{ $$ = $1->cons(variable("true")); }
		| HAVING predicate ordering	{ $$ = $3->cons($2); }
		;
ordering:	  /* empty */			{ $$ = Nil->cons(variable("NONE")); }
		| ORDER BY columns		{ $$ = Nil->cons(function(variable("columns"),
								 $3->reverse())); }
		;
predicate:	  NOT predicate			{ $$ = function(variable("not"),Cons($2,Nil)); }
		| predicate AND predicate	{ $$ = function(variable("and"),Cons($1,Cons($3,Nil))); }
		| predicate OR predicate	{ $$ = function(variable("or"),Cons($1,Cons($3,Nil))); }
		| EXISTS '(' query ')'		{ $$ = function(variable("exists"),Cons($3,Nil)); }
		| expr IS SNULL		{ $$ = function(variable("null"),Cons($1,Nil)); }
		| expr IS NOT SNULL	{ $$ = function(variable("not"),
						Cons(function(variable("null"),Cons($1,Nil)),Nil)); }
		| '(' predicate ')'	{ $$ = $2; }
		| expr opr expr		{ $$ = function($2,Nil->cons($3)->cons($1)); }
		;
expr:		  num				{ $$ = integer($1); }
		| CSTRING			{ yytext[strlen(yytext)-1]='\0';
						  $$ = estring(new string(yytext+1)); }
		| SNULL				{ $$ = variable("null"); }
		| column			{ $$ = $1; }
		| COUNT '(' '*' ')'		{ $$ = function(variable("count"),
								Cons(variable("ALL"),Nil)); }
		| COUNT '(' expr ')'		{ $$ = function(variable("count"),Cons($3,Nil)); }
		| function '(' exprs ')'	{ $$ = function($1,$3->reverse()); }
		| expr '*' expr			{ $$ = function(variable("times"),Cons($1,Cons($3,Nil))); }
		| expr '/' expr			{ $$ = function(variable("divide"),Cons($1,Cons($3,Nil))); }
		| expr '+' expr			{ $$ = function(variable("plus"),Cons($1,Cons($3,Nil))); }
		| expr '-' expr			{ $$ = function(variable("minus"),Cons($1,Cons($3,Nil))); }
		| '-' expr	%prec NEG	{ $$ = function(variable("neg"),Cons($2,Nil)); }
		| '(' expr ')'			{ $$ = $2; }
		;
exprs:		  expr				{ $$ = Nil->cons($1); }
		| exprs ',' expr		{ $$ = $1->cons($3); }
		;
function:	  name				{ $$ = variable($1); }
		| MIN				{ $$ = variable("min"); }
		| MAX				{ $$ = variable("max"); }
		| AVG				{ $$ = variable("avg"); }
		| SUM				{ $$ = variable("sum"); }
		;
opr:		  compr				{ $$ = variable($1); }
		| compr ALL			{ string s = *$1+"-all"; $$ = variable(new string(s)); }
		| compr SOME			{ string s = *$1+"-some"; $$ = variable(new string(s)); }
		| compr ANY			{ string s = *$1+"-any"; $$ = variable(new string(s)); }
		| IN				{ $$ = variable("in"); }
		;
compr:		  '='				{ $$ = new string("eq"); }
		| GE				{ $$ = new string("geq"); }
		| LE				{ $$ = new string("leq"); }
		| NE				{ $$ = new string("neq"); }
		| '>'				{ $$ = new string("gt"); }
		| '<'				{ $$ = new string("lt"); }		
		;
%%

parse_sql () {
  lineno = 1;
  yyparse();
};
