is_a = isinstance
import Parsing

def make_map (l):
    m = {}
    for i in range (len (l)):
        m[l[i]] = i
    return m

def build_tables (s):
    tokens   = s._tokens.keys()
    nonterms = s._nonterms.keys()
    goto     = s._goto
    action   = s._action
    token_map = make_map (tokens)
    nt_map = make_map (nonterms)
    action2 = []
    for i in range (len (action)):
        d = {}
        for k,v in action[i].iteritems():
            # v is always a one-element list?  GLR-related?
            if is_a (v[0], Parsing.ShiftAction):
                v = -1, v[0].nextState
            elif is_a (v[0], Parsing.ReduceAction):
                p = v[0].production
                v = -2, (len(p.rhs), p.lhs.name)
            else:
                raise ValueError
            d[token_map[k.name]] = v
        action2.append (d)
    goto2 = []
    for i in range (len (goto)):
        d = {}
        for k,v in goto[i].iteritems():
            d[k.name] = v
        goto2.append (d)
    return goto2, action2, token_map, nt_map

datatypes = """
(datatype action
  (:shift int)
  (:reduce int int))

(datatype action-list
  (:nil)
  (:cons int (action) (action-list)))

(datatype goto-list
  (:nil)
  (:cons int int (goto-list)))

"""

def gen_irken (file, tables):
    W = file.write
    W (datatypes)
    goto, actions, tm, ntm = tables
    items = tm.items()
    items.sort (lambda a,b: cmp (a[1],b[1]))
    W ('(define terminals \'#(\n')
    for item, index in items:
        W ('    %s\n' % (item,))
    W ('  ))\n')
    items = ntm.items()
    items.sort (lambda a,b: cmp (a[1],b[1]))
    W ('(define non-terminals \'#(\n')
    for item, index in items:
        W ('    %s\n' % (item,))
    W ('  ))\n')
    W ('(define actions\n')
    W ('    (literal #(\n')
    for action in actions:
        l = '(action-list:nil)'
        for k, v in action.items():
            shift_reduce, n = v
            if shift_reduce == -1:
                l = '(action-list:cons %d (action:shift %d) %s)' % (k, n, l)
            else:
                plen, nt = n
                l = '(action-list:cons %d (action:reduce %d %d) %s)' % (k, plen, ntm[nt], l)
        W ('      %s\n' % l)
    W ('   )))\n')
    W ('(define goto\n')
    W ('    (literal #(\n')
    for entry in goto:
        l = '(goto-list:nil)'
        items = entry.items()[:]
        items.reverse()
        for k, v in items:
            l = '(goto-list:cons %d %d %s)' % (ntm[k], v, l)
        W ('      %s\n' % l)
    W ('  )))\n')

if __name__ == '__main__':
    import sys
    base = sys.argv[1]
    exec ("import %s" % (base,))
    spec = eval ("%s.spec" % (base,))
    tables = build_tables (spec)
    file = open ('%s.scm' % (base,), 'wb')
    gen_irken (file, tables)