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/.") == "", (
91 f
"invalid table line header-cell contents -- must be a "
92 f
"binary string: {row}")
93 for i
, column
in zip(columns_range
, columns
):
95 if cell
.strip() == "":
98 r
" *<small> *` *(?P<xo>[01./][01 ./]*[01./]) *` *</small>"
99 r
" *<br/?> *(?P<mnemonic>[a-zA-Z0-9_.][a-zA-Z0-9_.()]*)?"
100 r
"(?(mnemonic)|(?:\([a-zA-Z0-9_.()]+\)|"
101 r
"\*\*TBD\*\*| |))"
102 r
"(?: *\(draft\))? *", cell
)
103 assert match
is not None, f
"invalid table cell: {cell!r}"
104 xo
, mnemonic
= match
.group("xo", "mnemonic")
105 shrunk_xo
= xo
.replace(" ", "").replace('.', '/')
106 expected_xo
= (row
+ column
).replace(" ", "").replace('.', '/')
107 assert shrunk_xo
== expected_xo
, \
108 f
"incorrect XO: found {shrunk_xo} expected {expected_xo}"
111 assert mnemonic
.endswith('(s)'), \
112 f
"PO=59/63 fptrans mnemonic must end in `(s)`: {mnemonic}"
113 assert mnemonic
not in mnemonic_to_xo_map
, (
114 f
"duplicate mnemonic: {mnemonic} -- has opcode "
115 f
"{xo} and {mnemonic_to_xo_map[mnemonic]}")
117 mnemonic_to_xo_map
[mnemonic
] = xo
120 line
, lineno
= lr
.read()
124 line
, lineno
= lr
.read()
126 def parse_mnemonic_to_opcode_map():
127 # type: () -> Iterable[str]
128 nonlocal line
, lineno
, mnemonic_to_xo_map
130 while line
!= PO_59_63_HEADER
:
131 assert line
is not None, "missing PO=59/63 header"
133 line
, lineno
= lr
.read()
136 line
, lineno
= lr
.read()
138 while line
is not None and not line
.startswith(("#", "|")):
140 line
, lineno
= lr
.read()
143 yield from parse_single_mnemonic_to_opcode_map()
146 # type: () -> Iterable[str]
147 nonlocal line
, lineno
149 assert line
is not None \
150 and line
.startswith("|") and line
.endswith('|'), \
151 "invalid table header"
152 line_parts
= line
.split("|")
153 len_line_parts
= len(line_parts
)
154 assert len_line_parts
>= 3, "invalid table header"
157 line
, lineno
= lr
.read()
159 yield from parse_table_separator_line(len_line_parts
)
160 while line
is not None and line
!= "":
161 line_parts
= line
.split('|')
162 assert line
.startswith('|') and line
.endswith('|'), \
163 "invalid table line, must start and end with |"
164 assert len(line_parts
) == len_line_parts
, (
165 f
"invalid table line, wrong part count: found "
166 f
"{len(line_parts)} expected {len_line_parts}")
169 line
, lineno
= lr
.read()
172 # type: () -> Iterable[str]
173 nonlocal line
, lineno
175 assert line
is not None \
176 and line
.startswith("|") and line
.endswith('|'), \
177 "invalid table header"
178 line_parts
= line
.split("|")
179 len_line_parts
= len(line_parts
)
180 assert len_line_parts
>= 3, "invalid table header"
181 mnemonic_index
= None
184 for i
, column
in enumerate(line_parts
):
185 column_width
= len(column
) # should use wcswidth here
186 column
= column
.strip()
187 if column
== MNEMONIC_COLUMN_NAME
:
188 assert mnemonic_index
is None, \
189 f
"two {MNEMONIC_COLUMN_NAME!r} columns in table " \
190 f
"-- can't handle that"
192 if column
== XO_COLUMN_NAME
:
193 assert xo_index
is None, \
194 f
"two {XO_COLUMN_NAME!r} columns in table " \
195 f
"-- can't handle that"
197 xo_column_width
= column_width
198 if mnemonic_index
is None and xo_index
is None:
199 # not an opcode table -- skip it
200 yield from skip_table()
203 assert mnemonic_index
is not None, \
204 f
"missing {MNEMONIC_COLUMN_NAME} column"
205 assert xo_index
is not None, f
"missing {XO_COLUMN_NAME} column"
208 line
, lineno
= lr
.read()
210 yield from parse_table_separator_line(len_line_parts
)
211 while line
is not None and line
!= "":
212 line_parts
= line
.split('|')
213 assert line
.startswith('|') and line
.endswith('|'), \
214 "invalid table line, must start and end with |"
215 assert len(line_parts
) == len_line_parts
, (
216 f
"invalid table line, wrong part count: found "
217 f
"{len(line_parts)} expected {len_line_parts}")
219 mnemonic
= line_parts
[mnemonic_index
].strip()
220 xo
= line_parts
[xo_index
].strip()
221 if mnemonic
not in mnemonic_to_xo_map
:
222 print(f
"mnemonic not assigned an XO value: {mnemonic!r}")
224 xo
= mnemonic_to_xo_map
[mnemonic
]
225 xo_width
= len(xo
) # should use wcswidth here
226 if xo_width
< xo_column_width
:
227 # should use wc_ljust here
228 xo
= (" " + xo
).ljust(xo_column_width
)
229 line_parts
[xo_index
] = xo
231 expected_xo
= mnemonic_to_xo_map
[mnemonic
].replace(" ", "")
232 assert xo
.replace(" ", "") == expected_xo
, (
233 f
"mismatch in {XO_COLUMN_NAME} column: expected "
234 f
"{mnemonic_to_xo_map[mnemonic]} found {xo!r}")
236 yield '|'.join(line_parts
)
237 line
, lineno
= lr
.read()
240 yield from parse_mnemonic_to_opcode_map()
242 print(mnemonic_to_xo_map
)
244 while line
is not None:
245 if line
.startswith('|'):
246 yield from handle_table()
249 line
, lineno
= lr
.read()
251 except AssertionError as e
:
252 raise AssertionError(f
"\n{FILENAME}:{lineno}: error: {e}")
255 inp
= FILENAME
.read_text(encoding
="utf-8")
256 output_lines
= list(process(LineReader(inp
.splitlines())))
257 if output_lines
[-1] != "":
258 output_lines
.append("") # ensure file ends with newline
259 NEW_FILENAME
.write_text("\n".join(output_lines
), encoding
="utf-8")
260 FILENAME
.replace(OLD_FILENAME
)
261 NEW_FILENAME
.rename(FILENAME
)