#!/usr/bin/env python
#
# Generate a PStrick version of a yacc grammar file
# WWW: http://se.wtb.tue.nl/~hat/showgrammar/
#
# $Id: showgrammar.py,v 1.2 2000/09/01 10:35:19 hat Exp $
#
import sys,getopt
from spark import GenericScanner, GenericParser

# Global list with tokens
# Should be non-empty when used
globterminallist=[]

#
# --------------------------------------------------
# SCANNER TOKENS
#

class Token:
    def __init__(self, type, line, attr=None):
	self.type = type
	self.attr = attr
	self.line = line

    def __cmp__(self, o):
	return cmp(self.type, o)

    def __repr__(self):
	if self.attr == None:
	    return self.type+"@"+str(self.line)
	else:
	    return self.type+"@"+str(self.line)+"("+self.attr+")"

#
# --------------------------------------------------
# SCANNER
#

class GrammarScanner(GenericScanner):
    def __init__(self):
	GenericScanner.__init__(self)
	self.lineno=1

    def tokenize(self,input):
	self.rv=[]
	self.lineno=1
	GenericScanner.tokenize(self,input)
	return self.rv

    #
    # Token definitions
    #

    def t_whitespace(self,s):
	r'[ \t]+'
	pass
    def t_nl(self,s):
	r'\n'
	self.lineno = self.lineno+1

    def t_colon(self,s):
	r':'
	self.rv.append(Token('colon',self.lineno))

    def t_semicol(self,s):
	r';'
	self.rv.append(Token('semicol',self.lineno))

    def t_alt(self,s):
	r'\|'
	self.rv.append(Token('alt',self.lineno))

    def t_iden(self,s):
	r'[a-zA-Z][a-zA-Z0-9]*'
	t=Token('iden', self.lineno, attr=s)
	self.rv.append(t)

    def t_start(self,s):
	r'%start'
	t = Token('start', self.lineno)
	self.rv.append(t)

    def t_token(self,s):
	r'%token'
	t = Token('token', self.lineno)
	self.rv.append(t)

    def t_percperc(self,s):
	r'%%'
	self.rv.append(Token('percperc', self.lineno))

def scan(input):
    data = input.read()
    scanner = GrammarScanner()
    return scanner.tokenize(data)

#
# --------------------------------------------------
# PARSER
#

class GrammarParser(GenericParser):
    def __init__(self,start='gramfile'):
	GenericParser.__init__(self,start)

    #
    # Parse rules
    #

    def p_gramfile(self,args):
	'gramfile ::= seca percperc secb'
	return (args[0][0],args[0][1],args[2])

    def p_seca_1(self,args):
	' seca ::= '
	return ([],[])
    def p_seca_2(self,args):
	' seca ::= seca start iden '
	r = args[0]
	r[0].append(args[2].attr)
	return r
    def p_seca_3(self,args):
	'seca ::= seca token idenlist'
	ret = args[0]
	for t in args[2]:
	    ret[1].append(t)
	return ret

    def p_idenlist_1(self,args):
	'idenlist ::= iden'
	return [args[0].attr]
    def p_idenlist_2(self,args):
	'idenlist ::= idenlist iden'
	return args[0]+[args[1].attr]

    def p_secb_1(self,args):
	'secb ::= '
	return []
    def p_secb_2(self,args):
	'secb ::= secb rule'
	return args[0]+args[1]

    def p_rule(self,args):
	'rule ::= iden colon rulealts semicol'
	ret = []
	for r in args[2]:
	    ret.append( (args[0].attr,r) )
	return ret

    def p_rulealts_1(self,args):
	'rulealts ::= seqrule'
	return [ args[0] ]
    def p_rulealts_2(self,args):
	'rulealts ::= rulealts alt seqrule'
	return args[0] + [ args[2] ]

    def p_seqrule_1(self,args):
	'seqrule ::='
	return []
    def p_seqrule_2(self,args):
	'seqrule ::= idenlist'
	return args[0]

# parse returns a triple
# ( [ start tokens ] [ tokens ] [ (lhs-iden, [ rhs-sequence ] ) ] )
def parse(tokens):
    parser = GrammarParser()
    return parser.parse(tokens)

