#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
algpp.py [opts] file.gr file.alg:

  a LaTeX pretty printer for algorithms.  For the moment, next to no
  documentation.  Sorry, read the source!
"""

import sys, codecs
import astparse
from scfparse import pretty_print_error, ParseFailed

# ______________________________________________________________________
# Parse optional arguments.
from optparse import OptionParser
parser = OptionParser()
parser.set_usage(__doc__[1:-1])
parser.add_option('-e', '--encoding', dest='encoding')
parser.add_option('-d', '--dump', action='store_true', dest='dump')
parser.set_defaults(
    encoding='utf-8',
    dump=False,
    )
(options, args) = parser.parse_args()

# ______________________________________________________________________
# Parse required arguments.
if len(args) != 2:
    sys.stderr.write("Wrong number of arguments.  Try -h.\n")
    sys.exit(1)
grammarfilenm, algfilenm = args

# ______________________________________________________________________
# Parse grammar.
grammarfile = codecs.open(grammarfilenm, "r", options.encoding)
grammar = astparse.grparse(grammarfilenm, grammarfile)
if not grammar: sys.exit(1)
all_nts = grammar.postprocess()
if options.dump:
    grammar.dump()

# ______________________________________________________________________
# Routines for parsing and pretty-printing algorithm.

line_cntr = 0

def all_lines():
    global line_cntr
    algfile = codecs.open(algfilenm, "r", options.encoding)
    for line in algfile:
        line_cntr += 1
        if line[0] == '#': continue           # skip comments
        if line[-1] == '\n': line = line[:-1] # strip newlines
        yield line

class MissingDollar(Exception):
    pass

class MissingEndAlgorithm(Exception):
    pass

def writeln(res, text):
    res.write('%s\n' % (text,))

#def latex(text):
#    " Parse any text between dollar signs as a prod and return latex "
#    head, sep, tail = text.partition('$')
#    if not sep: return head
#
#    fmtd, sep, rmndr = tail.partition('$')
#    if not sep: raise MissingDollar()
#
#    parsed_prod = astparse.nt.prod.parse(fmtd.strip())
#    parsed_prod.postprocess(all_nts)
#    prod_latex = '$%s$' % (parsed_prod.latex_str(),)
#    return head + prod_latex + latex(rmndr)

def latex(text):
    if text:
        parsed_prod = astparse.nt.prod.parse(text.strip())
        parsed_prod.postprocess(all_nts, grammar.terminals)
        return '$%s$' % (parsed_prod.latex_str(),)
    return ""

def parse_algfile(lines, res):
    ALG_TAG = "ALGORITHM "
    for line_txt in lines:
        if line_txt.startswith(ALG_TAG):
            alg_name = line_txt[len(ALG_TAG):]
            writeln(res, r'\begin{algorithm}[t]')
            writeln(res, r'\caption{%s}' % (latex(alg_name),))
            writeln(res, r'\begin{algorithmic}')
            parse_alg(lines, res)
            writeln(res, r'\end{algorithmic}')
            writeln(res, r'\end{algorithm}')

def parse_alg(lines, res):
    END_ALG_TAG = "ENDALGORITHM"
    LABEL_TAG = "LABEL "
    SPECIAL_TAGS = ["WHILE", "FORALL", "ENDFOR", "ENDWHILE",
                    "IF", "ELSIF", "ELSE", "ENDIF", "COMMENT", "RETURN"]
    for line_text in lines:
        if line_text.startswith(END_ALG_TAG):
            return
        
        if line_text.startswith(LABEL_TAG):
            writeln(res, r'\label{%s}' % (line_text[len(LABEL_TAG):],))
            continue

        line_text = line_text.strip()
        
        fstword, _, rmndr = line_text.partition(' ')
        if fstword in SPECIAL_TAGS:
            writeln(res, r'\%s{%s}' % (fstword, latex(rmndr),))
            continue

        writeln(res, r'\STATE %s' % (latex(line_text),))
        
    raise MissingEndAlgorithm()

# ______________________________________________________________________
# Actually perform parse.

try:
    parse_algfile(all_lines(), sys.stdout)
    sys.exit(0)
except MissingDollar:
    sys.stderr.write('%s:%d: Unterminated $\n' % (
        algfilenm, line_cntr))
except MissingEndAlgorithm:
    sys.stderr.write('%s:%d: Missing ENDALGORITHM\n' % (
        algfilenm, line_cntr))
except ParseFailed:
    sys.stderr.write('%s:%d: Parse of production failed\n' % (
        algfilenm, line_cntr))
#except UnicodeEncodeError:
#    sys.stderr.write('%s:%d: Unrecognized unicode character\n' % (
#        algfilenm, line_cntr))
sys.exit(1)


