/*
 * Copyrigth (C) 2002 Marcello Barnaba <vjt@openssl.it>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by Christopher G. Demetriou.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Simple pascal RD Parser.
 * Developed on linux/i386.
 */

#include "rvp.h"
#include "memory.h"
#include "parse.h"
#include "gettokname.h"
#include "y.tab.h"

#define boolean_expression expression

int yylval;
char *yylbuf;

int tok = 0;
int lookahead = 0;

extern int yylex();
extern int line_num, pos_num;

int main(void)
{
    int ret;
    (void) printf("rvp v."RVP_VERSION" (C) 2002 Marcello Barnaba <vjt@openssl.it>\n");
    (void) printf("parsing %ssuccessfull\n", 
	    (ret = start()) ? "" : "un");
    return ret == true ? EXIT_SUCCESS : EXIT_FAILURE;
}

_inline void error(char *s)
{
    fprintf(stderr, "*** error: %s\n", s);
}

_inline int gettok()
{
    if(lookahead)
    {
	tok = lookahead;
	lookahead = 0;
    }
    else
	tok = yylex();

    return tok;
}

_inline void expected(char * expected)
{
    fprintf(stderr, "[line %d:%d] unexpected token %s, expecting %s\n",
	    line_num, pos_num, get_tok_name(tok), expected);
}

_inline void setla()
{
    lookahead = tok;
}

/* grammar follows, one rule == one function.
 *
 * -------8<-------- REAL PARSING FUNCTIONS START HERE -------->8-------
 *
 * start:		program_declarations .	(* main program structure *)
 */

RD_func_t start()
{
    if(program_declarations())
    {
	if(gettok() == '.')
	    return true;
	else
	    expected(".");
    }
    error("wrong declarations");
    return false;
}

/* program_declarations:
 *			optional_head		(* optional PROGRAM line *) |
 *			declarations		(* optional VAR`s *) |
 *			compound_statement	(* BEGIN .. END *)
 */

RD_func_t program_declarations()
{
    if(optional_head() &&
	    declarations() &&
	    compound_statement())
	return true;
    error("wrong body");
    return false;
}

/* optional_head: lambda | PROGRAM identifier ;
 */

RD_func_t optional_head()
{
    if(gettok() == PROGRAM)
    {
	if(gettok() == ID)
	{
	    if(gettok() == ';')
		return true;
	    else
		expected(";");
	}
	expected("ID");
	return false;
    }
    setla();
    return true;
}

/* declarations:	lambda | VAR identifier : predef_type ; declarations
 */

RD_func_t declarations()
{
    if(gettok() == VAR)
    {
	if(gettok() == ID)
	{
	    if(gettok() == ':')
	    {
		if(predef_type())
		{
		    if(gettok() == ';')
			return declarations();
		    else
			expected(";");
		}
		error("wrong type");
		return false;
	    }
	    else
		expected(":");
	}
	else
	    expected("ID");
    }
    setla();
    return true;
}

/* predef_type:		INTEGER | REAL | BOOLEAN | CHAR
 */ 

RD_func_t predef_type()
{
    if(gettok() == INTEGER || tok == REAL || tok == BOOLEAN || tok == CHAR)
	return true;
    expected("PREDEF_TYPE");
    return false;
}

/* statement:		structured_statement	(* seq, choice and iter *) |
 *			declaring_statement	(* declaration *) |
 *			simple_statement	(* assignment or call *) |
 *			lambda
 */
RD_func_t statement()
{
    gettok(); /* get the subsequent followers */
    setla();
    if(tok == BEGIN_ || tok == IF || tok == WHILE)
	return structured_statement();
    else if (tok == VAR)
	return declaring_statement();
    else if (tok == STRING || tok == ID)
	return simple_statement();
    return true;
}


/* structured_statement:
 *			compound_statement	(* BEGIN .. END block *) |
 *			conditional_statement	(* IF block *) |
 *			iterative_statement	(* WHILE block *)
 */
RD_func_t structured_statement()
{
    gettok(); /* get the followers */
    setla();
    if(tok == BEGIN_)
	return compound_statement();
    else if (tok == IF)
	return conditional_statement();
    else if (tok == WHILE)
	return iterative_statement();
    expected("BEGIN or IF or WHILE");
    return false;
}

/* compound_statement:	BEGIN statement_list optional_semi END
 */
RD_func_t compound_statement()
{
    if(gettok() == BEGIN_)
    {
	if(statement_list())
	{
	    if(optional_semi())
	    {
		if(gettok() == END)
		    return true;
		else
		    expected("END");
	    }
	    else
		error("wrong semi");
	}
	else
	    error("wrong st.list");
	return false;
    }
    expected("BEGIN");
    return false;
}

/* conditional_statement: IF boolean_expression THEN statement optional_else
 */