#
# --------------------------------------------------
# GENERATOR
# Works in 2 steps for each set of production rules
# 1: Generate an abstract representation
# 2: Convert to actual LaTeX code
#

#
# First some classes of core graphical elements
#

class Graphic:
    def __init__(self,tp):
	self.type = tp
    
    def _coord(self,x,y):
	return '('+str(x)+','+str(y)+')'

    def _label(self,x,y):
	return '{p'+str(x)+'_'+str(y)+'}'

class Line(Graphic):
    def __init__(self,fx,fy,tx,ty, arr='-'):
	Graphic.__init__(self,'line')
	self.x1=fx
	self.y1=fy
	self.x2=tx
	self.y2=ty
	assert(fx>=-1000 and fx<1000)
	assert(fy>=-1000 and fy<1000)
	assert(tx>=-1000 and tx<1000)
	assert(ty>=-1000 and ty<1000)
	self.arrow=arr
    def __repr__(self):
	return 'Line<'+self._coord(self.x1,self.y1)+self._coord(self.x2,self.y2)+",'"+self.arrow+"'>"

    def area(self):
	if self.x1<self.x2:
	    sx=self.x1
	    bx=self.x2
	else:
	    sx=self.x2
	    bx=self.x1
	if self.y1<self.y2:
	    sy=self.y1
	    by=self.y2
	else:
	    sy=self.y2
	    by=self.y1
	return (sx,sy,bx,by)

    def mirrorX(self,maxx):
	self.x1=maxx-self.x1
	self.x2=maxx-self.x2
    def mirrorY(self,maxy):
	self.y1=maxy-self.y1
	self.y2=maxy-self.y2
    def shift(self,dx,dy):
	self.x1=self.x1+dx
	self.y1=self.y1+dy
	self.x2=self.x2+dx
	self.y2=self.y2+dy

    def _ensurelabel(self,output,nodelabels,x,y):
	if not nodelabels.has_key( (x,y) ):
	    nodelabels[(x,y)]=self._label(x,y)
	    s=self._label(x,y)
	    output.append( '\\pnode'+self._coord(x,y)+s )
	    return s
	else:
	    return nodelabels[(x,y)]

    def _gencode(self,output,nodelabels):
	label1=self._ensurelabel(output,nodelabels,self.x1,self.y1)
	label2=self._ensurelabel(output,nodelabels,self.x2,self.y2)
	if self.x1 == self.x2:
	    # vertical line
	    if self.y1<self.y2:
		output.append('\\ncline[angleA=0,angleB=180,arrows='+self.arrow+']'+label1+label2)
	    else:
		output.append('\\ncline[angleA=180,angleB=0,arrows='+self.arrow+']'+label1+label2)
	elif self.y1 == self.y2:
	    # Horizontal line
	    if self.x1<self.x2:
		output.append('\\ncline[angleA=270,angleB=90,arrows='+self.arrow+']'+label1+label2)
	    else:
		output.append('\\ncline[angleA=90,angleB=270,arrows='+self.arrow+']'+label1+label2)
	else:
	    raise "Line not horizontal nor vertical"

class Node(Graphic):
    def __init__(self,nx,ny,txt):
	Graphic.__init__(self,'node')
	self.x=nx
	self.y=ny
	self.text=txt
	if nx<-1000 or nx>1000:
	    raise "Node x coord out of range"
	if ny<-1000 or ny>1000:
	    raise "Node y coord out of range"

    def __repr__(self):
	return 'Node<'+self._coord(self.x,self.y)+",'"+self.text+"'>"

    def area(self):
	return (self.x,self.y,self.x,self.y)

    def mirrorX(self,maxx):
	self.x=maxx-self.x
    def mirrorY(self,maxy):
	self.y=maxy-self.y
    def shift(self,dx,dy):
	self.x=self.x+dx
	self.y=self.y+dy

    def _gencode(self,output,nodelabels):
	global globterminallist

	if nodelabels.has_key( (self.x, self.y) ):
	    raise "Node already defined"
	label = self._label(self.x,self.y)
	nodelabels[(self.x, self.y)]=label

	if len(self.text)==0:
	    output.append( '\\pnode'+self._coord(self.x,self.y)+label )
	else:
	    assert(len(globterminallist)>0)
	    if self.text in globterminallist:
		#tekst = '{\\rnode'+label+'{\\'+self.text+'}}'
		tekst = '{\\rnode'+label+'{\\psframebox{\\'+self.text+'}}}'
	    else:
		tekst = '{\\rnode'+label+'{\\psframebox[framearc=1]{\\'+self.text+'}}}'
	    output.append( '\\rput'+self._coord(self.x,self.y)+tekst )

