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"
14 # TODO: copy PO=59 table to PO=63 table
18 def __init__(self
, lines
):
19 # type: (list[str]) -> None
20 self
.__next
_line
_index
= 0
24 if self
.__next
_line
_index
== len(self
.__lines
):
25 self
.__next
_line
_index
+= 1
26 return None, self
.__next
_line
_index
27 assert self
.__next
_line
_index
< len(self
.__lines
), \
28 "read past end-of-file"
29 line
= self
.__lines
[self
.__next
_line
_index
].rstrip()
30 self
.__next
_line
_index
+= 1
31 return line
, self
.__next
_line
_index
35 # type: (LineReader) -> Iterable[str]
37 line
, lineno
= lr
.read()
39 mnemonic_to_xo_map
= {} # type: dict[str, str]
41 def parse_table_separator_line(len_line_parts
):
42 # type: (int) -> Iterable[str]
45 assert line
is not None \
46 and line
.startswith('|') \
47 and line
.endswith('|') \
48 and len(line
.split('|')) == len_line_parts \
49 and line
.strip(" |-") == "", "invalid table separator line"
52 line
, lineno
= lr
.read()
54 assert line
is not None and line
!= "", "empty table"
56 def parse_single_mnemonic_to_opcode_map():
57 # type: () -> Iterable[str]
58 nonlocal line
, lineno
, mnemonic_to_xo_map
60 assert line
is not None and line
.startswith(
61 "| XO LSB half →<br> XO MSB half ↓ |"), \
62 "can't find PO=59/63 table"
63 line_parts
= line
.split('|')
64 len_line_parts
= len(line_parts
)
65 assert line_parts
[-1] == "", "invalid PO=59/63 table top row"
66 columns
= [] # type: list[str]
67 columns_range
= range(2, len_line_parts
- 1)
68 for i
in columns_range
:
69 column
= line_parts
[i
].strip()
70 if column
.startswith('`') and column
.endswith('`'):
71 column
= column
[1:-1].strip()
72 assert column
.lstrip(" 01") == "", (f
"invalid table top row "
73 f
"contents -- must be a "
74 f
"binary string: {column}")
75 columns
.append(column
)
78 line
, lineno
= lr
.read()
80 yield from parse_table_separator_line(len_line_parts
)
82 while line
is not None and line
!= "":
83 line_parts
= line
.split('|')
84 assert line
.startswith('|') and line
.endswith('|'), \
85 "invalid table line, must start and end with |"
86 assert len(line_parts
) == len_line_parts
, (
87 f
"invalid table line, wrong part count: found "
88 f
"{len(line_parts)} expected {len_line_parts}")
89 row
= line_parts
[1].strip()
90 if row
.startswith('`') and row
.endswith('`'):
91 row
= row
[1:-1].strip()
92 assert row
.lstrip(" 01") == "", (f
"invalid table line header-cell "
93 f
"contents -- must be a "
94 f
"binary string: {row}")
95 for i
, column
in zip(columns_range
, columns
):
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_.()]+\)|\*\*TBD\*\*|))"
101 r
"(?: *\(draft\))? *", cell
)
102 assert match
is not None, f
"invalid table cell: {cell!r}"
103 xo
, mnemonic
= match
.group("xo", "mnemonic")
104 shrunk_xo
= xo
.replace(" ", "")
105 expected_xo
= (row
+ column
).replace(" ", "")
106 assert shrunk_xo
== expected_xo
, \
107 f
"incorrect XO: found {shrunk_xo} expected {expected_xo}"
110 assert mnemonic
.endswith('(s)'), \
111 f
"PO=59/63 fptrans mnemonic must end in `(s)`: {mnemonic}"
112 assert mnemonic
not in mnemonic_to_xo_map
, (
113 f
"duplicate mnemonic: {mnemonic} -- has opcode "
114 f
"{xo} and {mnemonic_to_xo_map[mnemonic]}")
116 mnemonic_to_xo_map
[mnemonic
] = xo
119 line
, lineno
= lr
.read()
123 line
, lineno
= lr
.read()
125 def parse_mnemonic_to_opcode_map():
126 # type: () -> Iterable[str]
127 nonlocal line
, lineno
, mnemonic_to_xo_map
129 while line
!= PO_59_63_HEADER
:
130 assert line
is not None, "missing PO=59/63 header"
132 line
, lineno
= lr
.read()
135 line
, lineno
= lr
.read()
137 while line
is not None and not line
.startswith(("#", "|")):
139 line
, lineno
= lr
.read()
142 yield from parse_single_mnemonic_to_opcode_map()
145 # type: () -> Iterable[str]
146 nonlocal line
, lineno
148 assert line
is not None \
149 and line
.startswith("|") and line
.endswith('|'), \
150 "invalid table header"
151 line_parts
= line
.split("|")
152 len_line_parts
= len(line_parts
)
153 assert len_line_parts
>= 3, "invalid table header"
156 line
, lineno
= lr
.read()
158 yield from parse_table_separator_line(len_line_parts
)
159 while line
is not None and line
!= "":
160 line_parts
= line
.split('|')
161 assert line
.startswith('|') and line
.endswith('|'), \
162 "invalid table line, must start and end with |"
163 assert len(line_parts
) == len_line_parts
, (
164 f
"invalid table line, wrong part count: found "
165 f
"{len(line_parts)} expected {len_line_parts}")
168 line
, lineno
= lr
.read()
171 # type: () -> Iterable[str]
172 nonlocal line
, lineno
174 assert line
is not None \
175 and line
.startswith("|") and line
.endswith('|'), \
176 "invalid table header"
177 line_parts
= line
.split("|")
178 len_line_parts
= len(line_parts
)
179 assert len_line_parts
>= 3, "invalid table header"
180 mnemonic_index
= None
183 for i
, column
in enumerate(line_parts
):
184 column_width
= len(column
) # should use wcswidth here
185 column
= column
.strip()
186 if column
== MNEMONIC_COLUMN_NAME
:
187 assert mnemonic_index
is None, \
188 f
"two {MNEMONIC_COLUMN_NAME!r} columns in table " \
189 f
"-- can't handle that"
191 if column
== XO_COLUMN_NAME
:
192 assert xo_index
is None, \
193 f
"two {XO_COLUMN_NAME!r} columns in table " \
194 f
"-- can't handle that"
196 xo_column_width
= column_width
197 if mnemonic_index
is None and xo_index
is None:
198 # not an opcode table -- skip it
199 yield from skip_table()
202 assert mnemonic_index
is not None, \
203 f
"missing {MNEMONIC_COLUMN_NAME} column"
204 assert xo_index
is not None, f
"missing {XO_COLUMN_NAME} column"
207 line
, lineno
= lr
.read()
209 yield from parse_table_separator_line(len_line_parts
)
210 while line
is not None and line
!= "":
211 line_parts
= line
.split('|')
212 assert line
.startswith('|') and line
.endswith('|'), \
213 "invalid table line, must start and end with |"
214 assert len(line_parts
) == len_line_parts
, (
215 f
"invalid table line, wrong part count: found "
216 f
"{len(line_parts)} expected {len_line_parts}")
218 mnemonic
= line_parts
[mnemonic_index
].strip()
219 xo
= line_parts
[xo_index
].strip()
220 if mnemonic
not in mnemonic_to_xo_map
:
221 print(f
"mnemonic not assigned an XO value: {mnemonic!r}")
223 xo
= mnemonic_to_xo_map
[mnemonic
]
224 xo_width
= len(xo
) # should use wcswidth here
225 if xo_width
< xo_column_width
:
226 # should use wc_ljust here
227 xo
= (" " + xo
).ljust(xo_column_width
)
228 line_parts
[xo_index
] = xo
230 expected_xo
= mnemonic_to_xo_map
[mnemonic
].replace(" ", "")
231 assert xo
.replace(" ", "") == expected_xo
, (
232 f
"mismatch in {XO_COLUMN_NAME} column: expected "
233 f
"{mnemonic_to_xo_map[mnemonic]} found {xo!r}")
235 yield '|'.join(line_parts
)
236 line
, lineno
= lr
.read()
239 yield from parse_mnemonic_to_opcode_map()
241 print(mnemonic_to_xo_map
)
243 while line
is not None:
244 if line
.startswith('|'):
245 yield from handle_table()
248 line
, lineno
= lr
.read()
250 except AssertionError as e
:
251 raise AssertionError(f
"\n{FILENAME}:{lineno}: error: {e}")
254 inp
= FILENAME
.read_text(encoding
="utf-8")
255 output_lines
= list(process(LineReader(inp
.splitlines())))
256 if output_lines
[-1] != "":
257 output_lines
.append("") # ensure file ends with newline
258 NEW_FILENAME
.write_text("\n".join(output_lines
), encoding
="utf-8")
259 FILENAME
.replace(OLD_FILENAME
)
260 NEW_FILENAME
.rename(FILENAME
)