// C++ interface to the bison generated parser.
//
// Copyright 2001, KUN
//
// 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 Library 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-1307, USA.

// $Id: parserinterface.cc,v 1.17 2006/04/12 16:46:23 olafs Exp $

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif // HAVE_CONFIG_H

using namespace std;
#include <stdio.h> // for snprintf()
#include <stdlib.h>
#include <string>
//#include <stream.h>     // FN: for dec(), obsolete

#include "affixtab.h"	/* for MaxSetSz */
#include "globsetafftable.h"
#include "globtables.h"
#include "parserinterface.h"

char* parser_error;
static int error_occured = 0;

static string current_module_name;

int no_error_occured()
{
    return !error_occured;
}

void set_current_module_name(string curname)
{
    current_module_name = curname;
}

static string file_err_pos()
{
    //return current_module_name + ":" + dec(get_line_nr(), 0) + ":"
    //       + dec(get_col_nr(), 0) + ": error: ";
    char cbuf[99];
    snprintf(cbuf, sizeof(cbuf), "%d:%d: error: ",
				get_line_nr(), get_col_nr());
    return current_module_name + ":" + string(cbuf);
}

static void unknown_affix_error(string aff_name)
{
    cout << file_err_pos() << "unknown affix \"" << aff_name << "\""
         << " (ignoring further occurences)" << endl;
    error_occured = 1;
}

//void enter_terminal(char* name, EntryList* entries)
//{
//    rule_table.enter(name, *entries);
//
//    delete entries;
//    free(name);
//}

static ID text_id = unknown_id;
static ID int_id = unknown_id;

Param* affterms_to_param(IDList* aff_terms)
{
    if (text_id == unknown_id) {
        string str_text = "TEXT";
        text_id = idtable.add(str_text);
        string str_int = "INT";
        int_id = idtable.add(str_int);
    }

    if (aff_terms->size() == 1) {
        ID param_id = *(aff_terms->begin());

        if (param_id == text_id) {
            delete aff_terms;
            return new Param(AnyText);
        } else if (param_id == int_id) {
            delete aff_terms;
            return new Param(AnyInt);
        } else {
            delete aff_terms;
            return new Param(IdType, param_id);
        }
    } else {
        unsigned set_sz = 0;
        AffixID set_ids[MaxSetSz];

        for (IDList::iterator i = aff_terms->begin(); i != aff_terms->end();
             ++i) {
            if (*i == text_id) {
                cout << get_line_nr()
                     << ": TEXT in affix set expression" << endl;
            } else if (*i == int_id) {
                cout << get_line_nr()
                     << ": INT in affix set expression" << endl;
            } else {
                set_ids[set_sz++] = *i;
            }
            if (set_sz >= MaxSetSz) {
                cout << "nr of params exceeds max set size" << endl;
            }
        }

        pAffixSet set = affix_table.enter(set_ids, set_sz);
        delete aff_terms;
        return new Param(set);
    }
}

IDList* aff_terml_new(string* str)
{
    IDList* l = new IDList;
    ID id = idtable.to_id(*str);
    if (id == unknown_id) {
        id = idtable.add(*str);
        unknown_affix_error(*str);
    }

    l->push_back(id);
    delete str;

    return l;
}

IDList* aff_terml_add(IDList* lst, string* str)
{
    ID id = idtable.to_id(*str);
    if (id == unknown_id) {
        id = idtable.add(*str);
        unknown_affix_error(*str);
    }

    lst->push_back(id);
    delete str;

    return lst;
}

pParam build_params(MyParamList* pl)
{
    pParam params = 0;

    for (MyParamList::iterator i = pl->begin(); i != pl->end(); ++i) {
        params = param_table.enter(**i, params);
        delete (*i);
    }

    delete pl;

    return params;
}

static int my_abs_is_syntax_nonterminal_id(char* txt)
{
    while (*txt) {
        if (!(islower(*txt & 0xff) || isupper(*txt & 0xff) || (*txt == '_') || (*txt == ' '))) {
            return 0;
        }

        txt++;
    }

    return 1;
}


EntryList* nont_def(string* name, pParam pars, long count)
{
    if (!my_abs_is_syntax_nonterminal_id((char*) (name->c_str()))) {
        cout << file_err_pos() << "\"" << *name
             << "\" is not a syntax nonterminal (ignoring further occurences)"
             << endl;
        error_occured = 1;
    }

    ID nont_id = idtable.to_id(*name);

    if (nont_id == unknown_id) {
        cout << file_err_pos()
             << "syntax nonterminal not defined in .lex file: \"" << *name
             << "\"" << endl;
        error_occured = 1;
        nont_id = idtable.add(*name);
    }

    if (!lex_nonterm_list.is_defined(*name, pars)) {
	cout << file_err_pos() << "syntax nonterminal \"" << *name
	     << "\" not available with arity "
	     << lex_nonterm_list.calculate_arity(pars) << endl;
	error_occured = 1;
    }

    delete name;

    return new EntryList(Entry(nonterm_table.enter(nont_id, pars),
						get_line_nr(), count));
}