class Drawing(Graphic):
    def __init__(self):
	Graphic.__init__(self,'drawing')
	self.contents = []
    def __repr__(self):
	s='Drawing['
	b=0
	for c in self.contents:
	    if b: s=s+','
	    else: b=1
	    s=s+c.__repr__()
	return s+']'

    def area(self):
	if len(self.contents)==0:
	    return (0,0,0,0)	# Not entirely correct, but will do
	(sx,sy,bx,by)=self.contents[0].area()
	for c in self.contents:
	    (a,b,v,w)=c.area()
	    if a<sx: sx=a
	    if b<sy: sy=b
	    if v>bx: bx=v
	    if w>by: by=w
	return (sx,sy,bx,by)

    def mirrorX(self,maxx):
	for c in self.contents:
	    c.mirrorX(maxx)
    def mirrorY(self,maxy):
	for c in self.contents:
	    c.mirrorY(maxy)
    def shift(self,dx,dy):
	for c in self.contents:
	    c.shift(dx,dy)

    def add(self,g):
	if g.type == 'line' or g.type == 'node':
	    self.contents.append(g)
	elif g.type == 'drawing':
	    for c in g.contents:
		self.contents.append(c)
	else:
	    raise "Don't know how to add type '"+g.type+"'"

    # Append the pspicture to the output
    def gencode(self,output):
	nodelabels={}
	(sx,sy,bx,by)=self.area()
	output.append('\\begin{pspicture}'+self._coord(sx,sy)+self._coord(bx,by))
	for c in self.contents:
	    c._gencode(output,nodelabels)
	output.append('\\end{pspicture}')
	return output

#
# --------------------------------------------------
# BLOCK GENERATOR
#
# A block is a square, with a vertical line on the left and right, and
# horizontal under each-other the alternatives. The generator constructs the
# block upside down, so before outputting the block you are advised to mirror
# it first in Y.
#
class BlockGenerator:
    def __init__(self):
	pass

    # prods is a list of alternatives sequnces, all assumed to be non-empty
    # maxlen = max #identifiers on 1 line
    # positionemptyrule: 0=do not generate
    #                    1=generate below (after mirrorring in Y at top)
    #                    2=generate above (after mirrorring in Y at bottom)
    # leftside: 1=connect at bottom, 2=connect at top, 3=connect at both
    # rightside: 1=connect at bottom, 2=connect at top, 3=connect at both
    def generate(self, prods, maxlen, positionemptyrule, leftside=1, rightside=2):
	dr = Drawing()
	unfinishedstart=[]
	unfinishedend=[]
	ypos=0
	wrapped=0

	if len(prods)==0 and positionemptyrule==0:
	    raise "Empty block"

	if positionemptyrule == 1:
	    (ypos,wr)=self._generateEmpty(dr,unfinishedstart,unfinishedend,ypos)
	    assert(wr==0)

	for p in prods:
	    if len(p)==0:
		raise "Empty sequence"

	    (ypos,wr)=self._generateAlt(p,dr,unfinishedstart,unfinishedend,maxlen,ypos)
	    if wr: wrapped=1

	if positionemptyrule == 2:
	    (ypos,wr)=self._generateEmpty(dr,unfinishedstart,unfinishedend,ypos)
	    assert(wr==0)
	
	# Left side of the block
	miny=10000
	if leftside & 1: miny=0
	maxy=0
	if leftside & 2: maxy=ypos-10
	for q in unfinishedstart:
	    assert(q[0]==0)
	    if q[1]<miny: miny=q[1]
	    if q[1]>maxy: maxy=q[1]
	if miny!=maxy:
	    dr.add( Line(0,miny, 0,maxy) )

	# Right side of the block
	maxx=10
	if wrapped: maxx=maxlen*10
	miny=10000
	if rightside & 1: miny=0
	maxy=0
	if rightside & 2: maxy=ypos-10
	for q in unfinishedend:
	    if q[0]>maxx: maxx=q[0]
	    if q[1]<miny: miny=q[1]
	    if q[1]>maxy: maxy=q[1]
	for q in unfinishedend:
	    dr.add( Line(q[0],q[1], maxx+10,q[1]) )
	if miny!=maxy:
	    dr.add( Line(maxx+10,miny, maxx+10, maxy) )
	return dr
    
    def _generateEmpty(self,dr,unfinishedstart,unfinishedend,ypos):
	unfinishedstart.append( (0,ypos) )
	dr.add( Line(0,ypos, 10,ypos, '->') )
	unfinishedend.append( (10,ypos) )
	return (ypos+10,0)

    def _generateAlt(self,prod,dr,unfinishedstart,unfinishedend,maxlen,ypos):
	startx=0
	unfinishedstart.append( (startx,ypos) )
	wrapped=0
	while 1:
	    if len(prod)>maxlen:
		line = prod[:maxlen]
		prod=prod[maxlen:]
	    else:
		line = prod
		prod=[]

	    xpos=10
	    for id in line:
		dr.add( Node(xpos,ypos,id) )
		xpos=xpos+10
	    for n in range(10,xpos-10,10):
		dr.add( Line(n,ypos,n+10,ypos) )
	    dr.add( Line(startx,ypos, 10,ypos, '->') )

	    if len(prod)>0:
		assert(maxlen*10 == xpos-10)
		dr.add( Line(maxlen*10,ypos, maxlen*10+8,ypos) )
		dr.add( Line(maxlen*10+8,ypos, maxlen*10+8,ypos+5) )
		dr.add( Line(maxlen*10+8,ypos+5, 2, ypos+5) )
		dr.add( Line(2,ypos+5, 2,ypos+10) )
		ypos=ypos+10
		startx=2
		wrapped=1
	    else:
		unfinishedend.append( (xpos-10,ypos) )
		ypos=ypos+10
		break;
	return (ypos,wrapped)

