/*
 *	aegis - project change supervisor
 *	Copyright (C) 1996, 1999, 2002, 2003 Peter Miller;
 *	All rights reserved.
 *
 *	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 2 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, write to the Free Software
 *	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 *
 * MANIFEST: functions to parse expression grammar
 */

%{

#include <ac/stdarg.h>
#include <ac/stdio.h>
#include <ac/stdlib.h>

#include <str.h>
#include <sub.h>
#include <sub/expr_gram.h>
#include <sub/expr_lex.h>
#include <trace.h>

#ifdef DEBUG
#define YYDEBUG 1
#endif

static void yyerror(const char *s);

%}

%token DIV
%token EQ
%token GE
%token GT
%token JUNK
%token LE
%token LP
%token LT
%token MINUS
%token MOD
%token MUL
%token NE
%token NOT
%token NUMBER
%token PLUS
%token RP

%union
{
	long	lv_number;
}

%type <lv_number> NUMBER expr

%left NE EQ
%left LT LE GT GE
%left PLUS MINUS
%left MUL DIV MOD
%right UNARY

%{

static long result;
static sub_context_ty *scp;


string_ty *
sub_expr_gram(sub_context_ty *scp_arg, string_ty *s)
{
    int             bad;
    extern int yyparse(void);
#ifdef DEBUG
    extern int      yydebug;
#endif

    trace(("sub_expr_gram()\n{\n"));
#ifdef DEBUG
    yydebug = trace_pretest_;
#endif
    scp = scp_arg;
    sub_expr_lex_open(s);
    bad = yyparse();
    sub_expr_lex_close();
    trace(("bad = %d\n", bad));

    trace(("}\n"));
    if (bad)
	return 0;
    return str_format("%ld", result);
}


static void
yyerror(const char *s)
{
    trace(("yyerror(\"%s\")\n{\n", s));
    sub_context_error_set(scp, s);
    trace(("}\n"));
}

#ifdef DEBUG

/*
 * jiggery-pokery for yacc
 *
 *	Replace all calls to printf with a call to trace_printf.  The
 *	trace_where_ is needed to set the location, and is safe, because
 *	yacc only invokes the printf with an if (be careful, the printf
 *	is not in a compound statement).
 */
#define printf trace_where_, trace_printf

/*
 * jiggery-pokery for bison
 *
 *	Replace all calls to fprintf with a call to yydebugger.  Ignore
 *	the first argument, it will be ``stderr''.  The trace_where_ is
 *	needed to set the location, and is safe, because bison only
 *	invokes the printf with an if (be careful, the fprintf is not in
 *	a compound statement).
 */
#define fprintf trace_where_, yydebugger

void
yydebugger(void *junk, const char *fmt, ...)
{
    va_list         ap;
    string_ty       *s;

    va_start(ap, fmt);
    s = str_vformat(fmt, ap);
    va_end(ap);
    trace_printf("%s", s->str_text);
    str_free(s);
}

#endif

%}

%%

grammar:
    expr
	{ result = $1; }
    ;

expr
    : LP expr RP
	{ $$ = $2; trace(("$$ = %ld;\n", $$)); }
    | NUMBER
	{ $$ = $1; trace(("$$ = %ld;\n", $$)); }
    | MINUS expr
	%prec UNARY
	{ $$ = -$2; trace(("$$ = %ld;\n", $$)); }
    | expr PLUS expr
	{ $$ = $1 + $3; trace(("$$ = %ld;\n", $$)); }
    | expr MINUS expr
	{ $$ = $1 - $3; trace(("$$ = %ld;\n", $$)); }
    | expr MUL expr
	{ $$ = $1 * $3; trace(("$$ = %ld;\n", $$)); }
    | expr DIV expr
	{ $$ = $3 ? $1 / $3 : 0; trace(("$$ = %ld;\n", $$)); }
    | expr MOD expr
	{ $$ = $3 ? $1 % $3 : 0; trace(("$$ = %ld;\n", $$)); }
    | expr EQ expr
	{ $$ = ($1 == $3); trace(("$$ = %ld;\n", $$)); }
    | expr NE expr
	{ $$ = ($1 != $3); trace(("$$ = %ld;\n", $$)); }
    | expr LT expr
	{ $$ = ($1 < $3); trace(("$$ = %ld;\n", $$)); }
    | expr LE expr
	{ $$ = ($1 <= $3); trace(("$$ = %ld;\n", $$)); }
    | expr GT expr
	{ $$ = ($1 > $3); trace(("$$ = %ld;\n", $$)); }
    | expr GE expr
	{ $$ = ($1 >= $3); trace(("$$ = %ld;\n", $$)); }
    | NOT expr
	%prec UNARY
	{ $$ = (!$2); trace(("$$ = %ld;\n", $$)); }
    ;
