# This file generates .tex files from the raw .json files.
# coding:utf-8:
import json

AUTOGEN_MSG = u'''%%
%% This file is automatically generated from %s using gendoc.py.
%% Do not edit by hand.
%%
'''

def string_to_tex(string):
    replacements = {
        u'á': '\\\'a',
        u'é': '\\\'e',
        u'í': '\\\'i',
        u'ó': '\\\'o',
        u'ú': '\\\'u',
        u'Á': '\\\'A',
        u'É': '\\\'E',
        u'Í': '\\\'I',
        u'Ó': '\\\'O',
        u'Ú': '\\\'U',
        u'ñ': '\\~n',
        u'Ñ': '\\~N',
        u'_': '\\_',
        u'{': '\\{',
        u'}': '\\}',
        u'&': '\\&',
        u'^': '\\^',
    }
    for s, t in replacements.items():
        string = string.replace(s, t)
    return string

def gendoc(json_fn, json_to_tex):
    assert json_fn.endswith('.json')
    tex_fn = json_fn[:-5] + '.tex' 
    print 'Generating %s from %s.' % (tex_fn, json_fn)
    json_data = json.load(open(json_fn))
    tex = json_to_tex(json_data)
    f = open(tex_fn, 'w')
    f.write(AUTOGEN_MSG % (json_fn,))
    f.write('\n'.join(tex))
    f.write('\n')
    f.write(AUTOGEN_MSG % (json_fn,))
    f.close()

## Tokens

def tokens_to_tex(json_tokens):
    tex = []
    for kind, symbol, name, description in json_tokens:
        tex.append(
            '\\texttt{%s} & \\token{%s} & %s \\\\' % (
              string_to_tex(symbol),
              string_to_tex(name),
              string_to_tex(description)
            )
        )
    return tex

## Grammar

def uncapitalize(x):
    return x[0].lower() + x[1:]

def symbol_to_tex(symbol):
    if symbol == 'EMPTY':
        return '\\EMPTY'
    elif symbol[0].upper() == symbol[0]:
        return '\\token{%s}' % (symbol,)
    elif symbol.startswith('nonEmpty'):
        return '\\nonterminal{\\nonEmpty{%s}}' % (
                 uncapitalize(symbol[len('nonEmpty'):]),
               )
    else:
        return '\\nonterminal{%s}' % (symbol,)

def production_to_tex(production):
    tex = []
    for symbol in production:
        if isinstance(symbol, list):
            assert symbol[0] in ['*', '+', '?']
            subproduction = symbol[1]
            subtex = production_to_tex(subproduction)
            tex.append('(' + subtex + ')' + symbol[0])
        else:
            tex.append(symbol_to_tex(symbol))
    return ' '.join(tex)

def grammar_to_tex(json_grammar):
    tex = []
    for grammar_item in json_grammar:
        nonterminal = grammar_item[0]
        productions = grammar_item[1:]
        tex.append('\\production{%s}{' % (symbol_to_tex(nonterminal),))
        first = True
        for production in productions:
            if first:
                first = False
            else:
                tex.append('\\ALT')
            tex.append(production_to_tex(production))
        tex.append('}')
    return tex

## AST

def type_to_tex(typ):
    if isinstance(typ, list):
        assert typ[0] in ['*', '?', '+']
        if typ[0] == '*':
            return '[%s]' % (type_to_tex(typ[1]),)
        elif typ[0] == '?':
            return '(%s)?' % (type_to_tex(typ[1]),)
        elif typ[0] == '+':
            return '%s + %s' % (type_to_tex(typ[1]), type_to_tex(typ[2]))
    else:
        return '\\type{%s}' % (typ,)

def constructors_to_tex(alternatives):
    tex = []
    first = True
    for constructor_declaration in alternatives:
        if first:
            first = False
        else:
            tex.append('\\ALT')
        fields = []
        for field_declaration in constructor_declaration[1:]:
            field_name, field_type = field_declaration
            fields.append('%s : %s' % (field_name, type_to_tex(field_type)))
        tex.append(
            '\\ast{%s}(%s)' % (constructor_declaration[0], ', '.join(fields))
        )
    return '\n'.join(tex)

def ast_to_tex(json_ast):
    tex = []
    for declaration in json_ast:
        form = declaration[0]
        name = declaration[1]
        alternatives = declaration[2:]
        if form == 'type':
            tex.append(
                '\\typedecl{%s}{%s}' % (
                    type_to_tex(name),
                    alternatives[0]
                )
            )
        else:
            tex.append(
                '\\datadecl{%s}{%s}' % (
                    type_to_tex(name), constructors_to_tex(alternatives)
                )
            )
    return tex

## Instructions

def instruction_signature_to_tex(signature):
    tex = []
    tex.append('\\instruction{%s}(' % (signature[0],))
    fields = []
    for fieldName, fieldType in signature[1:]:
        fields.append('%s : %s' % (fieldName, type_to_tex(fieldType)))
    tex.append(', '.join(fields))
    tex.append(')')
    return ''.join(tex)

def instructions_to_tex(json_instructions):
    tex = []
    tex.append('\\begin{itemize}')
    for instruction in json_instructions:
        signature = instruction[0]
        description = instruction[1]
        tex.append(
            '\\item %s\\\\%s' % (
                instruction_signature_to_tex(signature),
                string_to_tex(description)
            )
        )
    tex.append('\\end{itemize}')
    return tex

## Builtins

def builtin_type_to_tex(builtin):
    tex = []
    tex.append('\\item \\texttt{type} \\typename{%s}' % (string_to_tex(builtin[1]),))
    constructors = builtin[2:]
    if len(constructors) == 0:
        return tex
    tex.append(' --- constructores: \\begin{itemize}')
    for constructor in constructors:
        tex.append('\\item \\constructorname{%s}' % (string_to_tex(constructor[0]),))
        fields = constructor[1:]
        if len(fields) == 0:
            continue
        tex.append(' --- campos: \\begin{itemize}')
        for field in fields:
            tex.append('\\item \\fieldname{%s}' % (string_to_tex(field),))
        tex.append('\\end{itemize}')
    tex.append('\\end{itemize}')
    return tex

def builtin_routine_to_tex(builtin):
    tex = []
    parameters = builtin[2]
    tex.append(
      '\\item \\texttt{%s} \\typename{%s}(%s)' % (
        builtin[0], string_to_tex(builtin[1]), ', '.join(parameters)
      )
    )
    tex.append('\\begin{itemize}')
    if builtin[3] != '':
        tex.append('\\item {\\bf Precondici\\\'on:} %s' % (string_to_tex(builtin[3]),))
    tex.append('\\item {\\bf Prop\\\'osito:} %s' % (string_to_tex(builtin[4]),))
    tex.append('\\end{itemize}')
    return tex

def builtins_to_tex(json_builtins):
    tex = []
    tex.append('\\begin{itemize}')
    for builtin in json_builtins:
        if builtin[0] == 'type':
            tex.extend(builtin_type_to_tex(builtin))
        elif builtin[0] in ['procedure', 'function']:
            tex.extend(builtin_routine_to_tex(builtin))
        else:
            raise Error('Unknown type of builtin: %s' % (builtin[0]))
    tex.append('\\end{itemize}')
    return tex

if __name__ == '__main__':
    gendoc('01-tokens.json', tokens_to_tex)
    gendoc('02-grammar.json', grammar_to_tex)
    gendoc('03-ast.json', ast_to_tex)
    gendoc('04-instructions.json', instructions_to_tex)
    gendoc('05-builtins.json', builtins_to_tex)