class CodeGenerator:
    def __init__(self,mxlen,xunlen,yunlen,wanterrorrules,gennewcmds,newcmdsfname,genheaders,indexcount):
	self.errorrulescopied=wanterrorrules
	self.maxseqlen = mxlen
	self.xunitlen=xunlen
	self.yunitlen=yunlen
	self.generatenewcommands=gennewcmds
	self.newcommandsfname=newcmdsfname
	self.genlatexheaders=genheaders
	self.index=indexcount
	self.blockgen = BlockGenerator()

    def _LaTeXHeader(self,output):
	if self.genlatexheaders:
	    output.append('\\documentclass{article}')
	    output.append('\\usepackage{a4wide}')
	    output.append('\\usepackage{pstricks}')
	    output.append('\\usepackage{pst-node}')
	    output.append('')
	    output.append('\\psset{xunit='+self.xunitlen+',yunit='+self.yunitlen+'}')
	    output.append('')
	    output.append('\\begin{document}')
	    output.append('')
	return output
    def _LaTeXFooter(self,output):
	if self.genlatexheaders:
	    output.append('')
	    output.append('\\end{document}')
	return output

    def _newCommands(self,tokens,output):
	for t in tokens:
	    output.append('\\newcommand{\\'+t+'}{'+t+'}')
	output.append('')
	return output

    def _findRules(self,lhs,grammar):
	prods=[]
	for g in grammar:
	    if g[0]==lhs:
		if self.errorrulescopied or 'error' not in g[1]:
		    prods=prods+[ g[1] ]
	return prods

    def generate(self, startlist, terminallist, grammar):
	global globterminallist

	todolist = startlist[:]
	donelist = []
	output=[]
	if self.errorrulescopied: terminallist=terminallist+['error']
	globterminallist=terminallist  # Fill the global terminallist

	while len(todolist)>0:
	    lhs = todolist[0]
	    del todolist[0]

	    # Deal with rules for 'lhs'
	    prods = self._findRules(lhs,grammar)
	    output=self.generateSection(lhs,prods,output)

	    # Update todo/done administration
	    donelist.append(lhs)
	    for p in prods:
		for i in p:
		    if i not in todolist and i not in donelist and i not in terminallist and i!='error':
			todolist.append(i)
	
	if self.index>0:
	    index=self.generateIndex(donelist,[])
	else:
	    index=['% Index not generated']

	ncmds = []
	ncmds.append('% List of tokens')
	ncmds.append('%')
	ncmds.append('% Terminal symbols')
	ncmds = self._newCommands(terminallist,ncmds)
	ncmds.append('%')
	ncmds.append('% Non-terminal symbols')
	ncmds = self._newCommands(donelist,ncmds)

	if self.generatenewcommands and len(self.newcommandsfname)>0:
	    fp = open(self.newcommandsfname,'w')
	    for nc in ncmds:
		fp.write(nc+'\n')
	    fp.close()

	header=[]
	header = self._LaTeXHeader(header)
	if len(self.newcommandsfname)>0:
	    header.append('\\input{'+self.newcommandsfname+'}')
	else:
	    for nc in ncmds:
		header=header+[nc]
	header.append('')


	footer=[]
	footer=self._LaTeXFooter(footer)
	return header+output+index+footer

    def _splitEmptyNonempty(self,prods):
	empty=0
	ne=[]
	for p in prods:
	    if len(p)>0:
		ne=ne+[p]
	    else:
		empty=1
	return (empty,ne)

    # Generate a drawing from left to right with a choice between several
    # alternatives
    def generateDefault(self,prods,output):
	(hasempty,prods)=self._splitEmptyNonempty(prods)
	if hasempty:
	    drawing = self.blockgen.generate(prods,self.maxseqlen,1,1,2)
	else:
	    drawing = self.blockgen.generate(prods,self.maxseqlen,0,1,2)

	# Add ingoing and outgoing lines
	area=drawing.area()
	drawing.add( Line(area[0],area[1], area[0]-5,area[1]) )
	drawing.add( Line(area[2],area[3], area[2]+5,area[3]) )
	drawing.mirrorY(area[3])
	drawing.shift(5,0)
	return drawing.gencode(output)

    # Empty rule, followed by repetitive selection of one of the sufs
    def generateLoopback(self,sufs,output):
	drawing = self.blockgen.generate(sufs,self.maxseqlen,0,1,1)
	area=drawing.area()
	drawing.mirrorX(area[2])

	# Add the empty rule above
	drawing.shift(0,10)
	drawing.add( Line(0,0, 10,0,'->') )
	drawing.add( Line(10,0,area[2],0) )
	drawing.add( Line(0,10,0,0, '->') )
	drawing.add( Line(area[2],0, area[2],10) )

	# Add the start and end lines
	drawing.shift(5,0)
	drawing.add( Line(0,0, 5,0) )
	drawing.add( Line(area[2]+5,0, area[2]+5+5,0) )

	# Turn it upside down
	area=drawing.area()
	drawing.mirrorY(area[3])
	return drawing.gencode(output)

    # Non-empty prefix, followed by 0 or more sequences of one of the suffixes
    # and the prefix again. Suffix rule may be empty.
    def generateLooparound(self,pre,back,output):
	predr=self.blockgen.generate(pre,self.maxseqlen,0,2,2)
	prearea=predr.area()
	predr.mirrorY(prearea[3])

	(hasempty,back)=self._splitEmptyNonempty(back)
	if hasempty:
	    sufdr=self.blockgen.generate(back,self.maxseqlen,1,1,1)
	else:
	    sufdr=self.blockgen.generate(back,self.maxseqlen,0,1,1)
	sufarea = sufdr.area()
	sufdr.mirrorX(sufarea[2])
	sufdr.mirrorY(sufarea[3])

	# Find the biggest edge
	maxx = prearea[2]
	if maxx<sufarea[2]: maxx=sufarea[2]

	# Insert the prefix drawing into the suffix drawing
	predr.shift(0,sufarea[3]+10)
	sufdr.add(predr)

	sufdr.add( Line(0,sufarea[3], 0,sufarea[3]+10,'->') )
	sufdr.add( Line(-5,sufarea[3]+10, 0,sufarea[3]+10) )	# Entry line
	
	# exit line
	sufdr.add( Line(prearea[2],sufarea[3]+10,maxx+5,sufarea[3]+10) )

	sufdr.add( Line(maxx,sufarea[3], maxx,sufarea[3]+10) )	# Line down to suffix block
	if sufarea[2]<maxx:
	    sufdr.add( Line(sufarea[2],sufarea[3], maxx,sufarea[3]) )

	sufdr.shift(5,0)
	return sufdr.gencode(output)

    def generatePrefixSuffix(self,pre,suf,output):
	(preempty,prerules)=self._splitEmptyNonempty(pre)
	if preempty:
	    predrawing=self.blockgen.generate(prerules, self.maxseqlen, 1,1,2)
	else:
	    predrawing=self.blockgen.generate(prerules, self.maxseqlen, 0,1,2)
	prearea = predrawing.area()
	predrawing.mirrorY(prearea[3])
	predrawing.add( Line(prearea[2],prearea[1], prearea[2],prearea[1]-10,'->') )
	predrawing.add( Line(prearea[0]-5,prearea[3], prearea[0],prearea[3]) )

	sufdrawing = self.blockgen.generate(suf,self.maxseqlen,0,1,1)
	sufarea = sufdrawing.area()
	sufdrawing.mirrorY(sufarea[3])

	if prearea[2]>sufarea[2]:
		sufdrawing.shift(prearea[2]-sufarea[2],0)
		sufarea = sufdrawing.area()
	elif sufarea[2]>prearea[2]:
		predrawing.shift(sufarea[2]-prearea[2],0)
	else:
		pass	# prearea[2]==sufarea[2]
	
	predrawing.shift(0,sufarea[3]+10+10)
	sufdrawing.add(predrawing)

	sufdrawing.add( Line(sufarea[2],sufarea[3], sufarea[2],sufarea[3]+10,'->') )
	sufdrawing.add( Line(sufarea[2],sufarea[3]+10,sufarea[2]-10,sufarea[3]+10,'->') )
	if sufarea[2]-10>sufarea[0]:
		sufdrawing.add( Line(sufarea[0],sufarea[3]+10, sufarea[2]-10, sufarea[3]+10) )
	sufdrawing.add( Line(sufarea[0],sufarea[3]+10, sufarea[0],sufarea[3]) )

	sufdrawing.add( Line(sufarea[2],sufarea[3]+10, sufarea[2]+5, sufarea[3]+10) )

	# Align the entire drawing
	sufarea = sufdrawing.area()
	if sufarea[0]!=0 or sufarea[1]!=0:
		sufdrawing.shift(-sufarea[0],-sufarea[1])

	return sufdrawing.gencode(output)

    # Try to split the set of rules in prefixes and suffixes
    # Prefix is X ::= <some sequence not using X>
    # Suffix is X ::= X <some non-empty sequence not using X>
    # Return ( <list prefix rules>, <list suffix rukes>, <bool> )
    # bool is 1 if split was successful, 0 otherwise
    # suffix rules do not have lhs any more
    def _splitPrefixSuffix(self,lhs,prods):
	res=([],[],1)
	for p in prods:
	    if len(p)==0:
		res=(res[0]+[p],res[1],1)
	    elif lhs not in p:
		res=(res[0]+[p],res[1],1)
	    elif len(p)>1 and p[0]==lhs and lhs not in p[1:]:
		res=(res[0],res[1]+[p[1:]],1)
	    else:
		return ([],[],0)
	return res

    def generateIndex(self,ntlist,output):
	if self.index>0 and len(ntlist)>0:
	    ntlist.sort()
	    output.append('\\section*{Index}')
	    colspec=''
	    for i in range(0,self.index):
		colspec=colspec+'l'
	    output.append('\\begin{tabular}{'+colspec+'}')
	    ylen = len(ntlist)/self.index
	    if len(ntlist)%self.index>0:
		ylen=ylen+1
	    lines=[]
	    for y in range(0,ylen):
		lines.append('')

	    # Add the columns
	    while len(ntlist)>0:
		# Prefix an & if needed
		if len(lines[0])>0:
		    for y in range(0,ylen):
			lines[y]=lines[y]+'\t&'
		for y in range(0,ylen):
		    if len(ntlist)>0:
			lhs=ntlist[0]
			ntlist=ntlist[1:]
			lines[y]=lines[y]+' \\'+lhs+'{}, \\pageref{sec:'+lhs+'}'

	    for y in range(0,ylen-1):
		lines[y]=lines[y]+'\t\\\\'

	    for y in range(0,ylen):
		output.append(lines[y])
	    output.append('\\end{tabular}')
	return output

    def generateSection(self,lhs,prods,output):
    	if self.index==0:
	    output.append('\\section*{\\'+lhs+'}')
	else:
	    output.append('\\section*{\\'+lhs+'}\\label{sec:'+lhs+'}')

	(pre,suf,ok)=self._splitPrefixSuffix(lhs,prods)

	# It is not a set rules with the form
	# X::= A B C or the form X::= X A B C
	# Generate a diagram in default output format
	if not ok:
	    output = self.generateDefault(prods,output)
	    output.append('')
	    return output
	
	# rules could be split
	# Seperate empty/non empty prefix rules
	(preempty,nonemptypreprods)=self._splitEmptyNonempty(pre)

	# No sufiixes -> use the default
	if len(suf)==0:
	    output = self.generateDefault(prods,output)
	    output.append('')
	    return output

	# Empty prefix
	if len(pre)==1 and preempty:
	    output = self.generateLoopback(suf,output)
	    output.append('')
	    return output

	if len(pre)==1 and not preempty:
	    # one non-empty prefix
	    prerule=pre[0]
	    back=[]
	    b=1
	    for s in suf:
		if len(s)>=len(prerule) and s[-len(prerule):]==prerule:
		    back=back+[s[:-len(prerule)]]
		else:
		    b=0
		    break
	    if b:
		# One prefix, and is also used at the end of all suffixes
		# -> Loop around

		# Note that back is a shortened version of the sufs
		# it may contain empty rules (e.g. X ::= A | X A)
		output=self.generateLooparound(pre,back, output)
		output.append('')
		return output

	# Default prefix/suffix diagram
	output = self.generatePrefixSuffix(pre,suf,output)
	output.append('')
	return output

	# CODE BELOW NOT REACHED !!

	# Real ugly default (mainly for debugging purposes)
	for p in prods:
	    output.append(str(p))
	output.append('')
	return output


