# parser for Unix yacc-based grammars
#
# Author: David Beazley (dave@dabeaz.com)
# Date  : October 2, 2006

import ylex
tokens = ylex.tokens

from ply import *

tokenlist = []
preclist  = []

emit_code = 1

def p_yacc(p):
    '''yacc : defsection rulesection'''

def p_defsection(p):
    '''defsection : definitions SECTION
                  | SECTION'''
    p.lexer.lastsection = 1
    print "tokens = ", repr(tokenlist)
    print
    print "precedence = ", repr(preclist)
    print
    print "# -------------- RULES ----------------"
    print 

def p_rulesection(p):
    '''rulesection : rules SECTION'''

    print "# -------------- RULES END ----------------"
    print_code(p[2],0)

def p_definitions(p):
    '''definitions : definitions definition
                   | definition'''

def p_definition_literal(p):
    '''definition : LITERAL'''
    print_code(p[1],0)

def p_definition_start(p):
    '''definition : START ID'''
    print "start = '%s'" % p[2]

def p_definition_token(p):
    '''definition : toktype opttype idlist optsemi '''
    for i in p[3]:
       if i[0] not in "'\"":
           tokenlist.append(i)
    if p[1] == '%left':
        preclist.append(('left',) + tuple(p[3]))
    elif p[1] == '%right':
        preclist.append(('right',) + tuple(p[3]))
    elif p[1] == '%nonassoc':
        preclist.append(('nonassoc',)+ tuple(p[3]))

def p_toktype(p):
    '''toktype : TOKEN
               | LEFT
               | RIGHT
               | NONASSOC'''
    p[0] = p[1]

def p_opttype(p):
    '''opttype : '<' ID '>'
               | empty'''

def p_idlist(p):
    '''idlist  : idlist optcomma tokenid
               | tokenid'''
    if len(p) == 2:
        p[0] = [p[1]]
    else:
        p[0] = p[1]
        p[1].append(p[3])

def p_tokenid(p):
    '''tokenid : ID 
               | ID NUMBER
               | QLITERAL
               | QLITERAL NUMBER'''
    p[0] = p[1]
    
def p_optsemi(p):
    '''optsemi : ';'
               | empty'''

def p_optcomma(p):
    '''optcomma : ','
                | empty'''

def p_definition_type(p):
    '''definition : TYPE '<' ID '>' namelist optsemi'''
    # type declarations are ignored

def p_namelist(p):
    '''namelist : namelist optcomma ID
                | ID'''

def p_definition_union(p):
    '''definition : UNION CODE optsemi'''
    # Union declarations are ignored

def p_rules(p):
    '''rules   : rules rule
               | rule'''
    if len(p) == 2:
       rule = p[1]
    else:
       rule = p[2]

    # Print out a Python equivalent of this rule

    embedded = [ ]      # Embedded actions (a mess)
    embed_count = 0

    rulename = rule[0]
    rulecount = 1
    for r in rule[1]:
        # r contains one of the rule possibilities
        print "def p_%s_%d(p):" % (rulename,rulecount)
        prod = []
        prodcode = ""
        for i in range(len(r)):
             item = r[i]
             if item[0] == '{':    # A code block
                  if i == len(r) - 1:
                      prodcode = item
                      break
                  else:
                      # an embedded action
                      embed_name = "_embed%d_%s" % (embed_count,rulename)
                      prod.append(embed_name)
                      embedded.append((embed_name,item))
                      embed_count += 1
             else:
                  prod.append(item)
        print "    '''%s : %s'''" % (rulename, " ".join(prod))
        # Emit code
        print_code(prodcode,4)
        print
        rulecount += 1

    for e,code in embedded:
        print "def p_%s(p):" % e
        print "    '''%s : '''" % e
        print_code(code,4)
        print

def p_rule(p):
   '''rule : ID ':' rulelist ';' '''
   p[0] = (p[1],[p[3]])

def p_rule2(p):
   '''rule : ID ':' rulelist morerules ';' '''
   p[4].insert(0,p[3])
   p[0] = (p[1],p[4])

def p_rule_empty(p):
   '''rule : ID ':' ';' '''
   p[0] = (p[1],[[]])

def p_rule_empty2(p):
   '''rule : ID ':' morerules ';' '''
   
   p[3].insert(0,[])
   p[0] = (p[1],p[3])

def p_morerules(p):
   '''morerules : morerules '|' rulelist
                | '|' rulelist
                | '|'  '''
 
   if len(p) == 2:   
       p[0] = [[]]
   elif len(p) == 3: 
       p[0] = [p[2]]
   else:
       p[0] = p[1]
       p[0].append(p[3])

#   print "morerules", len(p), p[0]

def p_rulelist(p):
   '''rulelist : rulelist ruleitem
               | ruleitem'''

   if len(p) == 2:
        p[0] = [p[1]]
   else:
        p[0] = p[1]
        p[1].append(p[2])

def p_ruleitem(p):
   '''ruleitem : ID
               | QLITERAL
               | CODE
               | PREC'''
   p[0] = p[1]

def p_empty(p):
    '''empty : '''

def p_error(p):
    pass

yacc.yacc(debug=0)

def print_code(code,indent):
    if not emit_code: return
    codelines = code.splitlines()
    for c in codelines:
         print "%s# %s" % (" "*indent,c)

