|  | # 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 |