from itertools import chain

from ...core import grammar as g

ALLOWED_SYMBOLS = r'| !#$%&()*+,-./:;>=<?@[\]^_`{}~'

lang = g.Language.create('Syntax')
lang.add_rule(
    g.Rule(
        'Syntax',
        g.Syntax.create(
            g.TermGroup.create(
                g.RuleReference('Rules'),
                g.RuleReference('EOF')
            )
        )
    )
)
lang.add_rule(
    g.Rule(
        'Rules',
        g.Syntax.create(
            g.TermGroup.create(
                g.RuleReference('Rule')
            ),
            g.TermGroup.create(
                g.RuleReference('Rule'),
                g.RuleReference('Rules')
            )
        )
    )
)
lang.add_rule(
    g.Rule(
        'Rule',
        g.Syntax.create(
            g.TermGroup.create(
                g.RuleReference('OptWhitespace'),
                g.Literal('<'),
                g.RuleReference('RuleName'),
                g.Literal('>'),
                g.RuleReference('OptWhitespace'),
                g.Literal('::='),
                g.RuleReference('OptWhitespace'),
                g.RuleReference('Expression'),
                g.RuleReference('LineEnd')
            )
        )
    )
)
lang.add_rule(
    g.Rule(
        'OptWhitespace',
        g.Syntax.create(
            g.TermGroup.create(
                g.Literal(' '),
                g.RuleReference('OptWhitespace')
            ),
            g.TermGroup.create(
                g.Literal('')
            )
        )
    )
)
lang.add_rule(
    g.Rule(
        'Expression',
        g.Syntax.create(
            g.TermGroup.create(
                g.RuleReference('List')
            ),
            g.TermGroup.create(
                g.RuleReference('List'),
                g.RuleReference('OptWhitespace'),
                g.Literal('|'),
                g.RuleReference('OptWhitespace'),
                g.RuleReference('Expression')
            )
        )
    )
)
lang.add_rule(
    g.Rule(
        'LineEnd',
        g.Syntax.create(
            g.TermGroup.create(
                g.RuleReference('SingleLineEnd'),
            ),
            g.TermGroup.create(
                g.RuleReference('SingleLineEnd'),
                g.RuleReference('LineEnd'),
            )
        )
    )
)
lang.add_rule(
    g.Rule(
        'SingleLineEnd',
        g.Syntax.create(
            g.TermGroup.create(
                g.RuleReference('OptWhitespace'),
                g.RuleReference('EOL')
            )
        )
    )
)
lang.add_rule(
    g.Rule(
        'List',
        g.Syntax.create(
            g.TermGroup.create(
                g.RuleReference('Term')
            ),
            g.TermGroup.create(
                g.RuleReference('Term'),
                g.RuleReference('OptWhitespace'),
                g.RuleReference('List')
            )
        )
    )
)
lang.add_rule(
    g.Rule(
        'Term',
        g.Syntax.create(
            g.TermGroup.create(
                g.RuleReference('Literal')
            ),
            g.TermGroup.create(
                g.Literal('<'),
                g.RuleReference('RuleName'),
                g.Literal('>'),
            )
        )
    )
)
lang.add_rule(
    g.Rule(
        'Literal',
        g.Syntax.create(
            g.TermGroup.create(
                g.Literal('"'),
                g.RuleReference('Text1'),
                g.Literal('"'),
            ),
            g.TermGroup.create(
                g.Literal("'"),
                g.RuleReference('Text2'),
                g.Literal("'"),
            )
        )
    )
)
lang.add_rule(
    g.Rule(
        'Text1',
        g.Syntax.create(
            g.TermGroup.create(
                g.Literal('')
            ),
            g.TermGroup.create(
                g.RuleReference('Character1'),
                g.RuleReference('Text1'),
            )
        )
    )
)
lang.add_rule(
    g.Rule(
        'Text2',
        g.Syntax.create(
            g.TermGroup.create(
                g.Literal('')
            ),
            g.TermGroup.create(
                g.RuleReference('Character2'),
                g.RuleReference('Text2'),
            )
        )
    )
)
lang.add_rule(
    g.Rule(
        'Character',
        g.Syntax.create(
            g.TermGroup.create(
                g.RuleReference('Letter')
            ),
            g.TermGroup.create(
                g.RuleReference('Digit')
            ),
            g.TermGroup.create(
                g.RuleReference('Symbol')
            ),
        )
    )
)
lang.add_rule(
    g.Rule(
        'Letter',
        g.Syntax.create(
            *(
                g.TermGroup.create(
                    g.Literal(letter)
                ) for letter in chain(
                    (chr(ord('A') + offset) for offset in range(26)),
                    (chr(ord('a') + offset) for offset in range(26))
                )
            )
        )
    )
)
lang.add_rule(
    g.Rule(
        'Digit',
        g.Syntax.create(
            *(
                g.TermGroup.create(
                    g.Literal(chr(ord('0') + offset))
                ) for offset in range(10)
            )
        )
    )
)
lang.add_rule(
    g.Rule(
        'Symbol',
        g.Syntax.create(
            *(
                g.TermGroup.create(
                    g.Literal(symbol)
                ) for symbol in ALLOWED_SYMBOLS
            )
        )
    )
)
lang.add_rule(
    g.Rule(
        'Character1',
        g.Syntax.create(
            g.TermGroup.create(
                g.RuleReference('Character')
            ),
            g.TermGroup.create(
                g.Literal("'")
            )
        )
    )
)
lang.add_rule(
    g.Rule(
        'Character2',
        g.Syntax.create(
            g.TermGroup.create(
                g.RuleReference('Character')
            ),
            g.TermGroup.create(
                g.Literal('"')
            )
        )
    )
)
lang.add_rule(
    g.Rule(
        'RuleName',
        g.Syntax.create(
            g.TermGroup.create(
                g.RuleReference('Letter')
            ),
            g.TermGroup.create(
                g.RuleReference('Letter'),
                g.RuleReference('RuleEnd')
            )
        )
    )
)
lang.add_rule(
    g.Rule(
        'RuleEnd',
        g.Syntax.create(
            g.TermGroup.create(
                g.RuleReference('OneRuleEnd')
            ),
            g.TermGroup.create(
                g.RuleReference('OneRuleEnd'),
                g.RuleReference('RuleEnd')
            )
        )
    )
)
lang.add_rule(
    g.Rule(
        'OneRuleEnd',
        g.Syntax.create(
            g.TermGroup.create(
                g.RuleReference('Letter')
            ),
            g.TermGroup.create(
                g.RuleReference('Digit')
            ),
            g.TermGroup.create(
                g.Literal('-'),
                g.RuleReference('Letter')
            ),
            g.TermGroup.create(
                g.Literal('-'),
                g.RuleReference('Digit')
            )
        )
    )
)
lang.add_rule(
    g.Rule(
        'EOL',
        g.Syntax.create(
            g.TermGroup.create(
                g.Literal('\n')
            )
        )
    )
)
lang.add_rule(
    g.Rule(
        'EOF',
        g.Syntax.create(
            g.TermGroup.create(
                g.EOFTerm()
            )
        )
    )
)
