Blame view
egs/wsj/s5/utils/lang/grammar/augment_phones_txt.py
4.28 KB
8dcb6dfcb first commit |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
#!/usr/bin/env python3 import argparse import re import os import sys def get_args(): parser = argparse.ArgumentParser(description="""This script augments a phones.txt file (a phone-level symbol table) by adding certain special symbols relating to grammar support. See ../add_nonterminals.sh for context.""") parser.add_argument('input_phones_txt', type=str, help='Filename of input phones.txt file, to be augmented') parser.add_argument('nonterminal_symbols_list', type=str, help='Filename of a file containing a list of nonterminal ' 'symbols, one per line. E.g. #nonterm:contact_list') parser.add_argument('output_phones_txt', type=str, help='Filename of output ' 'phones.txt file. May be the same as input-phones-txt.') args = parser.parse_args() return args def read_phones_txt(filename): """Reads the phones.txt file in 'filename', returns a 2-tuple (lines, highest_symbol) where 'lines' is all the lines the phones.txt as a list of strings, and 'highest_symbol' is the integer value of the highest-numbered symbol in the symbol table. It is an error if the phones.txt is empty or mis-formatted.""" # The use of latin-1 encoding does not preclude reading utf-8. latin-1 # encoding means "treat words as sequences of bytes", and it is compatible # with utf-8 encoding as well as other encodings such as gbk, as long as the # spaces are also spaces in ascii (which we check). It is basically how we # emulate the behavior of python before python3. whitespace = re.compile("[ \t]+") with open(filename, 'r', encoding='latin-1') as f: lines = [line.strip(" \t\r ") for line in f] highest_numbered_symbol = 0 for line in lines: s = whitespace.split(line) try: i = int(s[1]) if i > highest_numbered_symbol: highest_numbered_symbol = i except: raise RuntimeError("Could not interpret line '{0}' in file '{1}'".format( line, filename)) if s[0] == '#nonterm_bos': raise RuntimeError("It looks like the symbol table {0} already has nonterminals " "in it.".format(filename)) return lines, highest_numbered_symbol def read_nonterminals(filename): """Reads the user-defined nonterminal symbols in 'filename', checks that it has the expected format and has no duplicates, and returns the nonterminal symbols as a list of strings, e.g. ['#nonterm:contact_list', '#nonterm:phone_number', ... ]. """ ans = [line.strip(" \t\r ") for line in open(filename, 'r', encoding='latin-1')] if len(ans) == 0: raise RuntimeError("The file {0} contains no nonterminal symbols.".format(filename)) for nonterm in ans: if nonterm[:9] != '#nonterm:': raise RuntimeError("In file '{0}', expected nonterminal symbols to start with '#nonterm:', found '{1}'" .format(filename, nonterm)) if len(set(ans)) != len(ans): raise RuntimeError("Duplicate nonterminal symbols are present in file {0}".format(filename)) return ans def write_phones_txt(orig_lines, highest_numbered_symbol, nonterminals, filename): """Writes updated phones.txt to 'filename'. 'orig_lines' is the original lines in the phones.txt file as a list of strings (without the newlines); highest_numbered_symbol is the highest numbered symbol in the original phones.txt; nonterminals is a list of strings like '#nonterm:foo'.""" with open(filename, 'w', encoding='latin-1') as f: for l in orig_lines: print(l, file=f) cur_symbol = highest_numbered_symbol + 1 for n in ['#nonterm_bos', '#nonterm_begin', '#nonterm_end', '#nonterm_reenter' ] + nonterminals: print("{0} {1}".format(n, cur_symbol), file=f) cur_symbol = cur_symbol + 1 def main(): args = get_args() (lines, highest_symbol) = read_phones_txt(args.input_phones_txt) nonterminals = read_nonterminals(args.nonterminal_symbols_list) write_phones_txt(lines, highest_symbol, nonterminals, args.output_phones_txt) if __name__ == '__main__': main() |