%{

static char RuleLockIn_y[] = "$Header: parse.y,v 1.1 89/02/20 16:27:30 hirohama Exp $";

#include <stdio.h>
#include <strings.h>
#include "postgres.h"
#include "log.h"
#include "rlock.h"
#include "internal.h"

#define DEFAULT_MAXLENGTH 100
#define MAX_SHORT_STRING_LENGTH 10

/*
 * The general format of a the lock attribute is as follows...
 * ( Note that "{ X }*" means 0 or more repetitions of "X", and 
 * "{ X }+" means 1 or more repetitions....)
 *
 * LOCK ::= { LOCK_PACKAGE }*
 * LOCK_PACKAGE ::= LOCK_HEADER { LOCK_DATA }+
 * LOCK_HEADER ::= RULEID PRIORITY RULETYPE
 * LOCK_DATA ::= LOCKTYPE ATTRNO PLANNO
 *
 * RULEID is the ObjectId of the corresponding rule
 * PRIORITY is the priority of the rule (one byte long)
 * RULETYPE is one byte long and its bits are divides as following:
 *    The low order 3 bits specify the type of the rule as follows...
 *		0 = Retrieve
 *		1 = Replace
 *		2 = Append
 *		3 = Delete
 *		4 = Refuce Retrieve
 *		5 = Refuse Replace
 *		6 = Refuce Append
 *		7 = Refuce Delete
 *    The 7th bit is 0 if the rule is late or 1 if the rule is early
 *    The 8th bit is 1 if and only if the LOCK_HEADER in question belongs
 *    to the last LOCK_PACKAGE of the LOCK.
 *
 * ATTRNO is the number of attribute to be locked.
 * PLANNO specifies the plan to be executed when the lock is broken.
 * LOCKTYPE is one byte long and its 2 first bits specify the type of
 * lock as follows...
 *     0=W
 *     1=R1
 *     2=R2
 *     3=R3
 *
 * The 8th bit is 1 if and only if this is the last LOCK_DATA in the 
 * LOCKPACKAGE.
 *
 */


/*
 * GLOBAL VARIABLES
 */

char *lock;		/* The buffer where the result is stored */
uint32 current_length;	/* the *current* length of the lock. */
uint32 maxlength;		/* the current length of the buffer */

long prev_lckheader;	/* points to the last header encountered */
long prev_lckdata;	/* points to the last lock_data encountered */

RuleLockHeader lckheader;
RuleLockBody lckdata;

char *yylex_ptr;
char string[MAX_SHORT_STRING_LENGTH];

char *parse_string();
unsigned long rule_parse_number();
int end_of_lexical_scan;

%}

%union {
    unsigned long ulong_val;
    char * string_val;
    int int_val;
}

%type <ulong_val> attrno planno ruleid priority ruletype lock_type isearly

%token <ulong_val> NUMBER
%token <string_val> SHORTSTRING
%token LEFT_PAR RIGHT_PAR
%token LEFT_BRACK RIGHT_BRACK
%token LEFT_SQ_BRACK RIGHT_SQ_BRACK

%start lock


%%

lock		: LEFT_SQ_BRACK lock_pack_list RIGHT_SQ_BRACK

lock_pack_list	: /* empty */
			{
			    /*
			     * That was the last lock_package. 
			     * Set the 8th bit of the coresponding
			     * "ruletype" found in its header.
			     */
			    mark_last_lckheader(prev_lckheader);
			}
		| lock_package lock_pack_list

lock_package	: LEFT_BRACK lock_header lock_data_list RIGHT_BRACK

lock_header	:  ruleid priority ruletype isearly
		{
		    prev_lckheader = current_length;
		    lckheader.ruleid = (ObjectId) $1;
		    lckheader.priority = (char) $2;
		    lckheader.ruletype = (char)($3 + 64 * $4);
		    lckheader.ruletype = (char)($3);
		    if ($4) {
			lckheader.ruletype |= RuleLockEarlyMask;
		    }
		    put_RuleLockHeader(lckheader);
		}


lock_data_list	: lock_data
		{
		    /*
		     * That was the last lock, we have to set
		     * the 8th bit of its "lock_type"
		     */
		    mark_last_lckdata(prev_lckdata);
		}

		| lock_data lock_data_list

lock_data	: LEFT_PAR lock_type attrno planno RIGHT_PAR
		{
		    prev_lckdata = current_length;
		    lckdata.locktype = (char)$2;
		    lckdata.attrno = (unsigned long)$3;
		    lckdata.planno = (unsigned long)$4;
		    put_RuleLockBody(lckdata);
		}

ruleid		: NUMBER

priority	: NUMBER

ruletype	: SHORTSTRING
		{
		    if (strlen($1) >2) {
			myerror("illegal rule type", string);
		    }
		    if (strlen($1) == 1) {
			switch ($1[0]) {
			    case 'R': $$=RuleTypeRetrieve; break;
			    case 'M': $$=RuleTypeReplace; break;
			    case 'A': $$=RuleTypeAppend; break;
			    case 'D': $$=RuleTypeDelete; break;
			    default : myerror("illegal rule type", $1);
			}
		    } else {
			if ($1[0]!= 'R') {
			    myerror("illegal rule type", $1);
			}
			switch ($1[1]) {
			    case 'R': $$=RuleTypeRefuseRetrieve; break;
			    case 'M': $$=RuleTypeRefuseReplace; break;
			    case 'A': $$=RuleTypeRefuseAppend; break;
			    case 'D': $$=RuleTypeRefuseDelete; break;
			    default : myerror("illegal rule type", $1);
			}
		    }
		}

