# This file is part of the Coral project.
# Please read the copyright notice in the LICENSE file.

#
# Converts a BNF grammar to a Coral metamodel
#
from coral import kernel as coral
from coral.lgen.bnf import bnf

class information:
    def __init__(self):
        self.name = ""
        self.version = 0
        self.revision = 0
        self.URInamespace = ""
        

def grammar2lang(grammar, info):
    assert isinstance(grammar, bnf.BNF)

    class __tmp:
        def __init__(self):
            self.cache = {}
            self.language = None
            self.property_namer = 1
            self.ed_namer = 1
            self.enums_with_one_value = {}

    tmp = __tmp()
    tmp.language = coral.Language(info.name, info.version, info.revision)
    tmp.language.setXMLNS(info.URInamespace)

    def one_value_enum(tmp, token):
        if token == bnf.Integer:
            return coral.UnlimitedInteger
        if token == bnf.Real:
            return coral.UnlimitedReal
        if token == bnf.String:
            return coral.String
        assert not isinstance(token, bnf.BuiltinNonterminal)
        if not tmp.enums_with_one_value.has_key(token.value):
            name = "__coral_enum_%06d" % tmp.ed_namer
            tmp.ed_namer += 1
            e = coral.EnumerationDefinition(tmp.language, name)
            e.__addEnum__(token.value)
            tmp.enums_with_one_value[token.value] = e
        return tmp.enums_with_one_value[token.value]
        

    def new_properties(tmp, ed, targets):
        for t in targets:
            k = coral.kind_t.KIND_COMPOSITION + \
                coral.kind_t.KIND_ORDERED
            ml = t.mult_lower
            mu = t.mult_upper
            name = "__coral_prop_%06d" % tmp.property_namer
            tmp.property_namer += 1

            points_to = None
            if isinstance(t.token, bnf.Group):
                points_to = new_group(tmp, t.token.choices)
            elif isinstance(t.token, bnf.Nonterminal):
                points_to = create(tmp, t.token)
            else:
                assert isinstance(t.token, bnf.Terminal)
                points_to = one_value_enum(tmp, t.token)
            p = coral.Property(name, points_to, "", ml, mu, k)
            ed.addPropertyRecursive(p)
    
    def new_group(tmp, choices, super = None):
        if not super:
            name = "__coral_eda_%06d" % tmp.ed_namer
            tmp.ed_namer += 1
            super = coral.ElementDefinition(tmp.language, name)
            super.setAbstract(1)
        for seq in choices:
            name = "__coral_ed_%06d" % tmp.ed_namer
            tmp.ed_namer += 1
            sub = coral.ElementDefinition(tmp.language, name)
            sub.addSuperclass(super)
            new_properties(tmp, sub, seq.targets)
        return super
    
    def create(tmp, nonterminal):
        assert isinstance(nonterminal, bnf.Nonterminal)
        if tmp.cache.has_key(nonterminal):
            return tmp.cache[nonterminal]
        
        targets = nonterminal.seq.targets
        #
        # Check if we're an enumeration
        #
        ok = False
        if len(targets) == 1 and isinstance(targets[0].token, bnf.Group) \
               and targets[0].mult_lower == 1 and targets[0].mult_upper == 1:
            ok = True
            for seq in targets[0].token.choices:
                if len(seq.targets) == 1:
                    s = seq.targets[0]
                    if isinstance(s.token, bnf.Terminal) \
                           and s.mult_lower == 1 \
                           and s.mult_upper == 1:
                        continue
                ok = False
                break
        
        if ok:
            e = coral.EnumerationDefinition(tmp.language, nonterminal.name)
            for seq in targets[0].token.choices:
                e.__addEnum__(seq.targets[0].token.value)
            tmp.cache[nonterminal] = e
            return e
        
        e = coral.ElementDefinition(tmp.language, nonterminal.name)
        tmp.cache[nonterminal] = e
        new_properties(tmp, e, targets)
        return e

    e = create(tmp, grammar.start_token)
    tmp.language.setRoot(e)
    return tmp.language