RD_func_t conditional_statement()
{
    if(gettok() == IF)
    {
	if(boolean_expression())
	{
	    if(gettok() == THEN)
	    {
		if(statement())
		    return optional_else();
	    }
	    expected("THEN");
	}
	error("wrong expression");
	return false;
    }
    expected("IF");
    return false;
}

/* optional_else:	lambda |
 *			ELSE statement
 */
RD_func_t optional_else()
{
    if(gettok() == ELSE)
	return statement();
    setla();
    return true;
}
/* iterative_statement:	WHILE boolean_expression DO statement_list optional_semi
 */
RD_func_t iterative_statement()
{
    if(gettok() == WHILE)
    {
	if(boolean_expression())
	{
	    if(gettok() == DO)
	    {
		if(statement_list())
		    return optional_semi();
	    }
	    else
		expected("DO");
	}
	error("wrong w. expression");
	return false;
    }
    expected("WHILE");
    return false;
}
/* declaring_statement:	VAR identifier : predef_type
 */
RD_func_t declaring_statement()
{
    if(gettok() == VAR)
    {
	if(identifier())
	{
	    if(gettok() == ':')
		return predef_type();
	    else
		expected(":");
	}
	error("wrong identifier");
	return false;
    }
    expected("VAR");
    return false;
}
/* simple_statement:	identifier ( id_list ) |
 *			identifier ASSIGN expression |
 *			identifier
 */
RD_func_t simple_statement()
{
    if(identifier())
    {
	if(gettok() == '(')
	{
	    if(id_list())
	    {
		if(gettok() == ')')
		    return true;
		else
		    expected(")");
	    }
	    error("wrong id_list");
	    return false;
	}
	else if (tok == ASSIGN)
	{
	    if (expression())
		return true;
	}
	else
	{
	    setla();
	    return true;
	}
    }
    error("Wrong simple stmt");
    return false;
}

/* id_list: identifier | identifier , id_list
 */
RD_func_t id_list()
{
    if(identifier())
    {
	if(gettok() == ',')
	    return id_list();
	else
	{
	    setla();
	    return true;
	}
    }
    error("wrong id");
    return false;
}

/* statement_list: statement | statement ; statement_list
 */
RD_func_t statement_list()
{
    if(statement())
    {
	if(gettok() == ';')
	    return statement_list();
	else
	{
	    setla();
	    return true;
	}
    }
    error("wrong stmt");
    return false;
}

/* expression:	simple_expression comparation_op simple_expression |
 * 		simple_expression
 */
RD_func_t expression()
{
    if(simple_expression())
    {
	if(comparation_op())
	    return simple_expression();
	else
	    return true;
    }
    return false;
}
    
/* simple_expression: term |
 * 	term plus_minus expression |
 * 	term OR expression |
 */
RD_func_t simple_expression()
{
    if(term())
    {
	if(plus_minus())
	    return simple_expression();
	else if(gettok() == OR)
	    return simple_expression();
	else
	{
	    setla();
	    return true;
	}
    }
    error("wrong term");
    return false;
}

/* term: factor | factor mul_div term| factor AND term
 */
RD_func_t term()
{
    if(factor())
    {
	if(mul_div())
	    return term();
	else if(gettok() == AND)
	    return term();
	else
	{
	    setla();
	    return true;
	}
    }
    error("wrong factor");
    return false;
}    

/* factor: identifier | unsigned_number | BOOL_CONST | NOT factor
 */
RD_func_t factor()
{
    if (identifier())
	return true;
    if (unsigned_number())
	return true;
    if (gettok() == BOOL_CONST)
	return true;
    else if(tok == NOT)
	return factor();
    error("wrong fctr");
    return false;
} 

/* comparation_op: = | NE | < | > | LE | GE
 */
RD_func_t comparation_op()
{
    if(gettok() == '=' || tok == NE || tok == '<' || tok == '>' || tok == LE || tok == GE)
	return true;
    setla();
    return false;
}
/* mul_div: * | / | DIV | MOD
 */ 
RD_func_t mul_div()
{
    if(gettok() == '*' || tok == '/' || tok == DIV || tok == MOD)
	return true;
    setla();
    return false;
}

/* plus_minus: + | -
 */
RD_func_t plus_minus()
{
    if(gettok() == '+' || tok == '-')
	return true;
    setla();
    return false;
}

/* identifier: ID | STRING
 */
RD_func_t identifier()
{
    if(gettok() == ID || tok == STRING)
	return true;
    setla();
    return false;
}

/* optional_semi: lambda | ;
 */
RD_func_t optional_semi()
{
    if(gettok() == ';')
	return true;
    setla();
    return true;
}

/* unsigned_number = INT_CONST | REAL_CONST
 */
RD_func_t unsigned_number()
{
    if(gettok() == INT_CONST || tok == REAL_CONST)
	return true;
    setla();
    return false;
}

/* -------8<------------ END OF PARSING FUNCTIONS ------------>8------- */

/* vi:set ts=8 sts=4 sw=4 tw=79: */
