from .grammar.parse_grammar import parse_grammar_file
from .grammar.production import *
from .languages import *

from viper.error import ViperError

from typing import Dict, List


RuleDict = Dict[str, Language]


class LinguifierError(ViperError):
    pass


def linguify_grammar_file(filename: str) -> RuleDict:
    parsed_rules = parse_grammar_file(filename)
    rule_langs = []
    rule_dict = {}
    for rule, production_list in parsed_rules.items():
        lang = linguify_rule(production_list, rule_dict)
        rule_langs.append(lang)
    return rule_dict


def linguify_rule(production_list: List[Production], rule_dict: RuleDict) -> Language:
    production_langs = []
    for production in production_list:
        lang = linguify_production(production, rule_dict)
        production_langs.append(lang)
    return alt(*production_langs)


def linguify_production(production: Production, rule_dict: RuleDict) -> Language:
    if isinstance(production, RuleAliasProduction):
        return linguify_rule_alias_production(production, rule_dict)
    elif isinstance(production, NamedProduction):
        return linguify_named_production(production)
    else:
        raise LinguifierError(f"Unsupported production type: {type(production)}")


def linguify_rule_alias_production(production: RuleAliasProduction, rule_dict: RuleDict) -> Language:
    return make_rule_literal(production.name, rule_dict)


def linguify_named_production(production: NamedProduction) -> Language:
    # TODO: make AST object
    # TODO: create language from production parts
    ...


def make_rule_literal(rule_name: str, rule_dict: RuleDict) -> RuleLiteral:
    return RuleLiteral(rule_name, rule_dict)

@@ -81,16 +81,115 @@
 mport *%0A
+from .grammar.production_part import *%0Afrom .grammar.tokenize.special_tokens import SPECIAL_TOKENS%0A
 from .la
@@ -240,16 +240,47 @@
 perError
+%0Afrom viper.lexer import Lexeme
 %0A%0Afrom t
@@ -384,16 +384,312 @@
  pass%0A%0A%0A
+class Grammar:%0A    def __init__(self, grammar_filename: str):%0A        self.file = grammar_filename%0A        self.rules = linguify_grammar_file(self.file)%0A%0A    def parse_rule(self, rule: str, lexemes: List%5BLexeme%5D) -%3E SPPF:%0A        lang = self.rules%5Brule%5D%0A        return make_sppf(lang, lexemes)%0A%0A%0A
 def ling
@@ -786,28 +786,8 @@
 me)%0A
-    rule_langs = %5B%5D%0A
     
@@ -926,34 +926,33 @@
    rule_
-langs.append(
+dict%5Brule%5D = 
 lang
-)
 %0A    ret
@@ -1555,16 +1555,27 @@
 oduction
+, rule_dict
 )%0A    el
@@ -1880,16 +1880,37 @@
 oduction
+, rule_dict: RuleDict
 ) -%3E Lan
@@ -2006,126 +2006,1401 @@
 ...%0A
-%0A%0Adef make_rule_literal(rule_name: str, rule_dict: RuleDict) -%3E RuleLiteral:%0A    return RuleLiteral(rule_name, rule_dict
+    part_langs = %5B%5D%0A    for part in production.parts:%0A        lang = linguify_production_part(part, rule_dict)%0A        part_langs.append(lang)%0A    prod_lang = concat(*part_langs)%0A    ...%0A    return prod_lang%0A%0A%0Adef make_rule_literal(rule_name: str, rule_dict: RuleDict) -%3E RuleLiteral:%0A    return RuleLiteral(rule_name, rule_dict)%0A%0A%0Adef linguify_production_part(part: ProductionPart, rule_dict: RuleDict) -%3E Language:%0A    if isinstance(part, LiteralPart):%0A        return literal(Lexeme(part.text))%0A    elif isinstance(part, SpecialPart):%0A        return literal(SPECIAL_TOKENS%5Bpart.token%5D)%0A    elif isinstance(part, RulePart):%0A        return make_rule_literal(part.name, rule_dict)%0A    elif isinstance(part, RepeatPart):%0A        return rep(linguify_production_part(part.part, rule_dict))%0A    elif isinstance(part, OptionPart):%0A        return opt(linguify_production_part(part.part, rule_dict))%0A    elif isinstance(part, ParameterPart):%0A        # TODO: Fix this.%0A        return linguify_production_part(part.part, rule_dict)%0A    elif isinstance(part, ExpandedParameterPart):%0A        # TODO: Fix this.%0A        return linguify_production_part(part.rule, rule_dict)%0A    elif isinstance(part, SpecialExpandedParameterPart):%0A        # TODO: Fix this.%0A        return linguify_production_part(part.rule, rule_dict)%0A    else:%0A        raise LinguifierError(f%22Cannot linguify unknown production part: %7Bpart%7D%22
 )%0A

Implement basic grammar linguification (not all correct)