| # Top level keywords defining the begin of a cell definition. | 
 | top_level = [ | 
 |     "model", | 
 |     "inputs", | 
 |     "outputs", | 
 |     "names", | 
 |     "latch", | 
 |     "subckt", | 
 | ] | 
 |  | 
 | # Keywords defining cell attributes / parameters. Those can be specified for | 
 | # each cell multiple times. Parameter names and values are stored in a dict | 
 | # under the parsed blif data. | 
 | # | 
 | # For example: the construct ".param MODE SYNC" will add to the dict under | 
 | # the key "param" entry "MODE":"SYNC". | 
 | # | 
 | sub_level = [ | 
 |     "attr", | 
 |     "param", | 
 | ] | 
 |  | 
 |  | 
 | def parse_blif(f): | 
 |     current = None | 
 |  | 
 |     data = {} | 
 |  | 
 |     def add(d): | 
 |         if d['type'] not in data: | 
 |             data[d['type']] = [] | 
 |         data[d['type']].append(d) | 
 |  | 
 |     current = None | 
 |     for oline in f: | 
 |         line = oline | 
 |         if '#' in line: | 
 |             line = line[:line.find('#')] | 
 |         line = line.strip() | 
 |         if not line: | 
 |             continue | 
 |  | 
 |         if line.startswith("."): | 
 |             args = line.split(" ", maxsplit=1) | 
 |             if len(args) < 2: | 
 |                 args.append("") | 
 |  | 
 |             ctype = args.pop(0) | 
 |             assert ctype.startswith("."), ctype | 
 |             ctype = ctype[1:] | 
 |  | 
 |             if ctype in top_level: | 
 |                 if current: | 
 |                     add(current) | 
 |                 current = { | 
 |                     'type': ctype, | 
 |                     'args': args[-1].split(), | 
 |                     'data': [], | 
 |                 } | 
 |             elif ctype in sub_level: | 
 |                 if ctype not in current: | 
 |                     current[ctype] = {} | 
 |                 key, value = args[-1].split(maxsplit=1) | 
 |                 current[ctype][key] = value | 
 |             else: | 
 |                 current[ctype] = args[-1].split() | 
 |             continue | 
 |         current['data'].append(line.strip().split()) | 
 |  | 
 |     if current: | 
 |         add(current) | 
 |  | 
 |     assert len(data['inputs']) == 1 | 
 |     data['inputs'] = data['inputs'][0] | 
 |     assert len(data['outputs']) == 1 | 
 |     data['outputs'] = data['outputs'][0] | 
 |     return data |