isearly		: SHORTSTRING
		{
		    if (strlen($1) >1) {
			myerror("illegal early/late spec.", $1);
		    }
		    switch ($1[0]) {
			case 'E': $$ = 1; break;
			case 'L': $$ = 0; break;
			default : myerror("illegal early/late spec.", $1);
		    }
		}

lock_type	: SHORTSTRING
		{
		    if (strlen($1) >2) {
			myerror("illegal lock type", $1);
		    }
		    if (strlen($1) == 1) {
			switch ($1[0]) {
			    case 'W': $$=RuleLockTypeWrite; break;
			    default : myerror("illegal lock type", $1);
			}
		    } else {
			if ($1[0]!= 'R') {
			    myerror("illegal lock type", $1);
			}
			switch ($1[1]) {
			    case '1': $$=RuleLockTypeRead1; break;
			    case '2': $$=RuleLockTypeRead2; break;
			    case '3': $$=RuleLockTypeRead3; break;
			    default : myerror("illegal lock type", $1);
			}
		    }
		}

attrno		: NUMBER

planno		: NUMBER

%%


RuleLock
RuleLockIn(lock_string)
    char *lock_string;
{
    RuleLock result;
    int i;

    maxlength = DEFAULT_MAXLENGTH;
    lock = (char *)MyAlloc((unsigned)maxlength);
    current_length = 0;

    end_of_lexical_scan = 0;
    yylex_ptr = lock_string;
    yyparse();

    result = (RuleLock) MyAlloc((unsigned)
				       (sizeof(long) + current_length));
    result->lockLength = current_length;
    /* strncpy(result->lockData, lock, (int)current_length); */
    for (i=0;i<current_length;i++)
	result->lockData[i] = lock[i];

    return(result);
}

#define ISDIGIT(X) ( X<='9' && X>='0')
#define ISSEPARATOR(X) ( X=='\n' || X==' ' || X=='\t' || X==',')
yylex()
{

    if (end_of_lexical_scan) 
	return(0);
    /*
     * skip separators
     */
    while (ISSEPARATOR(*yylex_ptr))
        yylex_ptr++;
    
    switch (*yylex_ptr) {
	case '[':
	    yylex_ptr ++;
	    return(LEFT_SQ_BRACK);
	case ']':
	    yylex_ptr ++;
	    end_of_lexical_scan = 1;
	    return(RIGHT_SQ_BRACK);
	case '{':
	    yylex_ptr ++;
	    return(LEFT_BRACK);
	case '}':
	    yylex_ptr ++;
	    return(RIGHT_BRACK);
	case '(':
	    yylex_ptr ++;
	    return(LEFT_PAR);
	case ')':
	    yylex_ptr ++;
	    return(RIGHT_PAR);
	default:
	    if (ISDIGIT(*yylex_ptr)) {
		yylval.ulong_val = rule_parse_number();
		return(NUMBER);
	    } else {
		yylval.string_val = parse_string();
		return(SHORTSTRING);
	    }
    } /* switch */
}

unsigned long
    rule_parse_number()
{
    unsigned long result;

    result = 0;
    while (ISDIGIT(*yylex_ptr)) {
	result = 10*result + *yylex_ptr - '0';
	yylex_ptr ++;
    }
    return(result);
}

char *
    parse_string()
{
    long len;

    len = 0;
    while (!ISSEPARATOR(*yylex_ptr) && len<MAX_SHORT_STRING_LENGTH) {
	string[len++] = *(yylex_ptr++);
    }

    string[len] = '\0';

    return(string);
}

put_byte(byte)
    char byte;
{
    if (current_length>=maxlength) {
	/*
	 * Need to reallocate more space...
	 */
	maxlength = 2*maxlength;
	lock = (char *)realloc(lock, (unsigned)maxlength);
    }

    lock[current_length++] = byte;
}

put_oid(data)
    ObjectId data;
{
    char * ppp;
    int i;
    ObjectId data2;

    data2 = data;
    ppp = (char *) &data2;
    for(i=0; i<sizeof(data); i++)
	put_byte(*(ppp++));
}

put_long(data)
    unsigned long data;
{
    char * ppp;
    int i;

    ppp = (char *) &data;
    for(i=0; i<sizeof(data); i++)
	put_byte(*(ppp++));
}

put_RuleLockHeader(s)
    RuleLockHeader s;
{

    char *p;
    int i;

    p = (char *) &s;
    for (i=0; i<sizeof(s); i++)
	put_byte(*p++);
}

put_RuleLockBody(s)
    RuleLockBody s;
{

    char *p;
    int i;

    p = (char *) &s;
    for (i=0; i<sizeof(s); i++)
	put_byte(*p++);
}

mark_last_lckheader(i)
    long i;
{
    RuleLockHeader *p;

    p = (RuleLockHeader *) &(lock[i]);
    p->ruletype |= RuleLockEndMask;
}

mark_last_lckdata(i)
    long i;
{
    RuleLockBody *p;

    p = (RuleLockBody *) &(lock[i]);
    p->locktype |= RuleLockEndMask;
}

yyerror(s)
char *s;
{
    myerror(s, "");
}

myerror(s1, s2)
char *s1;
char *s2;
{
    elog(WARN, "ERROR IN YACC: %s %s\n", s1, s2);
    exit(1);
}
