1#!/usr/bin/env python3
2
3import argparse
4import re
5import sys
6from pathlib import Path
7
8
9class Layout(object):
10    def __init__(self, layout, variant=None):
11        self.layout = layout
12        self.variant = variant
13        if '(' in layout:
14            assert variant is None
15            # parse a layout(variant) string
16            match = re.match(r'([^(]+)\(([^)]+)\)', layout)
17            self.layout = match.groups()[0]
18            self.variant = match.groups()[1]
19
20    def __str__(self):
21        if self.variant:
22            return '{}({})'.format(self.layout, self.variant)
23        else:
24            return '{}'.format(self.layout)
25
26
27def read_file(path):
28    '''Returns a list of two-layout tuples [(layout1, layout2), ...]'''
29
30    # This parses both input files, one with two elements, one with four elements,
31    # all separated by tabs
32    pattern = re.compile(r'([^\t]+)\s+([^\t]+)\s*([^\t]*)\s*([^\t]*)')
33
34    layouts = []
35    for line in open(path):
36        match = re.match(pattern, line.strip())
37        groups = [g for g in match.groups() if g]  # drop empty groups
38        if len(groups) == 2:
39            l1 = Layout(groups[0])
40            l2 = Layout(groups[1])
41        else:
42            l1 = Layout(groups[0], groups[1])
43            l2 = Layout(groups[2], groups[3])
44        layouts.append((l1, l2))
45    return layouts
46
47
48# ml_s
49def write_fixed_layout(dest, mappings, write_header):
50    if write_header:
51        dest.write('! model		layout				=	symbols\n')
52    for l1, l2 in mappings:
53        dest.write('  *		{}			=	pc+{}\n'.format(l1, l2))
54
55
56# mln_s
57def write_layout_n(dest, mappings, number, write_header):
58    if write_header:
59        dest.write('! model		layout[{}]	=	symbols\n'.format(number))
60
61    # symbols is one of
62    #   +layout(variant):2 ... where the map-to-layout has a proper variant
63    #   +layout%(v[2]):2 ... where the map-to-layout does not have a variant
64    # and where the number is 1, we have a base and drop the suffix, i.e.
65    # the above becomes
66    #   pc+layout(variant)
67    #   pc+layout%(v[1])
68
69    base = 'pc' if number == 1 else ''
70    suffix = '' if number == 1 else ':{}'.format(number)
71
72    for l1, l2 in mappings:
73        second_layout = str(l2) if l2.variant else '{}%(v[{}])'.format(l2.layout, number)
74        dest.write('  *		{}		=	{}+{}{}\n'.format(l1, base, second_layout, suffix))
75
76
77# mlv_s
78def write_fixed_layout_variant(dest, mappings, write_header):
79    if write_header:
80        dest.write('! model		layout		variant		=	symbols\n')
81    for l1, l2 in mappings:
82        dest.write('  *		{}		{}		=	pc+{}\n'.format(l1.layout, l1.variant, l2))
83
84
85# mlnvn_s
86def write_layout_n_variant_n(dest, mappings, number, write_header):
87    if write_header:
88        dest.write('! model		layout[{}]	variant[{}]	=	symbols\n'.format(number, number))
89
90    # symbols is
91    #   +layout(variant):2
92    # and where the number is 1, we have a base and drop the suffix, i.e.
93    # the above becomes
94    #   pc+layout(variant)
95    # This part is only executed for the variantMappings.lst
96
97    base = 'pc' if number == 1 else ''
98    suffix = '' if number == 1 else ':{}'.format(number)
99
100    for l1, l2 in mappings:
101        second_layout = str(l2) if l2.variant else '{}%(v[{}])'.format(l2.layout, number)
102        dest.write('  *		{}		{}	=	{}+{}{}\n'.format(l1.layout, l1.variant, base, second_layout, suffix))
103
104
105def map_variant(dest, files, want='mls', number=None):
106    if number == 0:
107        number = None
108
109    for idx, f in enumerate(files):
110        write_header = idx == 0
111
112        mappings = read_file(f)
113        if want == 'mls':
114            if number is None:
115                write_fixed_layout(dest, mappings, write_header)
116            else:
117                write_layout_n(dest, mappings, number, write_header)
118        elif want == 'mlvs':
119            if number is None:
120                write_fixed_layout_variant(dest, mappings, write_header)
121            else:
122                write_layout_n_variant_n(dest, mappings, number, write_header)
123        else:
124            raise NotImplementedError()
125
126
127if __name__ == '__main__':
128    parser = argparse.ArgumentParser('variant mapping script')
129    parser.add_argument('--want', type=str,
130                        choices=['mls', 'mlvs'])
131    parser.add_argument('--number', type=int, default=None)
132
133    parser.add_argument('dest', type=str)
134    parser.add_argument('files', nargs='+', type=str)
135    ns = parser.parse_args()
136
137    dest = None
138    if ns.dest == '-':
139        dest = sys.stdout
140
141    with dest or open(ns.dest, 'w') as fd:
142        map_variant(fd, ns.files, ns.want, ns.number)
143