diff --git a/Cargo.lock b/Cargo.lock index c45930f..f33bbae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" version = "0.2.18" @@ -81,12 +96,36 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base16ct" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6" + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bindgen" +version = "0.71.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + [[package]] name = "bitflags" version = "2.6.0" @@ -130,20 +169,47 @@ dependencies = [ ] [[package]] -name = "cc" -version = "1.1.28" +name = "bytes" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + +[[package]] +name = "cc" +version = "1.2.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" dependencies = [ + "find-msvc-tools", "shlex", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "4.5.20" @@ -209,9 +275,15 @@ checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" name = "cpu" version = "0.1.0" dependencies = [ + "base16ct 1.0.0", "fayalite", + "hex-literal", + "parse_powerisa_pdf", + "roxmltree", "serde", + "sha2", "simple-mermaid", + "ureq", ] [[package]] @@ -223,6 +295,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -342,7 +423,7 @@ name = "fayalite-proc-macros-impl" version = "0.3.0" source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#c97b44d9d646a4aa64fcc046538fc2354bb708ee" dependencies = [ - "base16ct", + "base16ct 0.2.0", "num-bigint", "prettyplease", "proc-macro2", @@ -367,12 +448,28 @@ dependencies = [ "thiserror", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" + [[package]] name = "fixedbitset" version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" +[[package]] +name = "flate2" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "foldhash" version = "0.1.5" @@ -395,6 +492,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "getrandom" version = "0.3.4" @@ -408,10 +516,10 @@ dependencies = [ ] [[package]] -name = "hashbrown" -version = "0.14.5" +name = "glob" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "hashbrown" @@ -424,12 +532,24 @@ dependencies = [ "foldhash", ] +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hex-literal" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e712f64ec3850b98572bffac52e2c6f282b29fe6c5fa6d42334b30be438d95c1" + [[package]] name = "home" version = "0.5.9" @@ -439,6 +559,22 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + [[package]] name = "indenter" version = "0.3.3" @@ -447,13 +583,14 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "2.5.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.16.1", "serde", + "serde_core", ] [[package]] @@ -462,6 +599,15 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -476,7 +622,7 @@ checksum = "58715c67c327da7f1558708348d68c207fd54900c4ae0529e29305d04d795b8c" dependencies = [ "cfg-if", "derive_destructure2", - "getrandom", + "getrandom 0.3.4", "libc", "scopeguard", "windows-sys 0.61.2", @@ -488,18 +634,79 @@ version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + [[package]] name = "linux-raw-sys" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mupdf-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e9a0d4e844ab50315d43312f3d62f72c77205b07c8ee21cbd4b52bdc2a9910" +dependencies = [ + "bindgen", + "cc", + "pkg-config", + "regex", + "zerocopy", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -545,6 +752,23 @@ dependencies = [ "serde", ] +[[package]] +name = "parse_powerisa_pdf" +version = "0.1.0" +source = "git+https://git.libre-chip.org/libre-chip/parse_powerisa_pdf.git?branch=master#38a1fb328bd44f26389c28fbf66716154f4113dc" +dependencies = [ + "indexmap", + "libm", + "mupdf-sys", + "quick-xml", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + [[package]] name = "petgraph" version = "0.8.3" @@ -557,6 +781,12 @@ dependencies = [ "serde", ] +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + [[package]] name = "prettyplease" version = "0.2.22" @@ -576,6 +806,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quick-xml" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.37" @@ -616,6 +855,64 @@ dependencies = [ "serde", ] +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "roxmltree" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1964b10c76125c36f8afe190065a4bf9a87bf324842c05701330bba9f1cacbb" +dependencies = [ + "memchr", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustix" version = "0.38.37" @@ -629,6 +926,41 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.23.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "ryu" version = "1.0.18" @@ -643,18 +975,28 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -676,9 +1018,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -691,6 +1033,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + [[package]] name = "simple-mermaid" version = "0.2.0" @@ -703,6 +1051,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.96" @@ -765,6 +1119,47 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d39cb1dbab692d82a977c0392ffac19e188bd9186a9f32806f0aaa859d75585a" +dependencies = [ + "base64", + "flate2", + "log", + "percent-encoding", + "rustls", + "rustls-pki-types", + "ureq-proto", + "utf-8", + "webpki-roots", +] + +[[package]] +name = "ureq-proto" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f" +dependencies = [ + "base64", + "http", + "httparse", + "log", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8parse" version = "0.2.2" @@ -783,6 +1178,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + [[package]] name = "wasip2" version = "1.0.1+wasi-0.2.4" @@ -792,6 +1193,15 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "webpki-roots" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "which" version = "6.0.3" @@ -921,3 +1331,29 @@ checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" dependencies = [ "tap", ] + +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" diff --git a/Cargo.toml b/Cargo.toml index 00f2e67..b7dc4d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,9 +14,15 @@ categories = [] rust-version = "1.89.0" [workspace.dependencies] +base16ct = "1.0.0" fayalite = { git = "https://git.libre-chip.org/libre-chip/fayalite.git", version = "0.3.0", branch = "master" } +hex-literal = "1.1.0" +parse_powerisa_pdf = { git = "https://git.libre-chip.org/libre-chip/parse_powerisa_pdf.git", version = "0.1.0", branch = "master" } +roxmltree = "0.21.1" serde = { version = "1.0.202", features = ["derive"] } +sha2 = "0.10.9" simple-mermaid = "0.2.0" +ureq = "3.1.4" [profile.dev] opt-level = 1 diff --git a/crates/cpu/Cargo.toml b/crates/cpu/Cargo.toml index 2f5f84c..0450556 100644 --- a/crates/cpu/Cargo.toml +++ b/crates/cpu/Cargo.toml @@ -16,5 +16,18 @@ version.workspace = true [dependencies] fayalite.workspace = true +roxmltree.workspace = true serde.workspace = true simple-mermaid.workspace = true + +[build-dependencies] +base16ct.workspace = true +hex-literal.workspace = true +parse_powerisa_pdf.workspace = true +sha2.workspace = true +ureq.workspace = true + +[dev-dependencies] +base16ct.workspace = true +hex-literal.workspace = true +sha2.workspace = true diff --git a/crates/cpu/build.rs b/crates/cpu/build.rs new file mode 100644 index 0000000..f87a72a --- /dev/null +++ b/crates/cpu/build.rs @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use parse_powerisa_pdf::parse_powerisa_pdf_and_generate_xml; +use sha2::{Digest, Sha256}; +use std::{ + error::Error, + io::{ErrorKind, Read}, + path::Path, +}; + +const EXPECTED_FILE_SIZE: u64 = 6425593; +const EXPECTED_FILE_HASH: &[u8; 32] = + &hex_literal::hex!("56372d23ece7e9e2c1b381a639443982a3e16e38109df1c141d655b779b61fdb"); + +fn verify_powerisa_file(file: impl std::io::Read) -> Result, Box> { + let mut buf = Vec::with_capacity(usize::try_from(EXPECTED_FILE_SIZE)? + 1); + file.take(EXPECTED_FILE_SIZE + 1).read_to_end(&mut buf)?; + if buf.len() > EXPECTED_FILE_SIZE as usize { + Err(format!("file is bigger than the expected length {EXPECTED_FILE_SIZE}").into()) + } else if buf.len() < EXPECTED_FILE_SIZE as usize { + Err(format!( + "file is smaller than the expected length {EXPECTED_FILE_SIZE}: actual length {}", + buf.len() + ) + .into()) + } else { + let hash = Sha256::digest(&buf); + let hash = &*hash; + if hash != EXPECTED_FILE_HASH { + Err(format!( + "file's SHA256 hash doesn't match the expected hash: expected: {:x} got: {:x}", + base16ct::HexDisplay(EXPECTED_FILE_HASH), + base16ct::HexDisplay(hash) + ) + .into()) + } else { + Ok(buf) + } + } +} + +fn is_powerisa_pdf(path: impl AsRef) -> Result> { + let path = path.as_ref(); + let metadata = match std::fs::metadata(path) { + Err(e) if e.kind() == ErrorKind::NotFound => return Ok(false), + v => v?, + }; + if !metadata.is_file() { + return Ok(false); + } + let file = std::fs::File::open(path)?; + verify_powerisa_file(file)?; + Ok(true) +} + +fn out_dir() -> String { + std::env::var("OUT_DIR").expect("OUT_DIR env var is not set or invalid") +} + +fn get_powerisa_pdf_name_and_dir<'a>( + out_dir: &'a str, +) -> Result<(&'static str, &'a Path), Box> { + const FILE_NAME: &str = "OPF_PowerISA_v3.1C.pdf"; + let out_dir = Path::new(out_dir); + if is_powerisa_pdf(FILE_NAME)? { + println!("cargo::rerun-if-changed={FILE_NAME}"); + return Ok((FILE_NAME, Path::new("."))); + } + let full_path = out_dir.join(FILE_NAME); + let full_path = full_path + .into_os_string() + .into_string() + .expect("should be valid UTF-8"); + if is_powerisa_pdf(&full_path)? { + println!("cargo::rerun-if-changed={full_path}"); + return Ok((FILE_NAME, out_dir)); + } + const URL: &str = "https://libre-chip.org/OPF_PowerISA_v3.1C.pdf"; + println!("cargo::warning={FILE_NAME} not found locally, downloading from {URL}"); + let buf = verify_powerisa_file(ureq::get(URL).call()?.into_body().into_reader())?; + std::fs::write(&full_path, buf)?; + println!("cargo::rerun-if-changed={full_path}"); + Ok((FILE_NAME, out_dir)) +} + +fn main() -> Result<(), Box> { + let out_dir = out_dir(); + let (pdf_name, pdf_dir) = get_powerisa_pdf_name_and_dir(&out_dir)?; + let old_dir = std::env::current_dir()?; + std::env::set_current_dir(pdf_dir)?; + let xml = parse_powerisa_pdf_and_generate_xml(pdf_name, None, false)?; + std::env::set_current_dir(old_dir)?; + std::fs::write(Path::new(&out_dir).join("powerisa-instructions.xml"), xml)?; + Ok(()) +} diff --git a/crates/cpu/src/lib.rs b/crates/cpu/src/lib.rs index a00b668..0a8a938 100644 --- a/crates/cpu/src/lib.rs +++ b/crates/cpu/src/lib.rs @@ -3,6 +3,7 @@ pub mod config; pub mod instruction; pub mod next_pc; +pub mod powerisa; pub mod reg_alloc; pub mod register; pub mod unit; diff --git a/crates/cpu/src/powerisa.rs b/crates/cpu/src/powerisa.rs new file mode 100644 index 0000000..4839a50 --- /dev/null +++ b/crates/cpu/src/powerisa.rs @@ -0,0 +1,2719 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use roxmltree::{Attribute, Document, Node, NodeType}; +use std::{backtrace::Backtrace, fmt, sync::OnceLock}; + +const POWERISA_INSTRUCTIONS_XML: &str = + include_str!(concat!(env!("OUT_DIR"), "/powerisa-instructions.xml")); + +enum Error<'a> { + XmlError(roxmltree::Error), + Unexpected { + backtrace: Backtrace, + node: Node<'a, 'static>, + }, + BodyTooShort { + backtrace: Backtrace, + node: Node<'a, 'static>, + }, + ExpectedTag { + backtrace: Backtrace, + expected_tag_name: &'a str, + got_element: Node<'a, 'static>, + }, + MissingAttribute { + backtrace: Backtrace, + attribute_name: &'a str, + element: Node<'a, 'static>, + }, + ExpectedAttribute { + backtrace: Backtrace, + expected_attribute_name: &'a str, + attribute: Attribute<'a, 'static>, + element: Node<'a, 'static>, + }, + UnexpectedAttribute { + backtrace: Backtrace, + attribute: Attribute<'a, 'static>, + element: Node<'a, 'static>, + }, + IsSubsetMustBeFalse { + backtrace: Backtrace, + is_subset: Attribute<'a, 'static>, + }, + UnknownValue { + name: &'static str, + value_formatted: String, + }, +} + +impl From for Error<'_> { + fn from(v: roxmltree::Error) -> Self { + Self::XmlError(v) + } +} + +impl fmt::Display for Error<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::XmlError(v) => v.fmt(f), + Error::Unexpected { backtrace, node } => { + writeln!(f, "unexpected node: {node:?}")?; + backtrace.fmt(f) + } + Error::BodyTooShort { backtrace, node } => { + writeln!(f, "node's body is too short: {node:?}")?; + backtrace.fmt(f) + } + Error::ExpectedTag { + backtrace, + expected_tag_name, + got_element, + } => { + writeln!( + f, + "expected tag {expected_tag_name:?} but got: {got_element:?}" + )?; + backtrace.fmt(f) + } + Error::MissingAttribute { + backtrace, + attribute_name, + element, + } => { + writeln!(f, "missing attribute {attribute_name:?}: {element:?}")?; + backtrace.fmt(f) + } + Error::ExpectedAttribute { + backtrace, + expected_attribute_name, + attribute, + element, + } => { + writeln!( + f, + "expected attribute with name {expected_attribute_name:?}: {attribute:?}\n\ + in element: {element:?}" + )?; + backtrace.fmt(f) + } + Error::UnexpectedAttribute { + backtrace, + attribute, + element, + } => { + writeln!( + f, + "unexpected attribute: {attribute:?}\n\ + in element: {element:?}" + )?; + backtrace.fmt(f) + } + Error::IsSubsetMustBeFalse { + backtrace, + is_subset, + } => { + writeln!(f, "`is-subset` attribute must be `False`: {is_subset:?}")?; + backtrace.fmt(f) + } + Error::UnknownValue { + name, + value_formatted: value, + } => { + write!(f, "unknown value for {name}: {value}") + } + } + } +} + +pub struct Instructions { + instructions: Box<[Instruction]>, +} + +impl fmt::Debug for Instructions { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Instructions ")?; + self.instructions.fmt(f) + } +} + +#[derive(Clone)] +struct Parser<'a> { + parent: Node<'a, 'static>, + cur_node: Option>, +} + +impl<'a> Parser<'a> { + fn skip_comments(&mut self) { + while let Some(cur_node) = self.cur_node { + match cur_node.node_type() { + NodeType::Comment => {} + NodeType::Text | NodeType::Root | NodeType::Element | NodeType::PI => break, + } + self.cur_node = cur_node.next_sibling(); + } + } + fn skip_ws_and_comments(&mut self) { + while let Some(cur_node) = self.cur_node { + match cur_node.node_type() { + NodeType::Comment => {} + NodeType::Text => { + if cur_node + .text() + .is_some_and(|s| !s.trim_ascii_start().is_empty()) + { + break; + } + } + NodeType::Root | NodeType::Element | NodeType::PI => break, + } + self.cur_node = cur_node.next_sibling(); + } + } + fn peek(&self) -> bool { + T::peek(self) + } + fn peek_any_element(&self) -> Option> { + let mut parser = self.clone(); + parser.skip_ws_and_comments(); + let element = parser.cur_node?; + let NodeType::Element = element.node_type() else { + return None; + }; + Some(element) + } + fn peek_element(&self, tag_name: &'a str) -> Option> { + self.peek_any_element() + .filter(|element| element.has_tag_name(tag_name)) + } + #[track_caller] + fn parse(&mut self) -> Result> { + T::parse(self) + } + #[track_caller] + fn parse_element( + &mut self, + tag_name: &'a str, + attr_names: [&'a str; N], + f: impl FnOnce( + Node<'a, 'static>, + [Attribute<'a, 'static>; N], + &mut Parser<'a>, + ) -> Result>, + ) -> Result> { + self.parse_any_element(|element, parser| { + if !element.has_tag_name(tag_name) { + return Err(Error::ExpectedTag { + backtrace: Backtrace::capture(), + expected_tag_name: tag_name, + got_element: element, + }); + } + let mut attrs = [const { None }; N]; + let mut attrs_iter = element.attributes(); + for i in 0..N { + let Some(attr) = attrs_iter.next() else { + return Err(Error::MissingAttribute { + backtrace: Backtrace::capture(), + attribute_name: attr_names[i], + element, + }); + }; + if (attr.namespace(), attr.name()) != (None, attr_names[i]) { + return Err(Error::ExpectedAttribute { + backtrace: Backtrace::capture(), + expected_attribute_name: attr_names[i], + attribute: attr, + element, + }); + } + attrs[i] = Some(attr); + } + if let Some(attribute) = attrs_iter.next() { + return Err(Error::UnexpectedAttribute { + backtrace: Backtrace::capture(), + attribute, + element, + }); + } + let attrs = attrs.map(|attr| attr.expect("filled in loop above")); + f(element, attrs, parser) + }) + } + fn parse_any_element( + &mut self, + f: impl FnOnce(Node<'a, 'static>, &mut Parser<'a>) -> Result>, + ) -> Result> { + self.skip_ws_and_comments(); + let Some(element) = self.cur_node else { + return Err(Error::BodyTooShort { + backtrace: Backtrace::capture(), + node: self.parent, + }); + }; + let NodeType::Element = element.node_type() else { + return Err(Error::Unexpected { + backtrace: Backtrace::capture(), + node: element, + }); + }; + let mut parser = Parser { + parent: element, + cur_node: element.first_child(), + }; + let retval = f(element, &mut parser)?; + parser.skip_ws_and_comments(); + if let Some(node) = parser.cur_node { + Err(Error::Unexpected { + backtrace: Backtrace::capture(), + node, + }) + } else { + self.cur_node = element.next_sibling(); + Ok(retval) + } + } + fn parse_document(document: &'a Document<'static>) -> Result> { + let parent = document.root(); + let mut parser = Parser { + parent, + cur_node: parent.first_child(), + }; + let retval = parser.parse()?; + parser.skip_ws_and_comments(); + if let Some(node) = parser.cur_node { + Err(Error::Unexpected { + backtrace: Backtrace::capture(), + node, + }) + } else { + Ok(retval) + } + } +} + +trait Parse: Sized { + fn peek<'a>(parser: &Parser<'a>) -> bool; + fn parse<'a>(parser: &mut Parser<'a>) -> Result>; +} + +impl Parse for Box { + fn peek<'a>(_parser: &Parser<'a>) -> bool { + true + } + fn parse<'a>(parser: &mut Parser<'a>) -> Result> { + Self::parse_with_options(parser, false) + } +} + +impl ParseTextLine for Box { + fn parse_with_options<'a>( + parser: &mut Parser<'a>, + remove_leading_nl: bool, + ) -> Result> { + let mut retval = String::new(); + loop { + parser.skip_comments(); + let Some(node) = parser.cur_node else { + break; + }; + if !node.is_text() { + break; + } + retval.extend(node.text()); + parser.cur_node = node.next_sibling(); + } + if remove_leading_nl { + if retval.starts_with("\r\n") { + retval.replace_range(0..2, ""); + } else if retval.starts_with(&['\r', '\n']) { + retval.remove(0); + } + } + Ok(retval.into_boxed_str()) + } +} + +impl Parse for Box<[T]> { + fn peek<'a>(_parser: &Parser<'a>) -> bool { + true + } + fn parse<'a>(parser: &mut Parser<'a>) -> Result> { + let mut retval = Vec::new(); + while parser.peek::() { + retval.push(parser.parse()?); + } + Ok(retval.into_boxed_slice()) + } +} + +impl Parse for Option { + fn peek<'a>(_parser: &Parser<'a>) -> bool { + true + } + fn parse<'a>(parser: &mut Parser<'a>) -> Result> { + parser.peek::().then(|| parser.parse()).transpose() + } +} + +trait ParseElementWithAttributes: Sized { + type Attributes<'a>: 'a; + + fn parse_element_with_attributes<'a, T: ParseElement>( + parser: &mut Parser<'a>, + ) -> Result>; +} + +impl ParseElementWithAttributes for [&'static str; N] { + type Attributes<'a> = [Attribute<'a, 'static>; N]; + + fn parse_element_with_attributes<'a, T: ParseElement>( + parser: &mut Parser<'a>, + ) -> Result> { + parser.parse_element(T::TAG_NAME, T::ATTRIBUTE_NAMES, T::parse_element) + } +} + +impl ParseElementWithAttributes for () { + type Attributes<'a> = (); + + fn parse_element_with_attributes<'a, T: ParseElement>( + parser: &mut Parser<'a>, + ) -> Result> { + parser.parse_element(T::TAG_NAME, [], |element, [], parser| { + T::parse_element(element, (), parser) + }) + } +} + +trait ParseElement: Parse { + type AttributeNames: ParseElementWithAttributes; + const TAG_NAME: &'static str; + const ATTRIBUTE_NAMES: Self::AttributeNames; + fn parse_element<'a>( + element: Node<'a, 'static>, + attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result>; +} + +impl Parse for T { + fn peek<'a>(parser: &Parser<'a>) -> bool { + parser.peek_element(Self::TAG_NAME).is_some() + } + + fn parse<'a>(parser: &mut Parser<'a>) -> Result> { + T::AttributeNames::parse_element_with_attributes::(parser) + } +} + +trait ParseElementEnum: + ParseElement + Copy + Eq + std::hash::Hash + fmt::Debug + 'static + Send + Sync +{ + const NAME_FOR_ERRORS: &'static str; + const TAG_NAME: &'static str; + type ParseValue: Parse + fmt::Debug; + type Value: Copy; + fn values() -> &'static [Self]; + fn value_eq(l: &Self::ParseValue, r: Self::Value) -> bool; + fn value(self) -> Self::Value; +} + +impl ParseElement for T { + type AttributeNames = (); + const TAG_NAME: &'static str = ::TAG_NAME; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + let parse_value: T::ParseValue = parser.parse()?; + for &value in T::values() { + if T::value_eq(&parse_value, value.value()) { + return Ok(value); + } + } + Err(Error::UnknownValue { + name: T::NAME_FOR_ERRORS, + value_formatted: format!("{parse_value:?}"), + }) + } +} + +impl Instructions { + pub fn instructions(&self) -> &[Instruction] { + &self.instructions + } + pub fn get() -> &'static Self { + static INSTRUCTIONS: OnceLock = OnceLock::new(); + INSTRUCTIONS.get_or_init(|| { + let handle_error = + |e: Error<'_>| unreachable!("powerisa-instructions.xml failed to parse: {e}"); + match Document::parse(POWERISA_INSTRUCTIONS_XML) { + Ok(document) => match Parser::parse_document(&document) { + Ok(v) => v, + Err(e) => handle_error(e), + }, + Err(e) => handle_error(e.into()), + } + }) + } +} + +impl ParseElement for Instructions { + type AttributeNames = [&'static str; 1]; + const TAG_NAME: &'static str = "instructions"; + const ATTRIBUTE_NAMES: Self::AttributeNames = ["is-subset"]; + + fn parse_element<'a>( + _element: Node<'a, 'static>, + attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + let [is_subset] = attributes; + if is_subset.value() != "False" { + return Err(Error::IsSubsetMustBeFalse { + backtrace: Backtrace::capture(), + is_subset, + }); + } + Ok(Self { + instructions: parser.parse()?, + }) + } +} + +pub struct Instruction { + header: Box<[InstructionHeader]>, + code: Option, + description: Option, + special_registers_altered: Option, +} + +struct FlattenedOption<'a, T>(&'a Option); + +impl fmt::Debug for FlattenedOption<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + Some(v) => v.fmt(f), + None => f.write_str("None"), + } + } +} + +impl fmt::Debug for Instruction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + header, + code, + description, + special_registers_altered, + } = self; + f.debug_struct("Instruction") + .field("header", header) + .field("code", &FlattenedOption(code)) + .field("description", &FlattenedOption(description)) + .field( + "special_registers_altered", + &FlattenedOption(special_registers_altered), + ) + .finish() + } +} + +impl Instruction { + pub fn header(&self) -> &[InstructionHeader] { + &self.header + } + pub fn code(&self) -> Option<&InstructionCode> { + self.code.as_ref() + } + pub fn description(&self) -> Option<&InstructionDescription> { + self.description.as_ref() + } + pub fn special_registers_altered(&self) -> Option<&InsnSpRegsAltered> { + self.special_registers_altered.as_ref() + } +} + +impl ParseElement for Instruction { + type AttributeNames = (); + const TAG_NAME: &'static str = "instruction"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + header: parser.parse()?, + code: parser.parse()?, + description: parser.parse()?, + special_registers_altered: parser.parse()?, + }) + } +} + +#[derive(Debug)] +pub struct InstructionHeader { + title: InstructionTitle, + mnemonics: InstructionMnemonics, + bit_fields: InstructionBitFields, +} + +impl InstructionHeader { + pub fn title(&self) -> &InstructionTitle { + &self.title + } + pub fn mnemonics(&self) -> &InstructionMnemonics { + &self.mnemonics + } + pub fn bit_fields(&self) -> &InstructionBitFields { + &self.bit_fields + } +} + +impl ParseElement for InstructionHeader { + type AttributeNames = (); + const TAG_NAME: &'static str = "header"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + title: parser.parse()?, + mnemonics: parser.parse()?, + bit_fields: parser.parse()?, + }) + } +} + +pub struct InstructionTitle { + text_lines: TextLines>, +} + +impl fmt::Debug for InstructionTitle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { text_lines } = self; + text_lines.debug_fmt("InstructionTitle", f) + } +} + +impl InstructionTitle { + pub fn text_lines(&self) -> &TextLines> { + &self.text_lines + } + pub fn lines(&self) -> &[Box] { + self.text_lines.lines() + } +} + +impl ParseElement for InstructionTitle { + type AttributeNames = (); + const TAG_NAME: &'static str = "title"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + text_lines: parser.parse()?, + }) + } +} + +pub struct InstructionMnemonics { + text_lines: TextLines>, +} + +impl fmt::Debug for InstructionMnemonics { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { text_lines } = self; + text_lines.debug_fmt("InstructionMnemonics", f) + } +} + +impl InstructionMnemonics { + pub fn text_lines(&self) -> &TextLines> { + &self.text_lines + } + pub fn lines(&self) -> &[Box] { + self.text_lines.lines() + } +} + +impl ParseElement for InstructionMnemonics { + type AttributeNames = (); + const TAG_NAME: &'static str = "mnemonics"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + text_lines: parser.parse()?, + }) + } +} + +#[derive(Debug)] +pub struct InstructionBitFieldName { + text_line: TextLine, +} + +impl InstructionBitFieldName { + pub fn text_line(&self) -> &TextLine { + &self.text_line + } +} + +impl ParseElement for InstructionBitFieldName { + type AttributeNames = (); + const TAG_NAME: &'static str = "name"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + text_line: parser.parse()?, + }) + } +} + +#[derive(Debug)] +pub struct InstructionBitFieldBitNumber { + text: Box, +} + +impl InstructionBitFieldBitNumber { + pub fn text(&self) -> &str { + &self.text + } +} + +impl ParseElement for InstructionBitFieldBitNumber { + type AttributeNames = (); + const TAG_NAME: &'static str = "bit-number"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + text: parser.parse()?, + }) + } +} + +pub struct InstructionBitField { + name: InstructionBitFieldName, + bit_number: InstructionBitFieldBitNumber, +} + +impl fmt::Debug for InstructionBitField { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + name: InstructionBitFieldName { text_line }, + bit_number: InstructionBitFieldBitNumber { text }, + } = self; + f.debug_struct("InstructionBitField") + .field("name", text_line) + .field("bit_number", text) + .finish() + } +} + +impl InstructionBitField { + pub fn name(&self) -> &InstructionBitFieldName { + &self.name + } + pub fn bit_number(&self) -> &InstructionBitFieldBitNumber { + &self.bit_number + } +} + +impl ParseElement for InstructionBitField { + type AttributeNames = (); + const TAG_NAME: &'static str = "field"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + name: parser.parse()?, + bit_number: parser.parse()?, + }) + } +} + +#[derive(Debug)] +pub struct InstructionBitFieldsPrefixText { + text_line: TextLine, +} + +impl InstructionBitFieldsPrefixText { + pub fn text_line(&self) -> &TextLine { + &self.text_line + } +} + +impl ParseElement for InstructionBitFieldsPrefixText { + type AttributeNames = (); + const TAG_NAME: &'static str = "prefix-text"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + text_line: parser.parse()?, + }) + } +} + +#[derive(Debug)] +pub struct InstructionBitFieldsSuffixText { + text_line: TextLine, +} + +impl InstructionBitFieldsSuffixText { + pub fn text_line(&self) -> &TextLine { + &self.text_line + } +} + +impl ParseElement for InstructionBitFieldsSuffixText { + type AttributeNames = (); + const TAG_NAME: &'static str = "suffix-text"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + text_line: parser.parse()?, + }) + } +} + +pub struct InstructionBitFieldsPrefix { + prefix_text: InstructionBitFieldsPrefixText, + fields_inner: InstructionBitFieldsInner, + suffix_text: InstructionBitFieldsSuffixText, +} + +impl InstructionBitFieldsPrefix { + pub fn prefix_text(&self) -> &InstructionBitFieldsPrefixText { + &self.prefix_text + } + pub fn fields_inner(&self) -> &InstructionBitFieldsInner { + &self.fields_inner + } + pub fn fields(&self) -> &[InstructionBitField] { + self.fields_inner.fields() + } + pub fn suffix_text(&self) -> &InstructionBitFieldsSuffixText { + &self.suffix_text + } +} + +impl fmt::Debug for InstructionBitFieldsPrefix { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + prefix_text: + InstructionBitFieldsPrefixText { + text_line: prefix_text, + }, + fields_inner: InstructionBitFieldsInner { fields }, + suffix_text: + InstructionBitFieldsSuffixText { + text_line: suffix_text, + }, + } = self; + f.debug_struct("InstructionBitFieldsPrefix") + .field("prefix_text", prefix_text) + .field("fields", fields) + .field("suffix_text", suffix_text) + .finish() + } +} + +impl ParseElement for InstructionBitFieldsPrefix { + type AttributeNames = (); + const TAG_NAME: &'static str = "prefix"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + prefix_text: parser.parse()?, + fields_inner: parser.parse()?, + suffix_text: parser.parse()?, + }) + } +} + +#[derive(Debug)] +pub struct InstructionBitFieldsInner { + fields: Box<[InstructionBitField]>, +} + +impl InstructionBitFieldsInner { + pub fn fields(&self) -> &[InstructionBitField] { + &self.fields + } +} + +impl ParseElement for InstructionBitFieldsInner { + type AttributeNames = (); + const TAG_NAME: &'static str = "fields"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + fields: parser.parse()?, + }) + } +} + +pub struct InstructionBitFields { + prefix: Option, + fields_inner: InstructionBitFieldsInner, +} + +impl fmt::Debug for InstructionBitFields { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + prefix, + fields_inner: InstructionBitFieldsInner { fields }, + } = self; + f.debug_struct("InstructionBitFields") + .field("prefix", &FlattenedOption(prefix)) + .field("fields", fields) + .finish() + } +} + +impl InstructionBitFields { + pub fn prefix(&self) -> Option<&InstructionBitFieldsPrefix> { + self.prefix.as_ref() + } + pub fn fields_inner(&self) -> &InstructionBitFieldsInner { + &self.fields_inner + } + pub fn fields(&self) -> &[InstructionBitField] { + self.fields_inner.fields() + } +} + +impl ParseElement for InstructionBitFields { + type AttributeNames = (); + const TAG_NAME: &'static str = "bit-fields"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + prefix: parser.parse()?, + fields_inner: parser.parse()?, + }) + } +} + +pub struct InstructionCode { + text_lines: TextLines, +} + +impl InstructionCode { + pub fn text_lines(&self) -> &TextLines { + &self.text_lines + } + pub fn lines(&self) -> &[TextLine] { + self.text_lines.lines() + } +} + +impl fmt::Debug for InstructionCode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { text_lines } = self; + text_lines.debug_fmt("InstructionCode", f) + } +} + +impl ParseElement for InstructionCode { + type AttributeNames = (); + const TAG_NAME: &'static str = "code"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + text_lines: parser.parse()?, + }) + } +} + +pub struct InstructionDescription { + text_lines: TextLines, +} + +impl InstructionDescription { + pub fn text_lines(&self) -> &TextLines { + &self.text_lines + } + pub fn lines(&self) -> &[TextLine] { + self.text_lines.lines() + } +} + +impl fmt::Debug for InstructionDescription { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { text_lines } = self; + text_lines.debug_fmt("InstructionDescription", f) + } +} + +impl ParseElement for InstructionDescription { + type AttributeNames = (); + const TAG_NAME: &'static str = "description"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + text_lines: parser.parse()?, + }) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)] +pub struct InsnSpRegsAlteredTitle; + +impl fmt::Debug for InsnSpRegsAlteredTitle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("InsnSpRegsAlteredTitle") + .field("text_line", self.text_line()) + .finish() + } +} + +impl InsnSpRegsAlteredTitle { + pub fn text_line(self) -> &'static TextLine { + static TEXT_LINE: OnceLock = OnceLock::new(); + TEXT_LINE.get_or_init( + #[cold] + || TextLine { + items: Box::new([TextLineItem::Bold(Box::new([TextLineItem::Text( + "Special Registers Altered:".into(), + )]))]), + }, + ) + } +} + +impl ParseElementEnum for InsnSpRegsAlteredTitle { + const NAME_FOR_ERRORS: &'static str = "InsnSpRegsAlteredTitle"; + const TAG_NAME: &'static str = "title"; + type ParseValue = TextLine; + type Value = &'static TextLine; + + fn values() -> &'static [Self] { + &[Self] + } + + fn value_eq(l: &Self::ParseValue, r: Self::Value) -> bool { + l == r + } + + fn value(self) -> Self::Value { + self.text_line() + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum InsnSpRegsAlteredSpecialText { + DependentOnTheSystemService, + None, + SeeAbove, + SeeTable5_1, +} + +impl fmt::Debug for InsnSpRegsAlteredSpecialText { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.value().fmt(f) + } +} + +macro_rules! inst_sp_regs_altered_special_text { + ($($Variant:ident => $value:literal,)*) => { + impl InsnSpRegsAlteredSpecialText { + const VALUES: &[Self] = &[ + $(Self::$Variant,)* + ]; + pub const fn text(self) -> &'static str { + match self { + $(Self::$Variant => $value,)* + } + } + } + }; +} + +inst_sp_regs_altered_special_text! { + DependentOnTheSystemService => "Dependent on the system service", + None => "None", + SeeAbove => "See above.", + SeeTable5_1 => "See Table 5.1", +} + +impl ParseElementEnum for InsnSpRegsAlteredSpecialText { + const NAME_FOR_ERRORS: &'static str = "InsnSpRegsAlteredSpecialText"; + const TAG_NAME: &'static str = "special-text"; + type ParseValue = Box; + type Value = &'static str; + fn values() -> &'static [Self] { + Self::VALUES + } + + fn value_eq(l: &Self::ParseValue, r: Self::Value) -> bool { + &**l == r + } + + fn value(self) -> Self::Value { + self.text() + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)] +pub struct InsnSpRegsAlteredTableHeaderReg; + +impl fmt::Debug for InsnSpRegsAlteredTableHeaderReg { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("InsnSpRegsAlteredTableHeaderReg") + .field("text_line", self.text_line()) + .finish() + } +} + +impl InsnSpRegsAlteredTableHeaderReg { + pub fn text_line(self) -> &'static TextLine { + static TEXT_LINE: OnceLock = OnceLock::new(); + TEXT_LINE.get_or_init( + #[cold] + || TextLine { + items: Box::new([TextLineItem::Bold(Box::new([TextLineItem::Italic( + Box::new([TextLineItem::Text("Register".into())]), + )]))]), + }, + ) + } +} + +impl ParseElementEnum for InsnSpRegsAlteredTableHeaderReg { + const NAME_FOR_ERRORS: &'static str = "InsnSpRegsAlteredTableHeaderReg"; + const TAG_NAME: &'static str = "table-header-register"; + type ParseValue = TextLine; + type Value = &'static TextLine; + + fn values() -> &'static [Self] { + &[Self] + } + + fn value_eq(l: &Self::ParseValue, r: Self::Value) -> bool { + l == r + } + + fn value(self) -> Self::Value { + self.text_line() + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)] +pub struct InsnSpRegsAlteredTableHeaderFields; + +impl fmt::Debug for InsnSpRegsAlteredTableHeaderFields { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let text_line = self.text_line(); + f.debug_struct("InsnSpRegsAlteredTableHeaderFields") + .field("text_line", text_line) + .finish() + } +} + +impl InsnSpRegsAlteredTableHeaderFields { + pub fn text_line(self) -> &'static TextLine { + static TEXT_LINE: OnceLock = OnceLock::new(); + TEXT_LINE.get_or_init( + #[cold] + || TextLine { + items: Box::new([TextLineItem::Bold(Box::new([TextLineItem::Italic( + Box::new([TextLineItem::Text("Field(s)".into())]), + )]))]), + }, + ) + } +} + +impl ParseElementEnum for InsnSpRegsAlteredTableHeaderFields { + const NAME_FOR_ERRORS: &'static str = "InsnSpRegsAlteredTableHeaderFields"; + const TAG_NAME: &'static str = "table-header-fields"; + type ParseValue = TextLine; + type Value = &'static TextLine; + + fn values() -> &'static [Self] { + &[Self] + } + + fn value_eq(l: &Self::ParseValue, r: Self::Value) -> bool { + l == r + } + + fn value(self) -> Self::Value { + self.text_line() + } +} + +#[derive(Debug)] +pub struct InsnSpRegsAlteredEntryReg { + text: Box, +} + +impl InsnSpRegsAlteredEntryReg { + pub fn text(&self) -> &str { + &self.text + } +} + +impl ParseElement for InsnSpRegsAlteredEntryReg { + type AttributeNames = (); + const TAG_NAME: &'static str = "register"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + text: parser.parse()?, + }) + } +} + +pub struct InsnSpRegsAlteredEntryFields { + text_lines: TextLines, +} + +impl fmt::Debug for InsnSpRegsAlteredEntryFields { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { text_lines } = self; + text_lines.debug_fmt("InsnSpRegsAlteredEntryFields", f) + } +} + +impl InsnSpRegsAlteredEntryFields { + pub fn text_lines(&self) -> &TextLines { + &self.text_lines + } + pub fn lines(&self) -> &[TextLine] { + self.text_lines.lines() + } +} + +impl ParseElement for InsnSpRegsAlteredEntryFields { + type AttributeNames = (); + const TAG_NAME: &'static str = "fields"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + text_lines: parser.parse()?, + }) + } +} + +pub struct InsnSpRegsAlteredEntryConds { + text_lines: TextLines, +} + +impl fmt::Debug for InsnSpRegsAlteredEntryConds { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { text_lines } = self; + text_lines.debug_fmt("InsnSpRegsAlteredEntryConds", f) + } +} + +impl InsnSpRegsAlteredEntryConds { + pub fn text_lines(&self) -> &TextLines { + &self.text_lines + } + pub fn lines(&self) -> &[TextLine] { + self.text_lines.lines() + } +} + +impl ParseElement for InsnSpRegsAlteredEntryConds { + type AttributeNames = (); + const TAG_NAME: &'static str = "conditions"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + text_lines: parser.parse()?, + }) + } +} + +pub struct InsnSpRegsAlteredEntry { + register: InsnSpRegsAlteredEntryReg, + fields: InsnSpRegsAlteredEntryFields, + conditions: InsnSpRegsAlteredEntryConds, +} + +impl fmt::Debug for InsnSpRegsAlteredEntry { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + register: InsnSpRegsAlteredEntryReg { text: register }, + fields, + conditions, + } = self; + f.debug_struct("InsnSpRegsAlteredEntry") + .field("register", register) + .field("fields", fields) + .field("conditions", conditions) + .finish() + } +} + +impl InsnSpRegsAlteredEntry { + pub fn register(&self) -> &InsnSpRegsAlteredEntryReg { + &self.register + } + pub fn fields(&self) -> &InsnSpRegsAlteredEntryFields { + &self.fields + } + pub fn conditions(&self) -> &InsnSpRegsAlteredEntryConds { + &self.conditions + } +} + +impl ParseElement for InsnSpRegsAlteredEntry { + type AttributeNames = (); + const TAG_NAME: &'static str = "entry"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + Ok(Self { + register: parser.parse()?, + fields: parser.parse()?, + conditions: parser.parse()?, + }) + } +} + +pub enum InsnSpRegsAltered { + Special { + title: InsnSpRegsAlteredTitle, + special_text: InsnSpRegsAlteredSpecialText, + }, + Table { + title: InsnSpRegsAlteredTitle, + table_header_reg: InsnSpRegsAlteredTableHeaderReg, + table_header_fields: InsnSpRegsAlteredTableHeaderFields, + entries: Box<[InsnSpRegsAlteredEntry]>, + }, +} + +impl fmt::Debug for InsnSpRegsAltered { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Special { + title, + special_text, + } => f + .debug_struct("Special") + .field("title", title) + .field("special_text", special_text) + .finish(), + Self::Table { + title, + table_header_reg, + table_header_fields, + entries, + } => f + .debug_struct("Table") + .field("title", title) + .field("table_header_reg", table_header_reg) + .field("table_header_fields", table_header_fields) + .field("entries", entries) + .finish(), + } + } +} + +impl InsnSpRegsAltered { + pub fn title(&self) -> InsnSpRegsAlteredTitle { + match *self { + Self::Special { title, .. } | Self::Table { title, .. } => title, + } + } + pub fn special_text(&self) -> Option { + match *self { + InsnSpRegsAltered::Special { special_text, .. } => Some(special_text), + InsnSpRegsAltered::Table { + title: _, + table_header_reg: _, + table_header_fields: _, + entries: _, + } => None, + } + } + pub fn table_header_reg(&self) -> Option { + match *self { + InsnSpRegsAltered::Special { + title: _, + special_text: _, + } => None, + InsnSpRegsAltered::Table { + table_header_reg, .. + } => Some(table_header_reg), + } + } + pub fn table_header_fields(&self) -> Option { + match *self { + InsnSpRegsAltered::Special { + title: _, + special_text: _, + } => None, + InsnSpRegsAltered::Table { + table_header_fields, + .. + } => Some(table_header_fields), + } + } + pub fn entries(&self) -> Option<&[InsnSpRegsAlteredEntry]> { + match self { + InsnSpRegsAltered::Special { + title: _, + special_text: _, + } => None, + InsnSpRegsAltered::Table { entries, .. } => Some(entries), + } + } +} + +impl ParseElement for InsnSpRegsAltered { + type AttributeNames = (); + const TAG_NAME: &'static str = "special-registers-altered"; + const ATTRIBUTE_NAMES: Self::AttributeNames = (); + + fn parse_element<'a>( + _element: Node<'a, 'static>, + _attributes: ::Attributes<'a>, + parser: &mut Parser<'a>, + ) -> Result> { + let title = parser.parse()?; + if let Some(special_text) = parser.parse()? { + Ok(Self::Special { + title, + special_text, + }) + } else { + Ok(Self::Table { + title, + table_header_reg: parser.parse()?, + table_header_fields: parser.parse()?, + entries: parser.parse()?, + }) + } + } +} + +#[derive(PartialEq, Eq)] +pub enum TextLineItem { + Text(Box), + Code(Box<[TextLineItem]>), + Bold(Box<[TextLineItem]>), + Italic(Box<[TextLineItem]>), + Subscript(Box<[TextLineItem]>), + Superscript(Box<[TextLineItem]>), +} + +impl fmt::Debug for TextLineItem { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Text(v) => v.fmt(f), + Self::Code(v) => { + f.write_str("Code")?; + v.fmt(f) + } + Self::Bold(v) => { + f.write_str("Bold")?; + v.fmt(f) + } + Self::Italic(v) => { + f.write_str("Italic")?; + v.fmt(f) + } + Self::Subscript(v) => { + f.write_str("Subscript")?; + v.fmt(f) + } + Self::Superscript(v) => { + f.write_str("Superscript")?; + v.fmt(f) + } + } + } +} + +trait TextLineItemMatch<'a>: Sized { + type Output; + fn text(self, node: Node<'a, 'static>) -> Self::Output; + fn code(self, node: Node<'a, 'static>) -> Self::Output; + fn bold(self, node: Node<'a, 'static>) -> Self::Output; + fn italic(self, node: Node<'a, 'static>) -> Self::Output; + fn subscript(self, node: Node<'a, 'static>) -> Self::Output; + fn superscript(self, node: Node<'a, 'static>) -> Self::Output; + fn match_node(self, node: Node<'a, 'static>) -> Option { + match node.node_type() { + NodeType::Element => { + if node.tag_name().namespace().is_none() { + Some(match node.tag_name().name() { + "code" => self.code(node), + "b" => self.bold(node), + "i" => self.italic(node), + "sub" => self.subscript(node), + "sup" => self.superscript(node), + _ => return None, + }) + } else { + None + } + } + NodeType::Root | NodeType::PI | NodeType::Comment => None, + NodeType::Text => Some(self.text(node)), + } + } +} + +impl Parse for TextLineItem { + fn peek<'a>(parser: &Parser<'a>) -> bool { + let mut parser = parser.clone(); + parser.skip_comments(); + struct PeekMatch; + impl<'a> TextLineItemMatch<'a> for PeekMatch { + type Output = (); + + fn text(self, _node: Node<'a, 'static>) -> Self::Output {} + fn code(self, _node: Node<'a, 'static>) -> Self::Output {} + fn bold(self, _node: Node<'a, 'static>) -> Self::Output {} + fn italic(self, _node: Node<'a, 'static>) -> Self::Output {} + fn subscript(self, _node: Node<'a, 'static>) -> Self::Output {} + fn superscript(self, _node: Node<'a, 'static>) -> Self::Output {} + } + parser + .cur_node + .is_some_and(|node| PeekMatch.match_node(node).is_some()) + } + + fn parse<'a>(parser: &mut Parser<'a>) -> Result> { + parser.skip_comments(); + struct ParseMatch<'b, 'a>(&'b mut Parser<'a>); + impl<'a> TextLineItemMatch<'a> for ParseMatch<'_, 'a> { + type Output = Result>; + + fn text(self, node: Node<'a, 'static>) -> Self::Output { + self.0.cur_node = node.next_sibling(); + self.0.skip_comments(); + Ok(TextLineItem::Text(node.text().unwrap_or("").into())) + } + + fn code(self, _node: Node<'a, 'static>) -> Self::Output { + let retval = self.0.parse_element("code", [], |_node, [], parser| { + Ok(TextLineItem::Code(TextLine::parse(parser)?.items)) + })?; + self.0.skip_comments(); + Ok(retval) + } + + fn bold(self, _node: Node<'a, 'static>) -> Self::Output { + let retval = self.0.parse_element("b", [], |_node, [], parser| { + Ok(TextLineItem::Bold(TextLine::parse(parser)?.items)) + })?; + self.0.skip_comments(); + Ok(retval) + } + + fn italic(self, _node: Node<'a, 'static>) -> Self::Output { + let retval = self.0.parse_element("i", [], |_node, [], parser| { + Ok(TextLineItem::Italic(TextLine::parse(parser)?.items)) + })?; + self.0.skip_comments(); + Ok(retval) + } + + fn subscript(self, _node: Node<'a, 'static>) -> Self::Output { + let retval = self.0.parse_element("sub", [], |_node, [], parser| { + Ok(TextLineItem::Subscript(TextLine::parse(parser)?.items)) + })?; + self.0.skip_comments(); + Ok(retval) + } + + fn superscript(self, _node: Node<'a, 'static>) -> Self::Output { + let retval = self.0.parse_element("sup", [], |_node, [], parser| { + Ok(TextLineItem::Superscript(TextLine::parse(parser)?.items)) + })?; + self.0.skip_comments(); + Ok(retval) + } + } + let Some(item) = parser + .cur_node + .and_then(|node| ParseMatch(parser).match_node(node)) + .transpose()? + else { + return Err(Error::BodyTooShort { + backtrace: Backtrace::capture(), + node: parser.parent, + }); + }; + Ok(item) + } +} + +#[derive(PartialEq, Eq)] +pub struct TextLine { + items: Box<[TextLineItem]>, +} + +impl fmt::Debug for TextLine { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { items } = self; + write!(f, "TextLine {items:?}") + } +} + +impl TextLine { + pub fn items(&self) -> &[TextLineItem] { + &self.items + } +} + +impl ParseTextLine for TextLine { + fn parse_with_options<'a>( + parser: &mut Parser<'a>, + remove_leading_nl: bool, + ) -> Result> { + parser.skip_comments(); + let mut items = Vec::new(); + if let Some(node) = parser.cur_node { + if node.is_text() { + let mut text = node.text().expect("known to be text"); + if remove_leading_nl { + text = text + .strip_prefix("\r\n") + .or_else(|| text.strip_prefix(&['\r', '\n'])) + .unwrap_or(text); + } + if !text.is_empty() { + items.push(TextLineItem::Text(text.into())); + } + parser.cur_node = node.next_sibling(); + parser.skip_comments(); + } + } + while TextLineItem::peek(parser) { + items.push(TextLineItem::parse(parser)?); + parser.skip_comments(); + } + Ok(Self { + items: items.into_boxed_slice(), + }) + } +} + +impl Parse for TextLine { + fn peek<'a>(_parser: &Parser<'a>) -> bool { + true + } + + fn parse<'a>(parser: &mut Parser<'a>) -> Result> { + Self::parse_with_options(parser, false) + } +} + +pub struct TextLines { + lines: Box<[Text]>, +} + +impl fmt::Debug for TextLines { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.debug_fmt("TextLines", f) + } +} + +impl TextLines { + pub fn lines(&self) -> &[Text] { + &self.lines + } + fn debug_fmt(&self, name: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result + where + Text: fmt::Debug, + { + let Self { lines } = self; + f.write_str(name)?; + fmt::Debug::fmt(lines, f) + } +} + +trait ParseTextLine: Sized { + fn parse_with_options<'a>( + parser: &mut Parser<'a>, + remove_leading_nl: bool, + ) -> Result>; +} + +impl Parse for TextLines { + fn peek<'a>(_parser: &Parser<'a>) -> bool { + true + } + + fn parse<'a>(parser: &mut Parser<'a>) -> Result> { + let mut lines = Vec::new(); + lines.push(Text::parse_with_options(parser, false)?); + while parser.peek_element("br").is_some() { + parser.parse_element("br", [], |_element, [], _parser| Ok(()))?; + lines.push(Text::parse_with_options(parser, true)?); + } + Ok(Self { + lines: lines.into_boxed_slice(), + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use base16ct::HexDisplay; + use hex_literal::hex; + use sha2::Digest; + use std::fmt::Write; + + #[test] + fn test_instructions_parses() -> Result<(), &'static str> { + #[derive(Clone, Copy)] + struct ExpectedInsnHash { + loc: &'static std::panic::Location<'static>, + expected_mnemonic: &'static str, + expected_hash: HexDisplay<'static>, + } + impl ExpectedInsnHash { + const fn empty() -> Self { + Self { + loc: std::panic::Location::caller(), + expected_mnemonic: "", + expected_hash: HexDisplay(&[]), + } + } + } + impl Default for ExpectedInsnHash { + fn default() -> Self { + Self::empty() + } + } + macro_rules! insn { + ($expected_mnemonic:literal, $expected_hash:literal) => { + ExpectedInsnHash { + loc: std::panic::Location::caller(), + expected_mnemonic: $expected_mnemonic, + expected_hash: HexDisplay({ + let v = &hex!($expected_hash); + assert!(v.len() >= 5); + v + }), + } + }; + () => { + ExpectedInsnHash::empty() + }; + } + static EXPECTED_HASHES: &[ExpectedInsnHash] = &[ + // Book 1 Branch + insn!("b", "05a4beffce"), + insn!("bc", "3487c6e08d"), + insn!("bclr", "e9e2e6ea43"), + insn!("bcctr", "0e55af907e"), + insn!("bctar", "ee91bf35e9"), + insn!("crand", "9bc66adf04"), + insn!("cror", "bb21baf028"), + insn!("crnand", "a6052066e3"), + insn!("crxor", "58a03c02a8"), + insn!("crnor", "d884219927"), + insn!("crandc", "c5d5aced85"), + insn!("creqv", "662590bf07"), + insn!("crorc", "14324bf398"), + insn!("mcrf", "f89641dc3c"), + insn!("sc", "685e6b3217"), + // Book 1 Fixed-Point + insn!("lbz", "c10dbcb96a"), + insn!("lbzx", "30c924d957"), + insn!("lbzu", "bcfdebd312"), + insn!("lbzux", "49f61eb762"), + insn!("lhz", "21a49348c8"), + insn!("lhzx", "49c4d116e8"), + insn!("lhzu", "48ee40ca93"), + insn!("lhzux", "d49f2ea84c"), + insn!("lha", "d05f6222e6"), + insn!("lhax", "486d712f77"), + insn!("lhau", "2ffda0f14b"), + insn!("lhaux", "a5492cf215"), + insn!("lwz", "8aec6eee9f"), + insn!("lwzx", "b024d64496"), + insn!("lwzu", "9a9b8bf164"), + insn!("lwzux", "7261cd36c9"), + insn!("lwa", "7ec6a105bc"), + insn!("lwax", "31d7a2bce9"), + insn!("lwaux", "bb62e25b3d"), + insn!("ld", "22cb1e7e22"), + insn!("ldx", "b4e5b63aba"), + insn!("ldu", "0697131c3c"), + insn!("ldux", "abd5b69d53"), + insn!("stb", "1da5aff76f"), + insn!("stbx", "59c2579957"), + insn!("stbu", "e69fd5da44"), + insn!("stbux", "d6078c481d"), + insn!("sth", "028879812a"), + insn!("sthx", "821d276106"), + insn!("sthu", "f467aa4926"), + insn!("sthux", "c662054a44"), + insn!("stw", "2fac68137e"), + insn!("stwx", "85ac5a7477"), + insn!("stwu", "105d53d7f9"), + insn!("stwux", "c600f25c69"), + insn!("std", "ed15d338ff"), + insn!("stdx", "c6de43c8c1"), + insn!("stdu", "b4dc89336a"), + insn!("stdux", "246d93cb11"), + insn!("lq", "d843b52568"), + insn!("stq", "0ed25a72b6"), + insn!("lhbrx", "5f2972ad56"), + insn!("sthbrx", "406a8cceb5"), + insn!("lwbrx", "24e2c59b12"), + insn!("stwbrx", "4bd14ba7ce"), + insn!("ldbrx", "f5b0703cad"), + insn!("stdbrx", "61ca2fce57"), + insn!("lmw", "9f0df5634f"), + insn!("stmw", "a907f7dfea"), + insn!("lswi", "00bcf243bc"), + insn!("lswx", "64fbec6450"), + insn!("stswi", "14684359ed"), + insn!("stswx", "2b8451ff5f"), + insn!("addi", "8b9c9ad540"), + insn!("addis", "8230aeb50d"), + insn!("addpcis", "ef2e4a9cc0"), + insn!("add", "1e188c4039"), + insn!("addic", "4ddd1cdbd9"), + insn!("addic.", "a545624df0"), + insn!("subf", "a1a4755310"), + insn!("subfic", "9038f7dbc6"), + insn!("addc", "6f58df2480"), + insn!("subfc", "3447db7b2e"), + insn!("adde", "32fed9e1ae"), + insn!("subfe", "a842d17ea0"), + insn!("addme", "a4b4644ecc"), + insn!("addze", "e8f0dddb47"), + insn!("subfme", "1d7b046ba1"), + insn!("subfze", "3e15f250f2"), + insn!("addex", "b8b3ff6ea9"), + insn!("neg", "7c824f5f8b"), + insn!("mulli", "170828b06d"), + insn!("mullw", "403186f819"), + insn!("mulhw", "dd1458b0d2"), + insn!("mulhwu", "ae9e5333ad"), + insn!("divw", "f588f40ac0"), + insn!("divwu", "80992471e6"), + insn!("divwe", "0b27818a42"), + insn!("divweu", "8a80f5b7ee"), + insn!("modsw", "feab80b9f4"), + insn!("moduw", "86bc11c0c8"), + insn!("darn", "4f9b829036"), + insn!("mulld", "336db32d97"), + insn!("mulhd", "9698fca65b"), + insn!("mulhdu", "c799c73ee2"), + insn!("maddhd", "367541e7e8"), + insn!("maddhdu", "46b5721042"), + insn!("maddld", "6baf51f955"), + insn!("divd", "4c75f5ae9c"), + insn!("divdu", "d8997f826d"), + insn!("divde", "980a477d2b"), + insn!("divdeu", "b8be82059f"), + insn!("modsd", "197c2d4fcc"), + insn!("modud", "8b0fd7d432"), + insn!("cmpi", "efc4f43fec"), + insn!("cmp", "b7b98db957"), + insn!("cmpli", "45f703e788"), + insn!("cmpl", "9d13a8c007"), + insn!("cmprb", "ad56077216"), + insn!("cmpeqb", "ba6836995f"), + insn!("twi", "0e0b75304a"), + insn!("tw", "2cf066dc36"), + insn!("tdi", "c9195f9974"), + insn!("td", "833cec86af"), + insn!("isel", "20df2457b6"), + insn!("andi.", "0c2e70e962"), + insn!("andis.", "63db245579"), + insn!("ori", "7aed28e6d5"), + insn!("oris", "fc3017ddea"), + insn!("xori", "f3caa0800e"), + insn!("xoris", "ce5ede0c81"), + insn!("and", "157dfa649f"), + insn!("xor", "4db3912d43"), + insn!("nand", "da7b75d4da"), + insn!("or", "46b6957818"), + insn!("orc", "6c9e0b5542"), + insn!("nor", "b2e58cb2d8"), + insn!("eqv", "c25614dede"), + insn!("andc", "1acc43e893"), + insn!("extsb", "5042f8137f"), + insn!("extsh", "30c6eb6ce9"), + insn!("cmpb", "937d763c11"), + insn!("cntlzw", "c01fe89ea2"), + insn!("cnttzw", "094b58148a"), + insn!("popcntb", "4191552bce"), + insn!("popcntw", "74b04ec702"), + insn!("prtyw", "a83bb73c14"), + insn!("extsw", "eb656a70e2"), + insn!("popcntd", "d64c1aec4f"), + insn!("prtyd", "f74ebccfdf"), + insn!("cntlzd", "e698cf73d3"), + insn!("cnttzd", "34588a7ce8"), + insn!("cntlzdm", "c0018d2d5b"), + insn!("cnttzdm", "09cc319caa"), + insn!("bpermd", "b2ee3589be"), + insn!("cfuged", "eda5da126c"), + insn!("pextd", "d42a1a777e"), + insn!("pdepd", "a7c9a9a7ba"), + insn!("rlwinm", "21a8c08fac"), + insn!("rlwnm", "f62cb77d8a"), + insn!("rlwimi", "9e3282b5ba"), + insn!("rldicl", "f22d9c12fd"), + insn!("rldicr", "d301784be5"), + insn!("rldic", "fd1ee6f0b3"), + insn!("rldcl", "364d285e33"), + insn!("rldcr", "dd5ead8ff4"), + insn!("rldimi", "605b8907f0"), + insn!("slw", "4093e47ea1"), + insn!("srw", "85a5fa5c36"), + insn!("srawi", "3ea22aabc1"), + insn!("sraw", "df33782ba4"), + insn!("sld", "e1da1538dd"), + insn!("sradi", "b8394e72aa"), + insn!("srd", "8ac3790243"), + insn!("srad", "e128b7e88d"), + insn!("extswsli", "1ca36ec0eb"), + insn!("cdtbcd", "13229b66a7"), + insn!("cbcdtd", "6fd233fe83"), + insn!("addg6s", "47caba2f9a"), + insn!("brh", "d91d4c6a00"), + insn!("brw", "45f937f47a"), + insn!("brd", "72a1255fd0"), + insn!("hashst", "a4c4c6f3d3"), + insn!("hashchk", "8c8f828bef"), + insn!("mfvsrd", "c7c71821c9"), + insn!("mfvsrld", "adb28df8a6"), + insn!("mfvsrwz", "37475acd00"), + insn!("mtvsrd", "9007aeb28e"), + insn!("mtvsrwa", "4b3e576548"), + insn!("mtvsrwz", "dbaa8e068f"), + insn!("mtvsrdd", "d1a47c1dc9"), + insn!("mtvsrws", "9b72624c7e"), + insn!("mtspr", "dbfae704f2"), + insn!("mfspr", "9a025cd08b"), + insn!("mcrxrx", "d7b6bb65d3"), + insn!("mtocrf", "80409a8951"), + insn!("mtcrf", "36af5db069"), + insn!("mfocrf", "8e3a6c2be2"), + insn!("mfcr", "e173a7be12"), + insn!("setb", "89fcdf508f"), + insn!("setbc", "3f2956521b"), + insn!("setbcr", "4795d46cae"), + insn!("setnbc", "e48c9e03d3"), + insn!("setnbcr", "de5002c7dc"), + insn!("pnop", "5478e5eb86"), + // Book 1 Floating-Point + insn!("lfs", "7331af45a6"), + insn!("lfsx", "d152f026f7"), + insn!("lfsu", "807d2999e1"), + insn!("lfsux", "b6b085af25"), + insn!("lfd", "b0a824af70"), + insn!("lfdx", "7576336546"), + insn!("lfdu", "4338f082d4"), + insn!("lfdux", "ac4e1c427b"), + insn!("lfiwax", "47fb8bd8b2"), + insn!("lfiwzx", "1971663968"), + insn!("stfs", "28e5ef4037"), + insn!("stfsx", "4e4d15543d"), + insn!("stfsu", "fdd1d5ef23"), + insn!("stfsux", "31f4aba0c0"), + insn!("stfd", "e699a03b30"), + insn!("stfdx", "b109ee3cee"), + insn!("stfdu", "e924c1b1ae"), + insn!("stfdux", "450d9d0db4"), + insn!("stfiwx", "34d205b697"), + insn!("lfdp", "52d86d153f"), + insn!("lfdpx", "65c7b797db"), + insn!("stfdp", "50d883692e"), + insn!("stfdpx", "9db9a09e7f"), + insn!("fmr", "1f28c1f4aa"), + insn!("fneg", "d2891238be"), + insn!("fabs", "43c0bd9da4"), + insn!("fnabs", "bd98ba993a"), + insn!("fcpsgn", "2f37d5875e"), + insn!("fmrgew", "8579f99f43"), + insn!("fmrgow", "9053658532"), + insn!("fadd", "68a59e6088"), + insn!("fsub", "809a73e375"), + insn!("fmul", "3df95ce519"), + insn!("fdiv", "8cd3692743"), + insn!("fsqrt", "4be9f5ada7"), + insn!("fre", "11d162eb90"), + insn!("frsqrte", "2b1a1f5d4f"), + insn!("ftdiv", "69b2527dad"), + insn!("ftsqrt", "ca9654eb62"), + insn!("fmadd", "435d24b73f"), + insn!("fmsub", "b3681250be"), + insn!("fnmadd", "24213814bf"), + insn!("fnmsub", "4ba4a4fe3f"), + insn!("frsp", "c43c7dba7b"), + insn!("fctid", "fe47191faf"), + insn!("fctidz", "a4e04126bf"), + insn!("fctidu", "4b92323ab3"), + insn!("fctiduz", "5dcbee6a1b"), + insn!("fctiw", "86a8710dd0"), + insn!("fctiwz", "100ccae019"), + insn!("fctiwu", "9782676ac5"), + insn!("fctiwuz", "450bc3a1c0"), + insn!("fcfid", "4d3a700267"), + insn!("fcfidu", "58fa858b7e"), + insn!("fcfids", "e9bbc818e9"), + insn!("fcfidus", "1f74eadc12"), + insn!("frin", "9133040b7d"), + insn!("friz", "f9a6e029ef"), + insn!("frip", "ce7a1a8dcf"), + insn!("frim", "c68b1668e1"), + insn!("fcmpu", "e9405bb558"), + insn!("fcmpo", "19295db629"), + insn!("fsel", "07883e27b0"), + insn!("mffs", "4217bef65e"), + insn!("mffsce", "8421c9f1d3"), + insn!("mffscdrn", "eef2922c65"), + insn!("mffscdrni", "d72b044dcd"), + insn!("mffscrn", "324b7604ce"), + insn!("mffscrni", "2d238783bd"), + insn!("mffsl", "46ffe2f0c2"), + insn!("mcrfs", "a8666f60f6"), + insn!("mtfsfi", "bf724ad53f"), + insn!("mtfsf", "c935bccde6"), + insn!("mtfsb0", "e73cbe7824"), + insn!("mtfsb1", "861e8142e8"), + // Book 1 DFP + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + // Book 1 Vector + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + // Book 1 VSX + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + insn!(), + // Book 2 Storage Control + insn!("icbi", "badb3cb30d"), + insn!("icbt", "1a9daa98c4"), + insn!("dcbz", "4b47e901a1"), + insn!("dcbst", "a9440cc687"), + insn!("dcbf", "d1ea08dec4"), + insn!("copy", "b82add82ad"), + insn!("paste.", "23ec6d671b"), + insn!("cpabort", "baee1d76a5"), + insn!("lwat", "d29905d0c2"), + insn!("ldat", "c2c7ac55ea"), + insn!("stwat", "6256c289e8"), + insn!("stdat", "17bc31faa3"), + insn!("isync", "ec505a42cd"), + insn!("lbarx", "df6b36f57f"), + insn!("lharx", "89b0f37c8d"), + insn!("lwarx", "83096e31ad"), + insn!("stbcx.", "2ad639d1c0"), + insn!("sthcx.", "73c922c413"), + insn!("stwcx.", "d84b8ea285"), + insn!("ldarx", "4819e395b5"), + insn!("stdcx.", "4a4c88a9d3"), + insn!("stqcx.", "a9927504d9"), + insn!("sync", "79391e4e86"), + insn!("wait", "2470682a01"), + // Book 2 Time Base + insn!("mftb", "2a2681e7fb"), + // Book 2 Branch History Rolling Buffer + insn!(), + insn!(), + // Book 3 Branch + insn!("sc", "3fb7fbeb7c"), + insn!("scv", "6c4d291ac5"), + insn!("rfscv", "442adf033a"), + insn!("rfid", "06191c8a3a"), + insn!("hrfid", "ed6f16b138"), + insn!("urfid", "1972956623"), + insn!("stop", "f3f09e9e80"), + // Book 3 Fixed-Point + insn!("lbzcix", "e226b904c7"), + insn!("lhzcix", "6fa23c2105"), + insn!("lwzcix", "59a852aba1"), + insn!("ldcix", "b8700488c0"), + insn!("stbcix", "1d0911b0f7"), + insn!("sthcix", "5f4eb5ca03"), + insn!("stwcix", "5d0a5b1c33"), + insn!("stdcix", "fb2bd7f0e3"), + insn!("hashstp", "89dee31207"), + insn!("hashchkp", "e3f6d311ee"), + insn!("mtspr", "b7fa29ee9d"), + insn!("mfspr", "399912a6d1"), + insn!("mtmsr", "cee82ab56c"), + insn!("mtmsrd", "b8871fa740"), + insn!("mfmsr", "b1d9d57b48"), + // Book 3 Storage Control + insn!("slbie", "da256f258d"), + insn!("slbieg", "15abe15598"), + insn!("slbia", "a79bfdc74f"), + insn!("slbiag", "67accf6b2c"), + insn!("slbmfev", "d06921f3c9"), + insn!("slbmfee", "2a201787e3"), + insn!("slbfee.", "473bf7ac9b"), + insn!("slbsync", "a3c4b68e12"), + insn!("tlbsync", "7b160e133f"), + // Book 3 Processor Control + insn!("msgsndu", "dad7de6355"), + insn!("msgclru", "c39ad4540b"), + insn!("msgsnd", "09224d9fe9"), + insn!("msgclr", "986ab4a2ea"), + insn!("msgsndp", "8eeecc7f0a"), + insn!("msgclrp", "62c5fe6115"), + insn!("msgsync", "e30602807b"), + ]; + let instructions = Instructions::get(); + let mut written = String::new(); + let mut insn_macros = String::new(); + for (insn_index, instruction) in instructions.instructions().iter().enumerate() { + written.clear(); + write!(written, "{instruction:#?}").expect("known to not error"); + println!("------\n{written}\n------"); + let hash = sha2::Sha256::digest(&written); + let hash = HexDisplay(&hash); + let ExpectedInsnHash { + loc, + expected_mnemonic, + expected_hash, + } = EXPECTED_HASHES.get(insn_index).copied().unwrap_or_default(); + println!("insn_index: {insn_index} hash: {hash:x}"); + let short_expected_hash = HexDisplay(&hash.0[0..5]); + println!("short hash: {short_expected_hash:x}"); + println!("expected_hash: {expected_hash:x}"); + println!("expected_mnemonic: {expected_mnemonic:?}"); + println!("loc: {loc}"); + let mnemonic_prefix = instruction.header()[0].mnemonics().lines()[0] + .split_whitespace() + .next(); + println!("mnemonic_prefix: {:?}", FlattenedOption(&mnemonic_prefix)); + if let Some(mnemonic_prefix) = mnemonic_prefix { + writeln!( + insn_macros, + " insn!({mnemonic_prefix:?}, \"{short_expected_hash:x}\")," + ) + .unwrap(); + } + if !expected_hash.0.is_empty() { + if mnemonic_prefix != Some(expected_mnemonic) + || &hash.0[..expected_hash.0.len()] != expected_hash.0 + { + return Err("mismatch"); + } + } + } + println!("------\n{insn_macros}------"); + assert_eq!(instructions.instructions().len(), EXPECTED_HASHES.len()); + Ok(()) + } +}