2 from pathlib
import Path
4 from typing
import Iterable
6 FILENAME
= Path("openpower/power_trans_ops.mdwn")
7 NEW_FILENAME
= FILENAME
.with_suffix(".new.mdwn")
8 OLD_FILENAME
= FILENAME
.with_suffix(".old.mdwn")
10 PO_59_63_HEADER
= "# Opcode Tables for PO=59/63 XO=1---011--"
11 MNEMONIC_COLUMN_NAME
= "opcode"
12 XO_COLUMN_NAME
= "Major 59 and 63"
16 def __init__(self
, lines
):
17 # type: (list[str]) -> None
18 self
.__next
_line
_index
= 0
22 if self
.__next
_line
_index
== len(self
.__lines
):
23 self
.__next
_line
_index
+= 1
24 return None, self
.__next
_line
_index
25 assert self
.__next
_line
_index
< len(self
.__lines
), \
26 "read past end-of-file"
27 line
= self
.__lines
[self
.__next
_line
_index
].rstrip()
28 self
.__next
_line
_index
+= 1
29 return line
, self
.__next
_line
_index
33 # type: (LineReader) -> Iterable[str]
35 line
, lineno
= lr
.read()
37 mnemonic_to_xo_map
= {} # type: dict[str, str]
39 def parse_table_separator_line(len_line_parts
):
40 # type: (int) -> Iterable[str]
43 assert line
is not None \
44 and line
.startswith('|') \
45 and line
.endswith('|') \
46 and len(line
.split('|')) == len_line_parts \
47 and line
.strip(" |-") == "", "invalid table separator line"
50 line
, lineno
= lr
.read()
52 assert line
is not None and line
!= "", "empty table"
54 def parse_single_mnemonic_to_opcode_map():
55 # type: () -> Iterable[str]
56 nonlocal line
, lineno
, mnemonic_to_xo_map
58 assert line
is not None and line
.startswith(
59 "| XO LSB half →<br> XO MSB half ↓ |"), \
60 "can't find PO=59/63 table"
61 line_parts
= line
.split('|')
62 len_line_parts
= len(line_parts
)
63 assert line_parts
[-1] == "", "invalid PO=59/63 table top row"
64 columns
= [] # type: list[str]
65 columns_range
= range(2, len_line_parts
- 1)
66 for i
in columns_range
:
67 column
= line_parts
[i
].strip()
68 if column
.startswith('`') and column
.endswith('`'):
69 column
= column
[1:-1].strip()
70 assert column
.lstrip(" 01") == "", (f
"invalid table top row "
71 f
"contents -- must be a "
72 f
"binary string: {column}")
73 columns
.append(column
)
76 line
, lineno
= lr
.read()
78 yield from parse_table_separator_line(len_line_parts
)
80 while line
is not None and line
!= "":
81 line_parts
= line
.split('|')
82 assert line
.startswith('|') and line
.endswith('|'), \
83 "invalid table line, must start and end with |"
84 assert len(line_parts
) == len_line_parts
, (
85 f
"invalid table line, wrong part count: found "
86 f
"{len(line_parts)} expected {len_line_parts}")
87 row
= line_parts
[1].strip()
88 if row
.startswith('`') and row
.endswith('`'):
89 row
= row
[1:-1].strip()
90 assert row
.lstrip(" 01") == "", (f
"invalid table line header-cell "
91 f
"contents -- must be a "
92 f
"binary string: {row}")
93 for i
, column
in zip(columns_range
, columns
):
96 r
" *<small> *` *(?P<xo>[01][01 ]*[01]) *` *</small>"
97 r
" *<br/?> *(?P<mnemonic>[a-zA-Z0-9_.][a-zA-Z0-9_.()]*)?"
98 r
"(?(mnemonic)|(?:\([a-zA-Z0-9_.()]+\)|\*\*TBD\*\*|))"
99 r
"(?: *\(draft\))? *", cell
)
100 assert match
is not None, f
"invalid table cell: {cell!r}"
101 xo
, mnemonic
= match
.group("xo", "mnemonic")
102 shrunk_xo
= xo
.replace(" ", "")
103 expected_xo
= (row
+ column
).replace(" ", "")
104 assert shrunk_xo
== expected_xo
, \
105 f
"incorrect XO: found {shrunk_xo} expected {expected_xo}"
108 assert mnemonic
.endswith('(s)'), \
109 f
"PO=59/63 fptrans mnemonic must end in `(s)`: {mnemonic}"
110 assert mnemonic
not in mnemonic_to_xo_map
, (
111 f
"duplicate mnemonic: {mnemonic} -- has opcode "
112 f
"{xo} and {mnemonic_to_xo_map[mnemonic]}")
114 mnemonic_to_xo_map
[mnemonic
] = xo
117 line
, lineno
= lr
.read()
121 line
, lineno
= lr
.read()
123 def parse_mnemonic_to_opcode_map():
124 # type: () -> Iterable[str]
125 nonlocal line
, lineno
, mnemonic_to_xo_map
127 while line
!= PO_59_63_HEADER
:
128 assert line
is not None, "missing PO=59/63 header"
130 line
, lineno
= lr
.read()
133 line
, lineno
= lr
.read()
135 while line
is not None and not line
.startswith(("#", "|")):
137 line
, lineno
= lr
.read()
140 yield from parse_single_mnemonic_to_opcode_map()
143 # type: () -> Iterable[str]
144 nonlocal line
, lineno
146 assert line
is not None \
147 and line
.startswith("|") and line
.endswith('|'), \
148 "invalid table header"
149 line_parts
= line
.split("|")
150 len_line_parts
= len(line_parts
)
151 assert len_line_parts
>= 3, "invalid table header"
154 line
, lineno
= lr
.read()
156 yield from parse_table_separator_line(len_line_parts
)
157 while line
is not None and line
!= "":
158 line_parts
= line
.split('|')
159 assert line
.startswith('|') and line
.endswith('|'), \
160 "invalid table line, must start and end with |"
161 assert len(line_parts
) == len_line_parts
, (
162 f
"invalid table line, wrong part count: found "
163 f
"{len(line_parts)} expected {len_line_parts}")
166 line
, lineno
= lr
.read()
169 # type: () -> Iterable[str]
170 nonlocal line
, lineno
172 assert line
is not None \
173 and line
.startswith("|") and line
.endswith('|'), \
174 "invalid table header"
175 line_parts
= line
.split("|")
176 len_line_parts
= len(line_parts
)
177 assert len_line_parts
>= 3, "invalid table header"
178 mnemonic_index
= None
181 for i
, column
in enumerate(line_parts
):
182 column_width
= len(column
) # should use wcswidth here
183 column
= column
.strip()
184 if column
== MNEMONIC_COLUMN_NAME
:
185 assert mnemonic_index
is None, \
186 f
"two {MNEMONIC_COLUMN_NAME!r} columns in table " \
187 f
"-- can't handle that"
189 if column
== XO_COLUMN_NAME
:
190 assert xo_index
is None, \
191 f
"two {XO_COLUMN_NAME!r} columns in table " \
192 f
"-- can't handle that"
194 xo_column_width
= column_width
195 if mnemonic_index
is None and xo_index
is None:
196 # not an opcode table -- skip it
197 yield from skip_table()
200 assert mnemonic_index
is not None, \
201 f
"missing {MNEMONIC_COLUMN_NAME} column"
202 assert xo_index
is not None, f
"missing {XO_COLUMN_NAME} column"
205 line
, lineno
= lr
.read()
207 yield from parse_table_separator_line(len_line_parts
)
208 while line
is not None and line
!= "":
209 line_parts
= line
.split('|')
210 assert line
.startswith('|') and line
.endswith('|'), \
211 "invalid table line, must start and end with |"
212 assert len(line_parts
) == len_line_parts
, (
213 f
"invalid table line, wrong part count: found "
214 f
"{len(line_parts)} expected {len_line_parts}")
216 mnemonic
= line_parts
[mnemonic_index
].strip()
217 xo
= line_parts
[xo_index
].strip()
218 if mnemonic
not in mnemonic_to_xo_map
:
219 print(f
"mnemonic not assigned an XO value: {mnemonic!r}")
221 xo
= mnemonic_to_xo_map
[mnemonic
]
222 xo_width
= len(xo
) # should use wcswidth here
223 if xo_width
< xo_column_width
:
224 # should use wc_ljust here
225 xo
= (" " + xo
).ljust(xo_column_width
)
226 line_parts
[xo_index
] = xo
228 expected_xo
= mnemonic_to_xo_map
[mnemonic
].replace(" ", "")
229 assert xo
.replace(" ", "") == expected_xo
, (
230 f
"mismatch in {XO_COLUMN_NAME} column: expected "
231 f
"{mnemonic_to_xo_map[mnemonic]} found {xo!r}")
233 yield '|'.join(line_parts
)
234 line
, lineno
= lr
.read()
237 yield from parse_mnemonic_to_opcode_map()
239 print(mnemonic_to_xo_map
)
241 while line
is not None:
242 if line
.startswith('|'):
243 yield from handle_table()
246 line
, lineno
= lr
.read()
248 except AssertionError as e
:
249 raise AssertionError(f
"\n{FILENAME}:{lineno}: error: {e}")
252 inp
= FILENAME
.read_text(encoding
="utf-8")
253 output_lines
= list(process(LineReader(inp
.splitlines())))
254 if output_lines
[-1] != "":
255 output_lines
.append("") # ensure file ends with newline
256 NEW_FILENAME
.write_text("\n".join(output_lines
), encoding
="utf-8")
257 FILENAME
.replace(OLD_FILENAME
)
258 NEW_FILENAME
.rename(FILENAME
)