
// Copyright {Jagger Software Limited} 2003

#include "grammar/non_terminal_symbol_definition.hpp"

#include "container/for_all.hpp"
#include "grammar/out_of_range_exception.hpp"
#include "grammar/production_symbol_definition.hpp"
#include "ownership/deleter.hpp"
#include "grammar/visitor.hpp"
#include <iostream>
#include <memory>

using namespace ::container;
using namespace ::ownership;
using namespace ::std;

namespace grammar // non_terminal_symbol_definition - 'tors
{   
    // When a tree symbol is generated from a production-symbol-definition or a 
    // terminal-symbol it refers to their symbol-definitions which naturally have
    // unique keys. However, a tree-building-parser might need to grow an "empty"
    // tree symbol for an optional symbol that was _not_ matched (this ensures
    // the number of branches always equals the number of qualified-symbols 
    // in the associated production and makes trees _much_ easier to use). 
    // Hence, (yes I'll stop soon) an empty tree symbol might need to refer to the 
    // non-terminal rather than to one of its productions. 
    // And so, non_terminal_symbol_definitions need keys too.

    non_terminal_symbol_definition::non_terminal_symbol_definition
    (
        const key_type & key,
        const char * name,
        size_t fixed_capacity
    )
        : symbol_definition(key, name)
        , productions(fixed_capacity)
    {
    }

    non_terminal_symbol_definition::~non_terminal_symbol_definition()
    {
        for_all(productions, deleter());
    }
}

namespace grammar // non_terminal_symbol_definition - building
{
    void non_terminal_symbol_definition::push_back
    (
        key_type key,
        const qualified_symbol_definition * begin, 
        const qualified_symbol_definition * end
    )
    {
        typedef production_symbol_definition resource_type;

        auto_ptr<resource_type> production(new resource_type(key, *this, begin, end));
        productions.push_back(production.get());
        production.release();
    }
}

namespace grammar // non_terminal_symbol_definition - subscripting
{
    size_t non_terminal_symbol_definition::size() const
    {
        return productions.size();
    }

    const production_symbol_definition & 
    non_terminal_symbol_definition::operator[](size_t at) const
    {
        range_check(at);
        return *productions[at];
    }
}

namespace grammar // non_terminal_symbol_definition - validating
{
    void non_terminal_symbol_definition::range_check(size_t at) const
    {
        if (at >= size())
        {
            throw out_of_range_exception(typeid(*this), name, at, size());
        }
    }
}

namespace grammar // non_terminal_symbol_definition - visiting
{   
    bool non_terminal_symbol_definition::accept(::grammar::visitor & visitor)  const
    {
        return visitor.visit(*this);
    }
}

namespace grammar // non_terminal_symbol_definition - streaming
{
    void non_terminal_symbol_definition::write(ostream & out) const
    {
        out << name << ':' << endl;

        for (size_t at = 0; at != size(); ++at)
        {
            out << (*this)[at] << '\n';
        }
    }   
}

