sempre/tables/dump-parsers/convert-to-postfix.py

131 lines
4.6 KiB
Python
Executable File

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Convert LispTree formulas to Postfix.
If the file is tab-separated, only process the first column.
"""
import sys, os, shutil, re, argparse, json
from codecs import open
from itertools import izip
from collections import defaultdict
import fileinput
import lisptree
NUMBER_PREFIX = 'N'
DATE_PREFIX = 'D'
TYPE_ROW = 'type-row'
AGGREGATE = ['count', 'min', 'max', 'sum', 'avg']
MERGE_ARITH = ['and', 'or', '-']
SUPERLATIVE = ['argmin', 'argmax']
def convert(tree, args):
answer = []
u_stack = []
def recurse(subtree):
if isinstance(subtree, basestring):
answer.append(subtree)
return
if isinstance(subtree[0], basestring):
opr = subtree[0]
if opr in AGGREGATE:
assert len(subtree) == 2, str(subtree)
recurse(subtree[1])
answer.append(opr)
elif opr in MERGE_ARITH:
assert len(subtree) == 3, str(subtree)
recurse(subtree[1])
recurse(subtree[2])
answer.append(opr)
elif opr in SUPERLATIVE:
assert len(subtree) in (3, 5), str(subtree)
if len(subtree) == 3:
u, b = subtree[1], subtree[2]
else:
u, b = subtree[3], subtree[4]
if args.implicit_superlative_lambda:
assert b[0] == 'reverse'
assert b[1][0] == 'lambda'
u_stack.append(convert(u, args))
recurse(b[1][2])
answer.append(opr)
u_stack.pop()
else:
recurse(u)
recurse(b)
answer.append(opr)
elif opr == 'lambda':
assert len(subtree) == 3, str(subtree)
recurse(subtree[2])
answer.append(opr)
elif opr == 'reverse':
assert len(subtree) == 2, str(subtree)
recurse(subtree[1])
answer.append(opr)
elif opr == 'var':
assert len(subtree) == 2, str(subtree)
if args.implicit_superlative_lambda:
answer.extend(u_stack[-1])
answer.append(subtree[1])
elif opr == 'number':
assert len(subtree) == 2, str(subtree)
answer.append(NUMBER_PREFIX + subtree[1])
elif opr == 'date':
assert len(subtree) == 4, str(subtree)
answer.append(DATE_PREFIX + '-'.join(
'XX' if x == '-1' else x for x in subtree[1:4]))
else: # Join with a name
assert len(subtree) == 2, str(subtree)
if (args.collapse_type_row and
'fb:type.object.type' == opr and
'fb:type.row' == subtree[1]):
answer.append(TYPE_ROW)
else:
recurse(subtree[1])
answer.append(opr)
if not args.implicit_join:
answer.append('.')
else: # Join with a complex construct
assert len(subtree) == 2, str(subtree)
if args.implicit_join:
# Only allows ((reverse ...) ...)
assert subtree[0][0] == 'reverse', str(subtree)
assert len(subtree[0]) == 2, str(subtree)
recurse(subtree[1])
answer.append('!' + subtree[0][1])
else:
recurse(subtree[1])
recurse(subtree[0])
answer.append('.')
recurse(tree)
return answer
def process(line, args):
line = line[:-1].split('\t')
print '\t'.join([' '.join(convert(lisptree.parse(line[0]), args))] + line[1:])
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-j', '--implicit-join', action='store_true',
help='Do not output "." for joins')
parser.add_argument('-s', '--implicit-superlative-lambda', action='store_true',
help='Do not output "lambda reverse" for superlatives')
parser.add_argument('-t', '--collapse-type-row', action='store_true',
help='Collapse "(type row)" into a single token')
parser.add_argument('infile')
args = parser.parse_args()
if args.infile != '-':
opener = gzip.open if args.infile.endswith('.gz') else open
with opener(args.infile, 'r', 'utf8') as fin:
for line in fin:
process(line, args)
else:
for line in sys.stdin:
process(line, args)
if __name__ == '__main__':
main()