diff options
Diffstat (limited to 'macros/latex/contrib/LingTrees/trees.py')
-rw-r--r-- | macros/latex/contrib/LingTrees/trees.py | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/macros/latex/contrib/LingTrees/trees.py b/macros/latex/contrib/LingTrees/trees.py new file mode 100644 index 0000000000..4fcbb243ef --- /dev/null +++ b/macros/latex/contrib/LingTrees/trees.py @@ -0,0 +1,309 @@ +#! /usr/bin/python +# +# COPYRIGHT 2005, 2006, Avery D Andrews 3rd +# +# GPL2 +# +# changelog at end + +import sys, string, os, re + +# +# would be an idea to subclass 'file', but then I wouldn't +# know how to make it work with sys.stdin +# +class Source: + + def __init__(self, source): + self.linenum = 0 + self.file = source + self.nextline = source.readline() + + def readline(self): + self.linenum = self.linenum+1 + returnline = self.nextline + self.nextline = self.file.readline() + return returnline + +class TreesError(Exception): + + def __init__(self, value): + self.value = value+'\n' + # + # UGH: there must be a better way to do this + # + global error_occurred + error_occurred=1 + + # + # UMM: actually this doesn't seem to do what I expect it to (be + # the spelling of an error instance in backquotes) + # + def __str__(self): + return self.value + +# +# complain and set error flag but don't stop +# +def TreesWhinge(issue): + sys.stderr.write(issue) + global error_occurred + error_occurred=1 + + +class Node: + def __init__(self, label): + self.tri = 0 + self.mother = None + self.daughters = None + self.next = None + self.tag = "" + self.label = label + +OUTPUT_DEFAULT = 0 # write output to command-line-specified file our sysout +OUTPUT_TEMP = 1 # write the next tree to a specified output file +OUTPUT_PERM = 2 # write all to a specified output file + +def parse_line(line, infile): + text = string.strip(line) + if text=='': + raise TreesError('Blank line in tree at line %d.'%infile.linenum) + white = line[:string.find(line,text[0])] + return len(white), text + + +def get_line(infile): + line = infile.readline() + while infile.nextline[:2]=='.-': + line = line + string.strip(infile.readline()[2:]) + if line=='': + raise TreesError('File ends during tree at line %d.'%infile.linenum) + return parse_line(line, infile) + + +# +# gets sub-trees whose first line has the same indentation as first param +# +def get_daughters(indent, text, infile, mother): + daughter, newindent, newtext = build_tree(indent, text, infile) + daughter.mother = mother + daughters = [daughter] + prevdaughter = daughter + while newindent==indent: + newdaughter, newindent, newtext = build_tree(newindent, newtext, infile) + newdaughter.mother = mother + prevdaughter.next = newdaughter + daughters.append(newdaughter) + prevdaughter=newdaughter + if newindent > indent: + raise TreesError('Indentation fault at line %d.'%infile.linenum) + return daughters, newindent, newtext + + +# +# pattern for option parsing +# +optpattern = re.compile("\s*(\S*)\s*(.*?)\s*$") + +# +# text is not supposed to be '.]' +# +def build_tree(indent, text, infile): + linenum = infile.linenum + textlist = re.split("[|]",text) + node = Node(textlist[0]) + dict = node.optdict = {} + for command in textlist[1:]: + matchcom = optpattern.match(command) + if matchcom: + comm = matchcom.group(1) + if not comm in [ + 'tag', # custom tag for the line + 'tri', # node has a triangle over it + 'none', # node has no line going to it + 'dot', # node line is dotted + 'dash', # node line is dashed + 'after', # something after the connection command + 'style', # style commands + 'dotted', # nice pstricks dots + 'connect', # custom connection command + 'width', # node is of set width + 'dtwidths',# widths of daughter nodes + ]: + TreesWhinge("Warning: unknown option '%s' near line %d\n"%(command,linenum)) + dict[comm] = matchcom.group(2) + if dict.has_key("tag"): + node.tag = dict["tag"] + else: + global tag_count + node.tag = "Z%d"%tag_count + tag_count=tag_count+1 + if dict.has_key("none"): + if dict.has_key("tri"): + TreesWhinge("Incompatible options 'none' and 'tri' near line %d (tri wins)\n"%linenum) + if dict.has_key("dash") or dict.has_key("dot") or dict.has_key("linestyle"): + TreesWhinge("Incompatible options 'none' and some line style near line %d (none wins)\n"%linenum) + + newindent, newtext = get_line(infile) + if newindent > indent: + node.daughters, newindent, newtext = get_daughters(newindent, newtext, infile, node) + return node, newindent, newtext + + +def write_tree(node, outfile, indent=0): + dict = node.optdict + lineoptions="" + if node.mother: + if dict.has_key('dash'): + line = "\\makedash{3pt}" + elif dict.has_key('dot'): + line = "\\makedash{1pt}" + else: + line = "" + if dict.has_key('dotted'): + lineoptions = lineoptions+"linestyle=dotted,linewidth=\\treedotwidth" + if dict.has_key('style'): + if lineoptions: + lineoptions = lineoptions+','+dict['style'] + else: + lineoptions = dict['style'] + if lineoptions: + lineoptions = "[%s]"%lineoptions + if dict.has_key('after'): + lineoptions = lineoptions+dict['after'] + if dict.has_key('tri'): + line = line+"\\nodetriangle{%s}{%s}%s%%\n"%(node.mother.tag, node.tag, lineoptions) + elif dict.has_key('none'): + if dict.has_key('after'): + line = dict['after'] + else: + if dict.has_key('connect'): + connect = dict['connect'] + else: + connect = "\\nodeconnect" + line = line+"%s{%s}{%s}%s"%(connect, node.mother.tag, node.tag, lineoptions) + else: + line = '' + if dict.has_key('width'): + width = '\\setnodewidth{%s}'%dict['width'] + elif node.mother!=None and node.mother.optdict.has_key('dtwidths'): + width = '\\setnodewidth{%s}'%node.mother.optdict['dtwidths'] + else: + width = "" + if node.daughters: + outfile.write("{%s\\ntnode{%s}{%s}{%s},\n"%(width,node.tag,node.label,line)) + for daughter in node.daughters: + write_tree(daughter, outfile, indent+2) + outfile.write('}') + else: + outfile.write("{%s\\tnode{%s}{%s}{%s}}"%(width,node.tag,node.label, line)) + if node.next: +# if node.mother.next: + outfile.write(",\n") + + +def process_tree(infile, outfile): + indent, text = get_line(infile) + if text[:2]=='.]': + return +# outfile.write('indent: %d; text: %s\n'%(indent, text)) + global tag_count + tag_count = 0 + tree, newindent, newtext = build_tree(indent, text, infile) + if newindent!=0: + raise TreesError('Indentation fault at line %d.'%infile.linenum) + if newtext!='.]': + raise TreesError('Bad tree end at line %d.'%infile.linenum) + outfile.write('%%% begin tree\n\\tree') + write_tree(tree, outfile) + outfile.write('%%\n%%end tree\n') + + +def make_folders(path): + folder, file = os.path.split(path) + folder = string.strip(folder) + if folder and not os.path.exists(folder): + os.makedirs(folder) + +def process_file(infile, outfile): + global error_occurred + error_occurred =0 + outfilemode = OUTPUT_DEFAULT + while 1: + line = infile.readline() + if line == '': + break; + if line[:3]=='.>>': + outfilemode = OUTPUT_PERM + line = string.strip(line[3:]) + make_folders(line) + outfile2 = outfile + outfile = open(line,'w') + elif line[:3] == '.<<': + outfilemode = OUTPUT_DEFAULT + elif line[:2] == '.>': + if outfilemode == OUTPUT_PERM: + sys.stderr.write("Warning: temp output directive after permanent at line %d"%infile.linenum) + else: + line = string.strip(line[2:]) + make_folders(line) + outfile2 = outfile + outfilemode = OUTPUT_TEMP + outfile = open(line,'w') + elif line[:2] == '.[': + try: + process_tree(infile, outfile) + except TreesError, error: + sys.stderr.write(error.value) + errfile=open(errfilename,'w') + errfile.write(error.value) + outfile.write('BAD TREE:\n %s'%error.value) + try: + if outfilemode == OUTPUT_TEMP and outfile2: + outfile = outfile2 + del outfile2 + except UnboundLocalError: + pass + else: + outfile.write(line) + + +if __name__=="__main__": + argv=sys.argv +# print 'cwd:'+os.getcwd() + if len(argv)<2:# run as filter + infile=sys.stdin + outfile=sys.stdout + errfilename = 'trees.err' +# errfile=open("trees.err","w") + else: + infile=open(argv[1]+'.txp','r') + outfile=open(argv[1]+'.tex','w') + errfilename = argv[1]+'.err' +# errfile=open(argv[1]+'.err','w') + + process_file(Source(infile), outfile) + if error_occurred: + sys.exit(1) + sys.stderr.write("trees ran without issues\n") + +# +# changelog +# +# v 3.1 Feb 28 2006 +# widths and dtwidths commands, for controlling width +# of nodes. +# +# v 3.0 Jan 30 2006 +# adjusted to write for pst-node +# name changed to lingtrees.py +# implement additional options for PSTricks +# +# v 2.2, Jan 18 2006 +# implemented none, dash and dot options from Chris Manning's version +# +# v 2.1, Dec 28 2005 +# .>, .>>, >> directives added for writing to files +# .- directive added for continuation of lines +# + |