def generate(p,maxlen,xunitlen,yunitlen,wanterrorrules,generatenewcommands,newcommandsfname,genheaders,indexcount):
    cg=CodeGenerator(maxlen,xunitlen,yunitlen,wanterrorrules,generatenewcommands,newcommandsfname,genheaders,indexcount)
    (sl,tl,g)=p
    output=cg.generate(sl,tl,g)
    return output

#
# --------------------------------------------------
# Main program
#

if __name__ == '__main__':
    # Default program option settings
    copyerror=0	# Default do not copy error grammar rules
    gennewcommands=0 # Generate a terminal file
    genheaders=0
    maxlen=7
    newcommandsfname=''
    xunitlen='1.7mm'
    yunitlen='1mm'
    indexcount=0

    (opts,args) = getopt.getopt(sys.argv[1:],'eghm:t:x:y:i:')
    for op in opts:
	if op[0]=='-e':
	    copyerror=1
	elif op[0]=='-g':
	    gennewcommands=1
	elif op[0]=='-h':
	    genheaders=1
	elif op[0]=='-m':
	    maxlen=int(op[1])
	elif op[0]=='-t':
	    newcommandsfname=op[1]
	elif op[0]=='-x':
	    xunitlen=op[1]
	elif op[0]=='-y':
	    yunitlen=op[1]
	elif op[0]=='-i':
	    indexcount=int(op[1])
	else:
	    assert(0)	# We should never get here

    if len(newcommandsfname)==0:
	gennewcommands=0

    # Read the input
    if len(args)==0:
	s=scan(sys.stdin)
    elif len(args)==1 or len(args)==2:
	input=open(args[0],'r')
	s=scan(input)
	input.close()
    else:
	stderr.write('showgrammar: Too many arguments\n')
	sys.exit(1)

    p=parse(s)
    output=generate(p,maxlen,xunitlen,yunitlen,copyerror,gennewcommands,
		    newcommandsfname,genheaders,indexcount)
    
    # Write the output
    if len(args)==0 or len(args)==1:
	for l in output:
	    print l
    elif len(args)==2:
	outfile=open(args[1],'w')
	for l in output:
	    outfile.write(l+'\n')
	outfile.close()



syntax highlighted by Code2HTML, v. 0.9.1